wasCSharpSQLite – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 #undef DEBUG
2  
3 /*
4 * Parser.java --
5 *
6 * This class contains methods that parse Tcl scripts. They
7 * do so in a general-purpose fashion that can be used for many
8 * different purposes, including compilation, direct execution,
9 * code analysis, etc. This class also includes a few additional
10 * procedures such as evalObjv, eval, and eval2, which allow
11 * scripts to be evaluated directly, without compiling.
12 *
13 * Copyright (c) 1998 by Sun Microsystems, Inc.
14 *
15 * See the file "license.terms" for information on usage and redistribution
16 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17 *
18 * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
19 *
20 * RCS @(#) $Id: Parser.java,v 1.17 2003/07/25 17:41:53 mdejong Exp $
21 */
22 using System;
23 using System.Diagnostics;
24 namespace tcl.lang
25 {
26  
27 public class Parser
28 {
29 public static TclParse parseCommand( Interp interp, char[] script_array, int script_index, int numBytes, string fileName, int lineNum, bool nested )
30 // True means that this is a nested command:
31 // close bracket should be considered
32 // a command terminator. If false, then close
33 // bracket has no special meaning.
34 {
35  
36 char cur; //the char we are currently parsing
37 int type; // Result returned by charType(src.charAt()).
38 TclToken token; // Pointer to token being filled in.
39 int wordIndex; // Index of word token for current word.
40 int level; // Nesting level of curly braces: gives
41 // number of right braces we must find to
42 // end word.
43 TclParse parse; // Return value to fill in with information
44 // about the parsed command.
45 int terminators; // charType() bits that indicate the end
46 // of a command.
47 BackSlashResult bs; // Result of a call to backslash(...).
48 int endIndex; // Index that points to the character after
49 // the last character to be parsed.
50 char savedChar; // To terminate the parsing correctly, the
51 // character at endIndex is set to \0. This
52 // stores the value to return when finished.
53  
54  
55 int saved_script_index = script_index; //save the original index
56  
57  
58 int script_length = script_array.Length - 1;
59  
60  
61 if ( numBytes < 0 )
62 {
63 numBytes = script_length - script_index;
64 }
65 endIndex = script_index + numBytes;
66 if ( endIndex > script_length )
67 {
68 endIndex = script_length;
69 }
70  
71 savedChar = script_array[endIndex];
72 script_array[endIndex] = '\x0000';
73  
74 parse = new TclParse( interp, script_array, endIndex, fileName, lineNum );
75  
76 if ( nested )
77 {
78 terminators = TYPE_COMMAND_END | TYPE_CLOSE_BRACK;
79 }
80 else
81 {
82 terminators = TYPE_COMMAND_END;
83 }
84  
85 // Parse any leading space and comments before the first word of the
86 // command.
87  
88  
89 try
90 {
91  
92 while ( true )
93 {
94  
95 cur = script_array[script_index];
96  
97 while ( ( ( cur <= TYPE_MAX ) && ( typeTable[cur] == TYPE_SPACE ) ) || ( cur == '\n' ) )
98 {
99 cur = script_array[++script_index];
100 }
101  
102 if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) )
103 {
104  
105 // Skip backslash-newline sequence: it should be treated
106 // just like white space.
107  
108 if ( ( script_index + 2 ) == parse.endIndex )
109 {
110 parse.incomplete = true;
111 }
112  
113 //this will add 2 to the offset and return to the top
114 //of the while(true) loop which will get the next cur
115  
116 script_index += 2;
117 continue;
118 }
119  
120 // If we have found the start of a command goto the word parsing loop
121 if ( cur != '#' )
122 {
123 break;
124 }
125  
126 // Record the index where the comment starts
127 if ( parse.commentStart < 0 )
128 {
129 parse.commentStart = script_index;
130 }
131  
132 while ( true )
133 {
134 cur = script_array[script_index];
135 if ( script_index == parse.endIndex )
136 {
137 if ( nested )
138 parse.incomplete = true;
139 parse.commentSize = script_index - parse.commentStart;
140 break;
141 }
142 else if ( cur == '\\' )
143 {
144 if ( ( script_array[script_index + 1] == '\n' ) && ( ( script_index + 2 ) == parse.endIndex ) )
145 {
146 parse.incomplete = true;
147 }
148 bs = backslash( script_array, script_index );
149 script_index = bs.nextIndex;
150 }
151 else if ( cur == '\n' )
152 {
153 script_index++;
154 parse.commentSize = script_index - parse.commentStart;
155 break;
156 }
157 else
158 {
159 script_index++;
160 }
161 }
162 }
163  
164  
165  
166 // The following loop parses the words of the command, one word
167 // in each iteration through the loop.
168  
169 parse.commandStart = script_index;
170  
171 while ( true )
172 {
173  
174 bool expandWord = false;
175  
176 // Create the token for the word.
177 wordIndex = parse.numTokens;
178  
179 token = parse.getToken( wordIndex );
180 token.type = TCL_TOKEN_WORD;
181  
182 // Skip white space before the word. Also skip a backslash-newline
183 // sequence: it should be treated just like white space.
184  
185  
186 while ( true )
187 {
188 cur = script_array[script_index];
189 type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] );
190  
191 if ( type == TYPE_SPACE )
192 {
193 script_index++;
194 continue;
195 }
196 else if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) )
197 {
198 if ( ( script_index + 2 ) == parse.endIndex )
199 {
200 parse.incomplete = true;
201 }
202 bs = backslash( script_array, script_index );
203 script_index = bs.nextIndex;
204 continue;
205 }
206 break;
207 }
208  
209 if ( ( type & terminators ) != 0 )
210 {
211 script_index++;
212 break;
213 }
214  
215 if ( script_index == parse.endIndex )
216 {
217 if ( nested && savedChar != ']' )
218 {
219 parse.incomplete = true;
220 throw new TclException( interp, "missing close-bracket" );
221 }
222 break;
223 }
224  
225 token.script_array = script_array;
226 token.script_index = script_index;
227  
228 parse.numTokens++;
229 parse.numWords++;
230  
231  
232 // At this point the word can have one of four forms: something
233 // enclosed in quotes, something enclosed in braces, and
234 // expanding word, or an unquoted word (anything else).
235  
236 parseWord:
237 cur = script_array[script_index];
238  
239 if ( cur == '"' )
240 {
241 script_index++;
242 parse = parseTokens( script_array, script_index, TYPE_QUOTE, parse );
243 if ( parse.result != TCL.CompletionCode.OK )
244 {
245 throw new TclException( parse.result );
246 }
247 if ( parse.inString[parse.termIndex] != '"' )
248 {
249 parse.termIndex = script_index - 1;
250 parse.incomplete = true;
251 throw new TclException( parse.interp, "missing \"" );
252 }
253 script_index = parse.termIndex + 1;
254 }
255 else if ( cur == '{' )
256 {
257 /*
258 * Hack for {*}
259 * Check whether the braces contained the word expansion prefix.
260 */
261 if ( script_index < script_array.Length - 3 // only if there is room
262 && script_array[script_index + 1] == '*' && script_array[script_index + 2] == '}' // and it is {*}
263 && typeTable[script_array[script_index + 1]] != TYPE_SPACE /* Non-whitespace follows */
264 && !expandWord // only one per token
265 )
266 {
267 script_index += 3; // Skip
268 expandWord = true;
269 //parse.numTokens--;
270 goto parseWord;
271 }
272  
273 // Find the matching right brace that terminates the word,
274 // then generate a single token for everything between the
275 // braces.
276  
277 script_index++;
278 token = parse.getToken( parse.numTokens );
279 token.type = TCL_TOKEN_TEXT;
280 token.script_array = script_array;
281 token.script_index = script_index;
282 token.numComponents = 0;
283 level = 1;
284 while ( true )
285 {
286 cur = script_array[script_index];
287  
288 // get the current char in the array and lookup its type
289 while ( ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] ) == TYPE_NORMAL )
290 {
291 cur = script_array[++script_index];
292 }
293 if ( script_array[script_index] == '}' )
294 {
295 level--;
296 if ( level == 0 )
297 {
298 break;
299 }
300 script_index++;
301 }
302 else if ( script_array[script_index] == '{' )
303 {
304 level++;
305 script_index++;
306 }
307 else if ( script_array[script_index] == '\\' )
308 {
309 bs = backslash( script_array, script_index );
310 if ( script_array[script_index + 1] == '\n' )
311 {
312 // A backslash-newline sequence requires special
313 // treatment: it must be collapsed, even inside
314 // braces, so we have to split the word into
315 // multiple tokens so that the backslash-newline
316 // can be represented explicitly.
317  
318 if ( ( script_index + 2 ) == parse.endIndex )
319 {
320 parse.incomplete = true;
321 }
322 token.size = script_index - token.script_index;
323 if ( token.size != 0 )
324 {
325 parse.numTokens++;
326 }
327 token = parse.getToken( parse.numTokens );
328 token.type = TCL_TOKEN_BS;
329 token.script_array = script_array;
330 token.script_index = script_index;
331 token.size = bs.nextIndex - script_index;
332 token.numComponents = 0;
333 parse.numTokens++;
334 script_index = bs.nextIndex;
335 token = parse.getToken( parse.numTokens );
336 token.type = TCL_TOKEN_TEXT;
337 token.script_array = script_array;
338 token.script_index = script_index;
339 token.numComponents = 0;
340 }
341 else
342 {
343 script_index = bs.nextIndex;
344 }
345 }
346 else if ( script_index == parse.endIndex )
347 {
348 parse.termIndex = parse.getToken( wordIndex ).script_index;
349 parse.incomplete = true;
350 throw new TclException( interp, "missing close-brace" );
351 }
352 else
353 {
354 script_index++;
355 }
356 }
357 if ( ( script_index != token.script_index ) || ( parse.numTokens == ( wordIndex + 1 ) ) )
358 {
359 token.size = script_index - token.script_index;
360 parse.numTokens++;
361 }
362 script_index++;
363 }
364 else
365 {
366 // This is an unquoted word. Call parseTokens and let it do
367 // all of the work.
368  
369 parse = parseTokens( script_array, script_index, TYPE_SPACE | terminators, parse );
370 if ( parse.result != TCL.CompletionCode.OK )
371 {
372 throw new TclException( parse.result );
373 }
374 script_index = parse.termIndex;
375 }
376  
377 // Finish filling in the token for the word and check for the
378 // special case of a word consisting of a single range of
379 // literal text.
380  
381 token = parse.getToken( wordIndex );
382 token.size = script_index - token.script_index;
383 token.numComponents = parse.numTokens - ( wordIndex + 1 );
384  
385 if ( expandWord )
386 {
387 int i = 1;
388 bool isLiteral = true;
389  
390 /*
391 * When a command includes a word that is an expanded literal; for
392 * example, {*}{1 2 3}, the parser performs that expansion
393 * immediately, generating several TCL_TOKEN_SIMPLE_WORDs instead
394 * of a single TCL_TOKEN_EXPAND_WORD that the Tcl_ParseCommand()
395 * caller might have to expand. This notably makes it simpler for
396 * those callers that wish to track line endings, such as those
397 * that implement key parts of TIP 280.
398 *
399 * First check whether the thing to be expanded is a literal,
400 * in the sense of being composed entirely of TCL_TOKEN_TEXT
401 * tokens.
402 */
403  
404 for ( i = 1; i <= token.numComponents; i++ )
405 {
406 if ( parse.getToken( wordIndex + i ).type != TCL_TOKEN_TEXT )//if (tokenPtr[i].type != TCL_TOKEN_TEXT)
407 {
408 isLiteral = false;
409 break;
410 }
411 }
412  
413 if ( isLiteral )
414 {
415 int elemCount = 0;
416 FindElemResult code = null;
417 bool nakedbs = false;
418 int nextElem, listEnd;
419 int elemStart = 0;
420  
421 /*
422 * The word to be expanded is a literal, so determine the
423 * boundaries of the literal string to be treated as a list
424 * and expanded. That literal string starts at
425 * tokenPtr[1].start, and includes all bytes up to, but not
426 * including (tokenPtr[token.numComponents].start +
427 * tokenPtr[token.numComponents].size)
428 */
429  
430 //listEnd = ( tokenPtr[tokenPtr->numComponents].start +
431 // tokenPtr[tokenPtr->numComponents].size );
432 listEnd = ( parse.getToken( wordIndex + token.numComponents ).script_index +
433 parse.getToken( wordIndex + token.numComponents ).size ) - 1;
434 nextElem = parse.getToken( wordIndex + token.numComponents ).script_index;//nextElem = tokenPtr[1].start;
435  
436  
437 /*
438 * Step through the literal string, parsing and counting list
439 * elements.
440 */
441  
442 string string_array = new string( token.script_array );
443 while ( nextElem < listEnd )
444 {
445 int size;
446  
447 code = Util.findElement( null, string_array, nextElem, listEnd );
448 //code = TclFindElement(NULL, nextElem, listEnd - nextElem,
449 // &elemStart, &nextElem, &size, &brace);
450 if ( code == null )
451 {
452 break;
453 }
454 if ( !code.brace )
455 {
456 size = code.size;
457 elemStart = nextElem;
458 int s;
459  
460 for ( s = elemStart; size > 0; s++, size-- )
461 {
462 if ( token.script_array[s] == '\\' )
463 {
464 nakedbs = true;
465 break;
466 }
467 }
468 }
469 elemCount++;
470 nextElem = code.elemEnd;
471 }
472  
473 if ( ( code == null ) || nakedbs )
474 {
475 /*
476 * Some list element could not be parsed, or contained
477 * naked backslashes. This means the literal string was
478 * not in fact a valid nor canonical list. Defer the
479 * handling of this to compile/eval time, where code is
480 * already in place to report the "attempt to expand a
481 * non-list" error or expand lists that require
482 * substitution.
483 */
484  
485 token.type = TCL_TOKEN_EXPAND_WORD;
486 }
487 else if ( elemCount == 0 )
488 {
489 /*
490 * We are expanding a literal empty list. This means that
491 * the expanding word completely disappears, leaving no
492 * word generated this pass through the loop. Adjust
493 * accounting appropriately.
494 */
495  
496 parse.numWords--;
497 parse.numTokens = wordIndex;
498 }
499 else
500 {
501 /*
502 * Recalculate the number of Tcl_Tokens needed to store
503 * tokens representing the expanded list.
504 */
505  
506 int growthNeeded = wordIndex + 2 * elemCount
507 - parse.numTokens;
508 parse.numWords += elemCount - 1;
509 if ( growthNeeded > 0 )
510 {
511 parse.expandTokenArray( growthNeeded );// TclGrowParseTokenArray( parse, growthNeeded );
512 token = parse.getToken( wordIndex );//&parsePtr->tokenPtr[wordIndex];
513 }
514 parse.numTokens = wordIndex + 2 * elemCount;
515  
516  
517 /*
518 * Generate a TCL_TOKEN_SIMPLE_WORD token sequence for
519 * each element of the literal list we are expanding in
520 * place. Take care with the start and size fields of each
521 * token so they point to the right literal characters in
522 * the original script to represent the right expanded
523 * word value.
524 */
525  
526 nextElem = parse.getToken( wordIndex ).script_index;//tokenPtr[1].start;
527 while ( token.script_array[nextElem] == ' ' )//isspace( UCHAR( *nextElem ) ) )
528 {
529 nextElem++;
530 }
531 while ( nextElem < listEnd )
532 {
533 token.type = TCL_TOKEN_SIMPLE_WORD;
534 token.numComponents = 1;
535 token.script_index = nextElem;
536  
537 token = parse.getToken( ++wordIndex );// tokenPtr++;
538 token.type = TCL_TOKEN_TEXT;
539 token.numComponents = 0;
540 code = Util.findElement( null, string_array, nextElem, listEnd );
541 //TclFindElement(NULL, nextElem, listEnd - nextElem,
542 // &(tokenPtr->start), &nextElem,
543 // &(tokenPtr->size), NULL);
544 token.script_index = nextElem + ( code.brace ? 1 : 0 );
545 token.size = code.size;
546 nextElem = code.elemEnd;
547 if ( token.script_index + token.size == listEnd )
548 {
549 parse.getToken( wordIndex - 1 ).size = listEnd - parse.getToken( wordIndex - 1 ).script_index;//tokenPtr[-1].size = listEnd - tokenPtr[-1].start;
550 }
551 else
552 {
553 //tokenPtr[-1].size = tokenPtr->start
554 // + tokenPtr->size - tokenPtr[-1].start;
555 parse.getToken( wordIndex - 1 ).size = token.script_index
556 + token.size - parse.getToken( wordIndex - 1 ).script_index;
557 if ( script_index + token.size < token.script_array.Length &&
558 ( token.script_array[script_index + token.size] == ' ' ) )
559 parse.getToken( wordIndex - 1 ).size += 1;
560 // tokenPtr[-1].size += ( isspace( UCHAR(
561 //tokenPtr->start[tokenPtr->size] ) ) == 0 );
562 }
563  
564 token = parse.getToken( ++wordIndex );// tokenPtr++;
565 }
566 }
567 }
568 else
569 {
570 /*
571 * The word to be expanded is not a literal, so defer
572 * expansion to compile/eval time by marking with a
573 * TCL_TOKEN_EXPAND_WORD token.
574 */
575  
576 token.type = TCL_TOKEN_EXPAND_WORD;
577 }
578 }
579 else if ( ( token.numComponents == 1 ) && ( parse.getToken( wordIndex + 1 ).type == TCL_TOKEN_TEXT ) )
580 {
581 token.type = TCL_TOKEN_SIMPLE_WORD;
582 }
583  
584  
585 // Do two additional checks: (a) make sure we're really at the
586 // end of a word (there might have been garbage left after a
587 // quoted or braced word), and (b) check for the end of the
588 // command.
589  
590 cur = script_array[script_index];
591 type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] );
592  
593 if ( type == TYPE_SPACE )
594 {
595 script_index++;
596 continue;
597 }
598 else
599 {
600 // Backslash-newline (and any following white space) must be
601 // treated as if it were a space character.
602  
603 if ( ( cur == '\\' ) && ( script_array[script_index + 1] == '\n' ) )
604 {
605 if ( ( script_index + 2 ) == parse.endIndex )
606 {
607 parse.incomplete = true;
608 }
609 bs = backslash( script_array, script_index );
610 script_index = bs.nextIndex;
611 continue;
612 }
613 }
614  
615 if ( ( type & terminators ) != 0 )
616 {
617 script_index++;
618 break;
619 }
620 if ( script_index == parse.endIndex )
621 {
622 if ( nested && savedChar != ']' )
623 {
624 parse.incomplete = true;
625 throw new TclException( interp, "missing close-bracket" );
626 }
627 break;
628 }
629 parse.termIndex = script_index;
630 if ( script_array[script_index - 1] == '"' )
631 {
632 throw new TclException( interp, "extra characters after close-quote" );
633 }
634 else
635 {
636 throw new TclException( interp, "extra characters after close-brace" );
637 }
638 }
639 }
640 catch ( TclException e )
641 {
642 script_array[endIndex] = savedChar;
643 if ( parse.commandStart < 0 )
644 {
645 parse.commandStart = saved_script_index;
646 }
647 parse.commandSize = parse.termIndex - parse.commandStart;
648 parse.result = TCL.CompletionCode.ERROR;
649 return parse;
650 }
651  
652 script_array[endIndex] = savedChar;
653 parse.commandSize = script_index - parse.commandStart;
654 parse.result = TCL.CompletionCode.OK;
655 return parse;
656 }
657  
658  
659  
660  
661  
662  
663  
664  
665  
666  
667  
668 internal static TclParse parseTokens( char[] script_array, int script_index, int mask, TclParse parse )
669 // Information about parse in progress.
670 // Updated with additional tokens and
671 // termination information.
672 {
673 char cur;
674 int type, originalTokens, varToken;
675 TclToken token;
676 TclParse nested;
677 BackSlashResult bs;
678  
679  
680  
681 #if DEBUG
682 System.Diagnostics.Debug.WriteLine();
683 System.Diagnostics.Debug.WriteLine("Entered Parser.parseTokens()");
684 System.Diagnostics.Debug.Write("now to parse the string \"");
685 for (int k = script_index; k < script_array.Length; k++)
686 {
687 System.Diagnostics.Debug.Write(script_array[k]);
688 }
689 System.Diagnostics.Debug.WriteLine("\"");
690 #endif
691  
692  
693 // Each iteration through the following loop adds one token of
694 // type TCL_TOKEN_TEXT, TCL_TOKEN_BS, TCL_TOKEN_COMMAND, or
695 // TCL_TOKEN_VARIABLE to parse. For TCL_TOKEN_VARIABLE additional,
696 // tokens tokens are added for the parsed variable name.
697  
698 originalTokens = parse.numTokens;
699 while ( true )
700 {
701 token = parse.getToken( parse.numTokens );
702 token.script_array = script_array;
703 token.script_index = script_index;
704 token.numComponents = 0;
705  
706 #if DEBUG
707 System.Diagnostics.Debug.WriteLine();
708 System.Diagnostics.Debug.WriteLine("Now on index " + script_index);
709 char tmp_c = script_array[script_index];
710 System.Diagnostics.Debug.WriteLine("Char is '" + tmp_c + "'");
711 System.Diagnostics.Debug.WriteLine("Unicode id is " + ((int) tmp_c));
712 int tmp_i = ((int) ((tmp_c > TYPE_MAX)?TYPE_NORMAL:typeTable[tmp_c]));
713 System.Diagnostics.Debug.WriteLine("Type is " + tmp_i);
714 System.Diagnostics.Debug.WriteLine("Mask is " + mask);
715 System.Diagnostics.Debug.WriteLine("(type & mask) is " + ((int) (tmp_i & mask)));
716 System.Diagnostics.Debug.WriteLine("orig token.size is " + token.size);
717 #endif
718  
719  
720 cur = script_array[script_index];
721 type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] );
722  
723 if ( ( type & mask ) != 0 )
724 {
725 System.Diagnostics.Debug.WriteLine( "mask break" );
726 break;
727 }
728  
729 if ( ( type & TYPE_SUBS ) == 0 )
730 {
731 // This is a simple range of characters. Scan to find the end
732 // of the range.
733  
734 System.Diagnostics.Debug.WriteLine( "simple range" );
735  
736 while ( true )
737 {
738 cur = script_array[++script_index];
739 type = ( ( cur > TYPE_MAX ) ? TYPE_NORMAL : typeTable[cur] );
740  
741 System.Diagnostics.Debug.WriteLine( "skipping '" + cur + "'" );
742  
743 if ( ( type & ( mask | TYPE_SUBS ) ) != 0 )
744 {
745 break;
746 }
747 }
748 token.type = TCL_TOKEN_TEXT;
749 token.size = script_index - token.script_index;
750 parse.numTokens++;
751  
752 System.Diagnostics.Debug.WriteLine( "end simple range" );
753 System.Diagnostics.Debug.WriteLine( "token.size is " + token.size );
754 System.Diagnostics.Debug.WriteLine( "parse.numTokens is " + parse.numTokens );
755 }
756 else if ( cur == '$' )
757 {
758 // This is a variable reference. Call parseVarName to do
759 // all the dirty work of parsing the name.
760  
761 System.Diagnostics.Debug.WriteLine( "dollar sign" );
762  
763 varToken = parse.numTokens;
764 parse = parseVarName( parse.interp, script_array, script_index, parse.endIndex - script_index, parse, true );
765 if ( parse.result != TCL.CompletionCode.OK )
766 {
767 return parse;
768 }
769 script_index += parse.getToken( varToken ).size;
770 }
771 else if ( cur == '[' )
772 {
773 // Command substitution. Call parseCommand recursively
774 // (and repeatedly) to parse the nested command(s), then
775 // throw away the parse information.
776  
777 System.Diagnostics.Debug.WriteLine( "command" );
778  
779 script_index++;
780 while ( true )
781 {
782 nested = parseCommand( parse.interp, script_array, script_index, parse.endIndex - script_index, parse.fileName, parse.lineNum, true );
783 if ( nested.result != TCL.CompletionCode.OK )
784 {
785 parse.termIndex = nested.termIndex;
786 parse.incomplete = nested.incomplete;
787 parse.result = nested.result;
788 return parse;
789 }
790 script_index = nested.commandStart + nested.commandSize;
791 if ( ( script_array[script_index - 1] == ']' ) && !nested.incomplete )
792 {
793 break;
794 }
795 if ( script_index == parse.endIndex )
796 {
797 if ( parse.interp != null )
798 {
799 parse.interp.setResult( "missing close-bracket" );
800 }
801 parse.termIndex = token.script_index;
802 parse.incomplete = true;
803 parse.result = TCL.CompletionCode.ERROR;
804 return parse;
805 }
806 }
807 token.type = TCL_TOKEN_COMMAND;
808 token.size = script_index - token.script_index;
809 parse.numTokens++;
810 }
811 else if ( cur == '\\' )
812 {
813 // Backslash substitution.
814  
815 System.Diagnostics.Debug.WriteLine( "backslash" );
816  
817 if ( script_array[script_index + 1] == '\n' )
818 {
819 if ( ( script_index + 2 ) == parse.endIndex )
820 {
821 parse.incomplete = true;
822 }
823  
824 // Note: backslash-newline is special in that it is
825 // treated the same as a space character would be. This
826 // means that it could terminate the token.
827  
828 if ( ( mask & TYPE_SPACE ) != 0 )
829 {
830 break;
831 }
832 }
833 token.type = TCL_TOKEN_BS;
834 bs = backslash( script_array, script_index );
835 token.size = bs.nextIndex - script_index;
836 parse.numTokens++;
837 script_index += token.size;
838 }
839 else if ( cur == '\x0000' )
840 {
841 // We encountered a null character. If it is the null
842 // character at the end of the string, then return.
843 // Otherwise generate a text token for the single
844 // character.
845  
846 System.Diagnostics.Debug.WriteLine( "null char" );
847 System.Diagnostics.Debug.WriteLine( "script_index is " + script_index );
848 System.Diagnostics.Debug.WriteLine( "parse.endIndex is " + parse.endIndex );
849  
850 if ( script_index == parse.endIndex )
851 {
852 break;
853 }
854  
855 token.type = TCL_TOKEN_TEXT;
856 token.size = 1;
857 parse.numTokens++;
858 script_index++;
859 }
860 else
861 {
862 throw new TclRuntimeError( "parseTokens encountered unknown character" );
863 }
864 } // end while (true)
865  
866  
867 if ( parse.numTokens == originalTokens )
868 {
869 // There was nothing in this range of text. Add an empty token
870 // for the empty range, so that there is always at least one
871 // token added.
872  
873 System.Diagnostics.Debug.WriteLine( "empty token" );
874  
875 token.type = TCL_TOKEN_TEXT;
876 token.size = 0;
877 parse.numTokens++;
878 }
879 else
880 {
881 System.Diagnostics.Debug.WriteLine( "non empty token case" );
882 }
883  
884 parse.termIndex = script_index;
885 parse.result = TCL.CompletionCode.OK;
886  
887 #if DEBUG
888 System.Diagnostics.Debug.WriteLine();
889 System.Diagnostics.Debug.WriteLine("Leaving Parser.parseTokens()");
890  
891 System.Diagnostics.Debug.WriteLine("after parse, parse.numTokens is " + parse.numTokens);
892 System.Diagnostics.Debug.WriteLine("after parse, token.size is " + token.size);
893 System.Diagnostics.Debug.WriteLine("after parse, token.hashCode() is " + token.GetHashCode());
894  
895  
896 //System.out.println( parse.toString() );
897  
898  
899 System.Diagnostics.Debug.Write("printing " + (parse.numTokens - originalTokens) + " token(s)");
900  
901 for (int k = originalTokens; k < parse.numTokens; k++)
902 {
903 token = parse.getToken(k);
904 System.Diagnostics.Debug.WriteLine(token);
905 }
906  
907 System.Diagnostics.Debug.WriteLine("done printing tokens");
908 #endif
909  
910 return parse;
911 }
912  
913 public static void evalObjv( Interp interp, TclObject[] objv, int length, int flags )
914 {
915 Command cmd;
916 WrappedCommand wCmd = null;
917 TclObject[] newObjv;
918 int i;
919 CallFrame savedVarFrame; //Saves old copy of interp.varFrame
920 // in case TCL.EVAL_GLOBAL was set.
921  
922 interp.resetResult();
923 if ( objv.Length == 0 )
924 {
925 return;
926 }
927  
928 // If the interpreter was deleted, return an error.
929  
930 if ( interp.deleted )
931 {
932 interp.setResult( "attempt to call eval in deleted interpreter" );
933 interp.setErrorCode( TclString.newInstance( "CORE IDELETE {attempt to call eval in deleted interpreter}" ) );
934 throw new TclException( TCL.CompletionCode.ERROR );
935 }
936  
937 // Check depth of nested calls to eval: if this gets too large,
938 // it's probably because of an infinite loop somewhere.
939  
940 if ( interp.nestLevel >= interp.maxNestingDepth )
941 {
942 throw new TclException( interp, "too many nested calls to eval (infinite loop?)" );
943 }
944 interp.nestLevel++;
945  
946 try
947 {
948 // Find the procedure to execute this command. If there isn't one,
949 // then see if there is a command "unknown". If so, create a new
950 // word array with "unknown" as the first word and the original
951 // command words as arguments. Then call ourselves recursively
952 // to execute it.
953  
954  
955 cmd = interp.getCommand( objv[0].ToString() );
956 if ( cmd == null )
957 wCmd = interp.getObjCommand( objv[0].ToString() );
958 // See if we are running as a slave interpretor, and this is a windows command
959 if ( cmd == null && wCmd == null && interp.slave != null )
960 {
961 wCmd = interp.slave.masterInterp.getObjCommand( objv[0].ToString() );
962 }
963 if ( cmd == null && wCmd == null )
964 {
965 newObjv = new TclObject[objv.Length + 1];
966 for ( i = ( objv.Length - 1 ); i >= 0; i-- )
967 {
968 newObjv[i + 1] = objv[i];
969 }
970 newObjv[0] = TclString.newInstance( "unknown" );
971 newObjv[0].preserve();
972 cmd = interp.getCommand( "unknown" );
973 if ( cmd == null )
974 {
975  
976 Debug.Assert( false, "invalid command name \"" + objv[0].ToString() + "\"" );
977 throw new TclException( interp, "invalid command name \"" + objv[0].ToString() + "\"" );
978 }
979 else
980 {
981 evalObjv( interp, newObjv, length, 0 );
982 }
983 newObjv[0].release();
984 return;
985 }
986  
987 // Finally, invoke the Command's cmdProc.
988  
989 interp.cmdCount++;
990 savedVarFrame = interp.varFrame;
991 if ( ( flags & TCL.EVAL_GLOBAL ) != 0 )
992 {
993 interp.varFrame = null;
994 }
995  
996 int rc = 0;
997 if ( cmd != null )
998 {
999 if ( cmd.cmdProc( interp, objv ) == TCL.CompletionCode.EXIT )
1000 throw new TclException( TCL.CompletionCode.EXIT );
1001 }
1002 else
1003 {
1004 rc = wCmd.objProc( wCmd.objClientData, interp, objv.Length, objv );
1005 if ( rc != 0 )
1006 {
1007 if ( rc == TCL.TCL_RETURN )
1008 throw new TclException( TCL.CompletionCode.RETURN );
1009 throw new TclException( TCL.CompletionCode.ERROR );
1010 }
1011 }
1012 interp.varFrame = savedVarFrame;
1013 }
1014 finally
1015 {
1016 interp.nestLevel--;
1017 }
1018 }
1019 internal static void logCommandInfo( Interp interp, char[] script_array, int script_index, int cmdIndex, int length, TclException e )
1020 // The exception caused by the script
1021 // evaluation.
1022 {
1023 string ellipsis;
1024 string msg;
1025 int offset;
1026 int pIndex;
1027  
1028 if ( interp.errAlreadyLogged )
1029 {
1030 // Someone else has already logged error information for this
1031 // command; we shouldn't add anything more.
1032  
1033 return;
1034 }
1035  
1036 // Compute the line number where the error occurred.
1037 // Note: The script array must be accessed directly
1038 // because we want to count from the beginning of
1039 // the script, not the current index.
1040  
1041 interp.errorLine = 1;
1042  
1043 for ( pIndex = 0; pIndex < cmdIndex; pIndex++ )
1044 {
1045 if ( script_array[pIndex] == '\n' )
1046 {
1047 interp.errorLine++;
1048 }
1049 }
1050  
1051  
1052 // Create an error message to add to errorInfo, including up to a
1053 // maximum number of characters of the command.
1054  
1055 if ( length < 0 )
1056 {
1057 //take into account the trailing '\0'
1058 int script_length = script_array.Length - 1;
1059  
1060 length = script_length - cmdIndex;
1061 }
1062 if ( length > 150 )
1063 {
1064 offset = 150;
1065 ellipsis = "...";
1066 }
1067 else
1068 {
1069 offset = length;
1070 ellipsis = "";
1071 }
1072  
1073 msg = new string( script_array, cmdIndex, offset );
1074 if ( !( interp.errInProgress ) )
1075 {
1076 interp.addErrorInfo( "\n while executing\n\"" + msg + ellipsis + "\"" );
1077 }
1078 else
1079 {
1080 interp.addErrorInfo( "\n invoked from within\n\"" + msg + ellipsis + "\"" );
1081 }
1082 interp.errAlreadyLogged = false;
1083 e.errIndex = cmdIndex + offset;
1084 }
1085 internal static TclObject evalTokens( Interp interp, TclToken[] tokenList, int tIndex, int count )
1086 {
1087 TclObject result, index, value;
1088 TclToken token;
1089 string p = null;
1090 string varName;
1091 BackSlashResult bs;
1092  
1093 // The only tricky thing about this procedure is that it attempts to
1094 // avoid object creation and string copying whenever possible. For
1095 // example, if the value is just a nested command, then use the
1096 // command's result object directly.
1097  
1098 result = null;
1099 for ( ; count > 0; count-- )
1100 {
1101 token = tokenList[tIndex];
1102  
1103 // The switch statement below computes the next value to be
1104 // concat to the result, as either a range of text or an
1105 // object.
1106  
1107 value = null;
1108 switch ( token.type )
1109 {
1110  
1111 case TCL_TOKEN_TEXT:
1112 p = token.TokenString;
1113 break;
1114  
1115  
1116 case TCL_TOKEN_BS:
1117 bs = backslash( token.script_array, token.script_index );
1118 if ( bs.isWordSep )
1119 {
1120 p = "\\" + bs.c;
1121 }
1122 else
1123 {
1124 System.Char ch = bs.c;
1125 p = ch.ToString();
1126 }
1127 break;
1128  
1129  
1130 case TCL_TOKEN_COMMAND:
1131 interp.evalFlags |= Parser.TCL_BRACKET_TERM;
1132 token.script_index++;
1133  
1134 //should the nest level be changed???
1135 //interp.nestLevel++;
1136  
1137 eval2( interp, token.script_array, token.script_index, token.size - 2, 0 );
1138  
1139 token.script_index--;
1140 //interp.nestLevel--;
1141 value = interp.getResult();
1142 break;
1143  
1144  
1145 case TCL_TOKEN_VARIABLE:
1146 if ( token.numComponents == 1 )
1147 {
1148 index = null;
1149 }
1150 else
1151 {
1152 index = evalTokens( interp, tokenList, tIndex + 2, token.numComponents - 1 );
1153 if ( index == null )
1154 {
1155 return null;
1156 }
1157 }
1158 varName = tokenList[tIndex + 1].TokenString;
1159  
1160 // In order to get the existing expr parser to work with the
1161 // new Parser, we test the interp.noEval flag which is set
1162 // by the expr parser. If it is != 0, then we do not evaluate
1163 // the variable. This should be removed when the new expr
1164 // parser is implemented.
1165  
1166 if ( interp.noEval == 0 )
1167 {
1168 if ( index != null )
1169 {
1170 try
1171 {
1172  
1173 value = interp.getVar( varName, index.ToString(), 0 );
1174 }
1175 finally
1176 {
1177 index.release();
1178 }
1179 }
1180 else
1181 {
1182 value = interp.getVar( varName, null, 0 );
1183 }
1184 }
1185 else
1186 {
1187 value = TclString.newInstance( "" );
1188 value.preserve();
1189 }
1190 count -= token.numComponents;
1191 tIndex += token.numComponents;
1192 break;
1193  
1194  
1195 default:
1196 throw new TclRuntimeError( "unexpected token type in evalTokens" );
1197  
1198 }
1199  
1200 // If value isn't null, the next piece of text comes from that
1201 // object; otherwise, take value of p.
1202  
1203 if ( result == null )
1204 {
1205 if ( value != null )
1206 {
1207 result = value;
1208 }
1209 else
1210 {
1211 result = TclString.newInstance( p );
1212 }
1213 result.preserve();
1214 }
1215 else
1216 {
1217 if ( result.Shared )
1218 {
1219 result.release();
1220 result = result.duplicate();
1221 result.preserve();
1222 }
1223 if ( value != null )
1224 {
1225  
1226 p = value.ToString();
1227 }
1228 TclString.append( result, p );
1229 }
1230 tIndex++;
1231 }
1232 return result;
1233 }
1234 public static void eval2( Interp interp, char[] script_array, int script_index, int numBytes, int flags )
1235 {
1236 int i;
1237 int objUsed = 0;
1238 int nextIndex, tokenIndex;
1239 int commandLength, bytesLeft;
1240 bool nested;
1241 TclObject[] objv;
1242 TclObject obj;
1243 TclParse parse = null;
1244 TclToken token;
1245  
1246 // Saves old copy of interp.varFrame in case TCL.EVAL_GLOBAL was set
1247 CallFrame savedVarFrame;
1248  
1249 // Take into account the trailing '\0'
1250 int script_length = script_array.Length - 1;
1251  
1252  
1253 // These are modified instead of script_array and script_index
1254 char[] src_array = script_array;
1255 int src_index = script_index;
1256  
1257 #if DEBUG
1258 System.Diagnostics.Debug.WriteLine();
1259 System.Diagnostics.Debug.WriteLine("Entered eval2()");
1260 System.Diagnostics.Debug.Write("now to eval2 the string \"");
1261 for (int k = script_index; k < script_array.Length; k++)
1262 {
1263 System.Diagnostics.Debug.Write(script_array[k]);
1264 }
1265 System.Diagnostics.Debug.WriteLine("\"");
1266 #endif
1267  
1268  
1269  
1270 if ( numBytes < 0 )
1271 {
1272 numBytes = script_length - script_index;
1273 }
1274 interp.resetResult();
1275 savedVarFrame = interp.varFrame;
1276 if ( ( flags & TCL.EVAL_GLOBAL ) != 0 )
1277 {
1278 interp.varFrame = null;
1279 }
1280  
1281 // Each iteration through the following loop parses the next
1282 // command from the script and then executes it.
1283  
1284 bytesLeft = numBytes;
1285  
1286 // Init objv with the most commonly used array size
1287 objv = grabObjv( interp, 3 );
1288  
1289 if ( ( interp.evalFlags & TCL_BRACKET_TERM ) != 0 )
1290 {
1291 nested = true;
1292 }
1293 else
1294 {
1295 nested = false;
1296 }
1297 interp.evalFlags &= ~TCL_BRACKET_TERM;
1298  
1299 try
1300 {
1301  
1302 do
1303 {
1304 parse = parseCommand( interp, src_array, src_index, bytesLeft, null, 0, nested );
1305  
1306 if ( parse.result != TCL.CompletionCode.OK )
1307 {
1308 throw new TclException( parse.result );
1309 }
1310  
1311 // The test on noEval is temporary. As soon as the new expr
1312 // parser is implemented it should be removed.
1313  
1314 if ( parse.numWords > 0 && interp.noEval == 0 )
1315 {
1316 // Generate an array of objects for the words of the command.
1317  
1318 try
1319 {
1320 tokenIndex = 0;
1321 token = parse.getToken( tokenIndex );
1322  
1323 // Test to see if new space needs to be allocated. If objv
1324 // is the EXACT size of parse.numWords, then no allocation
1325 // needs to be performed.
1326  
1327 if ( objv.Length != parse.numWords )
1328 {
1329 //System.out.println("need new size " + objv.length);
1330 releaseObjv( interp, objv ); //let go of resource
1331 objv = grabObjv( interp, parse.numWords ); //get new resource
1332 }
1333 else
1334 {
1335 //System.out.println("reusing size " + objv.length);
1336 }
1337  
1338 for ( objUsed = 0; objUsed < parse.numWords; objUsed++ )
1339 {
1340 obj = evalTokens( interp, parse.tokenList, tokenIndex + 1, token.numComponents );
1341 if ( obj == null )
1342 {
1343 throw new TclException( TCL.CompletionCode.ERROR );
1344 }
1345 else
1346 {
1347 objv[objUsed] = obj;
1348 if ( token.type == TCL_TOKEN_EXPAND_WORD )
1349 {
1350 int numElements;
1351 int code;
1352 TclList.setListFromAny( null, objv[objUsed] );
1353 TclObject[] elements = TclList.getElements( null, objv[objUsed] );
1354 if ( elements.Length == 0 )
1355 {
1356 elements = new TclObject[1];
1357 elements[0] = TclString.newInstance("{}") ;
1358 TclList.setListFromAny( null, elements[0] );
1359 }
1360 numElements = elements.Length;
1361 /*
1362 * Some word expansion was requested. Check for objv resize.
1363 */
1364  
1365 int objIdx = objUsed + numElements - 1;
1366 Array.Resize( ref objv, objIdx+1 );
1367 while ( numElements-- != 0 )
1368 {
1369 objv[objIdx] = elements[numElements];
1370 objv[objIdx].preserve();
1371 objIdx--;
1372 }
1373 objUsed = objv.Length-1;
1374 }
1375 }
1376 tokenIndex += ( token.numComponents + 1 );
1377 token = parse.getToken( tokenIndex );
1378 }
1379  
1380 // Execute the command and free the objects for its words.
1381 try
1382 {
1383 evalObjv( interp, objv, bytesLeft, 0 );
1384 }
1385 catch ( System.StackOverflowException e )
1386 {
1387 interp.setResult( "too many nested calls" + " to eval (infinite loop?)" );
1388 throw new TclException( TCL.CompletionCode.ERROR );
1389 }
1390 }
1391 catch ( TclException e )
1392 {
1393 // Generate various pieces of error information, such
1394 // as the line number where the error occurred and
1395 // information to add to the errorInfo variable. Then
1396 // free resources that had been allocated
1397 // to the command.
1398  
1399 if ( e.getCompletionCode() == TCL.CompletionCode.ERROR && !( interp.errAlreadyLogged ) )
1400 {
1401 commandLength = parse.commandSize;
1402  
1403 char term = script_array[parse.commandStart + commandLength - 1];
1404 int type = charType( term );
1405 int terminators;
1406 if ( nested )
1407 {
1408 terminators = TYPE_COMMAND_END | TYPE_CLOSE_BRACK;
1409 }
1410 else
1411 {
1412 terminators = TYPE_COMMAND_END;
1413 }
1414 if ( ( type & terminators ) != 0 )
1415 {
1416 // The command where the error occurred didn't end
1417 // at the end of the script (i.e. it ended at a
1418 // terminator character such as ";". Reduce the
1419 // length by one so that the error message
1420 // doesn't include the terminator character.
1421  
1422 commandLength -= 1;
1423 }
1424 interp.varFrame = savedVarFrame;
1425 logCommandInfo( interp, script_array, script_index, parse.commandStart, commandLength, e );
1426 throw e;
1427 }
1428 else
1429 throw;
1430 }
1431 finally
1432 {
1433 for ( i = 0; i < objUsed; i++ )
1434 {
1435 objv[i].release();
1436 }
1437 objUsed = 0;
1438  
1439 parse.release(); // Cleanup parser resources
1440 }
1441 }
1442  
1443  
1444 // Advance to the next command in the script.
1445  
1446 nextIndex = parse.commandStart + parse.commandSize;
1447 bytesLeft -= ( nextIndex - src_index );
1448 src_index = nextIndex;
1449 if ( nested && ( src_index > 1 ) && ( src_array[src_index - 1] == ']' ) )
1450 {
1451  
1452 // We get here in the special case where the TCL_BRACKET_TERM
1453 // flag was set in the interpreter and we reached a close
1454 // bracket in the script. Return immediately.
1455  
1456 interp.termOffset = ( src_index - 1 ) - script_index;
1457 interp.varFrame = savedVarFrame;
1458 return;
1459 }
1460 }
1461 while ( bytesLeft > 0 );
1462 }
1463 finally
1464 {
1465 if ( parse != null )
1466 {
1467 parse.release(); // Let go of parser resources
1468 }
1469 releaseObjv( interp, objv ); // Let go of objv buffer
1470 }
1471  
1472 interp.termOffset = src_index - script_index;
1473 interp.varFrame = savedVarFrame;
1474 return;
1475 }
1476 public static TclParse parseVarName( Interp interp, char[] script_array, int script_index, int numBytes, TclParse parse, bool append )
1477 // Non-zero means append tokens to existing
1478 // information in parse; zero means ignore
1479 // existing tokens in parse and reinitialize
1480 // it.
1481 {
1482 char cur;
1483 TclToken token, startToken;
1484 int endIndex, varIndex;
1485  
1486 #if DEBUG
1487 System.Diagnostics.Debug.WriteLine();
1488 System.Diagnostics.Debug.WriteLine("Entered parseVarName()");
1489 System.Diagnostics.Debug.Write("now to parse var off the string \"");
1490 for (int k = script_index; k < script_array.Length; k++)
1491 {
1492 System.Diagnostics.Debug.Write(script_array[k]);
1493 }
1494 System.Diagnostics.Debug.WriteLine("\"");
1495 #endif
1496  
1497  
1498 if ( numBytes >= 0 )
1499 {
1500 endIndex = script_index + numBytes;
1501 }
1502 else
1503 {
1504 endIndex = script_array.Length - 1;
1505 }
1506 if ( !append )
1507 {
1508 parse = new TclParse( interp, script_array, endIndex, null, -1 );
1509 }
1510  
1511 // Generate one token for the variable, an additional token for the
1512 // name, plus any number of additional tokens for the index, if
1513 // there is one.
1514  
1515 token = parse.getToken( parse.numTokens );
1516 token.type = TCL_TOKEN_VARIABLE;
1517 token.script_array = script_array;
1518 token.script_index = script_index;
1519 varIndex = parse.numTokens;
1520 parse.numTokens++;
1521 script_index++;
1522 if ( script_index >= endIndex )
1523 {
1524 // The dollar sign isn't followed by a variable name.
1525 // replace the TCL_TOKEN_VARIABLE token with a
1526 // TCL_TOKEN_TEXT token for the dollar sign.
1527  
1528 token.type = TCL_TOKEN_TEXT;
1529 token.size = 1;
1530 token.numComponents = 0;
1531 parse.result = TCL.CompletionCode.OK;
1532 return parse;
1533 }
1534 startToken = token;
1535 token = parse.getToken( parse.numTokens );
1536  
1537 // The name of the variable can have three forms:
1538 // 1. The $ sign is followed by an open curly brace. Then
1539 // the variable name is everything up to the next close
1540 // curly brace, and the variable is a scalar variable.
1541 // 2. The $ sign is not followed by an open curly brace. Then
1542 // the variable name is everything up to the next
1543 // character that isn't a letter, digit, or underscore.
1544 // :: sequences are also considered part of the variable
1545 // name, in order to support namespaces. If the following
1546 // character is an open parenthesis, then the information
1547 // between parentheses is the array element name.
1548 // 3. The $ sign is followed by something that isn't a letter,
1549 // digit, or underscore: in this case, there is no variable
1550 // name and the token is just "$".
1551  
1552 if ( script_array[script_index] == '{' )
1553 {
1554 System.Diagnostics.Debug.WriteLine( "parsing curley var name" );
1555  
1556 script_index++;
1557 token.type = TCL_TOKEN_TEXT;
1558 token.script_array = script_array;
1559 token.script_index = script_index;
1560 token.numComponents = 0;
1561  
1562 while ( true )
1563 {
1564 if ( script_index == endIndex )
1565 {
1566 if ( interp != null )
1567 {
1568 interp.setResult( "missing close-brace for variable name" );
1569 }
1570 parse.termIndex = token.script_index - 1;
1571 parse.incomplete = true;
1572 parse.result = TCL.CompletionCode.ERROR;
1573 return parse;
1574 }
1575 if ( script_array[script_index] == '}' )
1576 {
1577 break;
1578 }
1579 script_index++;
1580 }
1581 token.size = script_index - token.script_index;
1582 startToken.size = script_index - startToken.script_index;
1583 parse.numTokens++;
1584 script_index++;
1585 }
1586 else
1587 {
1588 System.Diagnostics.Debug.WriteLine( "parsing non curley var name" );
1589  
1590 token.type = TCL_TOKEN_TEXT;
1591 token.script_array = script_array;
1592 token.script_index = script_index;
1593 token.numComponents = 0;
1594 while ( script_index != endIndex )
1595 {
1596 cur = script_array[script_index];
1597 if ( ( System.Char.IsLetterOrDigit( cur ) ) || ( cur == '_' ) )
1598 {
1599 script_index++;
1600 continue;
1601 }
1602 if ( ( cur == ':' ) && ( ( ( script_index + 1 ) != endIndex ) && ( script_array[script_index + 1] == ':' ) ) )
1603 {
1604 script_index += 2;
1605 while ( ( script_index != endIndex ) && ( script_array[script_index] == ':' ) )
1606 {
1607 script_index++;
1608 }
1609 continue;
1610 }
1611 break;
1612 }
1613 token.size = script_index - token.script_index;
1614 if ( token.size == 0 )
1615 {
1616 // The dollar sign isn't followed by a variable name.
1617 // replace the TCL_TOKEN_VARIABLE token with a
1618 // TCL_TOKEN_TEXT token for the dollar sign.
1619  
1620 System.Diagnostics.Debug.WriteLine( "single $ with no var name found" );
1621  
1622 startToken.type = TCL_TOKEN_TEXT;
1623 startToken.size = 1;
1624 startToken.numComponents = 0;
1625 parse.result = TCL.CompletionCode.OK;
1626 return parse;
1627 }
1628 parse.numTokens++;
1629 if ( ( script_index != endIndex ) && ( script_array[script_index] == '(' ) )
1630 {
1631 // This is a reference to an array element. Call
1632 // parseTokens recursively to parse the element name,
1633 // since it could contain any number of substitutions.
1634  
1635 System.Diagnostics.Debug.WriteLine( "parsing array element" );
1636  
1637 script_index++;
1638 parse = parseTokens( script_array, script_index, TYPE_CLOSE_PAREN, parse );
1639 if ( parse.result != TCL.CompletionCode.OK )
1640 {
1641 return parse;
1642 }
1643 if ( ( parse.termIndex == endIndex ) || ( parse.inString[parse.termIndex] != ')' ) )
1644 {
1645 if ( interp != null )
1646 {
1647 interp.setResult( "missing )" );
1648 }
1649 parse.termIndex = script_index - 1;
1650 parse.incomplete = true;
1651 parse.result = TCL.CompletionCode.ERROR;
1652 return parse;
1653 }
1654 script_index = parse.termIndex + 1;
1655 }
1656 }
1657  
1658  
1659 #if DEBUG
1660 System.Diagnostics.Debug.WriteLine("default end parse case");
1661 System.Diagnostics.Debug.Write("var token is \"");
1662 for (int k = startToken.script_index; k < script_index; k++)
1663 {
1664 System.Diagnostics.Debug.Write(script_array[k]);
1665 }
1666 System.Diagnostics.Debug.WriteLine("\"");
1667 #endif
1668  
1669 startToken.size = script_index - startToken.script_index;
1670 startToken.numComponents = parse.numTokens - ( varIndex + 1 );
1671 parse.result = TCL.CompletionCode.OK;
1672 return parse;
1673 }
1674  
1675  
1676 public static ParseResult parseVar( Interp interp, string inString )
1677 {
1678 TclParse parse;
1679 TclObject obj;
1680  
1681 System.Diagnostics.Debug.WriteLine( "Entered parseVar()" );
1682 System.Diagnostics.Debug.Write( "now to parse var off the string \"" + inString + "\"" );
1683  
1684  
1685 CharPointer src = new CharPointer( inString );
1686 parse = parseVarName( interp, src.array, src.index, -1, null, false );
1687 if ( parse.result != TCL.CompletionCode.OK )
1688 {
1689  
1690 throw new TclException( interp, interp.getResult().ToString() );
1691 }
1692  
1693 try
1694 {
1695 System.Diagnostics.Debug.Write( "parsed " + parse.numTokens + " tokens" );
1696  
1697 if ( parse.numTokens == 1 )
1698 {
1699 // There isn't a variable name after all: the $ is just a $.
1700 return new ParseResult( "$", 1 );
1701 }
1702  
1703 obj = evalTokens( interp, parse.tokenList, 0, parse.numTokens );
1704 if ( !obj.Shared )
1705 {
1706 throw new TclRuntimeError( "parseVar got temporary object from evalTokens" );
1707 }
1708 return new ParseResult( obj, parse.tokenList[0].size );
1709 }
1710 finally
1711 {
1712 parse.release(); // Release parser resources
1713 }
1714 }
1715 internal static bool commandComplete( string inString, int length )
1716 // Number of bytes in script.
1717 {
1718 TclParse parse;
1719  
1720 CharPointer src = new CharPointer( inString );
1721  
1722 do
1723 {
1724 parse = parseCommand( null, src.array, src.index, length, null, 0, false );
1725  
1726 src.index = parse.commandStart + parse.commandSize;
1727  
1728 parse.release(); // Release parser resources
1729  
1730 if ( src.index >= length )
1731 {
1732 break;
1733 }
1734 }
1735 while ( parse.result == TCL.CompletionCode.OK );
1736  
1737 if ( parse.incomplete )
1738 {
1739 return false;
1740 }
1741 return true;
1742 }
1743 internal static bool objCommandComplete( TclObject obj )
1744 // Points to object holding script
1745 // to check.
1746 {
1747  
1748 string inString = obj.ToString();
1749 return commandComplete( inString, inString.Length );
1750 }
1751 internal static BackSlashResult backslash( char[] script_array, int script_index )
1752 {
1753 int result;
1754  
1755 script_index++;
1756 int endIndex = script_array.Length - 1;
1757  
1758 if ( script_index == endIndex )
1759 {
1760 return new BackSlashResult( '\\', script_index );
1761 }
1762  
1763 char c = script_array[script_index];
1764 switch ( c )
1765 {
1766  
1767 case 'a':
1768 return new BackSlashResult( (char)0x7, script_index + 1 );
1769  
1770 case 'b':
1771 return new BackSlashResult( (char)0x8, script_index + 1 );
1772  
1773 case 'f':
1774 return new BackSlashResult( (char)0xc, script_index + 1 );
1775  
1776 case 'n':
1777 return new BackSlashResult( '\n', script_index + 1 );
1778  
1779 case 'r':
1780 return new BackSlashResult( '\r', script_index + 1 );
1781  
1782 case 't':
1783 return new BackSlashResult( '\t', script_index + 1 );
1784  
1785 case 'v':
1786 return new BackSlashResult( (char)0xb, script_index + 1 );
1787  
1788 case 'x':
1789 script_index++;
1790 if ( script_index < endIndex )
1791 {
1792 c = script_array[script_index];
1793  
1794 if ( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) || ( ( c >= 'a' ) && ( c <= 'f' ) ) )
1795 {
1796  
1797 string str = new string( script_array, script_index, endIndex - script_index );
1798 StrtoulResult res = Util.strtoul( str, 0, 16 );
1799 if ( res.errno == 0 )
1800 {
1801 // We force res.value to be a 8-bit (ASCII) character
1802 // so that it is compatible with Tcl.
1803  
1804 char b = (char)( res.value & 0xff );
1805 return new BackSlashResult( b, script_index + res.index );
1806 }
1807 }
1808 }
1809 return new BackSlashResult( 'x', script_index );
1810  
1811 case 'u': // TODO -- determine how to handle Unicode
1812 int count, n;
1813 result = 0;
1814 for ( count = 0; count < 4; count++ )
1815 {
1816 script_index++;
1817 c = script_array[script_index];
1818 if ( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'a' ) && ( c <= 'f' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
1819 {
1820 n = c - '0';
1821 if ( n > 9 )
1822 {
1823 n = n + '0' + 10 - 'A';
1824 }
1825 if ( n > 16 )
1826 {
1827 n = n + 'A' - 'a';
1828 }
1829 result = ( result << 4 ) + n;
1830 }
1831 else
1832 {
1833 break;
1834 }
1835 }
1836 if ( count == 0 )
1837 {
1838 result = 'u';
1839 }
1840 return new BackSlashResult( (char)result, script_index + 1 );
1841  
1842 case '\r':
1843 case '\n':
1844 if ( c == '\r' )
1845 {
1846 if ( ( script_index + 1 ) < endIndex )
1847 {
1848 if ( script_array[script_index + 1] == '\n' )
1849 {
1850 script_index++;
1851 }
1852 }
1853 }
1854 do
1855 {
1856 script_index++;
1857 c = script_array[script_index];
1858 }
1859 while ( ( script_index < endIndex ) && ( ( c == ' ' ) || ( c == '\t' ) ) );
1860 return new BackSlashResult( (char)' ', script_index );
1861  
1862 case (char)( 0 ):
1863 return new BackSlashResult( (char)'\\', script_index + 1 );
1864  
1865 default:
1866 if ( ( c >= '0' ) && ( c <= '9' ) )
1867 {
1868 // Convert it to an octal number. This implementation is
1869 // compatible with tcl 7.6 - characters 8 and 9 are allowed.
1870  
1871 result = c - '0';
1872 script_index++;
1873  
1874 {
1875 if ( script_index == endIndex )
1876 {
1877 goto getoctal_brk;
1878 }
1879 c = script_array[script_index];
1880 if ( !( ( c >= '0' ) && ( c <= '9' ) ) )
1881 {
1882 goto getoctal_brk;
1883 }
1884 result = ( result * 8 ) + ( c - '0' );
1885 script_index++;
1886  
1887 if ( script_index == endIndex )
1888 {
1889 goto getoctal_brk;
1890 }
1891 c = script_array[script_index];
1892 if ( !( ( c >= '0' ) && ( c <= '9' ) ) )
1893 {
1894 goto getoctal_brk;
1895 }
1896 result = ( result * 8 ) + ( c - '0' );
1897 script_index++;
1898 }
1899  
1900 getoctal_brk:
1901 ;
1902  
1903  
1904 // We force result to be a 8-bit (ASCII) character so
1905 // that it compatible with Tcl 7.6.
1906  
1907 return new BackSlashResult( (char)( result & 0xff ), script_index );
1908 }
1909 else
1910 {
1911 return new BackSlashResult( c, script_index + 1 );
1912 }
1913 }
1914 }
1915  
1916  
1917  
1918  
1919  
1920  
1921 internal static char charType( char c )
1922 {
1923 return ( ( c > TYPE_MAX ) ? TYPE_NORMAL : typeTable[c] );
1924 }
1925  
1926 // The following table provides parsing information about each possible
1927 // character.
1928 //
1929 // The method charType is used to index into the table and return
1930 // information about its character argument. The following return
1931 // values are defined.
1932 //
1933 // TYPE_NORMAL - All characters that don't have special significance
1934 // to the Tcl parser.
1935 // TYPE_SPACE - The character is a whitespace character other
1936 // than newline.
1937 // TYPE_COMMAND_END - Character is newline or semicolon.
1938 // TYPE_SUBS - Character begins a substitution or has other
1939 // special meaning in parseTokens: backslash, dollar
1940 // sign, open bracket, or null.
1941 // TYPE_QUOTE - Character is a double quote.
1942 // TYPE_CLOSE_PAREN - Character is a right parenthesis.
1943 // TYPE_CLOSE_BRACK - Character is a right square bracket.
1944 // TYPE_BRACE - Character is a curly brace (either left or right).
1945  
1946 internal const char TYPE_NORMAL = (char)( 0 );
1947 internal const char TYPE_SPACE = (char)( 0x1 );
1948 internal const char TYPE_COMMAND_END = (char)( 0x2 );
1949 internal const char TYPE_SUBS = (char)( 0x4 );
1950 internal const char TYPE_QUOTE = (char)( 0x8 );
1951 internal const char TYPE_CLOSE_PAREN = (char)( 0x10 );
1952 internal const char TYPE_CLOSE_BRACK = (char)( 0x20 );
1953 internal const char TYPE_BRACE = (char)( 0x40 );
1954  
1955 // This is the largest value in the type table. If a
1956 // char value is larger then the char type is TYPE_NORMAL.
1957 // Lookup -> ((c > TYPE_MAX) ? TYPE_NORMAL : typeTable[c])
1958  
1959 internal const char TYPE_MAX = (char)( 127 );
1960  
1961  
1962 internal static char[] typeTable;
1963  
1964  
1965 // Type values defined for TclToken structures. These values are
1966 // defined as mask bits so that it's easy to check for collections of
1967 // types.
1968 //
1969 // TCL_TOKEN_WORD - The token describes one word of a command,
1970 // from the first non-blank character of
1971 // the word (which may be " or {) up to but
1972 // not including the space, semicolon, or
1973 // bracket that terminates the word.
1974 // NumComponents counts the total number of
1975 // sub-tokens that make up the word. This
1976 // includes, for example, sub-tokens of
1977 // TCL_TOKEN_VARIABLE tokens.
1978 // TCL_TOKEN_SIMPLE_WORD - This token is just like TCL_TOKEN_WORD
1979 // except that the word is guaranteed to
1980 // consist of a single TCL_TOKEN_TEXT
1981 // sub-token.
1982 // TCL_TOKEN_TEXT - The token describes a range of literal
1983 // text that is part of a word.
1984 // NumComponents is always 0.
1985 // TCL_TOKEN_BS - The token describes a backslash sequence
1986 // that must be collapsed. NumComponents
1987 // is always 0.
1988 // TCL_TOKEN_COMMAND - The token describes a command whose result
1989 // must be substituted into the word. The
1990 // token includes the enclosing brackets.
1991 // NumComponents is always 0.
1992 // TCL_TOKEN_VARIABLE - The token describes a variable
1993 // substitution, including the dollar sign,
1994 // variable name, and array index (if there
1995 // is one) up through the right
1996 // parentheses. NumComponents tells how
1997 // many additional tokens follow to
1998 // represent the variable name. The first
1999 // token will be a TCL_TOKEN_TEXT token
2000 // that describes the variable name. If
2001 // the variable is an array reference then
2002 // there will be one or more additional
2003 // tokens, of type TCL_TOKEN_TEXT,
2004 // TCL_TOKEN_BS, TCL_TOKEN_COMMAND, and
2005 // TCL_TOKEN_VARIABLE, that describe the
2006 // array index; numComponents counts the
2007 // total number of nested tokens that make
2008 // up the variable reference, including
2009 // sub-tokens of TCL_TOKEN_VARIABLE tokens.
2010  
2011 internal const int TCL_TOKEN_WORD = 1;
2012 internal const int TCL_TOKEN_SIMPLE_WORD = 2;
2013 internal const int TCL_TOKEN_TEXT = 4;
2014 internal const int TCL_TOKEN_BS = 8;
2015 internal const int TCL_TOKEN_COMMAND = 16;
2016 internal const int TCL_TOKEN_VARIABLE = 32;
2017 //#define TCL_TOKEN_SUB_EXPR 64
2018 //#define TCL_TOKEN_OPERATOR 128
2019 internal const int TCL_TOKEN_EXPAND_WORD = 256;
2020  
2021  
2022 // Note: Most of the variables below will not be used until the
2023 // Compilier is implemented, but are left for consistency.
2024  
2025 // A structure of the following type is filled in by parseCommand.
2026 // It describes a single command parsed from an input string.
2027  
2028 // evalFlag bits for Interp structures:
2029 //
2030 // TCL_BRACKET_TERM 1 means that the current script is terminated by
2031 // a close bracket rather than the end of the string.
2032 // TCL_ALLOW_EXCEPTIONS 1 means it's OK for the script to terminate with
2033 // a code other than TCL_OK or TCL_ERROR; 0 means
2034 // codes other than these should be turned into errors.
2035  
2036 public const int TCL_BRACKET_TERM = 1;
2037 public const int TCL_ALLOW_EXCEPTIONS = 4;
2038  
2039 // Flag bits for Interp structures:
2040 //
2041 // DELETED: Non-zero means the interpreter has been deleted:
2042 // don't process any more commands for it, and destroy
2043 // the structure as soon as all nested invocations of
2044 // Tcl_Eval are done.
2045 // ERR_IN_PROGRESS: Non-zero means an error unwind is already in
2046 // progress. Zero means a command proc has been
2047 // invoked since last error occurred.
2048 // ERR_ALREADY_LOGGED: Non-zero means information has already been logged
2049 // in $errorInfo for the current Tcl_Eval instance,
2050 // so Tcl_Eval needn't log it (used to implement the
2051 // "error message log" command).
2052 // ERROR_CODE_SET: Non-zero means that Tcl_SetErrorCode has been
2053 // called to record information for the current
2054 // error. Zero means Tcl_Eval must clear the
2055 // errorCode variable if an error is returned.
2056 // EXPR_INITIALIZED: Non-zero means initialization specific to
2057 // expressions has been carried out.
2058 // DONT_COMPILE_CMDS_INLINE: Non-zero means that the bytecode compiler
2059 // should not compile any commands into an inline
2060 // sequence of instructions. This is set 1, for
2061 // example, when command traces are requested.
2062 // RAND_SEED_INITIALIZED: Non-zero means that the randSeed value of the
2063 // interp has not be initialized. This is set 1
2064 // when we first use the rand() or srand() functions.
2065 // SAFE_INTERP: Non zero means that the current interp is a
2066 // safe interp (ie it has only the safe commands
2067 // installed, less priviledge than a regular interp).
2068 // USE_EVAL_DIRECT: Non-zero means don't use the compiler or byte-code
2069 // interpreter; instead, have Tcl_EvalObj call
2070 // Tcl_EvalDirect. Used primarily for testing the
2071 // new parser.
2072  
2073 public const int DELETED = 1;
2074 public const int ERR_IN_PROGRESS = 2;
2075 public const int ERR_ALREADY_LOGGED = 4;
2076 public const int ERROR_CODE_SET = 8;
2077 public const int EXPR_INITIALIZED = 0x10;
2078 public const int DONT_COMPILE_CMDS_INLINE = 0x20;
2079 public const int RAND_SEED_INITIALIZED = 0x40;
2080 public const int SAFE_INTERP = 0x80;
2081 public const int USE_EVAL_DIRECT = 0x100;
2082  
2083  
2084  
2085  
2086  
2087  
2088 // These are private read only values that are used by the parser
2089 // class to implement a TclObject[] cache
2090  
2091 // Max size of array to cache (1..N)
2092 private const int OBJV_CACHE_MAX = 10;
2093  
2094 // The number of array to cache for each size
2095 // for example if the number of 3 elements is set to 5
2096 // an array of 5 TclObject[] objects
2097 // which will each be 3 elements long
2098  
2099 private static readonly int[] OBJV_CACHE_SIZES = new int[] { 0, 4, 4, 10, 4, 4, 4, 4, 4, 4 };
2100  
2101 // use test results
2102 // 1 373
2103 // 2 2424
2104 // 3 11889
2105 // 4 840
2106 // 5 1374
2107 // 6 926
2108 // 7 0
2109 // 8 74
2110 // 9 0
2111  
2112  
2113 internal static void init( Interp interp )
2114 {
2115 //System.out.println("called Parser.init()");
2116  
2117 TclObject[][][] OBJV = new TclObject[OBJV_CACHE_MAX][][];
2118 int[] USED = new int[OBJV_CACHE_MAX];
2119  
2120 int i, j, size;
2121  
2122 for ( i = 0; i < OBJV_CACHE_MAX; i++ )
2123 {
2124 size = OBJV_CACHE_SIZES[i];
2125 //System.out.println("size " + i + " has " + size + " cache blocks");
2126 OBJV[i] = new TclObject[size][];
2127 USED[i] = 0;
2128 for ( j = 0; j < size; j++ )
2129 {
2130 OBJV[i][j] = new TclObject[i];
2131 }
2132 }
2133  
2134 interp.parserObjv = OBJV;
2135 interp.parserObjvUsed = USED;
2136 }
2137  
2138  
2139 private static TclObject[] grabObjv( Interp interp, int size )
2140 {
2141  
2142 if ( size >= OBJV_CACHE_MAX )
2143 {
2144 //System.out.println("allocate for big objv of size " + size);
2145 return new TclObject[size];
2146 }
2147  
2148 //get array of used markers for this size
2149 int OPEN = interp.parserObjvUsed[size];
2150  
2151 if ( OPEN < OBJV_CACHE_SIZES[size] )
2152 {
2153 // Found an open cache slot
2154 //System.out.println("cache hit for objv of size " + size);
2155 interp.parserObjvUsed[size] += 1;
2156 return interp.parserObjv[size][OPEN];
2157 }
2158 else
2159 {
2160 // Did not find a free cache array of this size
2161 //System.out.println("cache miss for objv of size " + size);
2162 return new TclObject[size];
2163 }
2164 }
2165  
2166  
2167 private static void releaseObjv( Interp interp, TclObject[] objv )
2168 {
2169 int size = objv.Length;
2170  
2171 if ( size >= OBJV_CACHE_MAX )
2172 {
2173 //System.out.println("release for big objv of size " + size);
2174 return;
2175 }
2176  
2177 int OPEN = interp.parserObjvUsed[size];
2178  
2179 if ( OPEN > 0 )
2180 {
2181 OPEN--;
2182 interp.parserObjvUsed[size] = OPEN;
2183 interp.parserObjv[size][OPEN] = objv;
2184 //System.out.println("released objv of size " + size);
2185 }
2186 /*
2187 else {
2188 System.out.println("no release for objv of size " + size);
2189 }
2190 */
2191  
2192 return;
2193 }
2194 static Parser()
2195 {
2196 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 };
2197 }
2198 } // end class Parser
2199 }