internal static ParseResult parseQuotes( Interp interp, string inString, int index, int length ) { TclObject obj; TclParse parse = null; TclToken token; CharPointer script; try { script = new CharPointer( inString ); script.index = index; parse = new TclParse( interp, script.array, length, null, 0 ); System.Diagnostics.Debug.WriteLine( "string is \"" + inString + "\"" ); System.Diagnostics.Debug.WriteLine( "script.array is \"" + new string( script.array ) + "\"" ); System.Diagnostics.Debug.WriteLine( "index is " + index ); System.Diagnostics.Debug.WriteLine( "length is " + length ); System.Diagnostics.Debug.WriteLine( "parse.endIndex is " + parse.endIndex ); parse.commandStart = script.index; token = parse.getToken( 0 ); token.type = Parser.TCL_TOKEN_WORD; token.script_array = script.array; token.script_index = script.index; parse.numTokens++; parse.numWords++; parse = Parser.parseTokens( script.array, script.index, Parser.TYPE_QUOTE, parse ); // Check for the error condition where the parse did not end on // a '"' char. Is this happened raise an error. if ( script.array[parse.termIndex] != '"' ) { throw new TclException( interp, "missing \"" ); } // if there was no error then parsing will continue after the // last char that was parsed from the string script.index = parse.termIndex + 1; // Finish filling in the token for the word and check for the // special case of a word consisting of a single range of // literal text. token = parse.getToken( 0 ); token.size = script.index - token.script_index; token.numComponents = parse.numTokens - 1; if ( ( token.numComponents == 1 ) && ( parse.getToken( 1 ).type == Parser.TCL_TOKEN_TEXT ) ) { token.type = Parser.TCL_TOKEN_SIMPLE_WORD; } parse.commandSize = script.index - parse.commandStart; if ( parse.numTokens > 0 ) { obj = Parser.evalTokens( interp, parse.tokenList, 1, parse.numTokens - 1 ); } else { throw new TclRuntimeError( "parseQuotes error: null obj result" ); } } finally { parse.release(); } return ( new ParseResult( obj, script.index ) ); }
internal static TclParse parseTokens( char[] script_array, int script_index, int mask, TclParse parse ) // Information about parse in progress. // Updated with additional tokens and // termination information. { char cur; int type, originalTokens, varToken; TclToken token; TclParse nested; BackSlashResult bs; #if DEBUG System.Diagnostics.Debug.WriteLine(); System.Diagnostics.Debug.WriteLine("Entered Parser.parseTokens()"); System.Diagnostics.Debug.Write("now to parse the string \""); for (int k = script_index; k < script_array.Length; k++) { System.Diagnostics.Debug.Write(script_array[k]); } System.Diagnostics.Debug.WriteLine("\""); #endif // Each iteration through the following loop adds one token of // type TCL_TOKEN_TEXT, TCL_TOKEN_BS, TCL_TOKEN_COMMAND, or // TCL_TOKEN_VARIABLE to parse. For TCL_TOKEN_VARIABLE additional, // tokens tokens are added for the parsed variable name. originalTokens = parse.numTokens; while ( true ) { token = parse.getToken( parse.numTokens ); token.script_array = script_array; token.script_index = script_index; token.numComponents = 0; #if DEBUG System.Diagnostics.Debug.WriteLine(); System.Diagnostics.Debug.WriteLine("Now on index " + script_index); char tmp_c = script_array[script_index]; System.Diagnostics.Debug.WriteLine("Char is '" + tmp_c + "'"); System.Diagnostics.Debug.WriteLine("Unicode id is " + ((int) tmp_c)); int tmp_i = ((int) ((tmp_c > TYPE_MAX)?TYPE_NORMAL:typeTable[tmp_c])); System.Diagnostics.Debug.WriteLine("Type is " + tmp_i); System.Diagnostics.Debug.WriteLine("Mask is " + mask); System.Diagnostics.Debug.WriteLine("(type & mask) is " + ((int) (tmp_i & mask))); System.Diagnostics.Debug.WriteLine("orig token.size is " + token.size); #endif cur = script_array[script_index]; type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ); if ( ( type & mask ) != 0 ) { System.Diagnostics.Debug.WriteLine( "mask break" ); break; } if ( ( type & TYPE_SUBS ) == 0 ) { // This is a simple range of characters. Scan to find the end // of the range. System.Diagnostics.Debug.WriteLine( "simple range" ); while ( true ) { cur = script_array[++script_index]; type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ); System.Diagnostics.Debug.WriteLine( "skipping '" + cur + "'" ); if ( ( type & ( mask | TYPE_SUBS ) ) != 0 ) { break; } } token.type = TCL_TOKEN_TEXT; token.size = script_index - token.script_index; parse.numTokens++; System.Diagnostics.Debug.WriteLine( "end simple range" ); System.Diagnostics.Debug.WriteLine( "token.size is " + token.size ); System.Diagnostics.Debug.WriteLine( "parse.numTokens is " + parse.numTokens ); } else if ( cur == '$' ) { // This is a variable reference. Call parseVarName to do // all the dirty work of parsing the name. System.Diagnostics.Debug.WriteLine( "dollar sign" ); varToken = parse.numTokens; parse = parseVarName( parse.interp, script_array, script_index, parse.endIndex - script_index, parse, true ); if ( parse.result != TCL.CompletionCode.OK ) { return parse; } script_index += parse.getToken( varToken ).size; } else if ( cur == '[' ) { // Command substitution. Call parseCommand recursively // (and repeatedly) to parse the nested command(s), then // throw away the parse information. System.Diagnostics.Debug.WriteLine( "command" ); script_index++; while ( true ) { nested = parseCommand( parse.interp, script_array, script_index, parse.endIndex - script_index, parse.fileName, parse.lineNum, true ); if ( nested.result != TCL.CompletionCode.OK ) { parse.termIndex = nested.termIndex; parse.incomplete = nested.incomplete; parse.result = nested.result; return parse; } script_index = nested.commandStart + nested.commandSize; if ( ( script_array[script_index - 1] == ']' ) && !nested.incomplete ) { break; } if ( script_index == parse.endIndex ) { if ( parse.interp != null ) { parse.interp.setResult( "missing close-bracket" ); } parse.termIndex = token.script_index; parse.incomplete = true; parse.result = TCL.CompletionCode.ERROR; return parse; } } token.type = TCL_TOKEN_COMMAND; token.size = script_index - token.script_index; parse.numTokens++; } else if ( cur == '\\' ) { // Backslash substitution. System.Diagnostics.Debug.WriteLine( "backslash" ); if ( script_array[script_index + 1] == '\n' ) { if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } // Note: backslash-newline is special in that it is // treated the same as a space character would be. This // means that it could terminate the token. if ( ( mask & TYPE_SPACE ) != 0 ) { break; } } token.type = TCL_TOKEN_BS; bs = backslash( script_array, script_index ); token.size = bs.nextIndex - script_index; parse.numTokens++; script_index += token.size; } else if ( cur == '\x0000' ) { // We encountered a null character. If it is the null // character at the end of the string, then return. // Otherwise generate a text token for the single // character. System.Diagnostics.Debug.WriteLine( "null char" ); System.Diagnostics.Debug.WriteLine( "script_index is " + script_index ); System.Diagnostics.Debug.WriteLine( "parse.endIndex is " + parse.endIndex ); if ( script_index == parse.endIndex ) { break; } token.type = TCL_TOKEN_TEXT; token.size = 1; parse.numTokens++; script_index++; } else { throw new TclRuntimeError( "parseTokens encountered unknown character" ); } } // end while (true) if ( parse.numTokens == originalTokens ) { // There was nothing in this range of text. Add an empty token // for the empty range, so that there is always at least one // token added. System.Diagnostics.Debug.WriteLine( "empty token" ); token.type = TCL_TOKEN_TEXT; token.size = 0; parse.numTokens++; } else { System.Diagnostics.Debug.WriteLine( "non empty token case" ); } parse.termIndex = script_index; parse.result = TCL.CompletionCode.OK; #if DEBUG System.Diagnostics.Debug.WriteLine(); System.Diagnostics.Debug.WriteLine("Leaving Parser.parseTokens()"); System.Diagnostics.Debug.WriteLine("after parse, parse.numTokens is " + parse.numTokens); System.Diagnostics.Debug.WriteLine("after parse, token.size is " + token.size); System.Diagnostics.Debug.WriteLine("after parse, token.hashCode() is " + token.GetHashCode()); //System.out.println( parse.toString() ); System.Diagnostics.Debug.Write("printing " + (parse.numTokens - originalTokens) + " token(s)"); for (int k = originalTokens; k < parse.numTokens; k++) { token = parse.getToken(k); System.Diagnostics.Debug.WriteLine(token); } System.Diagnostics.Debug.WriteLine("done printing tokens"); #endif return parse; }
public static TclParse parseCommand( Interp interp, char[] script_array, int script_index, int numBytes, string fileName, int lineNum, bool nested ) // True means that this is a nested command: // close bracket should be considered // a command terminator. If false, then close // bracket has no special meaning. { char cur; //the char we are currently parsing int type; // Result returned by charType(src.charAt()). TclToken token; // Pointer to token being filled in. int wordIndex; // Index of word token for current word. int level; // Nesting level of curly braces: gives // number of right braces we must find to // end word. TclParse parse; // Return value to fill in with information // about the parsed command. int terminators; // charType() bits that indicate the end // of a command. BackSlashResult bs; // Result of a call to backslash(...). int endIndex; // Index that points to the character after // the last character to be parsed. char savedChar; // To terminate the parsing correctly, the // character at endIndex is set to \0. This // stores the value to return when finished. int saved_script_index = script_index; //save the original index int script_length = script_array.Length - 1; if ( numBytes < 0 ) { numBytes = script_length - script_index; } endIndex = script_index + numBytes; if ( endIndex > script_length ) { endIndex = script_length; } savedChar = script_array[endIndex]; script_array[endIndex] = '\x0000'; parse = new TclParse( interp, script_array, endIndex, fileName, lineNum ); if ( nested ) { terminators = TYPE_COMMAND_END | TYPE_CLOSE_BRACK; } else { terminators = TYPE_COMMAND_END; } // Parse any leading space and comments before the first word of the // command. try { while ( true ) { cur = script_array[script_index]; while ( ( ( cur <= TYPE_MAX ) && ( typeTable[cur] == TYPE_SPACE ) ) || ( cur == '\n' ) ) { cur = script_array[++script_index]; } if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) ) { // Skip backslash-newline sequence: it should be treated // just like white space. if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } //this will add 2 to the offset and return to the top //of the while(true) loop which will get the next cur script_index += 2; continue; } // If we have found the start of a command goto the word parsing loop if ( cur != '#' ) { break; } // Record the index where the comment starts if ( parse.commentStart < 0 ) { parse.commentStart = script_index; } while ( true ) { cur = script_array[script_index]; if ( script_index == parse.endIndex ) { if ( nested ) parse.incomplete = true; parse.commentSize = script_index - parse.commentStart; break; } else if ( cur == '\\' ) { if ( ( script_array[script_index + 1] == '\n' ) && ( ( script_index + 2 ) == parse.endIndex ) ) { parse.incomplete = true; } bs = backslash( script_array, script_index ); script_index = bs.nextIndex; } else if ( cur == '\n' ) { script_index++; parse.commentSize = script_index - parse.commentStart; break; } else { script_index++; } } } // The following loop parses the words of the command, one word // in each iteration through the loop. parse.commandStart = script_index; while ( true ) { bool expandWord = false; // Create the token for the word. wordIndex = parse.numTokens; token = parse.getToken( wordIndex ); token.type = TCL_TOKEN_WORD; // Skip white space before the word. Also skip a backslash-newline // sequence: it should be treated just like white space. while ( true ) { cur = script_array[script_index]; type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ); if ( type == TYPE_SPACE ) { script_index++; continue; } else if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) ) { if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } bs = backslash( script_array, script_index ); script_index = bs.nextIndex; continue; } break; } if ( ( type & terminators ) != 0 ) { script_index++; break; } if ( script_index == parse.endIndex ) { if ( nested && savedChar != ']' ) { parse.incomplete = true; throw new TclException( interp, "missing close-bracket" ); } break; } token.script_array = script_array; token.script_index = script_index; parse.numTokens++; parse.numWords++; // At this point the word can have one of four forms: something // enclosed in quotes, something enclosed in braces, and // expanding word, or an unquoted word (anything else). parseWord: cur = script_array[script_index]; if ( cur == '"' ) { script_index++; parse = parseTokens( script_array, script_index, TYPE_QUOTE, parse ); if ( parse.result != TCL.CompletionCode.OK ) { throw new TclException( parse.result ); } if ( parse.inString[parse.termIndex] != '"' ) { parse.termIndex = script_index - 1; parse.incomplete = true; throw new TclException( parse.interp, "missing \"" ); } script_index = parse.termIndex + 1; } else if ( cur == '{' ) { /* * Hack for {*} * Check whether the braces contained the word expansion prefix. */ if ( script_index < script_array.Length - 3 // only if there is room && script_array[script_index + 1] == '*' && script_array[script_index + 2] == '}' // and it is {*} && typeTable[script_array[script_index + 1]] != TYPE_SPACE /* Non-whitespace follows */ && !expandWord // only one per token ) { script_index += 3; // Skip expandWord = true; //parse.numTokens--; goto parseWord; } // Find the matching right brace that terminates the word, // then generate a single token for everything between the // braces. script_index++; token = parse.getToken( parse.numTokens ); token.type = TCL_TOKEN_TEXT; token.script_array = script_array; token.script_index = script_index; token.numComponents = 0; level = 1; while ( true ) { cur = script_array[script_index]; // get the current char in the array and lookup its type while ( ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ) == TYPE_NORMAL ) { cur = script_array[++script_index]; } if ( script_array[script_index] == '}' ) { level--; if ( level == 0 ) { break; } script_index++; } else if ( script_array[script_index] == '{' ) { level++; script_index++; } else if ( script_array[script_index] == '\\' ) { bs = backslash( script_array, script_index ); if ( script_array[script_index + 1] == '\n' ) { // A backslash-newline sequence requires special // treatment: it must be collapsed, even inside // braces, so we have to split the word into // multiple tokens so that the backslash-newline // can be represented explicitly. if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } token.size = script_index - token.script_index; if ( token.size != 0 ) { parse.numTokens++; } token = parse.getToken( parse.numTokens ); token.type = TCL_TOKEN_BS; token.script_array = script_array; token.script_index = script_index; token.size = bs.nextIndex - script_index; token.numComponents = 0; parse.numTokens++; script_index = bs.nextIndex; token = parse.getToken( parse.numTokens ); token.type = TCL_TOKEN_TEXT; token.script_array = script_array; token.script_index = script_index; token.numComponents = 0; } else { script_index = bs.nextIndex; } } else if ( script_index == parse.endIndex ) { parse.termIndex = parse.getToken( wordIndex ).script_index; parse.incomplete = true; throw new TclException( interp, "missing close-brace" ); } else { script_index++; } } if ( ( script_index != token.script_index ) || ( parse.numTokens == ( wordIndex + 1 ) ) ) { token.size = script_index - token.script_index; parse.numTokens++; } script_index++; } else { // This is an unquoted word. Call parseTokens and let it do // all of the work. parse = parseTokens( script_array, script_index, TYPE_SPACE | terminators, parse ); if ( parse.result != TCL.CompletionCode.OK ) { throw new TclException( parse.result ); } script_index = parse.termIndex; } // Finish filling in the token for the word and check for the // special case of a word consisting of a single range of // literal text. token = parse.getToken( wordIndex ); token.size = script_index - token.script_index; token.numComponents = parse.numTokens - ( wordIndex + 1 ); if ( expandWord ) { int i = 1; bool isLiteral = true; /* * When a command includes a word that is an expanded literal; for * example, {*}{1 2 3}, the parser performs that expansion * immediately, generating several TCL_TOKEN_SIMPLE_WORDs instead * of a single TCL_TOKEN_EXPAND_WORD that the Tcl_ParseCommand() * caller might have to expand. This notably makes it simpler for * those callers that wish to track line endings, such as those * that implement key parts of TIP 280. * * First check whether the thing to be expanded is a literal, * in the sense of being composed entirely of TCL_TOKEN_TEXT * tokens. */ for ( i = 1; i <= token.numComponents; i++ ) { if ( parse.getToken( wordIndex + i ).type != TCL_TOKEN_TEXT )//if (tokenPtr[i].type != TCL_TOKEN_TEXT) { isLiteral = false; break; } } if ( isLiteral ) { int elemCount = 0; FindElemResult code = null; bool nakedbs = false; int nextElem, listEnd; int elemStart = 0; /* * The word to be expanded is a literal, so determine the * boundaries of the literal string to be treated as a list * and expanded. That literal string starts at * tokenPtr[1].start, and includes all bytes up to, but not * including (tokenPtr[token.numComponents].start + * tokenPtr[token.numComponents].size) */ //listEnd = ( tokenPtr[tokenPtr->numComponents].start + // tokenPtr[tokenPtr->numComponents].size ); listEnd = ( parse.getToken( wordIndex + token.numComponents ).script_index + parse.getToken( wordIndex + token.numComponents ).size ) - 1; nextElem = parse.getToken( wordIndex + token.numComponents ).script_index;//nextElem = tokenPtr[1].start; /* * Step through the literal string, parsing and counting list * elements. */ string string_array = new string( token.script_array ); while ( nextElem < listEnd ) { int size; code = Util.findElement( null, string_array, nextElem, listEnd ); //code = TclFindElement(NULL, nextElem, listEnd - nextElem, // &elemStart, &nextElem, &size, &brace); if ( code == null ) { break; } if ( !code.brace ) { size = code.size; elemStart = nextElem; int s; for ( s = elemStart; size > 0; s++, size-- ) { if ( token.script_array[s] == '\\' ) { nakedbs = true; break; } } } elemCount++; nextElem = code.elemEnd; } if ( ( code == null ) || nakedbs ) { /* * Some list element could not be parsed, or contained * naked backslashes. This means the literal string was * not in fact a valid nor canonical list. Defer the * handling of this to compile/eval time, where code is * already in place to report the "attempt to expand a * non-list" error or expand lists that require * substitution. */ token.type = TCL_TOKEN_EXPAND_WORD; } else if ( elemCount == 0 ) { /* * We are expanding a literal empty list. This means that * the expanding word completely disappears, leaving no * word generated this pass through the loop. Adjust * accounting appropriately. */ parse.numWords--; parse.numTokens = wordIndex; } else { /* * Recalculate the number of Tcl_Tokens needed to store * tokens representing the expanded list. */ int growthNeeded = wordIndex + 2 * elemCount - parse.numTokens; parse.numWords += elemCount - 1; if ( growthNeeded > 0 ) { parse.expandTokenArray( growthNeeded );// TclGrowParseTokenArray( parse, growthNeeded ); token = parse.getToken( wordIndex );//&parsePtr->tokenPtr[wordIndex]; } parse.numTokens = wordIndex + 2 * elemCount; /* * Generate a TCL_TOKEN_SIMPLE_WORD token sequence for * each element of the literal list we are expanding in * place. Take care with the start and size fields of each * token so they point to the right literal characters in * the original script to represent the right expanded * word value. */ nextElem = parse.getToken( wordIndex ).script_index;//tokenPtr[1].start; while ( token.script_array[nextElem] == ' ' )//isspace( UCHAR( *nextElem ) ) ) { nextElem++; } while ( nextElem < listEnd ) { token.type = TCL_TOKEN_SIMPLE_WORD; token.numComponents = 1; token.script_index = nextElem; token = parse.getToken( ++wordIndex );// tokenPtr++; token.type = TCL_TOKEN_TEXT; token.numComponents = 0; code = Util.findElement( null, string_array, nextElem, listEnd ); //TclFindElement(NULL, nextElem, listEnd - nextElem, // &(tokenPtr->start), &nextElem, // &(tokenPtr->size), NULL); token.script_index = nextElem + ( code.brace ? 1 : 0 ); token.size = code.size; nextElem = code.elemEnd; if ( token.script_index + token.size == listEnd ) { parse.getToken( wordIndex - 1 ).size = listEnd - parse.getToken( wordIndex - 1 ).script_index;//tokenPtr[-1].size = listEnd - tokenPtr[-1].start; } else { //tokenPtr[-1].size = tokenPtr->start // + tokenPtr->size - tokenPtr[-1].start; parse.getToken( wordIndex - 1 ).size = token.script_index + token.size - parse.getToken( wordIndex - 1 ).script_index; if ( script_index + token.size < token.script_array.Length && ( token.script_array[script_index + token.size] == ' ' ) ) parse.getToken( wordIndex - 1 ).size += 1; // tokenPtr[-1].size += ( isspace( UCHAR( //tokenPtr->start[tokenPtr->size] ) ) == 0 ); } token = parse.getToken( ++wordIndex );// tokenPtr++; } } } else { /* * The word to be expanded is not a literal, so defer * expansion to compile/eval time by marking with a * TCL_TOKEN_EXPAND_WORD token. */ token.type = TCL_TOKEN_EXPAND_WORD; } } else if ( ( token.numComponents == 1 ) && ( parse.getToken( wordIndex + 1 ).type == TCL_TOKEN_TEXT ) ) { token.type = TCL_TOKEN_SIMPLE_WORD; } // Do two additional checks: (a) make sure we're really at the // end of a word (there might have been garbage left after a // quoted or braced word), and (b) check for the end of the // command. cur = script_array[script_index]; type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ); if ( type == TYPE_SPACE ) { script_index++; continue; } else { // Backslash-newline (and any following white space) must be // treated as if it were a space character. if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) ) { if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } bs = backslash( script_array, script_index ); script_index = bs.nextIndex; continue; } } if ( ( type & terminators ) != 0 ) { script_index++; break; } if ( script_index == parse.endIndex ) { if ( nested && savedChar != ']' ) { parse.incomplete = true; throw new TclException( interp, "missing close-bracket" ); } break; } parse.termIndex = script_index; if ( script_array[script_index - 1] == '"' ) { throw new TclException( interp, "extra characters after close-quote" ); } else { throw new TclException( interp, "extra characters after close-brace" ); } } } catch ( TclException e ) { script_array[endIndex] = savedChar; if ( parse.commandStart < 0 ) { parse.commandStart = saved_script_index; } parse.commandSize = parse.termIndex - parse.commandStart; parse.result = TCL.CompletionCode.ERROR; return parse; } script_array[endIndex] = savedChar; parse.commandSize = script_index - parse.commandStart; parse.result = TCL.CompletionCode.OK; return parse; }
public static TclParse parseVarName( Interp interp, char[] script_array, int script_index, int numBytes, TclParse parse, bool append ) // Non-zero means append tokens to existing // information in parse; zero means ignore // existing tokens in parse and reinitialize // it. { char cur; TclToken token, startToken; int endIndex, varIndex; #if DEBUG System.Diagnostics.Debug.WriteLine(); System.Diagnostics.Debug.WriteLine("Entered parseVarName()"); System.Diagnostics.Debug.Write("now to parse var off the string \""); for (int k = script_index; k < script_array.Length; k++) { System.Diagnostics.Debug.Write(script_array[k]); } System.Diagnostics.Debug.WriteLine("\""); #endif if ( numBytes >= 0 ) { endIndex = script_index + numBytes; } else { endIndex = script_array.Length - 1; } if ( !append ) { parse = new TclParse( interp, script_array, endIndex, null, -1 ); } // Generate one token for the variable, an additional token for the // name, plus any number of additional tokens for the index, if // there is one. token = parse.getToken( parse.numTokens ); token.type = TCL_TOKEN_VARIABLE; token.script_array = script_array; token.script_index = script_index; varIndex = parse.numTokens; parse.numTokens++; script_index++; if ( script_index >= endIndex ) { // The dollar sign isn't followed by a variable name. // replace the TCL_TOKEN_VARIABLE token with a // TCL_TOKEN_TEXT token for the dollar sign. token.type = TCL_TOKEN_TEXT; token.size = 1; token.numComponents = 0; parse.result = TCL.CompletionCode.OK; return parse; } startToken = token; token = parse.getToken( parse.numTokens ); // The name of the variable can have three forms: // 1. The $ sign is followed by an open curly brace. Then // the variable name is everything up to the next close // curly brace, and the variable is a scalar variable. // 2. The $ sign is not followed by an open curly brace. Then // the variable name is everything up to the next // character that isn't a letter, digit, or underscore. // :: sequences are also considered part of the variable // name, in order to support namespaces. If the following // character is an open parenthesis, then the information // between parentheses is the array element name. // 3. The $ sign is followed by something that isn't a letter, // digit, or underscore: in this case, there is no variable // name and the token is just "$". if ( script_array[script_index] == '{' ) { System.Diagnostics.Debug.WriteLine( "parsing curley var name" ); script_index++; token.type = TCL_TOKEN_TEXT; token.script_array = script_array; token.script_index = script_index; token.numComponents = 0; while ( true ) { if ( script_index == endIndex ) { if ( interp != null ) { interp.setResult( "missing close-brace for variable name" ); } parse.termIndex = token.script_index - 1; parse.incomplete = true; parse.result = TCL.CompletionCode.ERROR; return parse; } if ( script_array[script_index] == '}' ) { break; } script_index++; } token.size = script_index - token.script_index; startToken.size = script_index - startToken.script_index; parse.numTokens++; script_index++; } else { System.Diagnostics.Debug.WriteLine( "parsing non curley var name" ); token.type = TCL_TOKEN_TEXT; token.script_array = script_array; token.script_index = script_index; token.numComponents = 0; while ( script_index != endIndex ) { cur = script_array[script_index]; if ( ( System.Char.IsLetterOrDigit( cur ) ) || ( cur == '_' ) ) { script_index++; continue; } if ( ( cur == ':' ) && ( ( ( script_index + 1 ) != endIndex ) && ( script_array[script_index + 1] == ':' ) ) ) { script_index += 2; while ( ( script_index != endIndex ) && ( script_array[script_index] == ':' ) ) { script_index++; } continue; } break; } token.size = script_index - token.script_index; if ( token.size == 0 ) { // The dollar sign isn't followed by a variable name. // replace the TCL_TOKEN_VARIABLE token with a // TCL_TOKEN_TEXT token for the dollar sign. System.Diagnostics.Debug.WriteLine( "single $ with no var name found" ); startToken.type = TCL_TOKEN_TEXT; startToken.size = 1; startToken.numComponents = 0; parse.result = TCL.CompletionCode.OK; return parse; } parse.numTokens++; if ( ( script_index != endIndex ) && ( script_array[script_index] == '(' ) ) { // This is a reference to an array element. Call // parseTokens recursively to parse the element name, // since it could contain any number of substitutions. System.Diagnostics.Debug.WriteLine( "parsing array element" ); script_index++; parse = parseTokens( script_array, script_index, TYPE_CLOSE_PAREN, parse ); if ( parse.result != TCL.CompletionCode.OK ) { return parse; } if ( ( parse.termIndex == endIndex ) || ( parse.inString[parse.termIndex] != ')' ) ) { if ( interp != null ) { interp.setResult( "missing )" ); } parse.termIndex = script_index - 1; parse.incomplete = true; parse.result = TCL.CompletionCode.ERROR; return parse; } script_index = parse.termIndex + 1; } } #if DEBUG System.Diagnostics.Debug.WriteLine("default end parse case"); System.Diagnostics.Debug.Write("var token is \""); for (int k = startToken.script_index; k < script_index; k++) { System.Diagnostics.Debug.Write(script_array[k]); } System.Diagnostics.Debug.WriteLine("\""); #endif startToken.size = script_index - startToken.script_index; startToken.numComponents = parse.numTokens - ( varIndex + 1 ); parse.result = TCL.CompletionCode.OK; return parse; }
public static TclParse parseCommand( Interp interp, char[] script_array, int script_index, int numBytes, string fileName, int lineNum, bool nested ) // True means that this is a nested command: // close bracket should be considered // a command terminator. If false, then close // bracket has no special meaning. { char cur; //the char we are currently parsing int type; // Result returned by charType(src.charAt()). TclToken token; // Pointer to token being filled in. int wordIndex; // Index of word token for current word. int level; // Nesting level of curly braces: gives // number of right braces we must find to // end word. TclParse parse; // Return value to fill in with information // about the parsed command. int terminators; // charType() bits that indicate the end // of a command. BackSlashResult bs; // Result of a call to backslash(...). int endIndex; // Index that points to the character after // the last character to be parsed. char savedChar; // To terminate the parsing correctly, the // character at endIndex is set to \0. This // stores the value to return when finished. int saved_script_index = script_index; //save the original index int script_length = script_array.Length - 1; if ( numBytes < 0 ) { numBytes = script_length - script_index; } endIndex = script_index + numBytes; if ( endIndex > script_length ) { endIndex = script_length; } savedChar = script_array[endIndex]; script_array[endIndex] = '\x0000'; parse = new TclParse( interp, script_array, endIndex, fileName, lineNum ); if ( nested ) { terminators = TYPE_COMMAND_END | TYPE_CLOSE_BRACK; } else { terminators = TYPE_COMMAND_END; } // Parse any leading space and comments before the first word of the // command. try { while ( true ) { cur = script_array[script_index]; while ( ( ( cur <= TYPE_MAX ) && ( typeTable[cur] == TYPE_SPACE ) ) || ( cur == '\n' ) ) { cur = script_array[++script_index]; } if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) ) { // Skip backslash-newline sequence: it should be treated // just like white space. if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } //this will add 2 to the offset and return to the top //of the while(true) loop which will get the next cur script_index += 2; continue; } // If we have found the start of a command goto the word parsing loop if ( cur != '#' ) { break; } // Record the index where the comment starts if ( parse.commentStart < 0 ) { parse.commentStart = script_index; } while ( true ) { cur = script_array[script_index]; if ( script_index == parse.endIndex ) { if ( nested ) parse.incomplete = true; parse.commentSize = script_index - parse.commentStart; break; } else if ( cur == '\\' ) { if ( ( script_array[script_index + 1] == '\n' ) && ( ( script_index + 2 ) == parse.endIndex ) ) { parse.incomplete = true; } bs = backslash( script_array, script_index ); script_index = bs.nextIndex; } else if ( cur == '\n' ) { script_index++; parse.commentSize = script_index - parse.commentStart; break; } else { script_index++; } } } // The following loop parses the words of the command, one word // in each iteration through the loop. parse.commandStart = script_index; while ( true ) { // Create the token for the word. wordIndex = parse.numTokens; token = parse.getToken( wordIndex ); token.type = TCL_TOKEN_WORD; // Skip white space before the word. Also skip a backslash-newline // sequence: it should be treated just like white space. while ( true ) { cur = script_array[script_index]; type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ); if ( type == TYPE_SPACE ) { script_index++; continue; } else if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) ) { if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } bs = backslash( script_array, script_index ); script_index = bs.nextIndex; continue; } break; } if ( ( type & terminators ) != 0 ) { script_index++; break; } if ( script_index == parse.endIndex ) { if ( nested && savedChar != ']' ) { parse.incomplete = true; throw new TclException( interp, "missing close-bracket" ); } break; } token.script_array = script_array; token.script_index = script_index; parse.numTokens++; parse.numWords++; // At this point the word can have one of three forms: something // enclosed in quotes, something enclosed in braces, or an // unquoted word (anything else). cur = script_array[script_index]; if ( cur == '"' ) { script_index++; parse = parseTokens( script_array, script_index, TYPE_QUOTE, parse ); if ( parse.result != TCL.CompletionCode.OK ) { throw new TclException( parse.result ); } if ( parse.inString[parse.termIndex] != '"' ) { parse.termIndex = script_index - 1; parse.incomplete = true; throw new TclException( parse.interp, "missing \"" ); } script_index = parse.termIndex + 1; } else if ( cur == '{' ) { // Find the matching right brace that terminates the word, // then generate a single token for everything between the // braces. script_index++; token = parse.getToken( parse.numTokens ); token.type = TCL_TOKEN_TEXT; token.script_array = script_array; token.script_index = script_index; token.numComponents = 0; level = 1; while ( true ) { cur = script_array[script_index]; // get the current char in the array and lookup its type while ( ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ) == TYPE_NORMAL ) { cur = script_array[++script_index]; } if ( script_array[script_index] == '}' ) { level--; if ( level == 0 ) { break; } script_index++; } else if ( script_array[script_index] == '{' ) { level++; script_index++; } else if ( script_array[script_index] == '\\' ) { bs = backslash( script_array, script_index ); if ( script_array[script_index + 1] == '\n' ) { // A backslash-newline sequence requires special // treatment: it must be collapsed, even inside // braces, so we have to split the word into // multiple tokens so that the backslash-newline // can be represented explicitly. if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } token.size = script_index - token.script_index; if ( token.size != 0 ) { parse.numTokens++; } token = parse.getToken( parse.numTokens ); token.type = TCL_TOKEN_BS; token.script_array = script_array; token.script_index = script_index; token.size = bs.nextIndex - script_index; token.numComponents = 0; parse.numTokens++; script_index = bs.nextIndex; token = parse.getToken( parse.numTokens ); token.type = TCL_TOKEN_TEXT; token.script_array = script_array; token.script_index = script_index; token.numComponents = 0; } else { script_index = bs.nextIndex; } } else if ( script_index == parse.endIndex ) { parse.termIndex = parse.getToken( wordIndex ).script_index; parse.incomplete = true; throw new TclException( interp, "missing close-brace" ); } else { script_index++; } } if ( ( script_index != token.script_index ) || ( parse.numTokens == ( wordIndex + 1 ) ) ) { token.size = script_index - token.script_index; parse.numTokens++; } script_index++; } else { // This is an unquoted word. Call parseTokens and let it do // all of the work. parse = parseTokens( script_array, script_index, TYPE_SPACE | terminators, parse ); if ( parse.result != TCL.CompletionCode.OK ) { throw new TclException( parse.result ); } script_index = parse.termIndex; } // Finish filling in the token for the word and check for the // special case of a word consisting of a single range of // literal text. token = parse.getToken( wordIndex ); token.size = script_index - token.script_index; token.numComponents = parse.numTokens - ( wordIndex + 1 ); if ( ( token.numComponents == 1 ) && ( parse.getToken( wordIndex + 1 ).type == TCL_TOKEN_TEXT ) ) { token.type = TCL_TOKEN_SIMPLE_WORD; } // Do two additional checks: (a) make sure we're really at the // end of a word (there might have been garbage left after a // quoted or braced word), and (b) check for the end of the // command. cur = script_array[script_index]; type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ); if ( type == TYPE_SPACE ) { script_index++; continue; } else { // Backslash-newline (and any following white space) must be // treated as if it were a space character. if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) ) { if ( ( script_index + 2 ) == parse.endIndex ) { parse.incomplete = true; } bs = backslash( script_array, script_index ); script_index = bs.nextIndex; continue; } } if ( ( type & terminators ) != 0 ) { script_index++; break; } if ( script_index == parse.endIndex ) { if ( nested && savedChar != ']' ) { parse.incomplete = true; throw new TclException( interp, "missing close-bracket" ); } break; } parse.termIndex = script_index; if ( script_array[script_index - 1] == '"' ) { throw new TclException( interp, "extra characters after close-quote" ); } else { throw new TclException( interp, "extra characters after close-brace" ); } } } catch ( TclException e ) { script_array[endIndex] = savedChar; if ( parse.commandStart < 0 ) { parse.commandStart = saved_script_index; } parse.commandSize = parse.termIndex - parse.commandStart; parse.result = TCL.CompletionCode.ERROR; return parse; } script_array[endIndex] = savedChar; parse.commandSize = script_index - parse.commandStart; parse.result = TCL.CompletionCode.OK; return parse; }
internal static ParseResult parseQuotes(Interp interp, string inString, int index, int length) { TclObject obj; TclParse parse = null; TclToken token; CharPointer script; try { script = new CharPointer(inString); script.index = index; parse = new TclParse(interp, script.array, length, null, 0); System.Diagnostics.Debug.WriteLine("string is \"" + inString + "\""); System.Diagnostics.Debug.WriteLine("script.array is \"" + new string( script.array ) + "\""); System.Diagnostics.Debug.WriteLine("index is " + index); System.Diagnostics.Debug.WriteLine("length is " + length); System.Diagnostics.Debug.WriteLine("parse.endIndex is " + parse.endIndex); parse.commandStart = script.index; token = parse.getToken(0); token.type = Parser.TCL_TOKEN_WORD; token.script_array = script.array; token.script_index = script.index; parse.numTokens++; parse.numWords++; parse = Parser.parseTokens(script.array, script.index, Parser.TYPE_QUOTE, parse); // Check for the error condition where the parse did not end on // a '"' char. Is this happened raise an error. if (script.array[parse.termIndex] != '"') { throw new TclException(interp, "missing \""); } // if there was no error then parsing will continue after the // last char that was parsed from the string script.index = parse.termIndex + 1; // Finish filling in the token for the word and check for the // special case of a word consisting of a single range of // literal text. token = parse.getToken(0); token.size = script.index - token.script_index; token.numComponents = parse.numTokens - 1; if ((token.numComponents == 1) && (parse.getToken(1).type == Parser.TCL_TOKEN_TEXT)) { token.type = Parser.TCL_TOKEN_SIMPLE_WORD; } parse.commandSize = script.index - parse.commandStart; if (parse.numTokens > 0) { obj = Parser.evalTokens(interp, parse.tokenList, 1, parse.numTokens - 1); } else { throw new TclRuntimeError("parseQuotes error: null obj result"); } } finally { parse.release(); } return(new ParseResult(obj, script.index)); }