diff options
Diffstat (limited to 'tcl8.6/generic/tclCompExpr.c')
-rw-r--r-- | tcl8.6/generic/tclCompExpr.c | 2806 |
1 files changed, 0 insertions, 2806 deletions
diff --git a/tcl8.6/generic/tclCompExpr.c b/tcl8.6/generic/tclCompExpr.c deleted file mode 100644 index 59eecf9..0000000 --- a/tcl8.6/generic/tclCompExpr.c +++ /dev/null @@ -1,2806 +0,0 @@ -/* - * 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::+ . - * - * Contributions from Don Porter, NIST, 2006-2007. (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. - */ - -#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. */ - union { - int parent; /* "Pointer" to the parent operand. */ - int prev; /* "Pointer" joining incomplete tree stack */ - } p; - unsigned char lexeme; /* Code that identifies the operator. */ - unsigned char precedence; /* Precedence of the operator */ - unsigned char mark; /* Mark used to control traversal. */ - unsigned char constant; /* Flag marking constant subexpressions. */ -} 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_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 traversals of the completed tree we perform are known to visit the - * leaves in the same order as the original parse. - * - * In a completed parse tree, those OpNodes that are themselves (roots of - * subexpression trees that are) operands of some operator store in their - * p.parent field a "pointer" to the OpNode of that operator. The p.parent - * field permits a 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. - * - * While the parse tree is being constructed, the same memory space is used to - * hold the p.prev field which chains together a stack of incomplete trees - * awaiting their right operands. - * - * 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. - * - * The precedence field provides a place to store the precedence of the - * operator, so it need not be looked up again and again. - * - * The mark field is use to control the traversal of the tree, so that it can - * be done non-recursively. The mark values are: - */ - -enum Marks { - MARK_LEFT, /* Next step of traversal is to visit left subtree */ - MARK_RIGHT, /* Next step of traversal is to visit right subtree */ - MARK_PARENT /* Next step of traversal is to return to parent */ -}; - -/* - * The constant field is a boolean flag marking which subexpressions are - * completely known at compile time, and are eligible for computing then - * rather than waiting until run time. - */ - -/* - * 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 - -/* - * The four category values are LEAF, UNARY, and BINARY, explained below, and - * "uncategorized", which is used either temporarily, until context determines - * which of the other three categories is correct, or for lexemes like - * INVALID, which aren't really lexemes at all, but indicators of a parsing - * error. Note that the codes must be distinct to distinguish categories, but - * need not take the form of a bit array. - */ - -#define BINARY 0x40 /* This lexeme is a binary operator. An OpNode - * representing it should go into the parse - * tree, and two operands should be parsed for - * it in the expression. */ -#define UNARY 0x80 /* This lexeme is a unary operator. An OpNode - * representing it should go into the parse - * tree, and one operand should be parsed for - * it in the expression. */ -#define LEAF 0xC0 /* This lexeme is a leaf operand in the parse - * tree. No OpNode will be placed in the tree - * for it. Either a literal value will be - * appended to the list of literals in this - * expression, or appropriate Tcl_Tokens will - * be appended in a Tcl_Parse struct to - * represent those leaves that require some - * form of substitution. */ - -/* Uncategorized lexemes */ - -#define PLUS 1 /* Ambiguous. Resolves to UNARY_PLUS or - * BINARY_PLUS according to context. */ -#define MINUS 2 /* Ambiguous. Resolves to UNARY_MINUS or - * BINARY_MINUS according to context. */ -#define BAREWORD 3 /* Ambigous. Resolves to BOOLEAN or to - * FUNCTION or a parse error according to - * context and value. */ -#define INCOMPLETE 4 /* A parse error. Used only when the single - * "=" 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 the expression: rand() */ - -/* Unary operator lexemes */ - -#define UNARY_PLUS (UNARY | PLUS) -#define UNARY_MINUS (UNARY | MINUS) -#define FUNCTION (UNARY | BAREWORD) - /* This is a bit of "creative interpretation" - * on the part of the parser. A function call - * is parsed into the parse tree according to - * the perspective that the function name is a - * unary operator and its argument list, - * enclosed in parens, is its operand. The - * additional requirements not implied - * generally by treatment as a unary operator - * -- for example, the requirement that the - * operand be enclosed in parens -- are hard - * coded in the relevant portions of - * ParseExpr(). We trade off the need to - * include such exceptional handling in the - * code against the need we would otherwise - * have for more lexeme categories. */ -#define START (UNARY | 4) - /* This lexeme isn't parsed from the - * expression text at all. It represents the - * start of the expression and sits at the - * root of the parse tree where it serves as - * the start/end point of traversals. */ -#define OPEN_PAREN (UNARY | 5) - /* Another bit of creative interpretation, - * where we treat "(" as a unary operator with - * the sub-expression between it and its - * matching ")" as its operand. See - * CLOSE_PAREN below. */ -#define NOT (UNARY | 6) -#define BIT_NOT (UNARY | 7) - -/* Binary operator lexemes */ - -#define BINARY_PLUS (BINARY | PLUS) -#define BINARY_MINUS (BINARY | MINUS) -#define COMMA (BINARY | 3) - /* The "," operator is a low precedence binary - * operator that separates the arguments in a - * function call. The additional constraint - * that this operator can only legally appear - * at the right places within a function call - * argument list are hard coded within - * ParseExpr(). */ -#define MULT (BINARY | 4) -#define DIVIDE (BINARY | 5) -#define MOD (BINARY | 6) -#define LESS (BINARY | 7) -#define GREATER (BINARY | 8) -#define BIT_AND (BINARY | 9) -#define BIT_XOR (BINARY | 10) -#define BIT_OR (BINARY | 11) -#define QUESTION (BINARY | 12) - /* These two lexemes make up the */ -#define COLON (BINARY | 13) - /* ternary conditional operator, $x ? $y : $z. - * We treat them as two binary operators to - * avoid another lexeme category, and code the - * additional constraints directly in - * ParseExpr(). For instance, the right - * operand of a "?" operator must be a ":" - * operator. */ -#define LEFT_SHIFT (BINARY | 14) -#define RIGHT_SHIFT (BINARY | 15) -#define LEQ (BINARY | 16) -#define GEQ (BINARY | 17) -#define EQUAL (BINARY | 18) -#define NEQ (BINARY | 19) -#define AND (BINARY | 20) -#define OR (BINARY | 21) -#define STREQ (BINARY | 22) -#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*/ -}; - -/* - * A table mapping lexemes to bytecode instructions, used by CompileExprTree(). - */ - -static const unsigned char instruction[] = { - /* 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 */ - INST_ADD, /* BINARY_PLUS */ - INST_SUB, /* BINARY_MINUS */ - 0, /* COMMA */ - INST_MULT, /* MULT */ - INST_DIV, /* DIVIDE */ - INST_MOD, /* MOD */ - INST_LT, /* LESS */ - INST_GT, /* GREATER */ - INST_BITAND, /* BIT_AND */ - INST_BITXOR, /* BIT_XOR */ - INST_BITOR, /* BIT_OR */ - 0, /* QUESTION */ - 0, /* COLON */ - INST_LSHIFT, /* LEFT_SHIFT */ - INST_RSHIFT, /* RIGHT_SHIFT */ - INST_LE, /* LEQ */ - INST_GE, /* GEQ */ - INST_EQ, /* EQUAL */ - INST_NEQ, /* NEQ */ - 0, /* AND */ - 0, /* OR */ - INST_STR_EQ, /* STREQ */ - INST_STR_NEQ, /* STRNEQ */ - INST_EXPON, /* EXPON */ - INST_LIST_IN, /* IN_LIST */ - INST_LIST_NOT_IN, /* NOT_IN_LIST */ - 0, /* CLOSE_PAREN */ - 0, /* 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 */ - INST_UPLUS, /* UNARY_PLUS */ - INST_UMINUS, /* UNARY_MINUS */ - 0, /* FUNCTION */ - 0, /* START */ - 0, /* OPEN_PAREN */ - INST_LNOT, /* NOT*/ - INST_BITNOT, /* BIT_NOT*/ -}; - -/* - * A table mapping a byte value to the corresponding lexeme for use by - * ParseLexeme(). - */ - -static const unsigned char Lexeme[] = { - INVALID /* NUL */, INVALID /* SOH */, - INVALID /* STX */, INVALID /* ETX */, - INVALID /* EOT */, INVALID /* ENQ */, - INVALID /* ACK */, INVALID /* BEL */, - INVALID /* BS */, INVALID /* HT */, - INVALID /* LF */, INVALID /* VT */, - INVALID /* FF */, INVALID /* CR */, - INVALID /* SO */, INVALID /* SI */, - INVALID /* DLE */, INVALID /* DC1 */, - INVALID /* DC2 */, INVALID /* DC3 */, - INVALID /* DC4 */, INVALID /* NAK */, - INVALID /* SYN */, INVALID /* ETB */, - INVALID /* CAN */, INVALID /* EM */, - INVALID /* SUB */, INVALID /* ESC */, - INVALID /* FS */, INVALID /* GS */, - INVALID /* RS */, INVALID /* US */, - INVALID /* SPACE */, 0 /* ! or != */, - QUOTED /* " */, INVALID /* # */, - VARIABLE /* $ */, MOD /* % */, - 0 /* & or && */, INVALID /* ' */, - OPEN_PAREN /* ( */, CLOSE_PAREN /* ) */, - 0 /* * or ** */, PLUS /* + */, - COMMA /* , */, MINUS /* - */, - 0 /* . */, DIVIDE /* / */, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-9 */ - COLON /* : */, INVALID /* ; */, - 0 /* < or << or <= */, - 0 /* == or INVALID */, - 0 /* > or >> or >= */, - QUESTION /* ? */, INVALID /* @ */, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A-M */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* N-Z */ - SCRIPT /* [ */, INVALID /* \ */, - INVALID /* ] */, BIT_XOR /* ^ */, - INVALID /* _ */, INVALID /* ` */, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a-m */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* n-z */ - BRACED /* { */, 0 /* | or || */, - INVALID /* } */, BIT_NOT /* ~ */, - INVALID /* DEL */ -}; - -/* - * 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(). */ - 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, - int index, Tcl_Obj *const **litObjvPtr, - Tcl_Obj *const *funcObjv, Tcl_Token *tokenPtr, - CompileEnv *envPtr, int optimize); -static void ConvertTreeToTokens(const char *start, int numBytes, - OpNode *nodes, Tcl_Token *tokenPtr, - Tcl_Parse *parsePtr); -static int ExecConstantExprTree(Tcl_Interp *interp, OpNode *nodes, - int index, Tcl_Obj * const **litObjvPtr); -static int ParseExpr(Tcl_Interp *interp, const char *start, - int numBytes, OpNode **opTreePtr, - Tcl_Obj *litList, Tcl_Obj *funcList, - Tcl_Parse *parsePtr, int parseOnly); -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. */ - 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. */ - int parseOnly) /* A boolean indicating whether the caller's - * aim is just a parse, or whether it will go - * on to compile the expression. Different - * optimizations are appropriate for the two - * scenarios. */ -{ - OpNode *nodes = NULL; /* Pointer to the OpNode storage array where - * we build the parse tree. */ - unsigned 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. */ - unsigned int nodesUsed = 0; /* Number of OpNodes filled. */ - int scanned = 0; /* Capture number of byte scanned by parsing - * routines. */ - int lastParsed; /* 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 an operator, lastParsed holds - * an OperandTypes value encoding what we need - * to know about it. */ - int incomplete; /* Index of the most recent incomplete tree in - * the OpNode array. Heads a stack of - * incomplete trees linked by p.prev. */ - int complete = OT_EMPTY; /* "Index" of the complete tree (that is, a - * complete subexpression) determined at the - * moment. OT_EMPTY is a nonsense value used - * only to silence compiler warnings. During a - * parse, complete will always hold an index - * or an OperandTypes value pointing to an - * actual leaf at the time the complete tree - * is needed. */ - - /* - * 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 *errCode = NULL; /* The detail word of the errorCode list, or - * NULL to indicate that no changes to the - * errorCode are to be done. */ - const char *subErrCode = NULL; - /* Extra information for use in generating the - * errorCode. */ - 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. */ - - TclParseInit(interp, start, numBytes, parsePtr); - - nodes = attemptckalloc(nodesAvailable * sizeof(OpNode)); - if (nodes == NULL) { - TclNewLiteralStringObj(msg, "not enough memory to parse expression"); - errCode = "NOMEM"; - goto error; - } - - /* - * Initialize the parse tree with the special "START" node. - */ - - nodes->lexeme = START; - nodes->precedence = prec[START]; - nodes->mark = MARK_RIGHT; - nodes->constant = 1; - incomplete = lastParsed = nodesUsed; - nodesUsed++; - - /* - * Main parsing loop parses one lexeme per iteration. We exit the loop - * only when there's a syntax error with a "goto error" which takes us to - * the error handling code following the loop, or when we've successfully - * completed the parse and we return to the caller. - */ - - while (1) { - OpNode *nodePtr; /* Points to the OpNode we may fill this pass - * through the loop. */ - unsigned char lexeme; /* The lexeme we parse this iteration. */ - Tcl_Obj *literal; /* Filled by the ParseLexeme() call when a - * literal is parsed that has a Tcl_Obj rep - * worth preserving. */ - - /* - * Each pass through this loop adds up to one more OpNode. Allocate - * space for one if required. - */ - - if (nodesUsed >= nodesAvailable) { - unsigned int size = nodesUsed * 2; - OpNode *newPtr = NULL; - - do { - if (size <= UINT_MAX/sizeof(OpNode)) { - newPtr = attemptckrealloc(nodes, size * sizeof(OpNode)); - } - } while ((newPtr == NULL) - && ((size -= (size - nodesUsed) / 2) > nodesUsed)); - if (newPtr == NULL) { - TclNewLiteralStringObj(msg, - "not enough memory to parse expression"); - errCode = "NOMEM"; - goto error; - } - nodesAvailable = size; - nodes = newPtr; - } - nodePtr = nodes + nodesUsed; - - /* - * Skip white space between lexemes. - */ - - scanned = TclParseAllWhiteSpace(start, numBytes); - start += scanned; - numBytes -= scanned; - - scanned = ParseLexeme(start, numBytes, &lexeme, &literal); - - /* - * Use context to categorize the lexemes that are ambiguous. - */ - - if ((NODE_TYPE & lexeme) == 0) { - int b; - - switch (lexeme) { - case INVALID: - msg = Tcl_ObjPrintf("invalid character \"%.*s\"", - scanned, start); - errCode = "BADCHAR"; - goto error; - case INCOMPLETE: - msg = Tcl_ObjPrintf("incomplete operator \"%.*s\"", - scanned, start); - errCode = "PARTOP"; - goto error; - 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 if (Tcl_GetBooleanFromObj(NULL,literal,&b) == TCL_OK) { - lexeme = BOOLEAN; - } else { - Tcl_DecrRefCount(literal); - msg = Tcl_ObjPrintf("invalid bareword \"%.*s%s\"", - (scanned < limit) ? scanned : limit - 3, start, - (scanned < limit) ? "" : "..."); - post = Tcl_ObjPrintf( - "should be \"$%.*s%s\" or \"{%.*s%s}\"", - (scanned < limit) ? scanned : limit - 3, - start, (scanned < limit) ? "" : "...", - (scanned < limit) ? scanned : limit - 3, - start, (scanned < limit) ? "" : "..."); - Tcl_AppendPrintfToObj(post, " or \"%.*s%s(...)\" or ...", - (scanned < limit) ? scanned : limit - 3, - start, (scanned < limit) ? "" : "..."); - errCode = "BAREWORD"; - if (start[0] == '0') { - const char *stop; - TclParseNumber(NULL, NULL, NULL, start, scanned, - &stop, TCL_PARSE_NO_WHITESPACE); - - if (isdigit(UCHAR(*stop)) || (stop == start + 1)) { - switch (start[1]) { - case 'b': - Tcl_AppendToObj(post, - " (invalid binary number?)", -1); - parsePtr->errorType = TCL_PARSE_BAD_NUMBER; - errCode = "BADNUMBER"; - subErrCode = "BINARY"; - break; - case 'o': - Tcl_AppendToObj(post, - " (invalid octal number?)", -1); - parsePtr->errorType = TCL_PARSE_BAD_NUMBER; - errCode = "BADNUMBER"; - subErrCode = "OCTAL"; - break; - default: - if (isdigit(UCHAR(start[1]))) { - Tcl_AppendToObj(post, - " (invalid octal number?)", -1); - parsePtr->errorType = TCL_PARSE_BAD_NUMBER; - errCode = "BADNUMBER"; - subErrCode = "OCTAL"; - } - break; - } - } - } - goto error; - } - 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; - } - } - } /* Uncategorized lexemes */ - - /* - * Handle lexeme based on its category. - */ - - switch (NODE_TYPE & lexeme) { - case LEAF: { - /* - * 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. - */ - - Tcl_Token *tokenPtr; - const char *end = start; - int wordIndex; - int code = TCL_OK; - - /* - * A leaf operand appearing just after something that's not an - * operator is a syntax error. - */ - - if (NotOperator(lastParsed)) { - msg = Tcl_ObjPrintf("missing operator at %s", mark); - errCode = "MISSING"; - scanned = 0; - insertMark = 1; - - /* - * Free any literal to avoid a memleak. - */ - - if ((lexeme == NUMBER) || (lexeme == BOOLEAN)) { - Tcl_DecrRefCount(literal); - } - goto error; - } - - switch (lexeme) { - case NUMBER: - case BOOLEAN: - /* - * TODO: Consider using a dict or hash to collapse all - * duplicate literals into a single representative value. - * (Like what is done with [split $s {}]). - * Pro: ~75% memory saving on expressions like - * {1+1+1+1+1+.....+1} (Convert "pointer + Tcl_Obj" cost - * to "pointer" cost only) - * Con: Cost of the dict store/retrieve on every literal in - * every expression when expressions like the above tend - * to be uncommon. - * The memory savings is temporary; Compiling to bytecode - * will collapse things as literals are registered - * anyway, so the savings applies only to the time - * between parsing and compiling. Possibly important due - * to high-water mark nature of memory allocation. - */ - - Tcl_ListObjAppendElement(NULL, litList, literal); - complete = 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(NULL, start, numBytes, - parsePtr, 1, &end); - scanned = end - start; - break; - - case BRACED: - code = Tcl_ParseBraces(NULL, start, numBytes, - parsePtr, 1, &end); - scanned = end - start; - break; - - case VARIABLE: - code = Tcl_ParseVarName(NULL, start, numBytes, parsePtr, 1); - - /* - * Handle the quirk that Tcl_ParseVarName reports a successful - * parse even when it gets only a "$" with no variable name. - */ - - tokenPtr = parsePtr->tokenPtr + wordIndex + 1; - if (code == TCL_OK && tokenPtr->type != TCL_TOKEN_VARIABLE) { - TclNewLiteralStringObj(msg, "invalid character \"$\""); - errCode = "BADCHAR"; - goto error; - } - scanned = tokenPtr->size; - break; - - case SCRIPT: { - Tcl_Parse *nestedPtr = - TclStackAlloc(interp, sizeof(Tcl_Parse)); - - tokenPtr = parsePtr->tokenPtr + parsePtr->numTokens; - tokenPtr->type = TCL_TOKEN_COMMAND; - tokenPtr->start = start; - tokenPtr->numComponents = 0; - - end = start + numBytes; - start++; - while (1) { - code = Tcl_ParseCommand(interp, start, end - start, 1, - nestedPtr); - if (code != TCL_OK) { - parsePtr->term = nestedPtr->term; - parsePtr->errorType = nestedPtr->errorType; - parsePtr->incomplete = nestedPtr->incomplete; - break; - } - start = nestedPtr->commandStart + nestedPtr->commandSize; - Tcl_FreeParse(nestedPtr); - if ((nestedPtr->term < end) && (nestedPtr->term[0] == ']') - && !nestedPtr->incomplete) { - break; - } - - if (start == end) { - TclNewLiteralStringObj(msg, "missing close-bracket"); - parsePtr->term = tokenPtr->start; - parsePtr->errorType = TCL_PARSE_MISSING_BRACKET; - parsePtr->incomplete = 1; - code = TCL_ERROR; - errCode = "UNBALANCED"; - break; - } - } - TclStackFree(interp, nestedPtr); - end = start; - start = tokenPtr->start; - scanned = end - start; - tokenPtr->size = scanned; - parsePtr->numTokens++; - break; - } /* SCRIPT case */ - } - if (code != TCL_OK) { - /* - * Here we handle all the syntax errors generated by the - * Tcl_Token generating parsing routines called in the switch - * just above. If the value of parsePtr->incomplete is 1, then - * the error was an unbalanced '[', '(', '{', or '"' and - * parsePtr->term is pointing to that unbalanced character. If - * the value of parsePtr->incomplete is 0, then the error is - * one of lacking whitespace following a quoted word, for - * example: expr {[an error {foo}bar]}, and parsePtr->term - * points to where the whitespace is missing. We reset our - * values of start and scanned so that when our error message - * is constructed, the location of the syntax error is sure to - * appear in it, even if the quoted expression is truncated. - */ - - start = parsePtr->term; - scanned = parsePtr->incomplete; - if (parsePtr->incomplete) { - errCode = "UNBALANCED"; - } - goto error; - } - - tokenPtr = parsePtr->tokenPtr + wordIndex; - tokenPtr->size = scanned; - tokenPtr->numComponents = parsePtr->numTokens - wordIndex - 1; - if (!parseOnly && ((lexeme == QUOTED) || (lexeme == BRACED))) { - /* - * When this expression is destined to be compiled, and a - * braced or quoted word within an expression is known at - * compile time (no runtime substitutions in it), 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 just as - * well to get there now. Another advantage is that with this - * conversion, larger constant expressions might be grown and - * optimized. - * - * On the contrary, if the end goal of this parse is to fill a - * Tcl_Parse for a caller of Tcl_ParseExpr(), then it's - * wasteful to convert to a literal only to convert back again - * later. - */ - - literal = Tcl_NewObj(); - if (TclWordKnownAtCompileTime(tokenPtr, literal)) { - Tcl_ListObjAppendElement(NULL, litList, literal); - complete = lastParsed = OT_LITERAL; - parsePtr->numTokens = wordIndex; - break; - } - Tcl_DecrRefCount(literal); - } - complete = lastParsed = OT_TOKENS; - break; - } /* case LEAF */ - - case UNARY: - - /* - * A unary operator appearing just after something that's not an - * operator is a syntax error -- something trying to be the left - * operand of an operator that doesn't take one. - */ - - if (NotOperator(lastParsed)) { - msg = Tcl_ObjPrintf("missing operator at %s", mark); - scanned = 0; - insertMark = 1; - errCode = "MISSING"; - goto error; - } - - /* - * Create an OpNode for the unary operator. - */ - - nodePtr->lexeme = lexeme; - nodePtr->precedence = prec[lexeme]; - nodePtr->mark = MARK_RIGHT; - - /* - * A FUNCTION cannot be a constant expression, because Tcl allows - * functions to return variable results with the same arguments; - * for example, rand(). Other unary operators can root a constant - * expression, so long as the argument is a constant expression. - */ - - nodePtr->constant = (lexeme != FUNCTION); - - /* - * This unary operator is a new incomplete tree, so push it onto - * our stack of incomplete trees. Also remember it as the last - * lexeme we parsed. - */ - - nodePtr->p.prev = incomplete; - incomplete = lastParsed = nodesUsed; - nodesUsed++; - break; - - case BINARY: { - OpNode *incompletePtr; - unsigned char precedence = prec[lexeme]; - - /* - * A binary operator appearing just after another operator is a - * syntax error -- one of the two operators is missing an operand. - */ - - 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. - * Treat this as a special LEAF lexeme, and restart - * the parsing loop with zero characters scanned. We - * will parse the ")" again the next time through, but - * with the OT_EMPTY leaf as the subexpression between - * the parens. - */ - - scanned = 0; - complete = lastParsed = OT_EMPTY; - break; - } - msg = Tcl_ObjPrintf("empty subexpression at %s", mark); - scanned = 0; - insertMark = 1; - errCode = "EMPTY"; - goto error; - } - - if (nodePtr[-1].precedence > precedence) { - if (nodePtr[-1].lexeme == OPEN_PAREN) { - TclNewLiteralStringObj(msg, "unbalanced open paren"); - parsePtr->errorType = TCL_PARSE_MISSING_PAREN; - errCode = "UNBALANCED"; - } else if (nodePtr[-1].lexeme == COMMA) { - msg = Tcl_ObjPrintf( - "missing function argument at %s", mark); - scanned = 0; - insertMark = 1; - errCode = "MISSING"; - } else if (nodePtr[-1].lexeme == START) { - TclNewLiteralStringObj(msg, "empty expression"); - errCode = "EMPTY"; - } - } else if (lexeme == CLOSE_PAREN) { - TclNewLiteralStringObj(msg, "unbalanced close paren"); - errCode = "UNBALANCED"; - } else if ((lexeme == COMMA) - && (nodePtr[-1].lexeme == OPEN_PAREN) - && (nodePtr[-2].lexeme == FUNCTION)) { - msg = Tcl_ObjPrintf("missing function argument at %s", - mark); - scanned = 0; - insertMark = 1; - errCode = "UNBALANCED"; - } - if (msg == NULL) { - msg = Tcl_ObjPrintf("missing operand at %s", mark); - scanned = 0; - insertMark = 1; - errCode = "MISSING"; - } - goto error; - } - - /* - * Here is where the tree comes together. At this point, we have a - * stack of incomplete trees corresponding to substrings that are - * incomplete expressions, followed by a complete tree - * corresponding to a substring that is itself a complete - * expression, followed by the binary operator we have just - * parsed. The incomplete trees can each be completed by adding a - * right operand. - * - * To illustrate with an example, when we parse the expression - * "1+2*3-4" and we reach this point having just parsed the "-" - * operator, we have these incomplete trees: START, "1+", and - * "2*". Next we have the complete subexpression "3". Last is the - * "-" we've just parsed. - * - * The next step is to join our complete tree to an operator. The - * choice is governed by the precedence and associativity of the - * competing operators. If we connect it as the right operand of - * our most recent incomplete tree, we get a new complete tree, - * and we can repeat the process. The while loop following repeats - * this until precedence indicates it is time to join the complete - * tree as the left operand of the just parsed binary operator. - * - * Continuing the example, the first pass through the loop will - * join "3" to "2*"; the next pass will join "2*3" to "1+". Then - * we'll exit the loop and join "1+2*3" to "-". When we return to - * parse another lexeme, our stack of incomplete trees is START - * and "1+2*3-". - */ - - while (1) { - incompletePtr = nodes + incomplete; - - if (incompletePtr->precedence < precedence) { - break; - } - - if (incompletePtr->precedence == precedence) { - /* - * Right association rules for exponentiation. - */ - - if (lexeme == EXPON) { - break; - } - - /* - * Special association rules for the conditional - * operators. The "?" and ":" operators have equal - * precedence, but must be linked up in sensible pairs. - */ - - if ((incompletePtr->lexeme == QUESTION) - && (NotOperator(complete) - || (nodes[complete].lexeme != COLON))) { - break; - } - if ((incompletePtr->lexeme == COLON) - && (lexeme == QUESTION)) { - break; - } - } - - /* - * Some special syntax checks... - */ - - /* Parens must balance */ - if ((incompletePtr->lexeme == OPEN_PAREN) - && (lexeme != CLOSE_PAREN)) { - TclNewLiteralStringObj(msg, "unbalanced open paren"); - parsePtr->errorType = TCL_PARSE_MISSING_PAREN; - errCode = "UNBALANCED"; - goto error; - } - - /* Right operand of "?" must be ":" */ - if ((incompletePtr->lexeme == QUESTION) - && (NotOperator(complete) - || (nodes[complete].lexeme != COLON))) { - msg = Tcl_ObjPrintf("missing operator \":\" at %s", mark); - scanned = 0; - insertMark = 1; - errCode = "MISSING"; - goto error; - } - - /* Operator ":" may only be right operand of "?" */ - if (IsOperator(complete) - && (nodes[complete].lexeme == COLON) - && (incompletePtr->lexeme != QUESTION)) { - TclNewLiteralStringObj(msg, - "unexpected operator \":\" " - "without preceding \"?\""); - errCode = "SURPRISE"; - goto error; - } - - /* - * Attach complete tree as right operand of most recent - * incomplete tree. - */ - - incompletePtr->right = complete; - if (IsOperator(complete)) { - nodes[complete].p.parent = incomplete; - incompletePtr->constant = incompletePtr->constant - && nodes[complete].constant; - } else { - incompletePtr->constant = incompletePtr->constant - && (complete == OT_LITERAL); - } - - /* - * The QUESTION/COLON and FUNCTION/OPEN_PAREN combinations - * each make up a single operator. Force them to agree whether - * they have a constant expression. - */ - - if ((incompletePtr->lexeme == QUESTION) - || (incompletePtr->lexeme == FUNCTION)) { - nodes[complete].constant = incompletePtr->constant; - } - - if (incompletePtr->lexeme == START) { - /* - * Completing the START tree indicates we're done. - * Transfer the parse tree to the caller and return. - */ - - *opTreePtr = nodes; - return TCL_OK; - } - - /* - * With a right operand attached, last incomplete tree has - * become the complete tree. Pop it from the incomplete tree - * stack. - */ - - complete = incomplete; - incomplete = incompletePtr->p.prev; - - /* CLOSE_PAREN can only close one OPEN_PAREN. */ - if (incompletePtr->lexeme == OPEN_PAREN) { - break; - } - } - - /* - * More syntax checks... - */ - - /* Parens must balance. */ - if (lexeme == CLOSE_PAREN) { - if (incompletePtr->lexeme != OPEN_PAREN) { - TclNewLiteralStringObj(msg, "unbalanced close paren"); - errCode = "UNBALANCED"; - goto error; - } - } - - /* Commas must appear only in function argument lists. */ - if (lexeme == COMMA) { - if ((incompletePtr->lexeme != OPEN_PAREN) - || (incompletePtr[-1].lexeme != FUNCTION)) { - TclNewLiteralStringObj(msg, - "unexpected \",\" outside function argument list"); - errCode = "SURPRISE"; - goto error; - } - } - - /* Operator ":" may only be right operand of "?" */ - if (IsOperator(complete) && (nodes[complete].lexeme == COLON)) { - TclNewLiteralStringObj(msg, - "unexpected operator \":\" without preceding \"?\""); - errCode = "SURPRISE"; - goto error; - } - - /* - * Create no node for a CLOSE_PAREN lexeme. - */ - - if (lexeme == CLOSE_PAREN) { - break; - } - - /* - * Link complete tree as left operand of new node. - */ - - nodePtr->lexeme = lexeme; - nodePtr->precedence = precedence; - nodePtr->mark = MARK_LEFT; - nodePtr->left = complete; - - /* - * The COMMA operator cannot be optimized, since the function - * needs all of its arguments, and optimization would reduce the - * number. Other binary operators root constant expressions when - * both arguments are constant expressions. - */ - - nodePtr->constant = (lexeme != COMMA); - - if (IsOperator(complete)) { - nodes[complete].p.parent = nodesUsed; - nodePtr->constant = nodePtr->constant - && nodes[complete].constant; - } else { - nodePtr->constant = nodePtr->constant - && (complete == OT_LITERAL); - } - - /* - * With a left operand attached and a right operand missing, the - * just-parsed binary operator is root of a new incomplete tree. - * Push it onto the stack of incomplete trees. - */ - - nodePtr->p.prev = incomplete; - incomplete = lastParsed = nodesUsed; - nodesUsed++; - break; - } /* case BINARY */ - } /* lexeme handler */ - - /* Advance past the just-parsed lexeme */ - start += scanned; - numBytes -= scanned; - } /* main parsing loop */ - - /* - * We only get here if there's been an error. Any errors that didn't get a - * suitable parsePtr->errorType, get recorded as syntax errors. - */ - - error: - if (parsePtr->errorType == TCL_PARSE_SUCCESS) { - parsePtr->errorType = TCL_PARSE_SYNTAX; - } - - /* - * Free any partial parse tree we've built. - */ - - if (nodes != NULL) { - ckfree(nodes); - } - - if (interp == NULL) { - /* - * Nowhere to report an error message, so just free it. - */ - - if (msg) { - Tcl_DecrRefCount(msg); - } - } else { - /* - * Construct the complete error message. Start with the simple error - * message, pulled from the interp result if necessary... - */ - - if (msg == NULL) { - msg = Tcl_GetObjResult(interp); - } - - /* - * Add a detailed quote from the bad expression, displaying and - * sometimes marking the precise location of the syntax error. - */ - - Tcl_AppendPrintfToObj(msg, "\nin expression \"%s%.*s%.*s%s%s%.*s%s\"", - ((start - limit) < parsePtr->string) ? "" : "...", - ((start - limit) < parsePtr->string) - ? (int) (start - parsePtr->string) : limit - 3, - ((start - limit) < parsePtr->string) - ? parsePtr->string : start - limit + 3, - (scanned < limit) ? scanned : limit - 3, start, - (scanned < limit) ? "" : "...", insertMark ? mark : "", - (start + scanned + limit > parsePtr->end) - ? (int) (parsePtr->end - start) - scanned : limit-3, - start + scanned, - (start + scanned + limit > parsePtr->end) ? "" : "..."); - - /* - * Next, append any postscript message. - */ - - if (post != NULL) { - Tcl_AppendToObj(msg, ";\n", -1); - Tcl_AppendObjToObj(msg, post); - Tcl_DecrRefCount(post); - } - Tcl_SetObjResult(interp, msg); - - /* - * Finally, place context information in the errorInfo. - */ - - 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 (errCode) { - Tcl_SetErrorCode(interp, "TCL", "PARSE", "EXPR", errCode, - subErrCode, NULL); - } - } - - return TCL_ERROR; -} - -/* - *---------------------------------------------------------------------- - * - * ConvertTreeToTokens -- - * - * Given a string, the numBytes bytes starting at start, and an OpNode - * tree and Tcl_Token array created by passing that same string to - * ParseExpr(), this function writes into *parsePtr the sequence of - * Tcl_Tokens needed so to satisfy the historical interface provided by - * Tcl_ParseExpr(). Note that this routine exists only for the sake of - * the public Tcl_ParseExpr() routine. It is not used by Tcl itself at - * all. - * - * Results: - * None. - * - * Side effects: - * The Tcl_Parse *parsePtr is filled with Tcl_Tokens representing the - * parsed expression. - * - *---------------------------------------------------------------------- - */ - -static void -ConvertTreeToTokens( - const char *start, - int numBytes, - OpNode *nodes, - Tcl_Token *tokenPtr, - Tcl_Parse *parsePtr) -{ - int subExprTokenIdx = 0; - OpNode *nodePtr = nodes; - int next = nodePtr->right; - - while (1) { - Tcl_Token *subExprTokenPtr; - int scanned, parentIdx; - unsigned char lexeme; - - /* - * Advance the mark so the next exit from this node won't retrace - * steps over ground already covered. - */ - - nodePtr->mark++; - - /* - * Handle next child node or leaf. - */ - - switch (next) { - case OT_EMPTY: - - /* No tokens and no characters for the OT_EMPTY leaf. */ - break; - - case OT_LITERAL: - - /* - * Skip any white space that comes before the literal. - */ - - scanned = TclParseAllWhiteSpace(start, numBytes); - start += scanned; - numBytes -= scanned; - - /* - * Reparse the literal to get pointers into source string. - */ - - scanned = ParseLexeme(start, numBytes, &lexeme, NULL); - - TclGrowParseTokenArray(parsePtr, 2); - subExprTokenPtr = parsePtr->tokenPtr + parsePtr->numTokens; - subExprTokenPtr->type = TCL_TOKEN_SUB_EXPR; - subExprTokenPtr->start = start; - subExprTokenPtr->size = scanned; - subExprTokenPtr->numComponents = 1; - subExprTokenPtr[1].type = TCL_TOKEN_TEXT; - subExprTokenPtr[1].start = start; - subExprTokenPtr[1].size = scanned; - subExprTokenPtr[1].numComponents = 0; - - parsePtr->numTokens += 2; - start += scanned; - numBytes -= scanned; - break; - - case OT_TOKENS: { - /* - * tokenPtr points to a token sequence that came from parsing a - * Tcl word. A Tcl word is made up of a sequence of one or more - * elements. When the word is only a single element, it's been the - * historical practice to replace the TCL_TOKEN_WORD token - * directly with a TCL_TOKEN_SUB_EXPR token. However, when the - * word has multiple elements, a TCL_TOKEN_WORD token is kept as a - * grouping device so that TCL_TOKEN_SUB_EXPR always has only one - * element. Wise or not, these are the rules the Tcl expr parser - * has followed, and for the sake of those few callers of - * Tcl_ParseExpr() we do not change them now. Internally, we can - * do better. - */ - - int toCopy = tokenPtr->numComponents + 1; - - if (tokenPtr->numComponents == tokenPtr[1].numComponents + 1) { - /* - * Single element word. Copy tokens and convert the leading - * token to TCL_TOKEN_SUB_EXPR. - */ - - TclGrowParseTokenArray(parsePtr, toCopy); - subExprTokenPtr = parsePtr->tokenPtr + parsePtr->numTokens; - memcpy(subExprTokenPtr, tokenPtr, - (size_t) toCopy * sizeof(Tcl_Token)); - subExprTokenPtr->type = TCL_TOKEN_SUB_EXPR; - parsePtr->numTokens += toCopy; - } else { - /* - * Multiple element word. Create a TCL_TOKEN_SUB_EXPR token to - * lead, with fields initialized from the leading token, then - * copy entire set of word tokens. - */ - - TclGrowParseTokenArray(parsePtr, toCopy+1); - subExprTokenPtr = parsePtr->tokenPtr + parsePtr->numTokens; - *subExprTokenPtr = *tokenPtr; - subExprTokenPtr->type = TCL_TOKEN_SUB_EXPR; - subExprTokenPtr->numComponents++; - subExprTokenPtr++; - memcpy(subExprTokenPtr, tokenPtr, - (size_t) toCopy * sizeof(Tcl_Token)); - parsePtr->numTokens += toCopy + 1; - } - - scanned = tokenPtr->start + tokenPtr->size - start; - start += scanned; - numBytes -= scanned; - tokenPtr += toCopy; - break; - } - - default: - - /* - * Advance to the child node, which is an operator. - */ - - nodePtr = nodes + next; - - /* - * Skip any white space that comes before the subexpression. - */ - - scanned = TclParseAllWhiteSpace(start, numBytes); - start += scanned; - numBytes -= scanned; - - /* - * Generate tokens for the operator / subexpression... - */ - - switch (nodePtr->lexeme) { - case OPEN_PAREN: - case COMMA: - case COLON: - - /* - * Historical practice has been to have no Tcl_Tokens for - * these operators. - */ - - break; - - default: { - - /* - * Remember the index of the last subexpression we were - * working on -- that of our parent. We'll stack it later. - */ - - parentIdx = subExprTokenIdx; - - /* - * Verify space for the two leading Tcl_Tokens representing - * the subexpression rooted by this operator. The first - * Tcl_Token will be of type TCL_TOKEN_SUB_EXPR; the second of - * type TCL_TOKEN_OPERATOR. - */ - - TclGrowParseTokenArray(parsePtr, 2); - subExprTokenIdx = parsePtr->numTokens; - subExprTokenPtr = parsePtr->tokenPtr + subExprTokenIdx; - parsePtr->numTokens += 2; - subExprTokenPtr->type = TCL_TOKEN_SUB_EXPR; - subExprTokenPtr[1].type = TCL_TOKEN_OPERATOR; - - /* - * Our current position scanning the string is the starting - * point for this subexpression. - */ - - subExprTokenPtr->start = start; - - /* - * Eventually, we know that the numComponents field of the - * Tcl_Token of type TCL_TOKEN_OPERATOR will be 0. This means - * we can make other use of this field for now to track the - * stack of subexpressions we have pending. - */ - - subExprTokenPtr[1].numComponents = parentIdx; - break; - } - } - break; - } - - /* Determine which way to exit the node on this pass. */ - router: - switch (nodePtr->mark) { - case MARK_LEFT: - next = nodePtr->left; - break; - - case MARK_RIGHT: - next = nodePtr->right; - - /* - * Skip any white space that comes before the operator. - */ - - scanned = TclParseAllWhiteSpace(start, numBytes); - start += scanned; - numBytes -= scanned; - - /* - * Here we scan from the string the operator corresponding to - * nodePtr->lexeme. - */ - - scanned = ParseLexeme(start, numBytes, &lexeme, NULL); - - switch(nodePtr->lexeme) { - case OPEN_PAREN: - case COMMA: - case COLON: - - /* - * No tokens for these lexemes -> nothing to do. - */ - - break; - - default: - - /* - * Record in the TCL_TOKEN_OPERATOR token the pointers into - * the string marking where the operator is. - */ - - subExprTokenPtr = parsePtr->tokenPtr + subExprTokenIdx; - subExprTokenPtr[1].start = start; - subExprTokenPtr[1].size = scanned; - break; - } - - start += scanned; - numBytes -= scanned; - break; - - case MARK_PARENT: - switch (nodePtr->lexeme) { - case START: - - /* When we get back to the START node, we're done. */ - return; - - case COMMA: - case COLON: - - /* No tokens for these lexemes -> nothing to do. */ - break; - - case OPEN_PAREN: - - /* - * Skip past matching close paren. - */ - - scanned = TclParseAllWhiteSpace(start, numBytes); - start += scanned; - numBytes -= scanned; - scanned = ParseLexeme(start, numBytes, &lexeme, NULL); - start += scanned; - numBytes -= scanned; - break; - - default: - - /* - * Before we leave this node/operator/subexpression for the - * last time, finish up its tokens.... - * - * Our current position scanning the string is where the - * substring for the subexpression ends. - */ - - subExprTokenPtr = parsePtr->tokenPtr + subExprTokenIdx; - subExprTokenPtr->size = start - subExprTokenPtr->start; - - /* - * All the Tcl_Tokens allocated and filled belong to - * this subexpresion. The first token is the leading - * TCL_TOKEN_SUB_EXPR token, and all the rest (one fewer) - * are its components. - */ - - subExprTokenPtr->numComponents = - (parsePtr->numTokens - subExprTokenIdx) - 1; - - /* - * Finally, as we return up the tree to our parent, pop the - * parent subexpression off our subexpression stack, and - * fill in the zero numComponents for the operator Tcl_Token. - */ - - parentIdx = subExprTokenPtr[1].numComponents; - subExprTokenPtr[1].numComponents = 0; - subExprTokenIdx = parentIdx; - break; - } - - /* - * Since we're returning to parent, skip child handling code. - */ - - nodePtr = nodes + nodePtr->p.parent; - goto router; - } - } -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ParseExpr -- - * - * Given a string, the numBytes bytes starting at start, this function - * parses it as a Tcl expression and stores information about the - * structure of the expression in the Tcl_Parse struct indicated 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 - * *parsePtr. 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: - * If there is insufficient space in parsePtr to hold all the information - * about the expression, then additional space is malloc-ed. If the - * function returns TCL_OK then the caller must eventually invoke - * Tcl_FreeParse to release any additional space that was allocated. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_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. */ - Tcl_Parse *parsePtr) /* Structure to fill with information about - * the parsed expression; any previous - * information in the structure is ignored. */ -{ - int code; - OpNode *opTree = NULL; /* Will point to the tree of operators. */ - Tcl_Obj *litList = Tcl_NewObj(); /* List to hold the literals. */ - Tcl_Obj *funcList = Tcl_NewObj(); /* List to hold the functon names. */ - Tcl_Parse *exprParsePtr = TclStackAlloc(interp, sizeof(Tcl_Parse)); - /* Holds the Tcl_Tokens of substitutions. */ - - if (numBytes < 0) { - numBytes = (start ? strlen(start) : 0); - } - - code = ParseExpr(interp, start, numBytes, &opTree, litList, funcList, - exprParsePtr, 1 /* parseOnly */); - Tcl_DecrRefCount(funcList); - Tcl_DecrRefCount(litList); - - TclParseInit(interp, start, numBytes, parsePtr); - if (code == TCL_OK) { - ConvertTreeToTokens(start, numBytes, - opTree, exprParsePtr->tokenPtr, parsePtr); - } else { - parsePtr->term = exprParsePtr->term; - parsePtr->errorType = exprParsePtr->errorType; - } - - Tcl_FreeParse(exprParsePtr); - TclStackFree(interp, exprParsePtr); - ckfree(opTree); - return code; -} - -/* - *---------------------------------------------------------------------- - * - * ParseLexeme -- - * - * Parse a single lexeme from the start of a string, scanning no more - * than numBytes bytes. - * - * Results: - * Returns the number of bytes scanned to produce the lexeme. - * - * Side effects: - * Code identifying lexeme parsed is writen to *lexemePtr. - * - *---------------------------------------------------------------------- - */ - -static int -ParseLexeme( - const char *start, /* Start of lexeme to parse. */ - int numBytes, /* Number of bytes in string. */ - unsigned char *lexemePtr, /* Write code of parsed lexeme to this - * storage. */ - Tcl_Obj **literalPtr) /* Write corresponding literal value to this - storage, if non-NULL. */ -{ - const char *end; - int scanned; - Tcl_UniChar ch; - Tcl_Obj *literal = NULL; - unsigned char byte; - - if (numBytes == 0) { - *lexemePtr = END; - return 0; - } - byte = UCHAR(*start); - if (byte < sizeof(Lexeme) && Lexeme[byte] != 0) { - *lexemePtr = Lexeme[byte]; - return 1; - } - switch (byte) { - case '*': - if ((numBytes > 1) && (start[1] == '*')) { - *lexemePtr = EXPON; - return 2; - } - *lexemePtr = MULT; - return 1; - - case '=': - if ((numBytes > 1) && (start[1] == '=')) { - *lexemePtr = EQUAL; - return 2; - } - *lexemePtr = INCOMPLETE; - return 1; - - case '!': - if ((numBytes > 1) && (start[1] == '=')) { - *lexemePtr = NEQ; - return 2; - } - *lexemePtr = NOT; - return 1; - - case '&': - if ((numBytes > 1) && (start[1] == '&')) { - *lexemePtr = AND; - return 2; - } - *lexemePtr = BIT_AND; - return 1; - - case '|': - if ((numBytes > 1) && (start[1] == '|')) { - *lexemePtr = OR; - return 2; - } - *lexemePtr = BIT_OR; - return 1; - - case '<': - if (numBytes > 1) { - switch (start[1]) { - case '<': - *lexemePtr = LEFT_SHIFT; - return 2; - case '=': - *lexemePtr = LEQ; - return 2; - } - } - *lexemePtr = LESS; - return 1; - - case '>': - if (numBytes > 1) { - switch (start[1]) { - case '>': - *lexemePtr = RIGHT_SHIFT; - return 2; - case '=': - *lexemePtr = GEQ; - return 2; - } - } - *lexemePtr = GREATER; - return 1; - - case 'i': - if ((numBytes > 1) && (start[1] == 'n') - && ((numBytes == 2) || start[2] & 0x80 || !isalpha(UCHAR(start[2])))) { - /* - * Must make this check so we can tell the difference between the - * "in" operator and the "int" function name and the "infinity" - * numeric value. - */ - - *lexemePtr = IN_LIST; - return 2; - } - break; - - case 'e': - if ((numBytes > 1) && (start[1] == 'q') - && ((numBytes == 2) || start[2] & 0x80 || !isalpha(UCHAR(start[2])))) { - *lexemePtr = STREQ; - return 2; - } - break; - - case 'n': - if ((numBytes > 1) - && ((numBytes == 2) || start[2] & 0x80 || !isalpha(UCHAR(start[2])))) { - switch (start[1]) { - case 'e': - *lexemePtr = STRNEQ; - return 2; - case 'i': - *lexemePtr = NOT_IN_LIST; - return 2; - } - } - } - - literal = Tcl_NewObj(); - if (TclParseNumber(NULL, literal, NULL, start, numBytes, &end, - TCL_PARSE_NO_WHITESPACE) == TCL_OK) { - if (end < start + numBytes && !TclIsBareword(*end)) { - - number: - TclInitStringRep(literal, start, end-start); - *lexemePtr = NUMBER; - if (literalPtr) { - *literalPtr = literal; - } else { - Tcl_DecrRefCount(literal); - } - return (end-start); - } else { - unsigned char lexeme; - - /* - * We have a number followed directly by bareword characters - * (alpha, digit, underscore). Is this a number followed by - * bareword syntax error? Or should we join into one bareword? - * Example: Inf + luence + () becomes a valid function call. - * [Bug 3401704] - */ - if (literal->typePtr == &tclDoubleType) { - const char *p = start; - - while (p < end) { - if (!TclIsBareword(*p++)) { - /* - * The number has non-bareword characters, so we - * must treat it as a number. - */ - goto number; - } - } - } - ParseLexeme(end, numBytes-(end-start), &lexeme, NULL); - if ((NODE_TYPE & lexeme) == BINARY) { - /* - * The bareword characters following the number take the - * form of an operator (eq, ne, in, ni, ...) so we treat - * as number + operator. - */ - goto number; - } - - /* - * Otherwise, fall through and parse the whole as a bareword. - */ - } - } - - /* - * We reject leading underscores in bareword. No sensible reason why. - * Might be inspired by reserved identifier rules in C, which of course - * have no direct relevance here. - */ - - if (!TclIsBareword(*start) || *start == '_') { - if (Tcl_UtfCharComplete(start, numBytes)) { - scanned = TclUtfToUniChar(start, &ch); - } else { - char utfBytes[TCL_UTF_MAX]; - - memcpy(utfBytes, start, (size_t) numBytes); - utfBytes[numBytes] = '\0'; - scanned = TclUtfToUniChar(utfBytes, &ch); - } - *lexemePtr = INVALID; - Tcl_DecrRefCount(literal); - return scanned; - } - end = start; - while (numBytes && TclIsBareword(*end)) { - end += 1; - numBytes -= 1; - } - *lexemePtr = BAREWORD; - if (literalPtr) { - Tcl_SetStringObj(literal, start, (int) (end-start)); - *literalPtr = literal; - } else { - Tcl_DecrRefCount(literal); - } - return (end-start); -} - -/* - *---------------------------------------------------------------------- - * - * TclCompileExpr -- - * - * This procedure compiles a string containing a Tcl expression into Tcl - * bytecodes. - * - * Results: - * None. - * - * Side effects: - * Adds instructions to envPtr to evaluate the expression at runtime. - * - *---------------------------------------------------------------------- - */ - -void -TclCompileExpr( - Tcl_Interp *interp, /* Used for error reporting. */ - const char *script, /* The source script to compile. */ - int numBytes, /* Number of bytes in script. */ - CompileEnv *envPtr, /* Holds resulting instructions. */ - int optimize) /* 0 for one-off expressions. */ -{ - OpNode *opTree = NULL; /* Will point to the tree of operators */ - Tcl_Obj *litList = Tcl_NewObj(); /* List to hold the literals */ - Tcl_Obj *funcList = Tcl_NewObj(); /* List to hold the functon names*/ - Tcl_Parse *parsePtr = TclStackAlloc(interp, sizeof(Tcl_Parse)); - /* Holds the Tcl_Tokens of substitutions */ - - int code = ParseExpr(interp, script, numBytes, &opTree, litList, - funcList, parsePtr, 0 /* parseOnly */); - - if (code == TCL_OK) { - /* - * Valid parse; compile the tree. - */ - - int objc; - Tcl_Obj *const *litObjv; - Tcl_Obj **funcObjv; - - /* TIP #280 : Track Lines within the expression */ - TclAdvanceLines(&envPtr->line, script, - script + TclParseAllWhiteSpace(script, numBytes)); - - TclListObjGetElements(NULL, litList, &objc, (Tcl_Obj ***)&litObjv); - TclListObjGetElements(NULL, funcList, &objc, &funcObjv); - CompileExprTree(interp, opTree, 0, &litObjv, funcObjv, - parsePtr->tokenPtr, envPtr, optimize); - } else { - TclCompileSyntaxError(interp, envPtr); - } - - Tcl_FreeParse(parsePtr); - TclStackFree(interp, parsePtr); - Tcl_DecrRefCount(funcList); - Tcl_DecrRefCount(litList); - ckfree(opTree); -} - -/* - *---------------------------------------------------------------------- - * - * ExecConstantExprTree -- - * Compiles and executes bytecode for the subexpression tree at index - * in the nodes array. This subexpression must be constant, made up - * of only constant operators (not functions) and literals. - * - * Results: - * A standard Tcl return code and result left in interp. - * - * Side effects: - * Consumes subtree of nodes rooted at index. Advances the pointer - * *litObjvPtr. - * - *---------------------------------------------------------------------- - */ - -static int -ExecConstantExprTree( - Tcl_Interp *interp, - OpNode *nodes, - int index, - Tcl_Obj *const **litObjvPtr) -{ - CompileEnv *envPtr; - ByteCode *byteCodePtr; - int code; - Tcl_Obj *byteCodeObj = Tcl_NewObj(); - NRE_callback *rootPtr = TOP_CB(interp); - - /* - * Note we are compiling an expression with literal arguments. This means - * there can be no [info frame] calls when we execute the resulting - * bytecode, so there's no need to tend to TIP 280 issues. - */ - - envPtr = TclStackAlloc(interp, sizeof(CompileEnv)); - TclInitCompileEnv(interp, envPtr, NULL, 0, NULL, 0); - CompileExprTree(interp, nodes, index, litObjvPtr, NULL, NULL, envPtr, - 0 /* optimize */); - TclEmitOpcode(INST_DONE, envPtr); - Tcl_IncrRefCount(byteCodeObj); - TclInitByteCodeObj(byteCodeObj, envPtr); - TclFreeCompileEnv(envPtr); - TclStackFree(interp, envPtr); - byteCodePtr = byteCodeObj->internalRep.twoPtrValue.ptr1; - TclNRExecuteByteCode(interp, byteCodePtr); - code = TclNRRunCallbacks(interp, TCL_OK, rootPtr); - Tcl_DecrRefCount(byteCodeObj); - return code; -} - -/* - *---------------------------------------------------------------------- - * - * CompileExprTree -- - * - * Compiles and writes to envPtr instructions for the subexpression tree - * at index in the nodes array. (*litObjvPtr) must point to the proper - * location in a corresponding literals list. Likewise, when non-NULL, - * funcObjv and tokenPtr must point into matching arrays of function - * names and Tcl_Token's derived from earlier call to ParseExpr(). When - * optimize is true, any constant subexpressions will be precomputed. - * - * Results: - * None. - * - * Side effects: - * Adds instructions to envPtr to evaluate the expression at runtime. - * Consumes subtree of nodes rooted at index. Advances the pointer - * *litObjvPtr. - * - *---------------------------------------------------------------------- - */ - -static void -CompileExprTree( - Tcl_Interp *interp, - OpNode *nodes, - int index, - Tcl_Obj *const **litObjvPtr, - Tcl_Obj *const *funcObjv, - Tcl_Token *tokenPtr, - CompileEnv *envPtr, - int optimize) -{ - OpNode *nodePtr = nodes + index; - OpNode *rootPtr = nodePtr; - int numWords = 0; - JumpList *jumpPtr = NULL; - int convert = 1; - - while (1) { - int next; - JumpList *freePtr, *newJump; - - if (nodePtr->mark == MARK_LEFT) { - next = nodePtr->left; - - if (nodePtr->lexeme == QUESTION) { - convert = 1; - } - } else if (nodePtr->mark == MARK_RIGHT) { - next = nodePtr->right; - - switch (nodePtr->lexeme) { - case FUNCTION: { - Tcl_DString cmdName; - const char *p; - int length; - - Tcl_DStringInit(&cmdName); - TclDStringAppendLiteral(&cmdName, "tcl::mathfunc::"); - p = TclGetStringFromObj(*funcObjv, &length); - funcObjv++; - Tcl_DStringAppend(&cmdName, p, length); - TclEmitPush(TclRegisterNewCmdLiteral(envPtr, - Tcl_DStringValue(&cmdName), - Tcl_DStringLength(&cmdName)), envPtr); - Tcl_DStringFree(&cmdName); - - /* - * Start a count of the number of words in this function - * command invocation. In case there's already a count in - * progress (nested functions), save it in our unused "left" - * field for restoring later. - */ - - nodePtr->left = numWords; - numWords = 2; /* Command plus one argument */ - break; - } - case QUESTION: - newJump = TclStackAlloc(interp, sizeof(JumpList)); - newJump->next = jumpPtr; - jumpPtr = newJump; - TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpPtr->jump); - break; - case COLON: - newJump = TclStackAlloc(interp, sizeof(JumpList)); - newJump->next = jumpPtr; - jumpPtr = newJump; - TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, - &jumpPtr->jump); - TclAdjustStackDepth(-1, envPtr); - if (convert) { - jumpPtr->jump.jumpType = TCL_TRUE_JUMP; - } - convert = 1; - break; - case AND: - case OR: - newJump = TclStackAlloc(interp, sizeof(JumpList)); - newJump->next = jumpPtr; - jumpPtr = newJump; - TclEmitForwardJump(envPtr, (nodePtr->lexeme == AND) - ? TCL_FALSE_JUMP : TCL_TRUE_JUMP, &jumpPtr->jump); - break; - } - } else { - int pc1, pc2, target; - - switch (nodePtr->lexeme) { - case START: - case QUESTION: - if (convert && (nodePtr == rootPtr)) { - TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr); - } - break; - case OPEN_PAREN: - - /* do nothing */ - break; - case FUNCTION: - /* - * Use the numWords count we've kept to invoke the function - * command with the correct number of arguments. - */ - - if (numWords < 255) { - TclEmitInvoke(envPtr, INST_INVOKE_STK1, numWords); - } else { - TclEmitInvoke(envPtr, INST_INVOKE_STK4, numWords); - } - - /* - * Restore any saved numWords value. - */ - - numWords = nodePtr->left; - convert = 1; - break; - case COMMA: - /* - * Each comma implies another function argument. - */ - - numWords++; - break; - case COLON: - CLANG_ASSERT(jumpPtr); - if (jumpPtr->jump.jumpType == TCL_TRUE_JUMP) { - jumpPtr->jump.jumpType = TCL_UNCONDITIONAL_JUMP; - convert = 1; - } - target = jumpPtr->jump.codeOffset + 2; - if (TclFixupForwardJumpToHere(envPtr, &jumpPtr->jump, 127)) { - target += 3; - } - freePtr = jumpPtr; - jumpPtr = jumpPtr->next; - TclStackFree(interp, freePtr); - TclFixupForwardJump(envPtr, &jumpPtr->jump, - target - jumpPtr->jump.codeOffset, 127); - - freePtr = jumpPtr; - jumpPtr = jumpPtr->next; - TclStackFree(interp, freePtr); - break; - case AND: - case OR: - CLANG_ASSERT(jumpPtr); - pc1 = CurrentOffset(envPtr); - TclEmitInstInt1((nodePtr->lexeme == AND) ? INST_JUMP_FALSE1 - : INST_JUMP_TRUE1, 0, envPtr); - TclEmitPush(TclRegisterNewLiteral(envPtr, - (nodePtr->lexeme == AND) ? "1" : "0", 1), envPtr); - pc2 = CurrentOffset(envPtr); - TclEmitInstInt1(INST_JUMP1, 0, envPtr); - TclAdjustStackDepth(-1, envPtr); - TclStoreInt1AtPtr(CurrentOffset(envPtr) - pc1, - envPtr->codeStart + pc1 + 1); - if (TclFixupForwardJumpToHere(envPtr, &jumpPtr->jump, 127)) { - pc2 += 3; - } - TclEmitPush(TclRegisterNewLiteral(envPtr, - (nodePtr->lexeme == AND) ? "0" : "1", 1), envPtr); - TclStoreInt1AtPtr(CurrentOffset(envPtr) - pc2, - envPtr->codeStart + pc2 + 1); - convert = 0; - freePtr = jumpPtr; - jumpPtr = jumpPtr->next; - TclStackFree(interp, freePtr); - break; - default: - TclEmitOpcode(instruction[nodePtr->lexeme], envPtr); - convert = 0; - break; - } - if (nodePtr == rootPtr) { - /* We're done */ - - return; - } - nodePtr = nodes + nodePtr->p.parent; - continue; - } - - nodePtr->mark++; - switch (next) { - case OT_EMPTY: - numWords = 1; /* No arguments, so just the command */ - break; - case OT_LITERAL: { - Tcl_Obj *const *litObjv = *litObjvPtr; - Tcl_Obj *literal = *litObjv; - - if (optimize) { - int length; - const char *bytes = TclGetStringFromObj(literal, &length); - int index = TclRegisterNewLiteral(envPtr, bytes, length); - Tcl_Obj *objPtr = TclFetchLiteral(envPtr, index); - - if ((objPtr->typePtr == NULL) && (literal->typePtr != NULL)) { - /* - * Would like to do this: - * - * lePtr->objPtr = literal; - * Tcl_IncrRefCount(literal); - * Tcl_DecrRefCount(objPtr); - * - * However, the design of the "global" and "local" - * LiteralTable does not permit the value of lePtr->objPtr - * to change. So rather than replace lePtr->objPtr, we do - * surgery to transfer our desired intrep into it. - */ - - objPtr->typePtr = literal->typePtr; - objPtr->internalRep = literal->internalRep; - literal->typePtr = NULL; - } - TclEmitPush(index, envPtr); - } else { - /* - * When optimize==0, we know the expression is a one-off and - * there's nothing to be gained from sharing literals when - * they won't live long, and the copies we have already have - * an appropriate intrep. In this case, skip literal - * registration that would enable sharing, and use the routine - * that preserves intreps. - */ - - TclEmitPush(TclAddLiteralObj(envPtr, literal, NULL), envPtr); - } - (*litObjvPtr)++; - break; - } - case OT_TOKENS: - CompileTokens(envPtr, tokenPtr, interp); - tokenPtr += tokenPtr->numComponents + 1; - break; - default: - if (optimize && nodes[next].constant) { - Tcl_InterpState save = Tcl_SaveInterpState(interp, TCL_OK); - - if (ExecConstantExprTree(interp, nodes, next, litObjvPtr) - == TCL_OK) { - int index; - Tcl_Obj *objPtr = Tcl_GetObjResult(interp); - - /* - * Don't generate a string rep, but if we have one - * already, then use it to share via the literal table. - */ - - if (objPtr->bytes) { - Tcl_Obj *tableValue; - - index = TclRegisterNewLiteral(envPtr, objPtr->bytes, - objPtr->length); - tableValue = TclFetchLiteral(envPtr, index); - if ((tableValue->typePtr == NULL) && - (objPtr->typePtr != NULL)) { - /* - * Same intrep surgery as for OT_LITERAL. - */ - - tableValue->typePtr = objPtr->typePtr; - tableValue->internalRep = objPtr->internalRep; - objPtr->typePtr = NULL; - } - } else { - index = TclAddLiteralObj(envPtr, objPtr, NULL); - } - TclEmitPush(index, envPtr); - } else { - TclCompileSyntaxError(interp, envPtr); - } - Tcl_RestoreInterpState(interp, save); - convert = 0; - } else { - nodePtr = nodes + next; - } - } - } -} - -/* - *---------------------------------------------------------------------- - * - * TclSingleOpCmd -- - * - * Implements the commands: ~, !, <<, >>, %, !=, ne, in, ni - * in the ::tcl::mathop namespace. These commands have no - * extension to arbitrary arguments; they accept only exactly one - * or exactly two arguments as suitable for the operator. - * - * Results: - * A standard Tcl return code and result left in interp. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TclSingleOpCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - TclOpCmdClientData *occdPtr = clientData; - unsigned char lexeme; - OpNode nodes[2]; - Tcl_Obj *const *litObjv = objv + 1; - - if (objc != 1 + occdPtr->i.numArgs) { - Tcl_WrongNumArgs(interp, 1, objv, occdPtr->expected); - return TCL_ERROR; - } - - ParseLexeme(occdPtr->op, strlen(occdPtr->op), &lexeme, NULL); - nodes[0].lexeme = START; - nodes[0].mark = MARK_RIGHT; - nodes[0].right = 1; - nodes[1].lexeme = lexeme; - if (objc == 2) { - nodes[1].mark = MARK_RIGHT; - } else { - nodes[1].mark = MARK_LEFT; - nodes[1].left = OT_LITERAL; - } - nodes[1].right = OT_LITERAL; - nodes[1].p.parent = 0; - - return ExecConstantExprTree(interp, nodes, 0, &litObjv); -} - -/* - *---------------------------------------------------------------------- - * - * TclSortingOpCmd -- - * Implements the commands: - * <, <=, >, >=, ==, eq - * in the ::tcl::mathop namespace. These commands are defined for - * arbitrary number of arguments by computing the AND of the base - * operator applied to all neighbor argument pairs. - * - * Results: - * A standard Tcl return code and result left in interp. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TclSortingOpCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - int code = TCL_OK; - - if (objc < 3) { - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); - } else { - TclOpCmdClientData *occdPtr = clientData; - Tcl_Obj **litObjv = TclStackAlloc(interp, - 2 * (objc-2) * sizeof(Tcl_Obj *)); - OpNode *nodes = TclStackAlloc(interp, 2 * (objc-2) * sizeof(OpNode)); - unsigned char lexeme; - int i, lastAnd = 1; - Tcl_Obj *const *litObjPtrPtr = litObjv; - - ParseLexeme(occdPtr->op, strlen(occdPtr->op), &lexeme, NULL); - - litObjv[0] = objv[1]; - nodes[0].lexeme = START; - nodes[0].mark = MARK_RIGHT; - for (i=2; i<objc-1; i++) { - litObjv[2*(i-1)-1] = objv[i]; - nodes[2*(i-1)-1].lexeme = lexeme; - nodes[2*(i-1)-1].mark = MARK_LEFT; - nodes[2*(i-1)-1].left = OT_LITERAL; - nodes[2*(i-1)-1].right = OT_LITERAL; - - litObjv[2*(i-1)] = objv[i]; - nodes[2*(i-1)].lexeme = AND; - nodes[2*(i-1)].mark = MARK_LEFT; - nodes[2*(i-1)].left = lastAnd; - nodes[lastAnd].p.parent = 2*(i-1); - - nodes[2*(i-1)].right = 2*(i-1)+1; - nodes[2*(i-1)+1].p.parent= 2*(i-1); - - lastAnd = 2*(i-1); - } - litObjv[2*(objc-2)-1] = objv[objc-1]; - - nodes[2*(objc-2)-1].lexeme = lexeme; - nodes[2*(objc-2)-1].mark = MARK_LEFT; - nodes[2*(objc-2)-1].left = OT_LITERAL; - nodes[2*(objc-2)-1].right = OT_LITERAL; - - nodes[0].right = lastAnd; - nodes[lastAnd].p.parent = 0; - - code = ExecConstantExprTree(interp, nodes, 0, &litObjPtrPtr); - - TclStackFree(interp, nodes); - TclStackFree(interp, litObjv); - } - return code; -} - -/* - *---------------------------------------------------------------------- - * - * TclVariadicOpCmd -- - * Implements the commands: +, *, &, |, ^, ** - * in the ::tcl::mathop namespace. These commands are defined for - * arbitrary number of arguments by repeatedly applying the base - * operator with suitable associative rules. When fewer than two - * arguments are provided, suitable identity values are returned. - * - * Results: - * A standard Tcl return code and result left in interp. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TclVariadicOpCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - TclOpCmdClientData *occdPtr = clientData; - unsigned char lexeme; - int code; - - if (objc < 2) { - Tcl_SetObjResult(interp, Tcl_NewIntObj(occdPtr->i.identity)); - return TCL_OK; - } - - ParseLexeme(occdPtr->op, strlen(occdPtr->op), &lexeme, NULL); - lexeme |= BINARY; - - if (objc == 2) { - Tcl_Obj *litObjv[2]; - OpNode nodes[2]; - int decrMe = 0; - Tcl_Obj *const *litObjPtrPtr = litObjv; - - if (lexeme == EXPON) { - litObjv[1] = Tcl_NewIntObj(occdPtr->i.identity); - Tcl_IncrRefCount(litObjv[1]); - decrMe = 1; - litObjv[0] = objv[1]; - nodes[0].lexeme = START; - nodes[0].mark = MARK_RIGHT; - nodes[0].right = 1; - nodes[1].lexeme = lexeme; - nodes[1].mark = MARK_LEFT; - nodes[1].left = OT_LITERAL; - nodes[1].right = OT_LITERAL; - nodes[1].p.parent = 0; - } else { - if (lexeme == DIVIDE) { - litObjv[0] = Tcl_NewDoubleObj(1.0); - } else { - litObjv[0] = Tcl_NewIntObj(occdPtr->i.identity); - } - Tcl_IncrRefCount(litObjv[0]); - litObjv[1] = objv[1]; - nodes[0].lexeme = START; - nodes[0].mark = MARK_RIGHT; - nodes[0].right = 1; - nodes[1].lexeme = lexeme; - nodes[1].mark = MARK_LEFT; - nodes[1].left = OT_LITERAL; - nodes[1].right = OT_LITERAL; - nodes[1].p.parent = 0; - } - - code = ExecConstantExprTree(interp, nodes, 0, &litObjPtrPtr); - - Tcl_DecrRefCount(litObjv[decrMe]); - return code; - } else { - Tcl_Obj *const *litObjv = objv + 1; - OpNode *nodes = TclStackAlloc(interp, (objc-1) * sizeof(OpNode)); - int i, lastOp = OT_LITERAL; - - nodes[0].lexeme = START; - nodes[0].mark = MARK_RIGHT; - if (lexeme == EXPON) { - for (i=objc-2; i>0; i--) { - nodes[i].lexeme = lexeme; - nodes[i].mark = MARK_LEFT; - nodes[i].left = OT_LITERAL; - nodes[i].right = lastOp; - if (lastOp >= 0) { - nodes[lastOp].p.parent = i; - } - lastOp = i; - } - } else { - for (i=1; i<objc-1; i++) { - nodes[i].lexeme = lexeme; - nodes[i].mark = MARK_LEFT; - nodes[i].left = lastOp; - if (lastOp >= 0) { - nodes[lastOp].p.parent = i; - } - nodes[i].right = OT_LITERAL; - lastOp = i; - } - } - nodes[0].right = lastOp; - nodes[lastOp].p.parent = 0; - - code = ExecConstantExprTree(interp, nodes, 0, &litObjv); - - TclStackFree(interp, nodes); - return code; - } -} - -/* - *---------------------------------------------------------------------- - * - * TclNoIdentOpCmd -- - * Implements the commands: -, / - * in the ::tcl::mathop namespace. These commands are defined for - * arbitrary non-zero number of arguments by repeatedly applying the base - * operator with suitable associative rules. When no arguments are - * provided, an error is raised. - * - * Results: - * A standard Tcl return code and result left in interp. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TclNoIdentOpCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - TclOpCmdClientData *occdPtr = clientData; - - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, occdPtr->expected); - return TCL_ERROR; - } - return TclVariadicOpCmd(clientData, interp, objc, objv); -} -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |