/* * e v a l * * Read an expression and evaluate it. Prints the value in decimal, * octal, and hex. Any C expression (with parentheses) may be * evaluated. Uses code developed for the C preprocessor. * * Define "MAIN" to include the driver program. */ #define MAIN /*)LIBRARY */ /*)BUILD $(TKBOPTIONS) = { TASK = ...EVL } */ #ifdef DOCUMENTATION title eval Expression Evaluation index Evaluate a C expression synopsis eval expression (as a main program) int eval(string, result) char *string; /* String to evaluate */ union { /* Pointer to result */ long value; /* Result if no error */ char *error; /* Message if error */ } *result; description By setting a compile-time parameter, eval may be configured as a main program that reads an expression, printing the result in decimal, octal, and hexadecimal. When compiled as a subroutine, eval() accepts a character string argument, returning a long result (and an error indicator): If eval() returns zero, the evaluation is stored in result.value. Else, there is an error and and the value returned is the index in string of the first byte not parsed, while result.error is a pointer to an error message. (Note that, if the error was discovered after the entire string was parsed, the index may be greater than the length of the string.) author Martin Minow, using an algorithm developed by Mike Lutz and Bob Harper of RPI. #endif #define EOS 0 #define TRUE 1 #define FALSE 0 /* * If the main program source is removed, check that the #include * at the start of eval() is reenabled as needed. */ #include #ifdef MAIN /* * This is compiled to create the eval main program. */ #include #ifdef vms #include static int ctrlc_typed = FALSE; static jmp_buf environment; #endif static char line[513]; static int prompting; main(argc, argv) int argc; char *argv[]; { register char *linep; register char *argp; #ifdef vms extern int ctrlc_trap(); signal(SIGINT, ctrlc_trap); #endif if (argc <= 1) { #ifdef vms if (setjmp(environment) == 0) while (!ctrlc_typed) { #else for (;;) { #endif if (isatty(fileno(stdin))) { printf("* "); fflush(stdout); } #ifdef vms if (gets(line) == NULL || ctrlc_typed) break; #else if (gets(line) == NULL) break; #endif else if (line[0] == '?') { help(); } else { process(); } } } else { if (argv[1][0] == '?' || streq(argv[1], "-?") != FALSE) { help(); } else { linep = line; while (argc > 1) { argp = *++argv; while (*argp != EOS) *linep++ = *argp++; *linep++ = ' '; argc--; } *linep = EOS; process(); } } } static process() /* * Do the work -- argument is in line. */ { int code; union { long value; char *message; } result; if ((code = eval(line, &result)) != 0) { printf("?%s in \"%s\"\n", result.message, line); if (code >= strlen(line)) { printf("Stopped after reading entire line\n"); } else { printf("Stopped at byte %d, \"%s\" not evaluated\n", code, &line[code]); } printf("\"%s?\" for help\n", (prompting) ? "" : "eval "); } else if (result.value == 0) printf("0\t0\t0\n"); else { printf("%ld\t0%lo\t0x%lx\n", result.value, result.value, result.value); if (result.value < 0) { printf("\tunsigned: %lu long", result.value); if ((result.value & ~65535L) == ~65535L) printf(", %lu short", result.value & 65535L); if ((result.value & ~255L) == ~255L) printf(", %lu char", result.value & 255L); printf("\n"); } } } static help() /* * Give help */ { printf("Evaluate an integer expression. Standard C syntax\n"); printf("is accepted, including parentheses. The result is\n"); printf("output in decimal, octal, and hexadecimal. When a\n"); printf("number is entered, a leading '0' means octal while\n"); printf("a leading '0x' or '0X' means hexadecimal.\n"); } #ifdef vms static ctrlc_trap() { ctrlc_typed = TRUE; longjmp(environment, TRUE); } #endif #endif /* * Constant expression evaluator -- performs a standard recursive * descent parse to evaluate any legal C constant expression. All * operators without side-effects are implemented (thus ++, --, and all * assignment operators are omitted). * * This code was originally written by Mike Lutz and was modified by * Bob Harper to fit into this program and to conform to C language * standards. */ /* * This is defined at the start of the file so setjmp can be used in * the main program. * #include */ #define EOS 0 #define TRUE 1 #define FALSE 0 #define EQL 0 #define NEQ 1 #define LSS 2 #define LEQ 3 #define GTR 4 #define GEQ 5 static char *nxtch = EOS; /* Parser scan pointer */ static char *errmsg; /* Gets error message */ static jmp_buf env; /* Setjmp/longjmp save area */ extern long query(), lor(), land(), bor(), band(), bxor(); extern long eql(), relat(), shift(), primary(), term(), unary(); extern long factor(), const(), number(), experr(); /* ::= */ int eval(string, result) char *string; /* Argument */ union { long value; char *message; } *result; /* * Evaluate driver */ { int i; long evaluate(); nxtch = string; if (setjmp(env) == 0) { result->value = evaluate(); return(0); } else { result->message = errmsg; i = nxtch - string; return ((i <= 0) ? 1 : i); } } static long evaluate() { long rval; rval = query(); if (skipws() == EOS) return(rval); experr("Ill-formed expression"); } /* ::= | '?' ':' */ static long query() { long bool, true_val, false_val; bool = lor(); if (skipws() != '?') { ungetch(); return(bool); } true_val = query(); if (skipws() != ':') experr("Bad query syntax (':' not found)"); false_val = query(); return((bool != 0) ? true_val : false_val); } /* ::= { '||' } */ static long lor() { register int c; long vl, vr; vl = land(); while ((c = skipws()) == '|' && getch() == '|') { vr = land(); #ifdef decus vl = (vl != 0 || vr != 0) ? 1 : 0; #else vl = vl || vr; #endif } if (c == '|') ungetch(); ungetch(); return(vl); } /* ::= { '&&' } */ static long land() { register int c; long vl, vr; vl = bor(); while ((c = skipws()) == '&' && getch() == '&') { vr = bor(); #ifdef decus vl = (vl != 0 && vr != 0) ? 1 : 0; #else vl = vl && vr; #endif } if (c == '&') ungetch(); ungetch(); return(vl); } /* ::= { '|' } */ static long bor() { register int c; long vl, vr; vl = bxor(); while ((c = skipws()) == '|' && getch() != '|') { ungetch(); vr = bxor(); vl |= vr; } if (c == '|') ungetch(); ungetch(); return(vl); } /* ::= { '^' } */ static long bxor() { long vl, vr; vl = band(); while (skipws() == '^') { vr = band(); vl = vl ^ vr; } ungetch(); return(vl); } /* ::= { '&' } */ static long band() { register int c; long vl, vr; vl = eql(); while ((c = skipws()) == '&' && getch() != '&') { ungetch(); vr = eql(); vl &= vr; } if (c == '&') ungetch(); ungetch(); return(vl); } /* ::= { } */ static long eql() { register int rel; long vl, vr; vl = relat(); while ((rel = geteql()) != -1) { vr = relat(); switch (rel) { case EQL: vl = (vl == vr); break; case NEQ: vl = (vl != vr); break; } } return(vl); } /* ::= { } */ static long relat() { register int rel; long vl, vr; vl = shift(); while ((rel = getrel()) != -1) { vr = shift(); switch (rel) { case LEQ: vl = (vl <= vr); break; case LSS: vl = (vl < vr); break; case GTR: vl = (vl > vr); break; case GEQ: vl = (vl >= vr); break; } } return(vl); } /* ::= { } */ static long shift() { register int c; long vl, vr; vl = primary(); while (((c = skipws()) == '<' || c == '>') && c == getch()) { vr = primary(); if (c == '<') vl <<= vr; else vl >>= vr; } if (c == '<' || c == '>') ungetch(); ungetch(); return(vl); } /* ::= { } */ static long primary() { register int c; long vl, vr; vl = term(); while ((c = skipws()) == '+' || c == '-') { vr = term(); if (c == '+') vl += vr; else vl -= vr; } ungetch(); return(vl); } /* := { } */ static long term() { register int c; long vl, vr; vl = unary(); while ((c = skipws()) == '*' || c == '/' || c == '%') { vr = unary(); switch (c) { case '*': vl *= vr; break; case '/': if (vr == 0) experr("Division by zero"); else vl /= vr; break; case '%': if (vr == 0) experr("Modulus by zero"); else vl %= vr; break; } } ungetch(); return(vl); } /* ::= | */ static long unary() { register int c; long val; if ((c = skipws()) == '!' || c == '~' || c == '-') { val = unary(); switch (c) { case '!': return((val == 0) ? 1 : 0); case '~': return(~ val); case '-': return(- val); } } ungetch(); return(factor()); } /* ::= | '(' ')' */ static long factor() { long val; if (skipws() == '(') { val = query(); if (skipws() != ')') experr("Missing right parenthesis"); return(val); } ungetch(); return(const()); } /* ::= | '' */ static long const() { /* * Note: const() handles multi-byte constants */ register int i; register int val; register char c; int v[sizeof (int)]; if (skipws() != '\'') { ungetch(); return(number()); } /* * Multi-byte character constant */ for (i = 0; i < sizeof(int); i++) { if ((c = getch()) == '\'') { ungetch(); break; } if (c == '\\') { switch (c = getch()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': ungetch(); c = octal_number(); break; case 'n': c = 012; break; case 'r': c = 015; break; case 't': c = 011; break; case 'b': c = 010; break; case 'f': c = 014; break; } } v[i] = c; } if (i == 0 || getch() != '\'') experr("Illegal character constant (missing \"'\")"); for (val = 0; --i >= 0;) { val <<= 8; val += v[i]; } return(val); } /* ::= | */ static int octal_number() /* * Note: returns an integer -- called only for '\xxx' constants. */ { register int c; register int val; register int ndigits; c = skipws(); val = 0; ndigits = 0; while (c >= '0' && c <= '7') { val *= 8; val += (c - '0'); c = getch(); ndigits++; } ungetch(); if (ndigits <= 0) experr("Bad '\\xxx' constant"); else return (val); } /* ::= | */ static long number() { register int c; long val; register int ndigits; c = skipws(); val = 0; ndigits = 0; if (c != '0') { while (c >= '0' && c <= '9') { val *= 10; val += (c - '0'); c = getch(); ndigits++; } } else { ndigits++; if ((c = tolower(getch())) == 'x') { for (;(c = tolower(getch())) >= '0' && (c <= '9' || (c >= 'a' && c <= 'z')); ndigits++) { val *= 16; val += ((c >= 'a') ? c - 'a' + 10 : c - '0'); } } else { while (c >= '0' && c <= '7') { val *= 8; val += (c - '0'); ndigits++; c = getch(); } } } ungetch(); if (ndigits <= 0) experr("Numeric constant expected"); else return(val); } /* ::= '=' | '==' | '!=' */ static int geteql() { register int c1, c2; c1 = skipws(); c2 = getch(); switch (c1) { case '=': if (c2 != '=') ungetch(); return(EQL); case '!': if (c2 == '=') return(NEQ); ungetch(); ungetch(); return(-1); default: ungetch(); ungetch(); return(-1); } } /* ::= '<' | '>' | '<=' | '>=' */ static int getrel() { register int c1, c2; c1 = skipws(); c2 = getch(); switch (c1) { case '<': if (c2 == '=') return(LEQ); ungetch(); return(LSS); case '>': if (c2 == '=') return(GEQ); ungetch(); return(GTR); default: ungetch(); ungetch(); return(-1); } } /* return next character from the expression string. */ static int getch() { return(*nxtch++); } /* Put back the last character examined. */ static int ungetch() { return(*--nxtch); } /* Skip over any white space and return terminating char. */ static int skipws() { register char c; while ((c = getch()) <= ' ' && c != EOS) ; return(c); } /* * Error handler - sets error flag and exits evaluation */ static long experr(msg) char *msg; { errmsg = msg; longjmp(env, 1); }