Differences From Artifact [1aebb12607]:
- File generic/tclCompExpr.c — part of check-in [c9bbc5d0ee] at 2007-07-09 13:00:37 on branch dgp-refactor — merge updates from HEAD (user: dgp size: 56544)
To Artifact [a097205a14]:
- File generic/tclCompExpr.c — part of check-in [60c07129e7] at 2007-07-10 21:44:23 on branch dgp-refactor — merge updates from HEAD (user: dgp size: 69411)
1 2 3 | /* * tclCompExpr.c -- * | | > > | > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
/*
* tclCompExpr.c --
*
* This file contains the code to parse and compile Tcl expressions
* and implementations of the Tcl commands corresponding to expression
* operators, such as the command ::tcl::mathop::+ .
*
* Copyright (c) 1997 Sun Microsystems, Inc.
* Copyright (c) 1998-2000 by Scriptics Corporation.
* Contributions from Don Porter, NIST, 2006. (not subject to US copyright)
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tclCompExpr.c,v 1.14.2.27 2007/07/10 21:44:23 dgp Exp $
*/
#include "tclInt.h"
#include "tclCompile.h" /* CompileEnv */
/*
* Expression parsing takes place in the routine ParseExpr(). It takes a
* string as input, parses that string, and generates a representation of
* the expression in the form of a tree of operators, a list of literals,
* a list of function names, and an array of Tcl_Token's within a Tcl_Parse
* struct. The tree is composed of OpNodes.
*/
typedef struct OpNode {
int left; /* "Pointer" to the left operand. */
int right; /* "Pointer" to the right operand. */
int parent; /* "Pointer" to the parent operand. */
unsigned char lexeme; /* Code that identifies the operator. */
unsigned char precedence; /* Precedence of the operator */
} OpNode;
/*
* The storage for the tree is dynamically allocated array of OpNodes. The
* array is grown as parsing needs dictate according to a scheme similar to
* Tcl's string growth algorithm, so that the resizing costs are O(N) and so
* that we use at least half the memory allocated as expressions get large.
*
* Each OpNode in the tree represents an operator in the expression, either
* unary or binary. When parsing is completed successfully, a binary operator
* OpNode will have its left and right fields filled with "pointers" to its
* left and right operands. A unary operator OpNode will have its right field
* filled with a pointer to its single operand. When an operand is a
* subexpression the "pointer" takes the form of the index -- a non-negative
* integer -- into the OpNode storage array where the root of that
* subexpression parse tree is found.
*
* Non-operator elements of the expression do not get stored in the OpNode
* tree. They are stored in the other structures according to their type.
* Literal values get appended to the literal list. Elements that denote
* forms of quoting or substitution known to the Tcl parser get stored as
* Tcl_Tokens. These non-operator elements of the expression are the
* leaves of the completed parse tree. When an operand of an OpNode is
* one of these leaf elements, the following negative integer codes are used
* to indicate which kind of elements it is.
*/
enum OperandTypes {
OT_NONE = -4, /* Operand not yet (or no longer) known */
OT_LITERAL = -3, /* Operand is a literal in the literal list */
OT_TOKENS = -2, /* Operand is sequence of Tcl_Tokens */
OT_EMPTY = -1 /* "Operand" is an empty string. This is a
* special case used only to represent the
* EMPTY lexeme. See below. */
};
/*
* Readable macros to test whether a "pointer" value points to an operator.
* They operate on the "non-negative integer -> operator; negative integer ->
* a non-operator OperandType" distinction.
*/
#define IsOperator(l) ((l) >= 0)
#define NotOperator(l) ((l) < 0)
/*
* Note that it is sufficient to store in the tree just the type of leaf
* operand, without any explicit pointer to which leaf. This is true because
* the inorder traversals of the completed tree we perform are known to visit
* the leaves in the same order as the original parse.
*
* Those OpNodes that are themselves (roots of subexpression trees that are)
* operands of some operator store in their parent field a "pointer" to the
* OpNode of that operator. The parent field permits a destructive inorder
* traversal of the tree within a non-recursive routine (ConvertTreeToTokens()
* and CompileExprTree()). This means that even expression trees of great
* depth pose no risk of blowing the C stack.
*
* The lexeme field is filled in with the lexeme of the operator that is
* returned by the ParseLexeme() routine. Only lexemes for unary and
* binary operators get stored in an OpNode. Other lexmes get different
* treatement.
*
* Each lexeme belongs to one of four categories, which determine
* its place in the parse tree. We use the two high bits of the
* (unsigned char) value to store a NODE_TYPE code.
*/
#define NODE_TYPE 0xC0
/*
|
| ︙ | ︙ | |||
66 67 68 69 70 71 72 | * "=" is encountered. */ #define INVALID 5 /* A parse error. Used when any punctuation * appears that's not a supported operator. */ /* Leaf lexemes */ #define NUMBER ( LEAF | 1) /* For literal numbers */ | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
* "=" is encountered. */
#define INVALID 5 /* A parse error. Used when any punctuation
* appears that's not a supported operator. */
/* Leaf lexemes */
#define NUMBER ( LEAF | 1) /* For literal numbers */
#define SCRIPT ( LEAF | 2) /* Script substitution; [foo] */
#define BOOLEAN ( LEAF | BAREWORD) /* For literal booleans */
#define BRACED ( LEAF | 4) /* Braced string; {foo bar} */
#define VARIABLE ( LEAF | 5) /* Variable substitution; $x */
#define QUOTED ( LEAF | 6) /* Quoted string; "foo $bar [soom]" */
#define EMPTY ( LEAF | 7) /* Used only for an empty argument
* list to a function. Represents
* the empty string within parens in
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 | #define STRNEQ ( BINARY | 23) #define EXPON ( BINARY | 24) /* Unlike the other binary operators, * EXPON is right associative and this * distinction is coded directly in * ParseExpr(). */ #define IN_LIST ( BINARY | 25) #define NOT_IN_LIST ( BINARY | 26) | | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > | < < < < < < < < < < < < < < < < | | | | > | > | > > | < < | < | > | > | | | | > | | | > | > > | | | < < | > > | > > | > | > > > > > | < | > | > | > > > > > > > > > > > > > > | > > | > | > > > > > > > > | < < < < < < < < < < > > > > | | | > > > > > > > | | | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
#define STRNEQ ( BINARY | 23)
#define EXPON ( BINARY | 24) /* Unlike the other binary operators,
* EXPON is right associative and this
* distinction is coded directly in
* ParseExpr(). */
#define IN_LIST ( BINARY | 25)
#define NOT_IN_LIST ( BINARY | 26)
#define CLOSE_PAREN ( BINARY | 27) /* By categorizing the CLOSE_PAREN
* lexeme as a BINARY operator, the
* normal parsing rules for binary
* operators assure that a close paren
* will not directly follow another
* operator, and the machinery already
* in place to connect operands to
* operators according to precedence
* performs most of the work of
* matching open and close parens for
* us. In the end though, a close
* paren is not really a binary
* operator, and some special coding
* in ParseExpr() make sure we never
* put an actual CLOSE_PAREN node
* in the parse tree. The
* sub-expression between parens
* becomes the single argument of
* the matching OPEN_PAREN unary
* operator. */
#define END ( BINARY | 28) /* This lexeme represents the end of
* the string being parsed. Treating
* it as a binary operator follows the
* same logic as the CLOSE_PAREN lexeme
* and END pairs with START, in the
* same way that CLOSE_PAREN pairs with
* OPEN_PAREN. */
/*
* When ParseExpr() builds the parse tree it must choose which operands to
* connect to which operators. This is done according to operator precedence.
* The greater an operator's precedence the greater claim it has to link to
* an available operand. The Precedence enumeration lists the precedence
* values used by Tcl expression operators, from lowest to highest claim.
* Each precedence level is commented with the operators that hold that
* precedence.
*/
enum Precedence {
PREC_END = 1, /* END */
PREC_START, /* START */
PREC_CLOSE_PAREN, /* ")" */
PREC_OPEN_PAREN, /* "(" */
PREC_COMMA, /* "," */
PREC_CONDITIONAL, /* "?", ":" */
PREC_OR, /* "||" */
PREC_AND, /* "&&" */
PREC_BIT_OR, /* "|" */
PREC_BIT_XOR, /* "^" */
PREC_BIT_AND, /* "&" */
PREC_EQUAL, /* "==", "!=", "eq", "ne", "in", "ni" */
PREC_COMPARE, /* "<", ">", "<=", ">=" */
PREC_SHIFT, /* "<<", ">>" */
PREC_ADD, /* "+", "-" */
PREC_MULT, /* "*", "/", "%" */
PREC_EXPON, /* "**" */
PREC_UNARY /* "+", "-", FUNCTION, "!", "~" */
};
/*
* Here the same information contained in the comments above is stored
* in inverted form, so that given a lexeme, one can quickly look up
* its precedence value.
*/
static const unsigned char prec[] = {
/* Non-operator lexemes */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
/* Binary operator lexemes */
PREC_ADD, /* BINARY_PLUS */
PREC_ADD, /* BINARY_MINUS */
PREC_COMMA, /* COMMA */
PREC_MULT, /* MULT */
PREC_MULT, /* DIVIDE */
PREC_MULT, /* MOD */
PREC_COMPARE, /* LESS */
PREC_COMPARE, /* GREATER */
PREC_BIT_AND, /* BIT_AND */
PREC_BIT_XOR, /* BIT_XOR */
PREC_BIT_OR, /* BIT_OR */
PREC_CONDITIONAL, /* QUESTION */
PREC_CONDITIONAL, /* COLON */
PREC_SHIFT, /* LEFT_SHIFT */
PREC_SHIFT, /* RIGHT_SHIFT */
PREC_COMPARE, /* LEQ */
PREC_COMPARE, /* GEQ */
PREC_EQUAL, /* EQUAL */
PREC_EQUAL, /* NEQ */
PREC_AND, /* AND */
PREC_OR, /* OR */
PREC_EQUAL, /* STREQ */
PREC_EQUAL, /* STRNEQ */
PREC_EXPON, /* EXPON */
PREC_EQUAL, /* IN_LIST */
PREC_EQUAL, /* NOT_IN_LIST */
PREC_CLOSE_PAREN, /* CLOSE_PAREN */
PREC_END, /* END */
/* Expansion room for more binary operators */
0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
/* Unary operator lexemes */
PREC_UNARY, /* UNARY_PLUS */
PREC_UNARY, /* UNARY_MINUS */
PREC_UNARY, /* FUNCTION */
PREC_START, /* START */
PREC_OPEN_PAREN, /* OPEN_PAREN */
PREC_UNARY, /* NOT*/
PREC_UNARY, /* BIT_NOT*/
0, 0, 0, 0, 0, 0, 0, 0,
};
/*
* The JumpList struct is used to create a stack of data needed for the
* TclEmitForwardJump() and TclFixupForwardJump() calls that are performed
* when compiling the short-circuiting operators QUESTION/COLON, AND, and OR.
* Keeping a stack permits the CompileExprTree() routine to be non-recursive.
*/
typedef struct JumpList {
JumpFixup jump; /* Pass this argument to matching calls of
* TclEmitForwardJump() and
* TclFixupForwardJump(). */
int depth; /* Remember the currStackDepth of the
* CompileEnv here. */
int offset; /* Data used to compute jump lengths to pass
* to TclFixupForwardJump() */
int convert; /* Temporary storage used to compute whether
* numeric conversion will be needed following
* the operator we're compiling. */
struct JumpList *next; /* Point to next item on the stack */
} JumpList;
/*
* Declarations for local functions to this file:
*/
static void CompileExprTree(Tcl_Interp *interp, OpNode *nodes,
Tcl_Obj *const litObjv[], Tcl_Obj *funcList,
Tcl_Token *tokenPtr, int *convertPtr,
CompileEnv *envPtr);
static void ConvertTreeToTokens(Tcl_Interp *interp,
const char *start, int numBytes, OpNode *nodes,
Tcl_Obj *litList, Tcl_Token *tokenPtr,
Tcl_Parse *parsePtr);
static int CopyTokens(Tcl_Token *sourcePtr, Tcl_Parse *parsePtr);
static int GenerateTokensForLiteral(const char *script,
int numBytes, Tcl_Obj *litList, int nextLiteral,
Tcl_Parse *parsePtr);
static int ParseExpr(Tcl_Interp *interp, const char *start,
int numBytes, OpNode **opTreePtr,
Tcl_Obj *litList, Tcl_Obj *funcList,
Tcl_Parse *parsePtr);
static int ParseLexeme(const char *start, int numBytes,
unsigned char *lexemePtr, Tcl_Obj **literalPtr);
/*
*----------------------------------------------------------------------
*
* ParseExpr --
*
* Given a string, the numBytes bytes starting at start, this function
* parses it as a Tcl expression and constructs a tree representing
* the structure of the expression. The caller must pass in empty
* lists as the funcList and litList arguments. The elements of the
* parsed expression are returned to the caller as that tree, a list of
* literal values, a list of function names, and in Tcl_Tokens
* added to a Tcl_Parse struct passed in by the caller.
*
* Results:
* If the string is successfully parsed as a valid Tcl expression, TCL_OK
* is returned, and data about the expression structure is written to
* the last four arguments. If the string cannot be parsed as a valid
* Tcl expression, TCL_ERROR is returned, and if interp is non-NULL, an
* error message is written to interp.
*
* Side effects:
* Memory will be allocated. If TCL_OK is returned, the caller must
* clean up the returned data structures. The (OpNode *) value written
* to opTreePtr should be passed to ckfree() and the parsePtr argument
* should be passed to Tcl_FreeParse(). The elements appended to the
* litList and funcList will automatically be freed whenever the
* refcount on those lists indicates they can be freed.
*
*----------------------------------------------------------------------
*/
static int
ParseExpr(
Tcl_Interp *interp, /* Used for error reporting. */
const char *start, /* Start of source string to parse. */
int numBytes, /* Number of bytes in string. If < 0, the
* string consists of all bytes up to the
* first null character. */
OpNode **opTreePtr, /* Points to space where a pointer to the
* allocated OpNode tree should go. */
Tcl_Obj *litList, /* List to append literals to. */
Tcl_Obj *funcList, /* List to append function names to. */
Tcl_Parse *parsePtr) /* Structure to fill with tokens representing
* those operands that require run time
* substitutions. */
{
OpNode *nodes = NULL; /* Pointer to the OpNode storage array where
* we build the parse tree. */
int nodesAvailable = 64; /* Initial size of the storage array. This
* value establishes a minimum tree memory cost
* of only about 1 kibyte, and is large enough
* for most expressions to parse with no need
* for array growth and reallocation. */
int nodesUsed = 0; /* Number of OpNodes filled. */
int code = TCL_OK; /* Return code */
int scanned = 0; /* Capture number of byte scanned by
* parsing routines. */
/* These variables hold the state of the parser */
unsigned char lexeme = START; /* Most recent lexeme parsed. */
int lastOpen = 0; /* Index of the OpNode of the OPEN_PAREN
* operator we most recently matched. */
int lastParsed = 0; /* Stores info about what the lexeme parsed
* the previous pass through the parsing loop
* was. If it was an operator, lastParsed is
* the index of the OpNode for that operator.
* If it was not and operator, lastParsed holds
* an OperandTypes value encoding what we
* need to know about it. The initial value
* is 0 indicating that as we start the "last
* thing we parsed" was the START lexeme stored
* in node 0. */
/* These variables control generation of the error message. */
Tcl_Obj *msg = NULL; /* The error message. */
Tcl_Obj *post = NULL; /* In a few cases, an additional postscript
* for the error message, supplying more
* information after the error msg and
* location have been reported. */
const char *mark = "_@_"; /* In the portion of the complete error message
* where the error location is reported, this
* "mark" substring is inserted into the
* string being parsed to aid in pinpointing
* the location of the syntax error in the
* expression. */
int insertMark = 0; /* A boolean controlling whether the "mark"
* should be inserted. */
const int limit = 25; /* Portions of the error message are
* constructed out of substrings of the
* original expression. In order to keep the
* error message readable, we impose this limit
* on the substring size we extract. */
if (numBytes < 0) {
numBytes = (start ? strlen(start) : 0);
}
TclParseInit(interp, start, numBytes, parsePtr);
nodes = (OpNode *) attemptckalloc(nodesAvailable * sizeof(OpNode));
if (nodes == NULL) {
TclNewLiteralStringObj(msg, "not enough memory to parse expression");
code = TCL_ERROR;
} else {
/*
* Initialize the parse tree with the special "START" node.
*/
nodes->lexeme = lexeme;
nodes->precedence = prec[lexeme];
nodes->left = OT_NONE;
nodes->right = OT_NONE;
nodes->parent = -1;
nodesUsed++;
}
while ((code == TCL_OK) && (lexeme != END)) {
OpNode *nodePtr; /* Points to the OpNode we may fill this
* pass through the loop. */
Tcl_Obj *literal; /* Filled by the ParseLexeme() call when
* a literal is parsed that has a Tcl_Obj
* rep worth preserving. */
const char *lastStart = start - scanned;
/* Compute where the lexeme parsed the
* previous pass through the loop began.
* This is helpful for detecting invalid
* octals and providing more complete error
* messages. */
/*
* Each pass through this loop adds up to one more OpNode. Allocate
* space for one if required.
*/
if (nodesUsed >= nodesAvailable) {
int size = nodesUsed * 2;
OpNode *newPtr;
do {
|
| ︙ | ︙ | |||
368 369 370 371 372 373 374 375 376 377 378 |
continue;
case INCOMPLETE:
msg = Tcl_ObjPrintf(
"incomplete operator \"%.*s\"", scanned, start);
code = TCL_ERROR;
continue;
case BAREWORD:
if (start[scanned+TclParseAllWhiteSpace(
start+scanned, numBytes-scanned)] == '(') {
lexeme = FUNCTION;
Tcl_ListObjAppendElement(NULL, funcList, literal);
| > > > > > > > > > > > > > > < | 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 |
continue;
case INCOMPLETE:
msg = Tcl_ObjPrintf(
"incomplete operator \"%.*s\"", scanned, start);
code = TCL_ERROR;
continue;
case BAREWORD:
/*
* Most barewords in an expression are a syntax error.
* The exceptions are that when a bareword is followed by
* an open paren, it might be a function call, and when the
* bareword is a legal literal boolean value, we accept that
* as well.
*/
if (start[scanned+TclParseAllWhiteSpace(
start+scanned, numBytes-scanned)] == '(') {
lexeme = FUNCTION;
/*
* When we compile the expression we'll need the function
* name, and there's no place in the parse tree to store
* it, so we keep a separate list of all the function
* names we've parsed in the order we found them.
*/
Tcl_ListObjAppendElement(NULL, funcList, literal);
} else {
int b;
if (Tcl_GetBooleanFromObj(NULL, literal, &b) == TCL_OK) {
lexeme = BOOLEAN;
} else {
Tcl_DecrRefCount(literal);
msg = Tcl_ObjPrintf(
|
| ︙ | ︙ | |||
400 401 402 403 404 405 406 | code = TCL_ERROR; continue; } } break; case PLUS: case MINUS: | | > > > > | | | > > > > > > > > > < | > | > | > > > > > > | 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 |
code = TCL_ERROR;
continue;
}
}
break;
case PLUS:
case MINUS:
if (IsOperator(lastParsed)) {
/*
* A "+" or "-" coming just after another operator
* must be interpreted as a unary operator.
*/
lexeme |= UNARY;
} else {
lexeme |= BINARY;
}
}
}
/*
* Handle lexeme based on its category.
*/
switch (NODE_TYPE & lexeme) {
/*
* Each LEAF results in either a literal getting appended to the
* litList, or a sequence of Tcl_Tokens representing a Tcl word
* getting appended to the parsePtr->tokens. No OpNode is filled
* for this lexeme.
*/
case LEAF: {
Tcl_Token *tokenPtr;
const char *end;
int wordIndex;
/*
* Store away any literals on the list now, so they'll
* be available for our caller to free if we error out
* of this routine. [Bug 1705778, leak K23]
*/
switch (lexeme) {
case NUMBER:
case BOOLEAN:
Tcl_ListObjAppendElement(NULL, litList, literal);
break;
default:
break;
}
if (NotOperator(lastParsed)) {
msg = Tcl_ObjPrintf("missing operator at %s", mark);
if (lastStart[0] == '0') {
Tcl_Obj *copy = Tcl_NewStringObj(lastStart,
start + scanned - lastStart);
if (TclCheckBadOctal(NULL, Tcl_GetString(copy))) {
TclNewLiteralStringObj(post,
"looks like invalid octal number");
}
Tcl_DecrRefCount(copy);
}
scanned = 0;
insertMark = 1;
parsePtr->errorType = TCL_PARSE_BAD_NUMBER;
code = TCL_ERROR;
continue;
}
switch (lexeme) {
case NUMBER:
case BOOLEAN:
lastParsed = OT_LITERAL;
start += scanned;
numBytes -= scanned;
continue;
default:
break;
}
/*
* Remaining LEAF cases may involve filling Tcl_Tokens, so
* make room for at least 2 more tokens.
*/
TclGrowParseTokenArray(parsePtr, 2);
wordIndex = parsePtr->numTokens;
tokenPtr = parsePtr->tokenPtr + wordIndex;
tokenPtr->type = TCL_TOKEN_WORD;
tokenPtr->start = start;
parsePtr->numTokens++;
switch (lexeme) {
case QUOTED:
code = Tcl_ParseQuotedString(interp, start, numBytes,
parsePtr, 1, &end);
if (code != TCL_OK) {
/* TODO: This adjustment of scanned is untested and
* and uncommented. Correct that. Its only possible
* purpose is to influence the error message. */
scanned = parsePtr->term - start;
scanned += (scanned < numBytes);
continue;
}
scanned = end - start;
break;
case BRACED:
code = Tcl_ParseBraces(interp, start, numBytes,
parsePtr, 1, &end);
if (code != TCL_OK) {
continue;
}
scanned = end - start;
break;
case VARIABLE:
code = Tcl_ParseVarName(interp, start, numBytes, parsePtr, 1);
if (code != TCL_OK) {
/* TODO: This adjustment of scanned is untested and
* and uncommented. Correct that. Its only possible
* purpose is to influence the error message. */
scanned = parsePtr->term - start;
scanned += (scanned < numBytes);
continue;
}
tokenPtr = parsePtr->tokenPtr + wordIndex + 1;
if (tokenPtr->type != TCL_TOKEN_VARIABLE) {
TclNewLiteralStringObj(msg, "invalid character \"$\"");
|
| ︙ | ︙ | |||
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 |
break;
}
}
TclStackFree(interp, nestedPtr);
end = start;
start = tokenPtr->start;
if (code != TCL_OK) {
scanned = parsePtr->term - start;
scanned += (scanned < numBytes);
continue;
}
scanned = end - start;
tokenPtr->size = scanned;
parsePtr->numTokens++;
break;
}
}
tokenPtr = parsePtr->tokenPtr + wordIndex;
tokenPtr->size = scanned;
tokenPtr->numComponents = parsePtr->numTokens - wordIndex - 1;
if ((lexeme == QUOTED) || (lexeme == BRACED)) {
literal = Tcl_NewObj();
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | | | | > | | | > | 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 |
break;
}
}
TclStackFree(interp, nestedPtr);
end = start;
start = tokenPtr->start;
if (code != TCL_OK) {
/* TODO: This adjustment of scanned is untested and
* and uncommented. Correct that. Its only possible
* purpose is to influence the error message. */
scanned = parsePtr->term - start;
scanned += (scanned < numBytes);
continue;
}
scanned = end - start;
tokenPtr->size = scanned;
parsePtr->numTokens++;
break;
}
}
tokenPtr = parsePtr->tokenPtr + wordIndex;
tokenPtr->size = scanned;
tokenPtr->numComponents = parsePtr->numTokens - wordIndex - 1;
if ((lexeme == QUOTED) || (lexeme == BRACED)) {
/*
* When a braced or quoted word within an expression
* is simple enough, we can store it as a literal rather
* than in its tokenized form. This is an advantage since
* the compiled bytecode is going to need the argument in
* Tcl_Obj form eventually, so it's to our advantage to just
* get there now, and avoid the need to convert from Tcl_Token
* form again later. Currently we only store literals
* for things parsed as single TEXT tokens (known as
* TCL_TOKEN_SIMPLE_WORD in other contexts). In this
* simple case, the literal string we store is identical
* to a substring of the original expression.
*
* TODO: We ought to be able to store as a literal any
* word which is known at compile-time, including those that
* contain backslash substitution. This can be helpful to
* store multi-line strings that include escaped newlines,
* or strings that include multi-byte characters expressed
* in \uHHHH form. Removing the first two tests here is
* sufficient to make that change, but will lead to a
* Tcl_Panic() in GenerateTokensForLiteral() until that routine
* is revised to handle such literals.
*/
literal = Tcl_NewObj();
if (tokenPtr->numComponents == 1
&& tokenPtr[1].type == TCL_TOKEN_TEXT
&& TclWordKnownAtCompileTime(tokenPtr, literal)) {
Tcl_ListObjAppendElement(NULL, litList, literal);
lastParsed = OT_LITERAL;
parsePtr->numTokens = wordIndex;
break;
}
Tcl_DecrRefCount(literal);
}
lastParsed = OT_TOKENS;
break;
}
case UNARY:
if (NotOperator(lastParsed)) {
msg = Tcl_ObjPrintf("missing operator at %s", mark);
scanned = 0;
insertMark = 1;
code = TCL_ERROR;
continue;
}
lastParsed = nodesUsed;
nodePtr->lexeme = lexeme;
nodePtr->precedence = prec[lexeme];
nodePtr->left = OT_NONE;
nodePtr->right = OT_NONE;
nodePtr->parent = nodePtr - nodes - 1;
nodesUsed++;
break;
case BINARY: {
OpNode *otherPtr = NULL;
unsigned char precedence = prec[lexeme];
if (IsOperator(lastParsed)) {
if ((lexeme == CLOSE_PAREN)
&& (nodePtr[-1].lexeme == OPEN_PAREN)) {
if (nodePtr[-2].lexeme == FUNCTION) {
/*
* Normally, "()" is a syntax error, but as a special
* case accept it as an argument list for a function.
*/
scanned = 0;
lastParsed = OT_EMPTY;
nodePtr[-1].left--;
break;
}
msg = Tcl_ObjPrintf("empty subexpression at %s", mark);
scanned = 0;
insertMark = 1;
code = TCL_ERROR;
continue;
}
if (nodePtr[-1].precedence > precedence) {
if (nodePtr[-1].lexeme == OPEN_PAREN) {
TclNewLiteralStringObj(msg, "unbalanced open paren");
parsePtr->errorType = TCL_PARSE_MISSING_PAREN;
} else if (nodePtr[-1].lexeme == COMMA) {
msg = Tcl_ObjPrintf(
"missing function argument at %s", mark);
scanned = 0;
insertMark = 1;
} else if (nodePtr[-1].lexeme == START) {
TclNewLiteralStringObj(msg, "empty expression");
|
| ︙ | ︙ | |||
654 655 656 657 658 659 660 | scanned = 0; insertMark = 1; } code = TCL_ERROR; continue; } | | | | | | | | > | | > | > | > | | | | | | 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
scanned = 0;
insertMark = 1;
}
code = TCL_ERROR;
continue;
}
if (lastParsed == OT_NONE) {
otherPtr = nodes + lastOpen - 1;
lastParsed = lastOpen;
} else {
otherPtr = nodePtr - 1;
}
while (1) {
/*
* lastParsed is "index" of item to be linked.
* otherPtr points to competing operator.
*/
if (otherPtr->precedence < precedence) {
break;
}
if (otherPtr->precedence == precedence) {
/*
* Right association rules for exponentiation.
*/
if (lexeme == EXPON) {
break;
}
/*
* Special association rules for the ternary operators.
* The "?" and ":" operators have equal precedence, but
* must be linked up in sensible pairs.
*/
if ((otherPtr->lexeme == QUESTION)
&& (NotOperator(lastParsed)
|| (nodes[lastParsed].lexeme != COLON))) {
break;
}
if ((otherPtr->lexeme == COLON) && (lexeme == QUESTION)) {
break;
}
}
/*
* We should link the lastParsed item to the otherPtr as its
* right operand. First make some syntax checks.
*/
if ((otherPtr->lexeme == OPEN_PAREN)
&& (lexeme != CLOSE_PAREN)) {
TclNewLiteralStringObj(msg, "unbalanced open paren");
parsePtr->errorType = TCL_PARSE_MISSING_PAREN;
code = TCL_ERROR;
break;
}
if ((otherPtr->lexeme == QUESTION)
&& (NotOperator(lastParsed)
|| (nodes[lastParsed].lexeme != COLON))) {
msg = Tcl_ObjPrintf(
"missing operator \":\" at %s", mark);
scanned = 0;
insertMark = 1;
code = TCL_ERROR;
break;
}
if (IsOperator(lastParsed)
&& (nodes[lastParsed].lexeme == COLON)
&& (otherPtr->lexeme != QUESTION)) {
TclNewLiteralStringObj(msg,
"unexpected operator \":\" without preceding \"?\"");
code = TCL_ERROR;
break;
}
/*
* Link orphan as right operand of otherPtr.
*/
otherPtr->right = lastParsed;
if (lastParsed >= 0) {
nodes[lastParsed].parent = otherPtr - nodes;
}
lastParsed = otherPtr - nodes;
if (otherPtr->lexeme == OPEN_PAREN) {
/*
* CLOSE_PAREN can only close one OPEN_PAREN.
*/
break;
|
| ︙ | ︙ | |||
758 759 760 761 762 763 764 |
if (lexeme == CLOSE_PAREN) {
if (otherPtr->lexeme == START) {
TclNewLiteralStringObj(msg, "unbalanced close paren");
code = TCL_ERROR;
continue;
}
| | | > > > > | | | | | | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 |
if (lexeme == CLOSE_PAREN) {
if (otherPtr->lexeme == START) {
TclNewLiteralStringObj(msg, "unbalanced close paren");
code = TCL_ERROR;
continue;
}
lastParsed = OT_NONE;
lastOpen = otherPtr - nodes;
otherPtr->left++;
/*
* Create no node for a CLOSE_PAREN lexeme.
*/
break;
}
if (lexeme == COMMA) {
if ((otherPtr->lexeme != OPEN_PAREN)
|| (otherPtr[-1].lexeme != FUNCTION)) {
TclNewLiteralStringObj(msg,
"unexpected \",\" outside function argument list");
code = TCL_ERROR;
continue;
}
otherPtr->left++;
}
if (IsOperator(lastParsed) && (nodes[lastParsed].lexeme == COLON)) {
TclNewLiteralStringObj(msg,
"unexpected operator \":\" without preceding \"?\"");
code = TCL_ERROR;
continue;
}
if (lexeme == END) {
continue;
}
/*
* Link orphan as left operand of new node.
*/
nodePtr->lexeme = lexeme;
nodePtr->precedence = precedence;
nodePtr->right = -1;
nodePtr->left = lastParsed;
if (lastParsed < 0) {
nodePtr->parent = nodePtr - nodes - 1;
} else {
nodePtr->parent = nodes[lastParsed].parent;
nodes[lastParsed].parent = nodePtr - nodes;
}
lastParsed = nodesUsed;
nodesUsed++;
break;
}
}
start += scanned;
numBytes -= scanned;
|
| ︙ | ︙ | |||
846 847 848 849 850 851 852 853 854 855 856 857 858 859 |
numBytes = parsePtr->end - parsePtr->string;
Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
"\n (parsing expression \"%.*s%s\")",
(numBytes < limit) ? numBytes : limit - 3,
parsePtr->string, (numBytes < limit) ? "" : "..."));
}
return code;
}
/*
*----------------------------------------------------------------------
*
* GenerateTokensForLiteral --
| > > > | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 |
numBytes = parsePtr->end - parsePtr->string;
Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
"\n (parsing expression \"%.*s%s\")",
(numBytes < limit) ? numBytes : limit - 3,
parsePtr->string, (numBytes < limit) ? "" : "..."));
}
if (code != TCL_OK && parsePtr->errorType == TCL_PARSE_SUCCESS) {
parsePtr->errorType = TCL_PARSE_SYNTAX;
}
return code;
}
/*
*----------------------------------------------------------------------
*
* GenerateTokensForLiteral --
|
| ︙ | ︙ | |||
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 |
Tcl_Obj *litList = Tcl_NewObj(); /* List to hold the literals */
Tcl_Obj *funcList = Tcl_NewObj(); /* List to hold the functon names*/
Tcl_Parse *exprParsePtr =
(Tcl_Parse *) TclStackAlloc(interp, sizeof(Tcl_Parse));
/* Holds the Tcl_Tokens of substitutions */
int code = ParseExpr(interp, start, numBytes, &opTree, litList,
funcList, exprParsePtr);
if (numBytes < 0) {
numBytes = (start ? strlen(start) : 0);
}
TclParseInit(interp, start, numBytes, parsePtr);
if (code == TCL_OK) {
ConvertTreeToTokens(interp, start, numBytes, opTree, litList,
exprParsePtr->tokenPtr, parsePtr);
} else {
| > > | > | 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 |
Tcl_Obj *litList = Tcl_NewObj(); /* List to hold the literals */
Tcl_Obj *funcList = Tcl_NewObj(); /* List to hold the functon names*/
Tcl_Parse *exprParsePtr =
(Tcl_Parse *) TclStackAlloc(interp, sizeof(Tcl_Parse));
/* Holds the Tcl_Tokens of substitutions */
int code = ParseExpr(interp, start, numBytes, &opTree, litList,
funcList, exprParsePtr);
int errorType = exprParsePtr->errorType;
const char* term = exprParsePtr->term;
if (numBytes < 0) {
numBytes = (start ? strlen(start) : 0);
}
TclParseInit(interp, start, numBytes, parsePtr);
if (code == TCL_OK) {
ConvertTreeToTokens(interp, start, numBytes, opTree, litList,
exprParsePtr->tokenPtr, parsePtr);
} else {
parsePtr->term = term;
parsePtr->errorType = errorType;
}
Tcl_FreeParse(exprParsePtr);
TclStackFree(interp, exprParsePtr);
Tcl_DecrRefCount(funcList);
Tcl_DecrRefCount(litList);
ckfree((char *) opTree);
|
| ︙ | ︙ |