bin/60914: m4 C operators are not
KS Braunsdorf
m4 at ksb.npcguild.org
Sun Jan 4 21:20:18 PST 2004
>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 at 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:
More information about the freebsd-bugs
mailing list