Date: Sun, 4 Jan 2004 23:16:05 -0600 (CST) From: KS Braunsdorf <m4@ksb.npcguild.org> To: FreeBSD-gnats-submit@FreeBSD.org Cc: petef@FreeBSD.org Subject: bin/60914: m4 C operators are not Message-ID: <200401050516.i055G5Xo002236@proxy.npcguild.org> Resent-Message-ID: <200401050520.i055KBJB079462@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 60914 >Category: bin >Synopsis: m4 C operators are not >Confidential: no >Severity: critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Sun Jan 04 21:20:10 PST 2004 >Closed-Date: >Last-Modified: >Originator: KS Braunsdorf >Release: FreeBSD 4.9-STABLE i386 >Organization: NonPlayer Character Guild >Environment: System: FreeBSD proxy.npcguild.org 4.9-STABLE FreeBSD 4.9-STABLE #0: Sun Jan 4 15:30:02 CST 2004 ksb@proxy.npcguild.org:/usr/obj/usr/src/sys/GENERIC i386 All version of m4 under FreeBSD I can test 2.1 to 5.1 >Description: m4 doesn't do what the manaul page says it should for many of the C operators. It do not work like m4 on other UNIX-like operating system either. >How-To-Repeat: These operators are just plain wrong (not the bitwise ones): $ echo "eval(7&3)" | m4 should ouptut "3", not "1". $ echo "eval(5^3)" | m4 should output "6", not "125" $ echo "eval(5|3)" | m4 should output "7", not "1" Since a C programmer depends on && and || being short circut (I think) $ echo "eval(0&&4/0)" | m4 should ouptut "0", not "m4: division by zero in eval." $ echo "eval(1||7/0)" | m4 should ouyput "1", not "m4: division by zero in eval." I think the error message for some cases we unclear, for example: $ echo "eval(4?78)" | m4 m4: bad query, missing ":" in expr 4?78. is better than $ echo "eval(4?78)" | /usr/bin/OLD/m4 m4: bad query in expr 4?78. The precedence of both == and != were wrong as was the precedence of "!". I fixed thise too. I left in the ** (eponent) operator, as it doesn't conflict with the C set, but it is not documented, either. >Fix: The patch below fixes these issues. --- expr.c.orig Sun Jan 4 18:08:50 2004 +++ expr.c Sun Jan 4 22:44:47 2004 @@ -60,45 +60,41 @@ /* - * expression evaluator: performs a standard recursive - * descent parse to evaluate any expression permissible - * within the following grammar: + * Expression evaluator: performs a standard recursive descent + * parse to evaluate any expression permissible within the + * following grammar, which should be C's operator set. + * N.B. We've added exponents (unary ** expr) for no good reason. * - * expr : query EOS - * query : lor - * | lor "?" query ":" query - * lor : land { "||" land } - * land : not { "&&" not } - * not : eqrel - * | '!' not - * eqrel : shift { eqrelop shift } - * shift : primary { shop primary } - * primary : term { addop term } - * term : exp { mulop exp } - * exp : unary { expop unary } - * unary : factor - * | unop unary - * factor : constant - * | "(" query ")" - * constant: num - * | "'" CHAR "'" - * num : DIGIT - * | DIGIT num - * shop : "<<" - * | ">>" - * eqrel : "=" - * | "==" - * | "!=" - * | "<" - * | ">" - * | "<=" - * | ">=" + * expr : query EOS + * query : lor + * | lor "?" query ":" query + * lor : land { "||" land } * + * land : nor { "&&" bor } * + * bor : xor { "|" xor } * + * xor : band { "^" band } * + * band : eqrel { "&" eqrel } * + * eqrel : nerel { ("==" | "!=") nerel } * + * nerel : shift { ("<" | ">" | "<=" | ">=") shift } * + * shift : primary { ("<<" | ">>") primary } * + * primary : term { ("+" | "-") term } * + * term : exp { ("*" | "/" | "%") exp } * + * exp : unary { "**" exp } * ! not doc'd + * unary : factor + * | ("+" | "-" | "~" | "!") unary + * factor : constant + * | "(" query ")" + * constant: num + * | "'" CHAR "'" + * num : DIGIT + * | DIGIT num + * 0xHEX ? * * - * This expression evaluator is lifted from a public-domain - * C Pre-Processor included with the DECUS C Compiler distribution. - * It is hacked somewhat to be suitable for m4. + * This expression evaluator is lifted from a public-domain + * C Pre-Processor included with the DECUS C Compiler distribution. + * It is hacked somewhat to be suitable for m4. * - * Originally by: Mike Lutz - * Bob Harper + * Originally by: Mike Lutz + * Bob Harper + * Made closer to the C operator set by KSBraunsdorf */ @@ -113,5 +109,5 @@ #define HEX 16 -static const char *nxtch; /* Parser scan pointer */ +static const char *nxtch; /* Parser scan pointer */ static const char *where; @@ -119,6 +115,9 @@ static int lor(void); static int land(void); -static int not(void); +static int bor(void); +static int xor(void); +static int band(void); static int eqrel(void); +static int nerel(void); static int shift(void); static int primary(void); @@ -129,5 +128,4 @@ static int constant(void); static int num(void); -static int geteqrel(void); static int skipws(void); static void experr(const char *); @@ -138,4 +136,8 @@ #include <setjmp.h> static jmp_buf expjump; +/* for short circut expression + * for exmple "0 && 10/0" should return 0, not an exception -- ksb + */ +static int mayeval = 0; /* @@ -144,6 +146,6 @@ * getch - return the next character from expr string. */ -#define ungetch() nxtch-- -#define getch() *nxtch++ +#define ungetch() nxtch-- +#define getch() *nxtch++ int @@ -154,4 +156,5 @@ nxtch = expbuf; where = expbuf; + mayeval = 1; if (setjmp(expjump) != 0) return FALSE; @@ -171,6 +174,7 @@ query(void) { - int result, true_val, false_val; + int result, true_val, false_val, waseval; + waseval = mayeval; result = lor(); if (skipws() != '?') { @@ -179,24 +183,34 @@ } + mayeval = result; true_val = query(); if (skipws() != ':') - experr("bad query"); + experr("bad query, missing \":\""); + mayeval = !result; false_val = query(); + mayeval = waseval; return result ? true_val : false_val; } /* - * lor : land { '||' land } + * lor : land { '||' land }, but we don't short out divide by zero + * or the like. We should. */ static int lor(void) { - int c, vl, vr; + int c, vl, vr, waseval; + waseval = mayeval; vl = land(); while ((c = skipws()) == '|') { - if (getch() != '|') + if (getch() != '|') { ungetch(); + break; + } + if (0 != vl) { + mayeval = 0; + } vr = land(); vl = vl || vr; @@ -204,4 +218,5 @@ ungetch(); + mayeval = waseval; return vl; } @@ -213,78 +228,144 @@ land(void) { - int c, vl, vr; + int c, vl, vr, waseval; - vl = not(); + waseval = mayeval; + vl = bor(); while ((c = skipws()) == '&') { - if (getch() != '&') + if (getch() != '&') { ungetch(); - vr = not(); + break; + } + if (0 == vl) { + mayeval = 0; + } + vr = bor(); vl = vl && vr; } ungetch(); + mayeval = waseval; return vl; } /* - * not : eqrel | '!' not + * or : xor { '|' xor } * */ static int -not(void) +bor(void) { - int val, c; + int vl, vr, c, cr; - if ((c = skipws()) == '!' && getch() != '=') { + vl = xor(); + while ((c = skipws()) == '|') { + cr = getch(); ungetch(); - val = not(); - return !val; + if ('|' == cr) { + break; + } + vr = xor(); + vl |= vr; } + ungetch(); + return vl; +} - if (c == '!') - ungetch(); +/* + * xor : and { '|' and } * + */ +static int +xor(void) +{ + int vl, vr, c; + + vl = band(); + while ((c = skipws()) == '^') { + vr = band(); + vl ^= vr; + } ungetch(); - return eqrel(); + return vl; } /* - * eqrel : shift { eqrelop shift } + * band : eqrel { '&' eqrel } * */ static int -eqrel(void) +band(void) { - int vl, vr, op; + int vl, vr, c, cr; - vl = shift(); - while ((op = geteqrel()) != -1) { - vr = shift(); + vl = eqrel(); + while ((c = skipws()) == '&') { + cr = getch(); + ungetch(); + if ('&' == cr) { + break; + } + vr = eqrel(); + vl &= vr; + } + ungetch(); + return vl; +} - switch (op) { +/* + * eqrel : nerel { ("==" | "!=") nerel } * + */ +static int +eqrel(void) +{ + int vl, vr, c, c2; - case EQL: + vl = nerel(); + while ((c = skipws()) == '!' || c == '=') { + if ((c2 = getch()) != '=') { + ungetch(); + break; + } + vr = nerel(); + switch (c) { + case '=': vl = (vl == vr); break; - case NEQ: + case '!': vl = (vl != vr); - break; + break; + } + } + ungetch(); + return vl; +} - case LEQ: - vl = (vl <= vr); - break; - case LSS: - vl = (vl < vr); - break; - case GTR: - vl = (vl > vr); +/* + * nerel : shift { ("<=" | ">=" | "<" | ">") shift } * + */ +static int +nerel(void) +{ + int vl, vr, c, c2; + + vl = shift(); + while ((c = skipws()) == '<' || c == '>') { + if ((c2 = getch()) != '=') { + ungetch(); + c2 = ' '; + } + vr = shift(); + switch (c) { + case '<': + vl = ' ' == c2 ? (vl < vr) : (vl <= vr); break; - case GEQ: - vl = (vl >= vr); + case '>': + vl = ' ' == c2 ? (vl > vr) : (vl >= vr); break; } } + ungetch(); return vl; } /* - * shift : primary { shop primary } + * shift : primary { ("<<" | ">>") primary } */ static int @@ -294,5 +375,9 @@ vl = primary(); - while (((c = skipws()) == '<' || c == '>') && getch() == c) { + while (((c = skipws()) == '<' || c == '>')) { + if (getch() != c) { + ungetch(); + break; + } vr = primary(); @@ -302,7 +387,4 @@ vl >>= vr; } - - if (c == '<' || c == '>') - ungetch(); ungetch(); return vl; @@ -310,5 +392,5 @@ /* - * primary : term { addop term } + * primary : term { ("+" | "-") term } * */ static int @@ -332,5 +414,5 @@ /* - * <term> := <exp> { <mulop> <exp> } + * term := exp { ("*" | "/" | "%") exp } * */ static int @@ -348,5 +430,7 @@ break; case '/': - if (vr == 0) + if (! mayeval) + /* nada */; + else if (vr == 0) errx(1, "division by zero in eval."); else @@ -354,5 +438,7 @@ break; case '%': - if (vr == 0) + if (! mayeval) + /* nada */; + else if (vr == 0) errx(1, "modulo zero in eval."); else @@ -366,5 +452,5 @@ /* - * <term> := <unary> { <expop> <unary> } + * term := unary { "**" exp } * */ static int @@ -374,13 +460,9 @@ vl = unary(); - switch (c = skipws()) { - - case '*': + while ((c = skipws()) == '*') { if (getch() != '*') { ungetch(); break; } - - case '^': vr = exp(); n = 1; @@ -389,5 +471,4 @@ return n; } - ungetch(); return vl; @@ -395,5 +476,5 @@ /* - * unary : factor | unop unary + * unary : factor | ("+" | "-" | "~" | "!") unary */ static int @@ -402,5 +483,5 @@ int val, c; - if ((c = skipws()) == '+' || c == '-' || c == '~') { + if ((c = skipws()) == '+' || c == '-' || c == '~' || c == '!') { val = unary(); @@ -412,4 +493,6 @@ case '~': return ~val; + case '!': + return !val; } } @@ -430,5 +513,5 @@ val = query(); if (skipws() != ')') - experr("bad factor"); + experr("missing close parenthesis"); return val; } @@ -526,8 +609,8 @@ switch(c) { case '8': case '9': - if (base == OCTAL) + if (base == OCTAL) goto bad_digit; /*FALLTHRU*/ - case '0': case '1': case '2': case '3': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': rval *= base; @@ -551,53 +634,9 @@ bad_digit: ungetch(); - - if (ndig == 0) - experr("bad constant"); - - return rval; -} - -/* - * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>=' - */ -static int -geteqrel(void) -{ - int c1, c2; - - c1 = skipws(); - c2 = getch(); - - switch (c1) { - - case '=': - if (c2 != '=') - ungetch(); - return EQL; - case '!': - if (c2 == '=') - return NEQ; - ungetch(); - ungetch(); - return -1; - - case '<': - if (c2 == '=') - return LEQ; - ungetch(); - return LSS; - - case '>': - if (c2 == '=') - return GEQ; - ungetch(); - return GTR; + if (ndig == 0) + experr("no digits in constant"); - default: - ungetch(); - ungetch(); - return -1; - } + return rval; } @@ -616,5 +655,5 @@ /* - * resets environment to eval(), prints an error + * resets environment to eval(), prints an error * and forces eval to return FALSE. */ >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200401050516.i055G5Xo002236>