sl@0: /* sl@0: * tclParseExpr.c -- sl@0: * sl@0: * This file contains procedures that parse Tcl expressions. They sl@0: * do so in a general-purpose fashion that can be used for many sl@0: * different purposes, including compilation, direct execution, sl@0: * code analysis, etc. sl@0: * sl@0: * Copyright (c) 1997 Sun Microsystems, Inc. sl@0: * Copyright (c) 1998-2000 by Scriptics Corporation. sl@0: * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: * Contributions from Don Porter, NIST, 2002. (not subject to US copyright) sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclParseExpr.c,v 1.17.2.2 2005/05/20 17:19:10 vasiljevic Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: sl@0: /* sl@0: * The stuff below is a bit of a hack so that this file can be used in sl@0: * environments that include no UNIX, i.e. no errno: just arrange to use sl@0: * the errno from tclExecute.c here. sl@0: */ sl@0: sl@0: #ifndef TCL_GENERIC_ONLY sl@0: #include "tclPort.h" sl@0: #else sl@0: #define NO_ERRNO_H sl@0: #endif sl@0: sl@0: #ifdef NO_ERRNO_H sl@0: extern int errno; /* Use errno from tclExecute.c. */ sl@0: #define ERANGE 34 sl@0: #endif sl@0: sl@0: /* sl@0: * Boolean variable that controls whether expression parse tracing sl@0: * is enabled. sl@0: */ sl@0: sl@0: #ifdef TCL_COMPILE_DEBUG sl@0: static int traceParseExpr = 0; sl@0: #endif /* TCL_COMPILE_DEBUG */ sl@0: sl@0: /* sl@0: * The ParseInfo structure holds state while parsing an expression. sl@0: * A pointer to an ParseInfo record is passed among the routines in sl@0: * this module. sl@0: */ sl@0: sl@0: typedef struct ParseInfo { sl@0: Tcl_Parse *parsePtr; /* Points to structure to fill in with sl@0: * information about the expression. */ sl@0: int lexeme; /* Type of last lexeme scanned in expr. sl@0: * See below for definitions. Corresponds to sl@0: * size characters beginning at start. */ sl@0: CONST char *start; /* First character in lexeme. */ sl@0: int size; /* Number of bytes in lexeme. */ sl@0: CONST char *next; /* Position of the next character to be sl@0: * scanned in the expression string. */ sl@0: CONST char *prevEnd; /* Points to the character just after the sl@0: * last one in the previous lexeme. Used to sl@0: * compute size of subexpression tokens. */ sl@0: CONST char *originalExpr; /* Points to the start of the expression sl@0: * originally passed to Tcl_ParseExpr. */ sl@0: CONST char *lastChar; /* Points just after last byte of expr. */ sl@0: } ParseInfo; sl@0: sl@0: /* sl@0: * Definitions of the different lexemes that appear in expressions. The sl@0: * order of these must match the corresponding entries in the sl@0: * operatorStrings array below. sl@0: * sl@0: * Basic lexemes: sl@0: */ sl@0: sl@0: #define LITERAL 0 sl@0: #define FUNC_NAME 1 sl@0: #define OPEN_BRACKET 2 sl@0: #define OPEN_BRACE 3 sl@0: #define OPEN_PAREN 4 sl@0: #define CLOSE_PAREN 5 sl@0: #define DOLLAR 6 sl@0: #define QUOTE 7 sl@0: #define COMMA 8 sl@0: #define END 9 sl@0: #define UNKNOWN 10 sl@0: #define UNKNOWN_CHAR 11 sl@0: sl@0: /* sl@0: * Binary numeric operators: sl@0: */ sl@0: sl@0: #define MULT 12 sl@0: #define DIVIDE 13 sl@0: #define MOD 14 sl@0: #define PLUS 15 sl@0: #define MINUS 16 sl@0: #define LEFT_SHIFT 17 sl@0: #define RIGHT_SHIFT 18 sl@0: #define LESS 19 sl@0: #define GREATER 20 sl@0: #define LEQ 21 sl@0: #define GEQ 22 sl@0: #define EQUAL 23 sl@0: #define NEQ 24 sl@0: #define BIT_AND 25 sl@0: #define BIT_XOR 26 sl@0: #define BIT_OR 27 sl@0: #define AND 28 sl@0: #define OR 29 sl@0: #define QUESTY 30 sl@0: #define COLON 31 sl@0: sl@0: /* sl@0: * Unary operators. Unary minus and plus are represented by the (binary) sl@0: * lexemes MINUS and PLUS. sl@0: */ sl@0: sl@0: #define NOT 32 sl@0: #define BIT_NOT 33 sl@0: sl@0: /* sl@0: * Binary string operators: sl@0: */ sl@0: sl@0: #define STREQ 34 sl@0: #define STRNEQ 35 sl@0: sl@0: /* sl@0: * Mapping from lexemes to strings; used for debugging messages. These sl@0: * entries must match the order and number of the lexeme definitions above. sl@0: */ sl@0: sl@0: static char *lexemeStrings[] = { sl@0: "LITERAL", "FUNCNAME", sl@0: "[", "{", "(", ")", "$", "\"", ",", "END", "UNKNOWN", "UNKNOWN_CHAR", sl@0: "*", "/", "%", "+", "-", sl@0: "<<", ">>", "<", ">", "<=", ">=", "==", "!=", sl@0: "&", "^", "|", "&&", "||", "?", ":", sl@0: "!", "~", "eq", "ne", sl@0: }; sl@0: sl@0: /* sl@0: * Declarations for local procedures to this file: sl@0: */ sl@0: sl@0: static int GetLexeme _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static void LogSyntaxError _ANSI_ARGS_((ParseInfo *infoPtr, sl@0: CONST char *extraInfo)); sl@0: static int ParseAddExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseBitAndExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseBitOrExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseBitXorExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseCondExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseEqualityExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseLandExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseLorExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseMaxDoubleLength _ANSI_ARGS_((CONST char *string, sl@0: CONST char *end)); sl@0: static int ParseMultiplyExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParsePrimaryExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseRelationalExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseShiftExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static int ParseUnaryExpr _ANSI_ARGS_((ParseInfo *infoPtr)); sl@0: static void PrependSubExprTokens _ANSI_ARGS_((CONST char *op, sl@0: int opBytes, CONST char *src, int srcBytes, sl@0: int firstIndex, ParseInfo *infoPtr)); sl@0: sl@0: /* sl@0: * Macro used to debug the execution of the recursive descent parser used sl@0: * to parse expressions. sl@0: */ sl@0: sl@0: #ifdef TCL_COMPILE_DEBUG sl@0: #define HERE(production, level) \ sl@0: if (traceParseExpr) { \ sl@0: fprintf(stderr, "%*s%s: lexeme=%s, next=\"%.20s\"\n", \ sl@0: (level), " ", (production), \ sl@0: lexemeStrings[infoPtr->lexeme], infoPtr->next); \ sl@0: } sl@0: #else sl@0: #define HERE(production, level) sl@0: #endif /* TCL_COMPILE_DEBUG */ sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_ParseExpr -- sl@0: * sl@0: * Given a string, this procedure parses the first Tcl expression sl@0: * in the string and returns information about the structure of sl@0: * the expression. This procedure is the top-level interface to the sl@0: * the expression parsing module. No more that numBytes bytes will sl@0: * be scanned. sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK if the command was parsed successfully sl@0: * and TCL_ERROR otherwise. If an error occurs and interp isn't NULL sl@0: * then an error message is left in its result. On a successful return, sl@0: * parsePtr is filled in with information about the expression that sl@0: * was parsed. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the expression, then additional space is sl@0: * malloc-ed. If the procedure returns TCL_OK then the caller must sl@0: * eventually invoke Tcl_FreeParse to release any additional space sl@0: * that was allocated. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C int sl@0: Tcl_ParseExpr(interp, string, numBytes, parsePtr) sl@0: Tcl_Interp *interp; /* Used for error reporting. */ sl@0: CONST char *string; /* The source string to parse. */ sl@0: int numBytes; /* Number of bytes in string. If < 0, the sl@0: * string consists of all bytes up to the sl@0: * first null character. */ sl@0: Tcl_Parse *parsePtr; /* Structure to fill with information about sl@0: * the parsed expression; any previous sl@0: * information in the structure is sl@0: * ignored. */ sl@0: { sl@0: ParseInfo info; sl@0: int code; sl@0: sl@0: if (numBytes < 0) { sl@0: numBytes = (string? strlen(string) : 0); sl@0: } sl@0: #ifdef TCL_COMPILE_DEBUG sl@0: if (traceParseExpr) { sl@0: fprintf(stderr, "Tcl_ParseExpr: string=\"%.*s\"\n", sl@0: numBytes, string); sl@0: } sl@0: #endif /* TCL_COMPILE_DEBUG */ sl@0: sl@0: parsePtr->commentStart = NULL; sl@0: parsePtr->commentSize = 0; sl@0: parsePtr->commandStart = NULL; sl@0: parsePtr->commandSize = 0; sl@0: parsePtr->numWords = 0; sl@0: parsePtr->tokenPtr = parsePtr->staticTokens; sl@0: parsePtr->numTokens = 0; sl@0: parsePtr->tokensAvailable = NUM_STATIC_TOKENS; sl@0: parsePtr->string = string; sl@0: parsePtr->end = (string + numBytes); sl@0: parsePtr->interp = interp; sl@0: parsePtr->term = string; sl@0: parsePtr->incomplete = 0; sl@0: sl@0: /* sl@0: * Initialize the ParseInfo structure that holds state while parsing sl@0: * the expression. sl@0: */ sl@0: sl@0: info.parsePtr = parsePtr; sl@0: info.lexeme = UNKNOWN; sl@0: info.start = NULL; sl@0: info.size = 0; sl@0: info.next = string; sl@0: info.prevEnd = string; sl@0: info.originalExpr = string; sl@0: info.lastChar = (string + numBytes); /* just after last char of expr */ sl@0: sl@0: /* sl@0: * Get the first lexeme then parse the expression. sl@0: */ sl@0: sl@0: code = GetLexeme(&info); sl@0: if (code != TCL_OK) { sl@0: goto error; sl@0: } sl@0: code = ParseCondExpr(&info); sl@0: if (code != TCL_OK) { sl@0: goto error; sl@0: } sl@0: if (info.lexeme != END) { sl@0: LogSyntaxError(&info, "extra tokens at end of expression"); sl@0: goto error; sl@0: } sl@0: return TCL_OK; sl@0: sl@0: error: sl@0: if (parsePtr->tokenPtr != parsePtr->staticTokens) { sl@0: ckfree((char *) parsePtr->tokenPtr); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseCondExpr -- sl@0: * sl@0: * This procedure parses a Tcl conditional expression: sl@0: * condExpr ::= lorExpr ['?' condExpr ':' condExpr] sl@0: * sl@0: * Note that this is the topmost recursive-descent parsing routine used sl@0: * by Tcl_ParseExpr to parse expressions. This avoids an extra procedure sl@0: * call since such a procedure would only return the result of calling sl@0: * ParseCondExpr. Other recursive-descent procedures that need to parse sl@0: * complete expressions also call ParseCondExpr. sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseCondExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: Tcl_Token *tokenPtr, *firstTokenPtr, *condTokenPtr; sl@0: int firstIndex, numToMove, code; sl@0: CONST char *srcStart; sl@0: sl@0: HERE("condExpr", 1); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseLorExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: if (infoPtr->lexeme == QUESTY) { sl@0: /* sl@0: * Emit two tokens: one TCL_TOKEN_SUB_EXPR token for the entire sl@0: * conditional expression, and a TCL_TOKEN_OPERATOR token for sl@0: * the "?" operator. Note that these two tokens must be inserted sl@0: * before the LOR operand tokens generated above. sl@0: */ sl@0: sl@0: if ((parsePtr->numTokens + 1) >= parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: firstTokenPtr = &parsePtr->tokenPtr[firstIndex]; sl@0: tokenPtr = (firstTokenPtr + 2); sl@0: numToMove = (parsePtr->numTokens - firstIndex); sl@0: memmove((VOID *) tokenPtr, (VOID *) firstTokenPtr, sl@0: (size_t) (numToMove * sizeof(Tcl_Token))); sl@0: parsePtr->numTokens += 2; sl@0: sl@0: tokenPtr = firstTokenPtr; sl@0: tokenPtr->type = TCL_TOKEN_SUB_EXPR; sl@0: tokenPtr->start = srcStart; sl@0: sl@0: tokenPtr++; sl@0: tokenPtr->type = TCL_TOKEN_OPERATOR; sl@0: tokenPtr->start = infoPtr->start; sl@0: tokenPtr->size = 1; sl@0: tokenPtr->numComponents = 0; sl@0: sl@0: /* sl@0: * Skip over the '?'. sl@0: */ sl@0: sl@0: code = GetLexeme(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Parse the "then" expression. sl@0: */ sl@0: sl@0: code = ParseCondExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: if (infoPtr->lexeme != COLON) { sl@0: LogSyntaxError(infoPtr, "missing colon from ternary conditional"); sl@0: return TCL_ERROR; sl@0: } sl@0: code = GetLexeme(infoPtr); /* skip over the ':' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Parse the "else" expression. sl@0: */ sl@0: sl@0: code = ParseCondExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Now set the size-related fields in the '?' subexpression token. sl@0: */ sl@0: sl@0: condTokenPtr = &parsePtr->tokenPtr[firstIndex]; sl@0: condTokenPtr->size = (infoPtr->prevEnd - srcStart); sl@0: condTokenPtr->numComponents = parsePtr->numTokens - (firstIndex+1); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseLorExpr -- sl@0: * sl@0: * This procedure parses a Tcl logical or expression: sl@0: * lorExpr ::= landExpr {'||' landExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseLorExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("lorExpr", 2); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseLandExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: while (infoPtr->lexeme == OR) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over the '||' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseLandExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the LOR subexpression and the '||' operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 2, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseLandExpr -- sl@0: * sl@0: * This procedure parses a Tcl logical and expression: sl@0: * landExpr ::= bitOrExpr {'&&' bitOrExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseLandExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("landExpr", 3); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseBitOrExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: while (infoPtr->lexeme == AND) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over the '&&' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseBitOrExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the LAND subexpression and the '&&' operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 2, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseBitOrExpr -- sl@0: * sl@0: * This procedure parses a Tcl bitwise or expression: sl@0: * bitOrExpr ::= bitXorExpr {'|' bitXorExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseBitOrExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("bitOrExpr", 4); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseBitXorExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: while (infoPtr->lexeme == BIT_OR) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over the '|' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: code = ParseBitXorExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the BITOR subexpression and the '|' operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 1, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseBitXorExpr -- sl@0: * sl@0: * This procedure parses a Tcl bitwise exclusive or expression: sl@0: * bitXorExpr ::= bitAndExpr {'^' bitAndExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseBitXorExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("bitXorExpr", 5); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseBitAndExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: while (infoPtr->lexeme == BIT_XOR) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over the '^' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: code = ParseBitAndExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the XOR subexpression and the '^' operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 1, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseBitAndExpr -- sl@0: * sl@0: * This procedure parses a Tcl bitwise and expression: sl@0: * bitAndExpr ::= equalityExpr {'&' equalityExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseBitAndExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("bitAndExpr", 6); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseEqualityExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: while (infoPtr->lexeme == BIT_AND) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over the '&' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseEqualityExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the BITAND subexpression and '&' operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 1, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseEqualityExpr -- sl@0: * sl@0: * This procedure parses a Tcl equality (inequality) expression: sl@0: * equalityExpr ::= relationalExpr sl@0: * {('==' | '!=' | 'ne' | 'eq') relationalExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseEqualityExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, lexeme, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("equalityExpr", 7); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseRelationalExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: lexeme = infoPtr->lexeme; sl@0: while ((lexeme == EQUAL) || (lexeme == NEQ) sl@0: || (lexeme == STREQ) || (lexeme == STRNEQ)) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over ==, !=, 'eq' or 'ne' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseRelationalExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the subexpression and '==', '!=', 'eq' or 'ne' sl@0: * operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 2, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: lexeme = infoPtr->lexeme; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseRelationalExpr -- sl@0: * sl@0: * This procedure parses a Tcl relational expression: sl@0: * relationalExpr ::= shiftExpr {('<' | '>' | '<=' | '>=') shiftExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseRelationalExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, lexeme, operatorSize, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("relationalExpr", 8); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseShiftExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: lexeme = infoPtr->lexeme; sl@0: while ((lexeme == LESS) || (lexeme == GREATER) || (lexeme == LEQ) sl@0: || (lexeme == GEQ)) { sl@0: operator = infoPtr->start; sl@0: if ((lexeme == LEQ) || (lexeme == GEQ)) { sl@0: operatorSize = 2; sl@0: } else { sl@0: operatorSize = 1; sl@0: } sl@0: code = GetLexeme(infoPtr); /* skip over the operator */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseShiftExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the subexpression and the operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, operatorSize, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: lexeme = infoPtr->lexeme; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseShiftExpr -- sl@0: * sl@0: * This procedure parses a Tcl shift expression: sl@0: * shiftExpr ::= addExpr {('<<' | '>>') addExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseShiftExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, lexeme, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("shiftExpr", 9); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseAddExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: lexeme = infoPtr->lexeme; sl@0: while ((lexeme == LEFT_SHIFT) || (lexeme == RIGHT_SHIFT)) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over << or >> */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseAddExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the subexpression and '<<' or '>>' operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 2, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: lexeme = infoPtr->lexeme; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseAddExpr -- sl@0: * sl@0: * This procedure parses a Tcl addition expression: sl@0: * addExpr ::= multiplyExpr {('+' | '-') multiplyExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseAddExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, lexeme, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("addExpr", 10); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseMultiplyExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: lexeme = infoPtr->lexeme; sl@0: while ((lexeme == PLUS) || (lexeme == MINUS)) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over + or - */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseMultiplyExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the subexpression and '+' or '-' operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 1, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: lexeme = infoPtr->lexeme; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseMultiplyExpr -- sl@0: * sl@0: * This procedure parses a Tcl multiply expression: sl@0: * multiplyExpr ::= unaryExpr {('*' | '/' | '%') unaryExpr} sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseMultiplyExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, lexeme, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("multiplyExpr", 11); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: code = ParseUnaryExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: lexeme = infoPtr->lexeme; sl@0: while ((lexeme == MULT) || (lexeme == DIVIDE) || (lexeme == MOD)) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over * or / or % */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseUnaryExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the subexpression and * or / or % operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 1, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: lexeme = infoPtr->lexeme; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseUnaryExpr -- sl@0: * sl@0: * This procedure parses a Tcl unary expression: sl@0: * unaryExpr ::= ('+' | '-' | '~' | '!') unaryExpr | primaryExpr sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseUnaryExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: int firstIndex, lexeme, code; sl@0: CONST char *srcStart, *operator; sl@0: sl@0: HERE("unaryExpr", 12); sl@0: srcStart = infoPtr->start; sl@0: firstIndex = parsePtr->numTokens; sl@0: sl@0: lexeme = infoPtr->lexeme; sl@0: if ((lexeme == PLUS) || (lexeme == MINUS) || (lexeme == BIT_NOT) sl@0: || (lexeme == NOT)) { sl@0: operator = infoPtr->start; sl@0: code = GetLexeme(infoPtr); /* skip over the unary operator */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseUnaryExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: * Generate tokens for the subexpression and the operator. sl@0: */ sl@0: sl@0: PrependSubExprTokens(operator, 1, srcStart, sl@0: (infoPtr->prevEnd - srcStart), firstIndex, infoPtr); sl@0: } else { /* must be a primaryExpr */ sl@0: code = ParsePrimaryExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParsePrimaryExpr -- sl@0: * sl@0: * This procedure parses a Tcl primary expression: sl@0: * primaryExpr ::= literal | varReference | quotedString | sl@0: * '[' command ']' | mathFuncCall | '(' condExpr ')' sl@0: * sl@0: * Results: sl@0: * The return value is TCL_OK on a successful parse and TCL_ERROR sl@0: * on failure. If TCL_ERROR is returned, then the interpreter's result sl@0: * contains an error message. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParsePrimaryExpr(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: Tcl_Interp *interp = parsePtr->interp; sl@0: Tcl_Token *tokenPtr, *exprTokenPtr; sl@0: Tcl_Parse nested; sl@0: CONST char *dollarPtr, *stringStart, *termPtr, *src; sl@0: int lexeme, exprIndex, firstIndex, numToMove, code; sl@0: sl@0: /* sl@0: * We simply recurse on parenthesized subexpressions. sl@0: */ sl@0: sl@0: HERE("primaryExpr", 13); sl@0: lexeme = infoPtr->lexeme; sl@0: if (lexeme == OPEN_PAREN) { sl@0: code = GetLexeme(infoPtr); /* skip over the '(' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: code = ParseCondExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: if (infoPtr->lexeme != CLOSE_PAREN) { sl@0: LogSyntaxError(infoPtr, "looking for close parenthesis"); sl@0: return TCL_ERROR; sl@0: } sl@0: code = GetLexeme(infoPtr); /* skip over the ')' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: * Start a TCL_TOKEN_SUB_EXPR token for the primary. sl@0: */ sl@0: sl@0: if (parsePtr->numTokens == parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: exprIndex = parsePtr->numTokens; sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->type = TCL_TOKEN_SUB_EXPR; sl@0: exprTokenPtr->start = infoPtr->start; sl@0: parsePtr->numTokens++; sl@0: sl@0: /* sl@0: * Process the primary then finish setting the fields of the sl@0: * TCL_TOKEN_SUB_EXPR token. Note that we can't use the pointer now sl@0: * stored in "exprTokenPtr" in the code below since the token array sl@0: * might be reallocated. sl@0: */ sl@0: sl@0: firstIndex = parsePtr->numTokens; sl@0: switch (lexeme) { sl@0: case LITERAL: sl@0: /* sl@0: * Int or double number. sl@0: */ sl@0: sl@0: tokenizeLiteral: sl@0: if (parsePtr->numTokens == parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens]; sl@0: tokenPtr->type = TCL_TOKEN_TEXT; sl@0: tokenPtr->start = infoPtr->start; sl@0: tokenPtr->size = infoPtr->size; sl@0: tokenPtr->numComponents = 0; sl@0: parsePtr->numTokens++; sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->size = infoPtr->size; sl@0: exprTokenPtr->numComponents = 1; sl@0: break; sl@0: sl@0: case DOLLAR: sl@0: /* sl@0: * $var variable reference. sl@0: */ sl@0: sl@0: dollarPtr = (infoPtr->next - 1); sl@0: code = Tcl_ParseVarName(interp, dollarPtr, sl@0: (infoPtr->lastChar - dollarPtr), parsePtr, 1); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: infoPtr->next = dollarPtr + parsePtr->tokenPtr[firstIndex].size; sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->size = parsePtr->tokenPtr[firstIndex].size; sl@0: exprTokenPtr->numComponents = sl@0: (parsePtr->tokenPtr[firstIndex].numComponents + 1); sl@0: break; sl@0: sl@0: case QUOTE: sl@0: /* sl@0: * '"' string '"' sl@0: */ sl@0: sl@0: stringStart = infoPtr->next; sl@0: code = Tcl_ParseQuotedString(interp, infoPtr->start, sl@0: (infoPtr->lastChar - stringStart), parsePtr, 1, &termPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: infoPtr->next = termPtr; sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->size = (termPtr - exprTokenPtr->start); sl@0: exprTokenPtr->numComponents = parsePtr->numTokens - firstIndex; sl@0: sl@0: /* sl@0: * If parsing the quoted string resulted in more than one token, sl@0: * insert a TCL_TOKEN_WORD token before them. This indicates that sl@0: * the quoted string represents a concatenation of multiple tokens. sl@0: */ sl@0: sl@0: if (exprTokenPtr->numComponents > 1) { sl@0: if (parsePtr->numTokens >= parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: tokenPtr = &parsePtr->tokenPtr[firstIndex]; sl@0: numToMove = (parsePtr->numTokens - firstIndex); sl@0: memmove((VOID *) (tokenPtr + 1), (VOID *) tokenPtr, sl@0: (size_t) (numToMove * sizeof(Tcl_Token))); sl@0: parsePtr->numTokens++; sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->numComponents++; sl@0: sl@0: tokenPtr->type = TCL_TOKEN_WORD; sl@0: tokenPtr->start = exprTokenPtr->start; sl@0: tokenPtr->size = exprTokenPtr->size; sl@0: tokenPtr->numComponents = (exprTokenPtr->numComponents - 1); sl@0: } sl@0: break; sl@0: sl@0: case OPEN_BRACKET: sl@0: /* sl@0: * '[' command {command} ']' sl@0: */ sl@0: sl@0: if (parsePtr->numTokens == parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens]; sl@0: tokenPtr->type = TCL_TOKEN_COMMAND; sl@0: tokenPtr->start = infoPtr->start; sl@0: tokenPtr->numComponents = 0; sl@0: parsePtr->numTokens++; sl@0: sl@0: /* sl@0: * Call Tcl_ParseCommand repeatedly to parse the nested command(s) sl@0: * to find their end, then throw away that parse information. sl@0: */ sl@0: sl@0: src = infoPtr->next; sl@0: while (1) { sl@0: if (Tcl_ParseCommand(interp, src, (parsePtr->end - src), 1, sl@0: &nested) != TCL_OK) { sl@0: parsePtr->term = nested.term; sl@0: parsePtr->errorType = nested.errorType; sl@0: parsePtr->incomplete = nested.incomplete; sl@0: return TCL_ERROR; sl@0: } sl@0: src = (nested.commandStart + nested.commandSize); sl@0: sl@0: /* sl@0: * This is equivalent to Tcl_FreeParse(&nested), but sl@0: * presumably inlined here for sake of runtime optimization sl@0: */ sl@0: sl@0: if (nested.tokenPtr != nested.staticTokens) { sl@0: ckfree((char *) nested.tokenPtr); sl@0: } sl@0: sl@0: /* sl@0: * Check for the closing ']' that ends the command substitution. sl@0: * It must have been the last character of the parsed command. sl@0: */ sl@0: sl@0: if ((nested.term < parsePtr->end) && (*nested.term == ']') sl@0: && !nested.incomplete) { sl@0: break; sl@0: } sl@0: if (src == parsePtr->end) { sl@0: if (parsePtr->interp != NULL) { sl@0: Tcl_SetResult(interp, "missing close-bracket", sl@0: TCL_STATIC); sl@0: } sl@0: parsePtr->term = tokenPtr->start; sl@0: parsePtr->errorType = TCL_PARSE_MISSING_BRACKET; sl@0: parsePtr->incomplete = 1; sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: tokenPtr->size = (src - tokenPtr->start); sl@0: infoPtr->next = src; sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->size = (src - tokenPtr->start); sl@0: exprTokenPtr->numComponents = 1; sl@0: break; sl@0: sl@0: case OPEN_BRACE: sl@0: /* sl@0: * '{' string '}' sl@0: */ sl@0: sl@0: code = Tcl_ParseBraces(interp, infoPtr->start, sl@0: (infoPtr->lastChar - infoPtr->start), parsePtr, 1, sl@0: &termPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: infoPtr->next = termPtr; sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->size = (termPtr - infoPtr->start); sl@0: exprTokenPtr->numComponents = parsePtr->numTokens - firstIndex; sl@0: sl@0: /* sl@0: * If parsing the braced string resulted in more than one token, sl@0: * insert a TCL_TOKEN_WORD token before them. This indicates that sl@0: * the braced string represents a concatenation of multiple tokens. sl@0: */ sl@0: sl@0: if (exprTokenPtr->numComponents > 1) { sl@0: if (parsePtr->numTokens >= parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: tokenPtr = &parsePtr->tokenPtr[firstIndex]; sl@0: numToMove = (parsePtr->numTokens - firstIndex); sl@0: memmove((VOID *) (tokenPtr + 1), (VOID *) tokenPtr, sl@0: (size_t) (numToMove * sizeof(Tcl_Token))); sl@0: parsePtr->numTokens++; sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->numComponents++; sl@0: sl@0: tokenPtr->type = TCL_TOKEN_WORD; sl@0: tokenPtr->start = exprTokenPtr->start; sl@0: tokenPtr->size = exprTokenPtr->size; sl@0: tokenPtr->numComponents = exprTokenPtr->numComponents-1; sl@0: } sl@0: break; sl@0: sl@0: case STREQ: sl@0: case STRNEQ: sl@0: case FUNC_NAME: { sl@0: /* sl@0: * math_func '(' expr {',' expr} ')' sl@0: */ sl@0: sl@0: ParseInfo savedInfo = *infoPtr; sl@0: sl@0: code = GetLexeme(infoPtr); /* skip over function name */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: if (infoPtr->lexeme != OPEN_PAREN) { sl@0: int code; sl@0: Tcl_DString functionName; sl@0: Tcl_HashEntry *hPtr; sl@0: Interp *iPtr = (Interp *) infoPtr->parsePtr->interp; sl@0: Tcl_Obj *objPtr = Tcl_NewStringObj(savedInfo.start, savedInfo.size); sl@0: sl@0: /* Check for boolean literals (true, false, yes, no, on, off) */ sl@0: Tcl_IncrRefCount(objPtr); sl@0: code = Tcl_ConvertToType(NULL, objPtr, &tclBooleanType); sl@0: Tcl_DecrRefCount(objPtr); sl@0: if (code == TCL_OK) { sl@0: *infoPtr = savedInfo; sl@0: goto tokenizeLiteral; sl@0: } sl@0: sl@0: /* sl@0: * Guess what kind of error we have by trying to tell sl@0: * whether we have a function or variable name here. sl@0: * Alas, this makes the parser more tightly bound with the sl@0: * rest of the interpreter, but that is the only way to sl@0: * give a sensible message here. Still, it is not too sl@0: * serious as this is only done when generating an error. sl@0: */ sl@0: sl@0: /* sl@0: * Look up the name as a function name. We need a writable sl@0: * copy (DString) so we can terminate it with a NULL for sl@0: * the benefit of Tcl_FindHashEntry which operates on sl@0: * NULL-terminated string keys. sl@0: */ sl@0: Tcl_DStringInit(&functionName); sl@0: hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, sl@0: Tcl_DStringAppend(&functionName, sl@0: savedInfo.start, savedInfo.size)); sl@0: Tcl_DStringFree(&functionName); sl@0: sl@0: /* sl@0: * Assume that we have an attempted variable reference sl@0: * unless we've got a function name, as the set of sl@0: * potential function names is typically much smaller. sl@0: */ sl@0: if (hPtr != NULL) { sl@0: LogSyntaxError(infoPtr, sl@0: "expected parenthesis enclosing function arguments"); sl@0: } else { sl@0: LogSyntaxError(infoPtr, sl@0: "variable references require preceding $"); sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: if (parsePtr->numTokens == parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens]; sl@0: tokenPtr->type = TCL_TOKEN_OPERATOR; sl@0: tokenPtr->start = savedInfo.start; sl@0: tokenPtr->size = savedInfo.size; sl@0: tokenPtr->numComponents = 0; sl@0: parsePtr->numTokens++; sl@0: sl@0: code = GetLexeme(infoPtr); /* skip over '(' */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: while (infoPtr->lexeme != CLOSE_PAREN) { sl@0: code = ParseCondExpr(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: sl@0: if (infoPtr->lexeme == COMMA) { sl@0: code = GetLexeme(infoPtr); /* skip over , */ sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: } else if (infoPtr->lexeme != CLOSE_PAREN) { sl@0: LogSyntaxError(infoPtr, sl@0: "missing close parenthesis at end of function call"); sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: sl@0: exprTokenPtr = &parsePtr->tokenPtr[exprIndex]; sl@0: exprTokenPtr->size = (infoPtr->next - exprTokenPtr->start); sl@0: exprTokenPtr->numComponents = parsePtr->numTokens - firstIndex; sl@0: break; sl@0: } sl@0: sl@0: case COMMA: sl@0: LogSyntaxError(infoPtr, sl@0: "commas can only separate function arguments"); sl@0: return TCL_ERROR; sl@0: case END: sl@0: LogSyntaxError(infoPtr, "premature end of expression"); sl@0: return TCL_ERROR; sl@0: case UNKNOWN: sl@0: LogSyntaxError(infoPtr, "single equality character not legal in expressions"); sl@0: return TCL_ERROR; sl@0: case UNKNOWN_CHAR: sl@0: LogSyntaxError(infoPtr, "character not legal in expressions"); sl@0: return TCL_ERROR; sl@0: case QUESTY: sl@0: LogSyntaxError(infoPtr, "unexpected ternary 'then' separator"); sl@0: return TCL_ERROR; sl@0: case COLON: sl@0: LogSyntaxError(infoPtr, "unexpected ternary 'else' separator"); sl@0: return TCL_ERROR; sl@0: case CLOSE_PAREN: sl@0: LogSyntaxError(infoPtr, "unexpected close parenthesis"); sl@0: return TCL_ERROR; sl@0: sl@0: default: { sl@0: char buf[64]; sl@0: sl@0: sprintf(buf, "unexpected operator %s", lexemeStrings[lexeme]); sl@0: LogSyntaxError(infoPtr, buf); sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * Advance to the next lexeme before returning. sl@0: */ sl@0: sl@0: code = GetLexeme(infoPtr); sl@0: if (code != TCL_OK) { sl@0: return code; sl@0: } sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * GetLexeme -- sl@0: * sl@0: * Lexical scanner for Tcl expressions: scans a single operator or sl@0: * other syntactic element from an expression string. sl@0: * sl@0: * Results: sl@0: * TCL_OK is returned unless an error occurred. In that case a standard sl@0: * Tcl error code is returned and, if infoPtr->parsePtr->interp is sl@0: * non-NULL, the interpreter's result is set to hold an error sl@0: * message. TCL_ERROR is returned if an integer overflow, or a sl@0: * floating-point overflow or underflow occurred while reading in a sl@0: * number. If the lexical analysis is successful, infoPtr->lexeme sl@0: * refers to the next symbol in the expression string, and sl@0: * infoPtr->next is advanced past the lexeme. Also, if the lexeme is a sl@0: * LITERAL or FUNC_NAME, then infoPtr->start is set to the first sl@0: * character of the lexeme; otherwise it is set NULL. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold all the sl@0: * information about the subexpression, then additional space is sl@0: * malloc-ed.. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: GetLexeme(infoPtr) sl@0: ParseInfo *infoPtr; /* Holds state needed to parse the expr, sl@0: * including the resulting lexeme. */ sl@0: { sl@0: register CONST char *src; /* Points to current source char. */ sl@0: char c; sl@0: int offset, length, numBytes; sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: Tcl_Interp *interp = parsePtr->interp; sl@0: Tcl_UniChar ch; sl@0: sl@0: /* sl@0: * Record where the previous lexeme ended. Since we always read one sl@0: * lexeme ahead during parsing, this helps us know the source length of sl@0: * subexpression tokens. sl@0: */ sl@0: sl@0: infoPtr->prevEnd = infoPtr->next; sl@0: sl@0: /* sl@0: * Scan over leading white space at the start of a lexeme. sl@0: */ sl@0: sl@0: src = infoPtr->next; sl@0: numBytes = parsePtr->end - src; sl@0: do { sl@0: char type; sl@0: int scanned = TclParseWhiteSpace(src, numBytes, parsePtr, &type); sl@0: src += scanned; numBytes -= scanned; sl@0: } while (numBytes && (*src == '\n') && (src++,numBytes--)); sl@0: parsePtr->term = src; sl@0: if (numBytes == 0) { sl@0: infoPtr->lexeme = END; sl@0: infoPtr->next = src; sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: * Try to parse the lexeme first as an integer or floating-point sl@0: * number. Don't check for a number if the first character c is sl@0: * "+" or "-". If we did, we might treat a binary operator as unary sl@0: * by mistake, which would eventually cause a syntax error. sl@0: */ sl@0: sl@0: c = *src; sl@0: if ((c != '+') && (c != '-')) { sl@0: CONST char *end = infoPtr->lastChar; sl@0: if ((length = TclParseInteger(src, (end - src)))) { sl@0: /* sl@0: * First length bytes look like an integer. Verify by sl@0: * attempting the conversion to the largest integer we have. sl@0: */ sl@0: int code; sl@0: Tcl_WideInt wide; sl@0: Tcl_Obj *value = Tcl_NewStringObj(src, length); sl@0: sl@0: Tcl_IncrRefCount(value); sl@0: code = Tcl_GetWideIntFromObj(interp, value, &wide); sl@0: Tcl_DecrRefCount(value); sl@0: if (code == TCL_ERROR) { sl@0: parsePtr->errorType = TCL_PARSE_BAD_NUMBER; sl@0: return TCL_ERROR; sl@0: } sl@0: infoPtr->lexeme = LITERAL; sl@0: infoPtr->start = src; sl@0: infoPtr->size = length; sl@0: infoPtr->next = (src + length); sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: } else if ((length = ParseMaxDoubleLength(src, end))) { sl@0: /* sl@0: * There are length characters that could be a double. sl@0: * Let strtod() tells us for sure. Need a writable copy sl@0: * so we can set an terminating NULL to keep strtod from sl@0: * scanning too far. sl@0: */ sl@0: char *startPtr, *termPtr; sl@0: double doubleValue; sl@0: Tcl_DString toParse; sl@0: sl@0: errno = 0; sl@0: Tcl_DStringInit(&toParse); sl@0: startPtr = Tcl_DStringAppend(&toParse, src, length); sl@0: doubleValue = strtod(startPtr, &termPtr); sl@0: Tcl_DStringFree(&toParse); sl@0: if (termPtr != startPtr) { sl@0: if (errno != 0) { sl@0: if (interp != NULL) { sl@0: TclExprFloatError(interp, doubleValue); sl@0: } sl@0: parsePtr->errorType = TCL_PARSE_BAD_NUMBER; sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: /* sl@0: * startPtr was the start of a valid double, copied sl@0: * from src. sl@0: */ sl@0: sl@0: infoPtr->lexeme = LITERAL; sl@0: infoPtr->start = src; sl@0: if ((termPtr - startPtr) > length) { sl@0: infoPtr->size = length; sl@0: } else { sl@0: infoPtr->size = (termPtr - startPtr); sl@0: } sl@0: infoPtr->next = src + infoPtr->size; sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * Not an integer or double literal. Initialize the lexeme's fields sl@0: * assuming the common case of a single character lexeme. sl@0: */ sl@0: sl@0: infoPtr->start = src; sl@0: infoPtr->size = 1; sl@0: infoPtr->next = src+1; sl@0: parsePtr->term = infoPtr->next; sl@0: sl@0: switch (*src) { sl@0: case '[': sl@0: infoPtr->lexeme = OPEN_BRACKET; sl@0: return TCL_OK; sl@0: sl@0: case '{': sl@0: infoPtr->lexeme = OPEN_BRACE; sl@0: return TCL_OK; sl@0: sl@0: case '(': sl@0: infoPtr->lexeme = OPEN_PAREN; sl@0: return TCL_OK; sl@0: sl@0: case ')': sl@0: infoPtr->lexeme = CLOSE_PAREN; sl@0: return TCL_OK; sl@0: sl@0: case '$': sl@0: infoPtr->lexeme = DOLLAR; sl@0: return TCL_OK; sl@0: sl@0: case '\"': sl@0: infoPtr->lexeme = QUOTE; sl@0: return TCL_OK; sl@0: sl@0: case ',': sl@0: infoPtr->lexeme = COMMA; sl@0: return TCL_OK; sl@0: sl@0: case '*': sl@0: infoPtr->lexeme = MULT; sl@0: return TCL_OK; sl@0: sl@0: case '/': sl@0: infoPtr->lexeme = DIVIDE; sl@0: return TCL_OK; sl@0: sl@0: case '%': sl@0: infoPtr->lexeme = MOD; sl@0: return TCL_OK; sl@0: sl@0: case '+': sl@0: infoPtr->lexeme = PLUS; sl@0: return TCL_OK; sl@0: sl@0: case '-': sl@0: infoPtr->lexeme = MINUS; sl@0: return TCL_OK; sl@0: sl@0: case '?': sl@0: infoPtr->lexeme = QUESTY; sl@0: return TCL_OK; sl@0: sl@0: case ':': sl@0: infoPtr->lexeme = COLON; sl@0: return TCL_OK; sl@0: sl@0: case '<': sl@0: infoPtr->lexeme = LESS; sl@0: if ((infoPtr->lastChar - src) > 1) { sl@0: switch (src[1]) { sl@0: case '<': sl@0: infoPtr->lexeme = LEFT_SHIFT; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: break; sl@0: case '=': sl@0: infoPtr->lexeme = LEQ; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: break; sl@0: } sl@0: } sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: sl@0: case '>': sl@0: infoPtr->lexeme = GREATER; sl@0: if ((infoPtr->lastChar - src) > 1) { sl@0: switch (src[1]) { sl@0: case '>': sl@0: infoPtr->lexeme = RIGHT_SHIFT; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: break; sl@0: case '=': sl@0: infoPtr->lexeme = GEQ; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: break; sl@0: } sl@0: } sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: sl@0: case '=': sl@0: infoPtr->lexeme = UNKNOWN; sl@0: if ((src[1] == '=') && ((infoPtr->lastChar - src) > 1)) { sl@0: infoPtr->lexeme = EQUAL; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: } sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: sl@0: case '!': sl@0: infoPtr->lexeme = NOT; sl@0: if ((src[1] == '=') && ((infoPtr->lastChar - src) > 1)) { sl@0: infoPtr->lexeme = NEQ; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: } sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: sl@0: case '&': sl@0: infoPtr->lexeme = BIT_AND; sl@0: if ((src[1] == '&') && ((infoPtr->lastChar - src) > 1)) { sl@0: infoPtr->lexeme = AND; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: } sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: sl@0: case '^': sl@0: infoPtr->lexeme = BIT_XOR; sl@0: return TCL_OK; sl@0: sl@0: case '|': sl@0: infoPtr->lexeme = BIT_OR; sl@0: if ((src[1] == '|') && ((infoPtr->lastChar - src) > 1)) { sl@0: infoPtr->lexeme = OR; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: } sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: sl@0: case '~': sl@0: infoPtr->lexeme = BIT_NOT; sl@0: return TCL_OK; sl@0: sl@0: case 'e': sl@0: if ((src[1] == 'q') && ((infoPtr->lastChar - src) > 1)) { sl@0: infoPtr->lexeme = STREQ; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: } else { sl@0: goto checkFuncName; sl@0: } sl@0: sl@0: case 'n': sl@0: if ((src[1] == 'e') && ((infoPtr->lastChar - src) > 1)) { sl@0: infoPtr->lexeme = STRNEQ; sl@0: infoPtr->size = 2; sl@0: infoPtr->next = src+2; sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: } else { sl@0: goto checkFuncName; sl@0: } sl@0: sl@0: default: sl@0: checkFuncName: sl@0: length = (infoPtr->lastChar - src); sl@0: if (Tcl_UtfCharComplete(src, length)) { sl@0: offset = Tcl_UtfToUniChar(src, &ch); sl@0: } else { sl@0: char utfBytes[TCL_UTF_MAX]; sl@0: memcpy(utfBytes, src, (size_t) length); sl@0: utfBytes[length] = '\0'; sl@0: offset = Tcl_UtfToUniChar(utfBytes, &ch); sl@0: } sl@0: c = UCHAR(ch); sl@0: if (isalpha(UCHAR(c))) { /* INTL: ISO only. */ sl@0: infoPtr->lexeme = FUNC_NAME; sl@0: while (isalnum(UCHAR(c)) || (c == '_')) { /* INTL: ISO only. */ sl@0: src += offset; length -= offset; sl@0: if (Tcl_UtfCharComplete(src, length)) { sl@0: offset = Tcl_UtfToUniChar(src, &ch); sl@0: } else { sl@0: char utfBytes[TCL_UTF_MAX]; sl@0: memcpy(utfBytes, src, (size_t) length); sl@0: utfBytes[length] = '\0'; sl@0: offset = Tcl_UtfToUniChar(utfBytes, &ch); sl@0: } sl@0: c = UCHAR(ch); sl@0: } sl@0: infoPtr->size = (src - infoPtr->start); sl@0: infoPtr->next = src; sl@0: parsePtr->term = infoPtr->next; sl@0: return TCL_OK; sl@0: } sl@0: infoPtr->lexeme = UNKNOWN_CHAR; sl@0: return TCL_OK; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclParseInteger -- sl@0: * sl@0: * Scans up to numBytes bytes starting at src, and checks whether sl@0: * the leading bytes look like an integer's string representation. sl@0: * sl@0: * Results: sl@0: * Returns 0 if the leading bytes do not look like an integer. sl@0: * Otherwise, returns the number of bytes examined that look sl@0: * like an integer. This may be less than numBytes if the integer sl@0: * is only the leading part of the string. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclParseInteger(string, numBytes) sl@0: register CONST char *string;/* The string to examine. */ sl@0: register int numBytes; /* Max number of bytes to scan. */ sl@0: { sl@0: register CONST char *p = string; sl@0: sl@0: /* Take care of introductory "0x" */ sl@0: if ((numBytes > 1) && (p[0] == '0') && ((p[1] == 'x') || (p[1] == 'X'))) { sl@0: int scanned; sl@0: Tcl_UniChar ch; sl@0: p+=2; numBytes -= 2; sl@0: scanned = TclParseHex(p, numBytes, &ch); sl@0: if (scanned) { sl@0: return scanned + 2; sl@0: } sl@0: sl@0: /* Recognize the 0 as valid integer, but x is left behind */ sl@0: return 1; sl@0: } sl@0: while (numBytes && isdigit(UCHAR(*p))) { /* INTL: digit */ sl@0: numBytes--; p++; sl@0: } sl@0: if (numBytes == 0) { sl@0: return (p - string); sl@0: } sl@0: if ((*p != '.') && (*p != 'e') && (*p != 'E')) { sl@0: return (p - string); sl@0: } sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ParseMaxDoubleLength -- sl@0: * sl@0: * Scans a sequence of bytes checking that the characters could sl@0: * be in a string rep of a double. sl@0: * sl@0: * Results: sl@0: * Returns the number of bytes starting with string, runing to, but sl@0: * not including end, all of which could be part of a string rep. sl@0: * of a double. Only character identity is used, no actual sl@0: * parsing is done. sl@0: * sl@0: * The legal bytes are '0' - '9', 'A' - 'F', 'a' - 'f', sl@0: * '.', '+', '-', 'i', 'I', 'n', 'N', 'p', 'P', 'x', and 'X'. sl@0: * This covers the values "Inf" and "Nan" as well as the sl@0: * decimal and hexadecimal representations recognized by a sl@0: * C99-compliant strtod(). sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: ParseMaxDoubleLength(string, end) sl@0: register CONST char *string;/* The string to examine. */ sl@0: CONST char *end; /* Point to the first character past the end sl@0: * of the string we are examining. */ sl@0: { sl@0: CONST char *p = string; sl@0: while (p < end) { sl@0: switch (*p) { sl@0: case '0': case '1': case '2': case '3': case '4': case '5': sl@0: case '6': case '7': case '8': case '9': case 'A': case 'B': sl@0: case 'C': case 'D': case 'E': case 'F': case 'I': case 'N': sl@0: case 'P': case 'X': case 'a': case 'b': case 'c': case 'd': sl@0: case 'e': case 'f': case 'i': case 'n': case 'p': case 'x': sl@0: case '.': case '+': case '-': sl@0: p++; sl@0: break; sl@0: default: sl@0: goto done; sl@0: } sl@0: } sl@0: done: sl@0: return (p - string); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * PrependSubExprTokens -- sl@0: * sl@0: * This procedure is called after the operands of an subexpression have sl@0: * been parsed. It generates two tokens: a TCL_TOKEN_SUB_EXPR token for sl@0: * the subexpression, and a TCL_TOKEN_OPERATOR token for its operator. sl@0: * These two tokens are inserted before the operand tokens. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * If there is insufficient space in parsePtr to hold the new tokens, sl@0: * additional space is malloc-ed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: PrependSubExprTokens(op, opBytes, src, srcBytes, firstIndex, infoPtr) sl@0: CONST char *op; /* Points to first byte of the operator sl@0: * in the source script. */ sl@0: int opBytes; /* Number of bytes in the operator. */ sl@0: CONST char *src; /* Points to first byte of the subexpression sl@0: * in the source script. */ sl@0: int srcBytes; /* Number of bytes in subexpression's sl@0: * source. */ sl@0: int firstIndex; /* Index of first token already emitted for sl@0: * operator's first (or only) operand. */ sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: { sl@0: Tcl_Parse *parsePtr = infoPtr->parsePtr; sl@0: Tcl_Token *tokenPtr, *firstTokenPtr; sl@0: int numToMove; sl@0: sl@0: if ((parsePtr->numTokens + 1) >= parsePtr->tokensAvailable) { sl@0: TclExpandTokenArray(parsePtr); sl@0: } sl@0: firstTokenPtr = &parsePtr->tokenPtr[firstIndex]; sl@0: tokenPtr = (firstTokenPtr + 2); sl@0: numToMove = (parsePtr->numTokens - firstIndex); sl@0: memmove((VOID *) tokenPtr, (VOID *) firstTokenPtr, sl@0: (size_t) (numToMove * sizeof(Tcl_Token))); sl@0: parsePtr->numTokens += 2; sl@0: sl@0: tokenPtr = firstTokenPtr; sl@0: tokenPtr->type = TCL_TOKEN_SUB_EXPR; sl@0: tokenPtr->start = src; sl@0: tokenPtr->size = srcBytes; sl@0: tokenPtr->numComponents = parsePtr->numTokens - (firstIndex + 1); sl@0: sl@0: tokenPtr++; sl@0: tokenPtr->type = TCL_TOKEN_OPERATOR; sl@0: tokenPtr->start = op; sl@0: tokenPtr->size = opBytes; sl@0: tokenPtr->numComponents = 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * LogSyntaxError -- sl@0: * sl@0: * This procedure is invoked after an error occurs when parsing an sl@0: * expression. It sets the interpreter result to an error message sl@0: * describing the error. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Sets the interpreter result to an error message describing the sl@0: * expression that was being parsed when the error occurred, and why sl@0: * the parser considers that to be a syntax error at all. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: LogSyntaxError(infoPtr, extraInfo) sl@0: ParseInfo *infoPtr; /* Holds the parse state for the sl@0: * expression being parsed. */ sl@0: CONST char *extraInfo; /* String to provide extra information sl@0: * about the syntax error. */ sl@0: { sl@0: int numBytes = (infoPtr->lastChar - infoPtr->originalExpr); sl@0: char buffer[100]; sl@0: sl@0: if (numBytes > 60) { sl@0: sprintf(buffer, "syntax error in expression \"%.60s...\"", sl@0: infoPtr->originalExpr); sl@0: } else { sl@0: sprintf(buffer, "syntax error in expression \"%.*s\"", sl@0: numBytes, infoPtr->originalExpr); sl@0: } sl@0: Tcl_ResetResult(infoPtr->parsePtr->interp); sl@0: Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->parsePtr->interp), sl@0: buffer, ": ", extraInfo, (char *) NULL); sl@0: infoPtr->parsePtr->errorType = TCL_PARSE_SYNTAX; sl@0: infoPtr->parsePtr->term = infoPtr->start; sl@0: }