wasCSharpSQLite – Rev 1

Subversion Repositories:
Rev:
#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
}