|
|
1.1 root 1: /*
2: * tclExpr.c --
3: *
4: * This file contains the code to evaluate expressions for
5: * Tcl.
6: *
7: * Copyright 1987 Regents of the University of California
8: * Permission to use, copy, modify, and distribute this
9: * software and its documentation for any purpose and without
10: * fee is hereby granted, provided that the above copyright
11: * notice appear in all copies. The University of California
12: * makes no representations about the suitability of this
13: * software for any purpose. It is provided "as is" without
14: * express or implied warranty.
15: */
16:
17: #ifndef lint
18: static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclExpr.c,v 1.13 90/03/22 15:24:59 ouster Exp $ SPRITE (Berkeley)";
19: #pragma ref rcsid
20: #endif not lint
21:
22: #define _POSIX_SOURCE
23:
24: #include <stdio.h>
25: #include <ctype.h>
26: #include "tcl.h"
27: #include "tclInt.h"
28:
29: /*
30: * The data structure below describes the state of parsing an expression.
31: * It's passed among the routines in this module.
32: */
33:
34: typedef struct {
35: Tcl_Interp *interp; /* Intepreter to use for command execution
36: * and variable lookup. */
37: char *originalExpr; /* The entire expression, as originally
38: * passed to Tcl_Expr. */
39: char *expr; /* Position to the next character to be
40: * scanned from the expression string. */
41: int token; /* Type of the last token to be parsed from
42: * expr. See below for definitions.
43: * Corresponds to the characters just
44: * before expr. */
45: int number; /* If token is NUMBER, gives value of
46: * the number. */
47: } ExprInfo;
48:
49: /*
50: * The token types are defined below. In addition, there is a table
51: * associating a precedence with each operator. The order of types
52: * is important. Consult the code before changing it.
53: */
54:
55: #define NUMBER 0
56: #define OPEN_PAREN 1
57: #define CLOSE_PAREN 2
58: #define END 3
59: #define UNKNOWN 4
60:
61: /*
62: * Binary operators:
63: */
64:
65: #define MULT 8
66: #define DIVIDE 9
67: #define MOD 10
68: #define PLUS 11
69: #define MINUS 12
70: #define LEFT_SHIFT 13
71: #define RIGHT_SHIFT 14
72: #define LESS 15
73: #define GREATER 16
74: #define LEQ 17
75: #define GEQ 18
76: #define EQUAL 19
77: #define NEQ 20
78: #define BIT_AND 21
79: #define BIT_XOR 22
80: #define BIT_OR 23
81: #define AND 24
82: #define OR 25
83: #define QUESTY 26
84: #define COLON 27
85:
86: /*
87: * Unary operators:
88: */
89:
90: #define UNARY_MINUS 28
91: #define NOT 29
92: #define BIT_NOT 30
93:
94: /*
95: * Precedence table. The values for non-operator token types are ignored.
96: */
97:
98: int precTable[] = {
99: 0, 0, 0, 0, 0, 0, 0, 0,
100: 11, 11, 11, /* MULT, DIVIDE, MOD */
101: 10, 10, /* PLUS, MINUS */
102: 9, 9, /* LEFT_SHIFT, RIGHT_SHIFT */
103: 8, 8, 8, 8, /* LESS, GREATER, LEQ, GEQ */
104: 7, 7, /* EQUAL, NEQ */
105: 6, /* BIT_AND */
106: 5, /* BIT_XOR */
107: 4, /* BIT_OR */
108: 3, /* AND */
109: 2, /* OR */
110: 1, 1, /* QUESTY, COLON */
111: 12, 12, 12 /* UNARY_MINUS, NOT, BIT_NOT */
112: };
113:
114: /*
115: *----------------------------------------------------------------------
116: *
117: * ExprGetNum --
118: *
119: * Parse off a number from a string.
120: *
121: * Results:
122: * The return value is the integer value corresponding to the
123: * leading digits of string. If termPtr isn't NULL, *termPtr
124: * is filled in with the address of the character after the
125: * last one that is part of the number.
126: *
127: * Side effects:
128: * None.
129: *
130: *----------------------------------------------------------------------
131: */
132:
133: int
134: ExprGetNum(string, termPtr)
135: register char *string; /* ASCII representation of number.
136: * If leading digit is "0" then read
137: * in base 8; if "0x", then read in
138: * base 16. */
139: register char **termPtr; /* If non-NULL, fill in with address
140: * of terminating character. */
141: {
142: int result, sign;
143: register char c;
144:
145: c = *string;
146: result = 0;
147: if (c == '-') {
148: sign = -1;
149: string++; c = *string;
150: } else {
151: sign = 1;
152: }
153: if (c == '0') {
154: string++; c = *string;
155: if (c == 'x') {
156: while (1) {
157: string++; c = *string;
158: if ((c >= '0') && (c <= '9')) {
159: result = (result << 4) + (c - '0');
160: } else if ((c >= 'a') && (c <= 'f')) {
161: result = (result << 4) + 10 + (c - 'a');
162: } else if ((c >= 'A') && (c <= 'F')) {
163: result = (result << 4) + 10 + (c - 'A');
164: } else {
165: break;
166: }
167: }
168: } else {
169: while ((c >= '0') && (c <= '7')) {
170: result = (result << 3) + (c - '0');
171: string++; c = *string;
172: }
173: }
174: } else {
175: while ((c >= '0') && (c <= '9')) {
176: result = (result*10) + (c - '0');
177: string++; c = *string;
178: }
179: }
180: if (termPtr != NULL) {
181: *termPtr = string;
182: }
183: return result*sign;
184: }
185:
186: /*
187: *----------------------------------------------------------------------
188: *
189: * ExprLex --
190: *
191: * Lexical analyzer for expression parser.
192: *
193: * Results:
194: * TCL_OK is returned unless an error occurred while doing lexical
195: * analysis or executing an embedded command. In that case a
196: * standard Tcl error is returned, using interp->result to hold
197: * an error message. In the event of a successful return, the token
198: * and (possibly) number fields in infoPtr are updated to refer to
199: * the next symbol in the expression string, and the expr field is
200: * advanced.
201: *
202: * Side effects:
203: * None.
204: *
205: *----------------------------------------------------------------------
206: */
207:
208: int
209: ExprLex(interp, infoPtr)
210: Tcl_Interp *interp; /* Interpreter to use for error
211: * reporting. */
212: register ExprInfo *infoPtr; /* Describes the state of the parse. */
213: {
214: register char *p, c;
215: char *var, *term;
216: int result;
217:
218: /*
219: * The next token is either:
220: * (a) a variable name (indicated by a $ sign plus a variable
221: * name in the standard Tcl fashion); lookup the value
222: * of the variable and return its numeric equivalent as a
223: * number.
224: * (b) an embedded command (anything between '[' and ']').
225: * Execute the command and convert its result to a number.
226: * (c) a series of decimal digits. Convert it to a number.
227: * (d) space: skip it.
228: * (d) an operator. See what kind it is.
229: */
230:
231: p = infoPtr->expr;
232: c = *p;
233: while (isspace(c)) {
234: p++; c = *p;
235: }
236: infoPtr->expr = p+1;
237: switch (c) {
238: case '0':
239: case '1':
240: case '2':
241: case '3':
242: case '4':
243: case '5':
244: case '6':
245: case '7':
246: case '8':
247: case '9':
248: infoPtr->token = NUMBER;
249: infoPtr->number = ExprGetNum(p, &infoPtr->expr);
250: return TCL_OK;
251:
252: case '$':
253: infoPtr->token = NUMBER;
254: var = Tcl_ParseVar(infoPtr->interp, p, &infoPtr->expr);
255: if (var == NULL) {
256: return TCL_ERROR;
257: }
258: if (((Interp *) infoPtr->interp)->noEval) {
259: infoPtr->number = 0;
260: return TCL_OK;
261: }
262: infoPtr->number = ExprGetNum(var, &term);
263: if ((term == var) || (*term != 0)) {
264: c = *infoPtr->expr;
265: *infoPtr->expr = 0;
266: Tcl_Return(interp, (char *) NULL, TCL_STATIC);
267: sprintf(interp->result,
268: "variable \"%.50s\" contained non-numeric value \"%.50s\"",
269: p, var);
270: *infoPtr->expr = c;
271: return TCL_ERROR;
272: }
273: return TCL_OK;
274:
275: case '[':
276: infoPtr->token = NUMBER;
277: result = Tcl_Eval(infoPtr->interp, p+1, TCL_BRACKET_TERM,
278: &infoPtr->expr);
279: if (result != TCL_OK) {
280: return result;
281: }
282: infoPtr->expr++;
283: if (((Interp *) infoPtr->interp)->noEval) {
284: infoPtr->number = 0;
285: Tcl_Return(interp, (char *) NULL, TCL_STATIC);
286: return TCL_OK;
287: }
288: infoPtr->number = ExprGetNum(interp->result, &term);
289: if ((term == interp->result) || (*term != 0)) {
290: char string[200];
291: infoPtr->expr[-1];
292: infoPtr->expr[-1] = 0;
293: sprintf(string, "command \"%.50s\" returned non-numeric result \"%.50s\"",
294: p+1, interp->result);
295: infoPtr->expr[-1] = c;
296: Tcl_Return(interp, string, TCL_VOLATILE);
297: return TCL_ERROR;
298: }
299: Tcl_Return(interp, (char *) NULL, TCL_STATIC);
300: return TCL_OK;
301:
302: case '(':
303: infoPtr->token = OPEN_PAREN;
304: return TCL_OK;
305:
306: case ')':
307: infoPtr->token = CLOSE_PAREN;
308: return TCL_OK;
309:
310: case '*':
311: infoPtr->token = MULT;
312: return TCL_OK;
313:
314: case '/':
315: infoPtr->token = DIVIDE;
316: return TCL_OK;
317:
318: case '%':
319: infoPtr->token = MOD;
320: return TCL_OK;
321:
322: case '+':
323: infoPtr->token = PLUS;
324: return TCL_OK;
325:
326: case '-':
327: infoPtr->token = MINUS;
328: return TCL_OK;
329:
330: case '?':
331: infoPtr->token = QUESTY;
332: return TCL_OK;
333:
334: case ':':
335: infoPtr->token = COLON;
336: return TCL_OK;
337:
338: case '<':
339: switch (p[1]) {
340: case '<':
341: infoPtr->expr = p+2;
342: infoPtr->token = LEFT_SHIFT;
343: break;
344: case '=':
345: infoPtr->expr = p+2;
346: infoPtr->token = LEQ;
347: break;
348: default:
349: infoPtr->token = LESS;
350: break;
351: }
352: return TCL_OK;
353:
354: case '>':
355: switch (p[1]) {
356: case '>':
357: infoPtr->expr = p+2;
358: infoPtr->token = RIGHT_SHIFT;
359: break;
360: case '=':
361: infoPtr->expr = p+2;
362: infoPtr->token = GEQ;
363: break;
364: default:
365: infoPtr->token = GREATER;
366: break;
367: }
368: return TCL_OK;
369:
370: case '=':
371: if (p[1] == '=') {
372: infoPtr->expr = p+2;
373: infoPtr->token = EQUAL;
374: } else {
375: infoPtr->token = UNKNOWN;
376: }
377: return TCL_OK;
378:
379: case '!':
380: if (p[1] == '=') {
381: infoPtr->expr = p+2;
382: infoPtr->token = NEQ;
383: } else {
384: infoPtr->token = NOT;
385: }
386: return TCL_OK;
387:
388: case '&':
389: if (p[1] == '&') {
390: infoPtr->expr = p+2;
391: infoPtr->token = AND;
392: } else {
393: infoPtr->token = BIT_AND;
394: }
395: return TCL_OK;
396:
397: case '^':
398: infoPtr->token = BIT_XOR;
399: return TCL_OK;
400:
401: case '|':
402: if (p[1] == '|') {
403: infoPtr->expr = p+2;
404: infoPtr->token = OR;
405: } else {
406: infoPtr->token = BIT_OR;
407: }
408: return TCL_OK;
409:
410: case '~':
411: infoPtr->token = BIT_NOT;
412: return TCL_OK;
413:
414: case 0:
415: infoPtr->token = END;
416: infoPtr->expr = p;
417: return TCL_OK;
418:
419: default:
420: infoPtr->expr = p+1;
421: infoPtr->token = UNKNOWN;
422: return TCL_OK;
423: }
424: }
425:
426: /*
427: *----------------------------------------------------------------------
428: *
429: * ExprGetValue --
430: *
431: * Parse a "value" from the remainder of the expression in infoPtr.
432: *
433: * Results:
434: * Normally TCL_OK is returned. The value of the parsed number is
435: * returned in infoPtr->number. If an error occurred, then
436: * interp->result contains an error message and TCL_ERROR is returned.
437: *
438: * Side effects:
439: * Information gets parsed from the remaining expression, and the
440: * expr and token fields in infoPtr get updated. Information is
441: * parsed until either the end of the expression is reached (null
442: * character or close paren), an error occurs, or a binary operator
443: * is encountered with precedence <= prec. In any of these cases,
444: * infoPtr->token will be left pointing to the token AFTER the
445: * expression.
446: *
447: *----------------------------------------------------------------------
448: */
449:
450: int
451: ExprGetValue(interp, infoPtr, prec)
452: Tcl_Interp *interp; /* Interpreter to use for error
453: * reporting. */
454: register ExprInfo *infoPtr; /* Describes the state of the parse
455: * just before the value (i.e. ExprLex
456: * will be called to get first token
457: * of value). */
458: int prec; /* Treat any un-parenthesized operator
459: * with precedence <= this as the end
460: * of the expression. */
461: {
462: Interp *iPtr = (Interp *) interp;
463: int result, operator, operand;
464: int gotOp; /* Non-zero means already lexed the
465: * operator (while picking up value
466: * for unary operator). Don't lex
467: * again. */
468:
469: /*
470: * There are two phases to this procedure. First, pick off an initial
471: * value. Then, parse (binary operator, value) pairs until done.
472: */
473:
474: gotOp = 0;
475: result = ExprLex(interp, infoPtr);
476: if (result != TCL_OK) {
477: return result;
478: }
479: if (infoPtr->token == OPEN_PAREN) {
480:
481: /*
482: * Parenthesized sub-expression.
483: */
484:
485: result = ExprGetValue(interp, infoPtr, -1);
486: if (result != TCL_OK) {
487: return result;
488: }
489: if (infoPtr->token != CLOSE_PAREN) {
490: Tcl_Return(interp, (char *) NULL, TCL_STATIC);
491: sprintf(interp->result,
492: "unmatched parentheses in expression \"%.50s\"",
493: infoPtr->originalExpr);
494: return TCL_ERROR;
495: }
496: } else {
497: if (infoPtr->token == MINUS) {
498: infoPtr->token = UNARY_MINUS;
499: }
500: if (infoPtr->token >= UNARY_MINUS) {
501:
502: /*
503: * Process unary operators.
504: */
505:
506: operator = infoPtr->token;
507: result = ExprGetValue(interp, infoPtr, precTable[infoPtr->token]);
508: if (result != TCL_OK) {
509: return result;
510: }
511: switch (operator) {
512: case UNARY_MINUS:
513: infoPtr->number = -infoPtr->number;
514: break;
515: case NOT:
516: infoPtr->number = !infoPtr->number;
517: break;
518: case BIT_NOT:
519: infoPtr->number = ~infoPtr->number;
520: break;
521: }
522: gotOp = 1;
523: } else if (infoPtr->token != NUMBER) {
524: goto syntaxError;
525: }
526: }
527:
528: /*
529: * Got the first operand. Now fetch (operator, operand) pairs.
530: */
531:
532: if (!gotOp) {
533: result = ExprLex(interp, infoPtr);
534: if (result != TCL_OK) {
535: return result;
536: }
537: }
538: while (1) {
539: operand = infoPtr->number;
540: operator = infoPtr->token;
541: if ((operator < MULT) || (operator >= UNARY_MINUS)) {
542: if ((operator == END) || (operator == CLOSE_PAREN)) {
543: return TCL_OK;
544: } else {
545: goto syntaxError;
546: }
547: }
548: if (precTable[operator] <= prec) {
549: return TCL_OK;
550: }
551:
552: /*
553: * If we're doing an AND or OR and the first operand already
554: * determines the result, don't execute anything in the
555: * second operand: just parse. Same style for ?: pairs.
556: */
557:
558: if (((operator == AND) && !operand)
559: || ((operator == OR) && operand)) {
560: iPtr->noEval++;
561: result = ExprGetValue(interp, infoPtr, precTable[operator]);
562: iPtr->noEval--;
563: } else if (operator == QUESTY) {
564: if (operand != 0) {
565: result = ExprGetValue(interp, infoPtr, precTable[operator]);
566: operand = infoPtr->number;
567: if (result != TCL_OK)
568: return result;
569: if (infoPtr->token != COLON)
570: goto syntaxError;
571: iPtr->noEval++;
572: result = ExprGetValue(interp, infoPtr, precTable[operator]);
573: iPtr->noEval--;
574: } else {
575: iPtr->noEval++;
576: result = ExprGetValue(interp, infoPtr, precTable[operator]);
577: iPtr->noEval--;
578: if (result != TCL_OK)
579: return result;
580: if (infoPtr->token != COLON)
581: goto syntaxError;
582: result = ExprGetValue(interp, infoPtr, precTable[operator]);
583: operand = infoPtr->number;
584: }
585: infoPtr->number = operand;
586: } else {
587: result = ExprGetValue(interp, infoPtr, precTable[operator]);
588: }
589: if (result != TCL_OK) {
590: return result;
591: }
592: if ((infoPtr->token < MULT) && (infoPtr->token != NUMBER)
593: && (infoPtr->token != END)
594: && (infoPtr->token != CLOSE_PAREN)) {
595: goto syntaxError;
596: }
597: switch (operator) {
598: case MULT:
599: infoPtr->number = operand * infoPtr->number;
600: break;
601: case DIVIDE:
602: if (infoPtr->number == 0) {
603: Tcl_Return(interp, "divide by zero", TCL_STATIC);
604: return TCL_ERROR;
605: }
606: infoPtr->number = operand / infoPtr->number;
607: break;
608: case MOD:
609: if (infoPtr->number == 0) {
610: Tcl_Return(interp, "divide by zero", TCL_STATIC);
611: return TCL_ERROR;
612: }
613: infoPtr->number = operand % infoPtr->number;
614: break;
615: case PLUS:
616: infoPtr->number = operand + infoPtr->number;
617: break;
618: case MINUS:
619: infoPtr->number = operand - infoPtr->number;
620: break;
621: case LEFT_SHIFT:
622: infoPtr->number = operand << infoPtr->number;
623: break;
624: case RIGHT_SHIFT:
625: infoPtr->number = operand >> infoPtr->number;
626: break;
627: case LESS:
628: infoPtr->number = operand < infoPtr->number;
629: break;
630: case GREATER:
631: infoPtr->number = operand > infoPtr->number;
632: break;
633: case LEQ:
634: infoPtr->number = operand <= infoPtr->number;
635: break;
636: case GEQ:
637: infoPtr->number = operand >= infoPtr->number;
638: break;
639: case EQUAL:
640: infoPtr->number = operand == infoPtr->number;
641: break;
642: case NEQ:
643: infoPtr->number = operand != infoPtr->number;
644: break;
645: case BIT_AND:
646: infoPtr->number = operand & infoPtr->number;
647: break;
648: case BIT_XOR:
649: infoPtr->number = operand ^ infoPtr->number;
650: break;
651: case BIT_OR:
652: infoPtr->number = operand | infoPtr->number;
653: break;
654: case AND:
655: infoPtr->number = operand && infoPtr->number;
656: break;
657: case OR:
658: infoPtr->number = operand || infoPtr->number;
659: break;
660: }
661: }
662:
663: syntaxError:
664: Tcl_Return(interp, (char *) NULL, TCL_STATIC);
665: sprintf(interp->result, "syntax error in expression \"%.50s\"",
666: infoPtr->originalExpr);
667: return TCL_ERROR;
668: }
669:
670: /*
671: *----------------------------------------------------------------------
672: *
673: * Tcl_Expr --
674: *
675: * Parse and evaluate an expression.
676: *
677: * Results:
678: * The return value is TCL_OK if the expression was correctly parsed;
679: * if there was a syntax error or some other error during parsing,
680: * then another Tcl return value is returned and Tcl_Result points
681: * to an error message. If all went well, *valuePtr is filled in
682: * with the result corresponding to the expression string.
683: *
684: * Side effects:
685: * None.
686: *
687: *----------------------------------------------------------------------
688: */
689:
690: int
691: Tcl_Expr(interp, string, valuePtr)
692: Tcl_Interp *interp; /* Intepreter to use for variables etc. */
693: char *string; /* Expression to evaluate. */
694: int *valuePtr; /* Where to store result of evaluation. */
695: {
696: ExprInfo info;
697: int result;
698:
699: info.interp = interp;
700: info.originalExpr = string;
701: info.expr = string;
702: result = ExprGetValue(interp, &info, -1);
703: if (result != TCL_OK) {
704: return result;
705: }
706: if (info.token != END) {
707: Tcl_Return(interp, (char *) NULL, TCL_STATIC);
708: sprintf(interp->result, "syntax error in expression \"%.50s\"", string);
709: return TCL_ERROR;
710: }
711: *valuePtr = info.number;
712: return TCL_OK;
713: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.