wasCSharpSQLite – Rev
?pathlinks?
#undef DEBUG
/*
* Parser.java --
*
* This class contains methods that parse Tcl scripts. They
* do so in a general-purpose fashion that can be used for many
* different purposes, including compilation, direct execution,
* code analysis, etc. This class also includes a few additional
* procedures such as evalObjv, eval, and eval2, which allow
* scripts to be evaluated directly, without compiling.
*
* Copyright (c) 1998 by Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
*
* RCS @(#) $Id: Parser.java,v 1.17 2003/07/25 17:41:53 mdejong Exp $
*/
using System;
using System.Diagnostics;
namespace tcl.lang
{
public class Parser
{
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;
}
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 void evalObjv( Interp interp, TclObject[] objv, int length, int flags )
{
Command cmd;
WrappedCommand wCmd = null;
TclObject[] newObjv;
int i;
CallFrame savedVarFrame; //Saves old copy of interp.varFrame
// in case TCL.EVAL_GLOBAL was set.
interp.resetResult();
if ( objv.Length == 0 )
{
return;
}
// If the interpreter was deleted, return an error.
if ( interp.deleted )
{
interp.setResult( "attempt to call eval in deleted interpreter" );
interp.setErrorCode( TclString.newInstance( "CORE IDELETE {attempt to call eval in deleted interpreter}" ) );
throw new TclException( TCL.CompletionCode.ERROR );
}
// Check depth of nested calls to eval: if this gets too large,
// it's probably because of an infinite loop somewhere.
if ( interp.nestLevel >= interp.maxNestingDepth )
{
throw new TclException( interp, "too many nested calls to eval (infinite loop?)" );
}
interp.nestLevel++;
try
{
// Find the procedure to execute this command. If there isn't one,
// then see if there is a command "unknown". If so, create a new
// word array with "unknown" as the first word and the original
// command words as arguments. Then call ourselves recursively
// to execute it.
cmd = interp.getCommand( objv[0].ToString() );
if ( cmd == null )
wCmd = interp.getObjCommand( objv[0].ToString() );
// See if we are running as a slave interpretor, and this is a windows command
if ( cmd == null && wCmd == null && interp.slave != null )
{
wCmd = interp.slave.masterInterp.getObjCommand( objv[0].ToString() );
}
if ( cmd == null && wCmd == null )
{
newObjv = new TclObject[objv.Length + 1];
for ( i = ( objv.Length - 1 ); i >= 0; i-- )
{
newObjv[i + 1] = objv[i];
}
newObjv[0] = TclString.newInstance( "unknown" );
newObjv[0].preserve();
cmd = interp.getCommand( "unknown" );
if ( cmd == null )
{
Debug.Assert( false, "invalid command name \"" + objv[0].ToString() + "\"" );
throw new TclException( interp, "invalid command name \"" + objv[0].ToString() + "\"" );
}
else
{
evalObjv( interp, newObjv, length, 0 );
}
newObjv[0].release();
return;
}
// Finally, invoke the Command's cmdProc.
interp.cmdCount++;
savedVarFrame = interp.varFrame;
if ( ( flags & TCL.EVAL_GLOBAL ) != 0 )
{
interp.varFrame = null;
}
int rc = 0;
if ( cmd != null )
{
if ( cmd.cmdProc( interp, objv ) == TCL.CompletionCode.EXIT )
throw new TclException( TCL.CompletionCode.EXIT );
}
else
{
rc = wCmd.objProc( wCmd.objClientData, interp, objv.Length, objv );
if ( rc != 0 )
{
if ( rc == TCL.TCL_RETURN )
throw new TclException( TCL.CompletionCode.RETURN );
throw new TclException( TCL.CompletionCode.ERROR );
}
}
interp.varFrame = savedVarFrame;
}
finally
{
interp.nestLevel--;
}
}
internal static void logCommandInfo( Interp interp, char[] script_array, int script_index, int cmdIndex, int length, TclException e )
// The exception caused by the script
// evaluation.
{
string ellipsis;
string msg;
int offset;
int pIndex;
if ( interp.errAlreadyLogged )
{
// Someone else has already logged error information for this
// command; we shouldn't add anything more.
return;
}
// Compute the line number where the error occurred.
// Note: The script array must be accessed directly
// because we want to count from the beginning of
// the script, not the current index.
interp.errorLine = 1;
for ( pIndex = 0; pIndex < cmdIndex; pIndex++ )
{
if ( script_array[pIndex] == '\n' )
{
interp.errorLine++;
}
}
// Create an error message to add to errorInfo, including up to a
// maximum number of characters of the command.
if ( length < 0 )
{
//take into account the trailing '\0'
int script_length = script_array.Length - 1;
length = script_length - cmdIndex;
}
if ( length > 150 )
{
offset = 150;
ellipsis = "...";
}
else
{
offset = length;
ellipsis = "";
}
msg = new string( script_array, cmdIndex, offset );
if ( !( interp.errInProgress ) )
{
interp.addErrorInfo( "\n while executing\n\"" + msg + ellipsis + "\"" );
}
else
{
interp.addErrorInfo( "\n invoked from within\n\"" + msg + ellipsis + "\"" );
}
interp.errAlreadyLogged = false;
e.errIndex = cmdIndex + offset;
}
internal static TclObject evalTokens( Interp interp, TclToken[] tokenList, int tIndex, int count )
{
TclObject result, index, value;
TclToken token;
string p = null;
string varName;
BackSlashResult bs;
// The only tricky thing about this procedure is that it attempts to
// avoid object creation and string copying whenever possible. For
// example, if the value is just a nested command, then use the
// command's result object directly.
result = null;
for ( ; count > 0; count-- )
{
token = tokenList[tIndex];
// The switch statement below computes the next value to be
// concat to the result, as either a range of text or an
// object.
value = null;
switch ( token.type )
{
case TCL_TOKEN_TEXT:
p = token.TokenString;
break;
case TCL_TOKEN_BS:
bs = backslash( token.script_array, token.script_index );
if ( bs.isWordSep )
{
p = "\\" + bs.c;
}
else
{
System.Char ch = bs.c;
p = ch.ToString();
}
break;
case TCL_TOKEN_COMMAND:
interp.evalFlags |= Parser.TCL_BRACKET_TERM;
token.script_index++;
//should the nest level be changed???
//interp.nestLevel++;
eval2( interp, token.script_array, token.script_index, token.size - 2, 0 );
token.script_index--;
//interp.nestLevel--;
value = interp.getResult();
break;
case TCL_TOKEN_VARIABLE:
if ( token.numComponents == 1 )
{
index = null;
}
else
{
index = evalTokens( interp, tokenList, tIndex + 2, token.numComponents - 1 );
if ( index == null )
{
return null;
}
}
varName = tokenList[tIndex + 1].TokenString;
// In order to get the existing expr parser to work with the
// new Parser, we test the interp.noEval flag which is set
// by the expr parser. If it is != 0, then we do not evaluate
// the variable. This should be removed when the new expr
// parser is implemented.
if ( interp.noEval == 0 )
{
if ( index != null )
{
try
{
value = interp.getVar( varName, index.ToString(), 0 );
}
finally
{
index.release();
}
}
else
{
value = interp.getVar( varName, null, 0 );
}
}
else
{
value = TclString.newInstance( "" );
value.preserve();
}
count -= token.numComponents;
tIndex += token.numComponents;
break;
default:
throw new TclRuntimeError( "unexpected token type in evalTokens" );
}
// If value isn't null, the next piece of text comes from that
// object; otherwise, take value of p.
if ( result == null )
{
if ( value != null )
{
result = value;
}
else
{
result = TclString.newInstance( p );
}
result.preserve();
}
else
{
if ( result.Shared )
{
result.release();
result = result.duplicate();
result.preserve();
}
if ( value != null )
{
p = value.ToString();
}
TclString.append( result, p );
}
tIndex++;
}
return result;
}
public static void eval2( Interp interp, char[] script_array, int script_index, int numBytes, int flags )
{
int i;
int objUsed = 0;
int nextIndex, tokenIndex;
int commandLength, bytesLeft;
bool nested;
TclObject[] objv;
TclObject obj;
TclParse parse = null;
TclToken token;
// Saves old copy of interp.varFrame in case TCL.EVAL_GLOBAL was set
CallFrame savedVarFrame;
// Take into account the trailing '\0'
int script_length = script_array.Length - 1;
// These are modified instead of script_array and script_index
char[] src_array = script_array;
int src_index = script_index;
#if DEBUG
System.Diagnostics.Debug.WriteLine();
System.Diagnostics.Debug.WriteLine("Entered eval2()");
System.Diagnostics.Debug.Write("now to eval2 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 )
{
numBytes = script_length - script_index;
}
interp.resetResult();
savedVarFrame = interp.varFrame;
if ( ( flags & TCL.EVAL_GLOBAL ) != 0 )
{
interp.varFrame = null;
}
// Each iteration through the following loop parses the next
// command from the script and then executes it.
bytesLeft = numBytes;
// Init objv with the most commonly used array size
objv = grabObjv( interp, 3 );
if ( ( interp.evalFlags & TCL_BRACKET_TERM ) != 0 )
{
nested = true;
}
else
{
nested = false;
}
interp.evalFlags &= ~TCL_BRACKET_TERM;
try
{
do
{
parse = parseCommand( interp, src_array, src_index, bytesLeft, null, 0, nested );
if ( parse.result != TCL.CompletionCode.OK )
{
throw new TclException( parse.result );
}
// The test on noEval is temporary. As soon as the new expr
// parser is implemented it should be removed.
if ( parse.numWords > 0 && interp.noEval == 0 )
{
// Generate an array of objects for the words of the command.
try
{
tokenIndex = 0;
token = parse.getToken( tokenIndex );
// Test to see if new space needs to be allocated. If objv
// is the EXACT size of parse.numWords, then no allocation
// needs to be performed.
if ( objv.Length != parse.numWords )
{
//System.out.println("need new size " + objv.length);
releaseObjv( interp, objv ); //let go of resource
objv = grabObjv( interp, parse.numWords ); //get new resource
}
else
{
//System.out.println("reusing size " + objv.length);
}
for ( objUsed = 0; objUsed < parse.numWords; objUsed++ )
{
obj = evalTokens( interp, parse.tokenList, tokenIndex + 1, token.numComponents );
if ( obj == null )
{
throw new TclException( TCL.CompletionCode.ERROR );
}
else
{
objv[objUsed] = obj;
if ( token.type == TCL_TOKEN_EXPAND_WORD )
{
int numElements;
int code;
TclList.setListFromAny( null, objv[objUsed] );
TclObject[] elements = TclList.getElements( null, objv[objUsed] );
if ( elements.Length == 0 )
{
elements = new TclObject[1];
elements[0] = TclString.newInstance("{}") ;
TclList.setListFromAny( null, elements[0] );
}
numElements = elements.Length;
/*
* Some word expansion was requested. Check for objv resize.
*/
int objIdx = objUsed + numElements - 1;
Array.Resize( ref objv, objIdx+1 );
while ( numElements-- != 0 )
{
objv[objIdx] = elements[numElements];
objv[objIdx].preserve();
objIdx--;
}
objUsed = objv.Length-1;
}
}
tokenIndex += ( token.numComponents + 1 );
token = parse.getToken( tokenIndex );
}
// Execute the command and free the objects for its words.
try
{
evalObjv( interp, objv, bytesLeft, 0 );
}
catch ( System.StackOverflowException e )
{
interp.setResult( "too many nested calls" + " to eval (infinite loop?)" );
throw new TclException( TCL.CompletionCode.ERROR );
}
}
catch ( TclException e )
{
// Generate various pieces of error information, such
// as the line number where the error occurred and
// information to add to the errorInfo variable. Then
// free resources that had been allocated
// to the command.
if ( e.getCompletionCode() == TCL.CompletionCode.ERROR && !( interp.errAlreadyLogged ) )
{
commandLength = parse.commandSize;
char term = script_array[parse.commandStart + commandLength - 1];
int type = charType( term );
int terminators;
if ( nested )
{
terminators = TYPE_COMMAND_END | TYPE_CLOSE_BRACK;
}
else
{
terminators = TYPE_COMMAND_END;
}
if ( ( type & terminators ) != 0 )
{
// The command where the error occurred didn't end
// at the end of the script (i.e. it ended at a
// terminator character such as ";". Reduce the
// length by one so that the error message
// doesn't include the terminator character.
commandLength -= 1;
}
interp.varFrame = savedVarFrame;
logCommandInfo( interp, script_array, script_index, parse.commandStart, commandLength, e );
throw e;
}
else
throw;
}
finally
{
for ( i = 0; i < objUsed; i++ )
{
objv[i].release();
}
objUsed = 0;
parse.release(); // Cleanup parser resources
}
}
// Advance to the next command in the script.
nextIndex = parse.commandStart + parse.commandSize;
bytesLeft -= ( nextIndex - src_index );
src_index = nextIndex;
if ( nested && ( src_index > 1 ) && ( src_array[src_index - 1] == ']' ) )
{
// We get here in the special case where the TCL_BRACKET_TERM
// flag was set in the interpreter and we reached a close
// bracket in the script. Return immediately.
interp.termOffset = ( src_index - 1 ) - script_index;
interp.varFrame = savedVarFrame;
return;
}
}
while ( bytesLeft > 0 );
}
finally
{
if ( parse != null )
{
parse.release(); // Let go of parser resources
}
releaseObjv( interp, objv ); // Let go of objv buffer
}
interp.termOffset = src_index - script_index;
interp.varFrame = savedVarFrame;
return;
}
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 ParseResult parseVar( Interp interp, string inString )
{
TclParse parse;
TclObject obj;
System.Diagnostics.Debug.WriteLine( "Entered parseVar()" );
System.Diagnostics.Debug.Write( "now to parse var off the string \"" + inString + "\"" );
CharPointer src = new CharPointer( inString );
parse = parseVarName( interp, src.array, src.index, -1, null, false );
if ( parse.result != TCL.CompletionCode.OK )
{
throw new TclException( interp, interp.getResult().ToString() );
}
try
{
System.Diagnostics.Debug.Write( "parsed " + parse.numTokens + " tokens" );
if ( parse.numTokens == 1 )
{
// There isn't a variable name after all: the $ is just a $.
return new ParseResult( "$", 1 );
}
obj = evalTokens( interp, parse.tokenList, 0, parse.numTokens );
if ( !obj.Shared )
{
throw new TclRuntimeError( "parseVar got temporary object from evalTokens" );
}
return new ParseResult( obj, parse.tokenList[0].size );
}
finally
{
parse.release(); // Release parser resources
}
}
internal static bool commandComplete( string inString, int length )
// Number of bytes in script.
{
TclParse parse;
CharPointer src = new CharPointer( inString );
do
{
parse = parseCommand( null, src.array, src.index, length, null, 0, false );
src.index = parse.commandStart + parse.commandSize;
parse.release(); // Release parser resources
if ( src.index >= length )
{
break;
}
}
while ( parse.result == TCL.CompletionCode.OK );
if ( parse.incomplete )
{
return false;
}
return true;
}
internal static bool objCommandComplete( TclObject obj )
// Points to object holding script
// to check.
{
string inString = obj.ToString();
return commandComplete( inString, inString.Length );
}
internal static BackSlashResult backslash( char[] script_array, int script_index )
{
int result;
script_index++;
int endIndex = script_array.Length - 1;
if ( script_index == endIndex )
{
return new BackSlashResult( '\\', script_index );
}
char c = script_array[script_index];
switch ( c )
{
case 'a':
return new BackSlashResult( (char)0x7, script_index + 1 );
case 'b':
return new BackSlashResult( (char)0x8, script_index + 1 );
case 'f':
return new BackSlashResult( (char)0xc, script_index + 1 );
case 'n':
return new BackSlashResult( '\n', script_index + 1 );
case 'r':
return new BackSlashResult( '\r', script_index + 1 );
case 't':
return new BackSlashResult( '\t', script_index + 1 );
case 'v':
return new BackSlashResult( (char)0xb, script_index + 1 );
case 'x':
script_index++;
if ( script_index < endIndex )
{
c = script_array[script_index];
if ( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) || ( ( c >= 'a' ) && ( c <= 'f' ) ) )
{
string str = new string( script_array, script_index, endIndex - script_index );
StrtoulResult res = Util.strtoul( str, 0, 16 );
if ( res.errno == 0 )
{
// We force res.value to be a 8-bit (ASCII) character
// so that it is compatible with Tcl.
char b = (char)( res.value & 0xff );
return new BackSlashResult( b, script_index + res.index );
}
}
}
return new BackSlashResult( 'x', script_index );
case 'u': // TODO -- determine how to handle Unicode
int count, n;
result = 0;
for ( count = 0; count < 4; count++ )
{
script_index++;
c = script_array[script_index];
if ( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'a' ) && ( c <= 'f' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
{
n = c - '0';
if ( n > 9 )
{
n = n + '0' + 10 - 'A';
}
if ( n > 16 )
{
n = n + 'A' - 'a';
}
result = ( result << 4 ) + n;
}
else
{
break;
}
}
if ( count == 0 )
{
result = 'u';
}
return new BackSlashResult( (char)result, script_index + 1 );
case '\r':
case '\n':
if ( c == '\r' )
{
if ( ( script_index + 1 ) < endIndex )
{
if ( script_array[script_index + 1] == '\n' )
{
script_index++;
}
}
}
do
{
script_index++;
c = script_array[script_index];
}
while ( ( script_index < endIndex ) && ( ( c == ' ' ) || ( c == '\t' ) ) );
return new BackSlashResult( (char)' ', script_index );
case (char)( 0 ):
return new BackSlashResult( (char)'\\', script_index + 1 );
default:
if ( ( c >= '0' ) && ( c <= '9' ) )
{
// Convert it to an octal number. This implementation is
// compatible with tcl 7.6 - characters 8 and 9 are allowed.
result = c - '0';
script_index++;
{
if ( script_index == endIndex )
{
goto getoctal_brk;
}
c = script_array[script_index];
if ( !( ( c >= '0' ) && ( c <= '9' ) ) )
{
goto getoctal_brk;
}
result = ( result * 8 ) + ( c - '0' );
script_index++;
if ( script_index == endIndex )
{
goto getoctal_brk;
}
c = script_array[script_index];
if ( !( ( c >= '0' ) && ( c <= '9' ) ) )
{
goto getoctal_brk;
}
result = ( result * 8 ) + ( c - '0' );
script_index++;
}
getoctal_brk:
;
// We force result to be a 8-bit (ASCII) character so
// that it compatible with Tcl 7.6.
return new BackSlashResult( (char)( result & 0xff ), script_index );
}
else
{
return new BackSlashResult( c, script_index + 1 );
}
}
}
internal static char charType( char c )
{
return ( ( c > TYPE_MAX ) ? TYPE_NORMAL : typeTable[c] );
}
// The following table provides parsing information about each possible
// character.
//
// The method charType is used to index into the table and return
// information about its character argument. The following return
// values are defined.
//
// TYPE_NORMAL - All characters that don't have special significance
// to the Tcl parser.
// TYPE_SPACE - The character is a whitespace character other
// than newline.
// TYPE_COMMAND_END - Character is newline or semicolon.
// TYPE_SUBS - Character begins a substitution or has other
// special meaning in parseTokens: backslash, dollar
// sign, open bracket, or null.
// TYPE_QUOTE - Character is a double quote.
// TYPE_CLOSE_PAREN - Character is a right parenthesis.
// TYPE_CLOSE_BRACK - Character is a right square bracket.
// TYPE_BRACE - Character is a curly brace (either left or right).
internal const char TYPE_NORMAL = (char)( 0 );
internal const char TYPE_SPACE = (char)( 0x1 );
internal const char TYPE_COMMAND_END = (char)( 0x2 );
internal const char TYPE_SUBS = (char)( 0x4 );
internal const char TYPE_QUOTE = (char)( 0x8 );
internal const char TYPE_CLOSE_PAREN = (char)( 0x10 );
internal const char TYPE_CLOSE_BRACK = (char)( 0x20 );
internal const char TYPE_BRACE = (char)( 0x40 );
// This is the largest value in the type table. If a
// char value is larger then the char type is TYPE_NORMAL.
// Lookup -> ((c > TYPE_MAX) ? TYPE_NORMAL : typeTable[c])
internal const char TYPE_MAX = (char)( 127 );
internal static char[] typeTable;
// Type values defined for TclToken structures. These values are
// defined as mask bits so that it's easy to check for collections of
// types.
//
// TCL_TOKEN_WORD - The token describes one word of a command,
// from the first non-blank character of
// the word (which may be " or {) up to but
// not including the space, semicolon, or
// bracket that terminates the word.
// NumComponents counts the total number of
// sub-tokens that make up the word. This
// includes, for example, sub-tokens of
// TCL_TOKEN_VARIABLE tokens.
// TCL_TOKEN_SIMPLE_WORD - This token is just like TCL_TOKEN_WORD
// except that the word is guaranteed to
// consist of a single TCL_TOKEN_TEXT
// sub-token.
// TCL_TOKEN_TEXT - The token describes a range of literal
// text that is part of a word.
// NumComponents is always 0.
// TCL_TOKEN_BS - The token describes a backslash sequence
// that must be collapsed. NumComponents
// is always 0.
// TCL_TOKEN_COMMAND - The token describes a command whose result
// must be substituted into the word. The
// token includes the enclosing brackets.
// NumComponents is always 0.
// TCL_TOKEN_VARIABLE - The token describes a variable
// substitution, including the dollar sign,
// variable name, and array index (if there
// is one) up through the right
// parentheses. NumComponents tells how
// many additional tokens follow to
// represent the variable name. The first
// token will be a TCL_TOKEN_TEXT token
// that describes the variable name. If
// the variable is an array reference then
// there will be one or more additional
// tokens, of type TCL_TOKEN_TEXT,
// TCL_TOKEN_BS, TCL_TOKEN_COMMAND, and
// TCL_TOKEN_VARIABLE, that describe the
// array index; numComponents counts the
// total number of nested tokens that make
// up the variable reference, including
// sub-tokens of TCL_TOKEN_VARIABLE tokens.
internal const int TCL_TOKEN_WORD = 1;
internal const int TCL_TOKEN_SIMPLE_WORD = 2;
internal const int TCL_TOKEN_TEXT = 4;
internal const int TCL_TOKEN_BS = 8;
internal const int TCL_TOKEN_COMMAND = 16;
internal const int TCL_TOKEN_VARIABLE = 32;
//#define TCL_TOKEN_SUB_EXPR 64
//#define TCL_TOKEN_OPERATOR 128
internal const int TCL_TOKEN_EXPAND_WORD = 256;
// Note: Most of the variables below will not be used until the
// Compilier is implemented, but are left for consistency.
// A structure of the following type is filled in by parseCommand.
// It describes a single command parsed from an input string.
// evalFlag bits for Interp structures:
//
// TCL_BRACKET_TERM 1 means that the current script is terminated by
// a close bracket rather than the end of the string.
// TCL_ALLOW_EXCEPTIONS 1 means it's OK for the script to terminate with
// a code other than TCL_OK or TCL_ERROR; 0 means
// codes other than these should be turned into errors.
public const int TCL_BRACKET_TERM = 1;
public const int TCL_ALLOW_EXCEPTIONS = 4;
// Flag bits for Interp structures:
//
// DELETED: Non-zero means the interpreter has been deleted:
// don't process any more commands for it, and destroy
// the structure as soon as all nested invocations of
// Tcl_Eval are done.
// ERR_IN_PROGRESS: Non-zero means an error unwind is already in
// progress. Zero means a command proc has been
// invoked since last error occurred.
// ERR_ALREADY_LOGGED: Non-zero means information has already been logged
// in $errorInfo for the current Tcl_Eval instance,
// so Tcl_Eval needn't log it (used to implement the
// "error message log" command).
// ERROR_CODE_SET: Non-zero means that Tcl_SetErrorCode has been
// called to record information for the current
// error. Zero means Tcl_Eval must clear the
// errorCode variable if an error is returned.
// EXPR_INITIALIZED: Non-zero means initialization specific to
// expressions has been carried out.
// DONT_COMPILE_CMDS_INLINE: Non-zero means that the bytecode compiler
// should not compile any commands into an inline
// sequence of instructions. This is set 1, for
// example, when command traces are requested.
// RAND_SEED_INITIALIZED: Non-zero means that the randSeed value of the
// interp has not be initialized. This is set 1
// when we first use the rand() or srand() functions.
// SAFE_INTERP: Non zero means that the current interp is a
// safe interp (ie it has only the safe commands
// installed, less priviledge than a regular interp).
// USE_EVAL_DIRECT: Non-zero means don't use the compiler or byte-code
// interpreter; instead, have Tcl_EvalObj call
// Tcl_EvalDirect. Used primarily for testing the
// new parser.
public const int DELETED = 1;
public const int ERR_IN_PROGRESS = 2;
public const int ERR_ALREADY_LOGGED = 4;
public const int ERROR_CODE_SET = 8;
public const int EXPR_INITIALIZED = 0x10;
public const int DONT_COMPILE_CMDS_INLINE = 0x20;
public const int RAND_SEED_INITIALIZED = 0x40;
public const int SAFE_INTERP = 0x80;
public const int USE_EVAL_DIRECT = 0x100;
// These are private read only values that are used by the parser
// class to implement a TclObject[] cache
// Max size of array to cache (1..N)
private const int OBJV_CACHE_MAX = 10;
// The number of array to cache for each size
// for example if the number of 3 elements is set to 5
// an array of 5 TclObject[] objects
// which will each be 3 elements long
private static readonly int[] OBJV_CACHE_SIZES = new int[] { 0, 4, 4, 10, 4, 4, 4, 4, 4, 4 };
// use test results
// 1 373
// 2 2424
// 3 11889
// 4 840
// 5 1374
// 6 926
// 7 0
// 8 74
// 9 0
internal static void init( Interp interp )
{
//System.out.println("called Parser.init()");
TclObject[][][] OBJV = new TclObject[OBJV_CACHE_MAX][][];
int[] USED = new int[OBJV_CACHE_MAX];
int i, j, size;
for ( i = 0; i < OBJV_CACHE_MAX; i++ )
{
size = OBJV_CACHE_SIZES[i];
//System.out.println("size " + i + " has " + size + " cache blocks");
OBJV[i] = new TclObject[size][];
USED[i] = 0;
for ( j = 0; j < size; j++ )
{
OBJV[i][j] = new TclObject[i];
}
}
interp.parserObjv = OBJV;
interp.parserObjvUsed = USED;
}
private static TclObject[] grabObjv( Interp interp, int size )
{
if ( size >= OBJV_CACHE_MAX )
{
//System.out.println("allocate for big objv of size " + size);
return new TclObject[size];
}
//get array of used markers for this size
int OPEN = interp.parserObjvUsed[size];
if ( OPEN < OBJV_CACHE_SIZES[size] )
{
// Found an open cache slot
//System.out.println("cache hit for objv of size " + size);
interp.parserObjvUsed[size] += 1;
return interp.parserObjv[size][OPEN];
}
else
{
// Did not find a free cache array of this size
//System.out.println("cache miss for objv of size " + size);
return new TclObject[size];
}
}
private static void releaseObjv( Interp interp, TclObject[] objv )
{
int size = objv.Length;
if ( size >= OBJV_CACHE_MAX )
{
//System.out.println("release for big objv of size " + size);
return;
}
int OPEN = interp.parserObjvUsed[size];
if ( OPEN > 0 )
{
OPEN--;
interp.parserObjvUsed[size] = OPEN;
interp.parserObjv[size][OPEN] = objv;
//System.out.println("released objv of size " + size);
}
/*
else {
System.out.println("no release for objv of size " + size);
}
*/
return;
}
static Parser()
{
typeTable = new char[] { TYPE_SUBS, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_SPACE, TYPE_COMMAND_END, TYPE_SPACE, TYPE_SPACE, TYPE_SPACE, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_SPACE, TYPE_NORMAL, TYPE_QUOTE, TYPE_NORMAL, TYPE_SUBS, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_CLOSE_PAREN, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_COMMAND_END, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_SUBS, TYPE_SUBS, TYPE_CLOSE_BRACK, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_BRACE, TYPE_NORMAL, TYPE_BRACE, TYPE_NORMAL, TYPE_NORMAL };
}
} // end class Parser
}