wasCSharpSQLite – Blame information for rev
?pathlinks?
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 | } |