From ksb@proxy.npcguild.org  Sun Jan  4 21:16:10 2004
Return-Path: <ksb@proxy.npcguild.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP
	id 4359616A4CE; Sun,  4 Jan 2004 21:16:10 -0800 (PST)
Received: from proxy.npcguild.org (rrcs-ma-24-56-87-2.biz.rr.com [24.56.87.2])
	by mx1.FreeBSD.org (Postfix) with ESMTP
	id C937043D39; Sun,  4 Jan 2004 21:16:06 -0800 (PST)
	(envelope-from ksb@proxy.npcguild.org)
Received: from proxy.npcguild.org (ksb@localhost [127.0.0.1])
	by proxy.npcguild.org (8.12.10/8.12.10) with ESMTP id i055G5jK002237;
	Sun, 4 Jan 2004 23:16:05 -0600 (CST)
	(envelope-from ksb@proxy.npcguild.org)
Received: (from ksb@localhost)
	by proxy.npcguild.org (8.12.10/8.12.10/Submit) id i055G5Xo002236;
	Sun, 4 Jan 2004 23:16:05 -0600 (CST)
	(envelope-from ksb)
Message-Id: <200401050516.i055G5Xo002236@proxy.npcguild.org>
Date: Sun, 4 Jan 2004 23:16:05 -0600 (CST)
From: KS Braunsdorf <m4@ksb.npcguild.org>
Reply-To: KS Braunsdorf <m4@ksb.npcguild.org>
To: FreeBSD-gnats-submit@freebsd.org
Cc: petef@freebsd.org
Subject: m4 C operators are not 
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         60914
>Category:       bin
>Synopsis:       m4 C operators are not
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    smkelly
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Jan 04 21:20:10 PST 2004
>Closed-Date:    Mon Feb 07 20:12:45 GMT 2005
>Last-Modified:  Mon Feb 07 20:12:45 GMT 2005
>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:
Responsible-Changed-From-To: freebsd-bugs->smkelly 
Responsible-Changed-By: smkelly 
Responsible-Changed-When: Mon Apr 19 15:38:59 PDT 2004 
Responsible-Changed-Why:  
I'll work on this one. It has sat around long enough. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=60914 

From: Sean Kelly <smkelly@FreeBSD.org>
To: freebsd-gnats-submit@FreeBSD.org,
	KS Braunsdorf <m4@ksb.npcguild.org>
Cc: petef@freebsd.org
Subject: Re: bin/60914: m4 C operators are not
Date: Wed, 21 Apr 2004 17:21:51 -0500

 I have looked at this PR and have a few concerns with it.  First off,
 the fixing of the operators and precedence is great. However, I'm not
 sure whether short circuit evaluation is the Right Thing. After testing
 GNU m4, HP-UX m4 (which has serious issues), and Solaris m4, I've found
 that none of them seem to do it.
 
 I checked SUSv3, and it doesn't clearly say whether short-circuit
 evaluation should be used or not. All it says is:
 
    All of the C-language operators shall be supported, except for:
    []       ->       ++       --       (type)
    unary *  sizeof   .        ?:       unary &          
    and all assignment operators. Precedence and associativity shall
    be as in the ISO C standard.
 
 Section 2.12 in K&R C 2nd edition, "Precedence and Order of Evaluation"
 also makes no mention of short-circuit evaluation.
 
 It is unclear to me whether the right solution is to keep it the way it
 is since it matches other implementations, or change it to be likely
 more useful (while less "compatible") by doing short-circuit evaluation.
 
 -- 
 Sean Kelly          | PGP KeyID: D2E5E296
 smkelly@FreeBSD.org | http://www.sean-kelly.org/

From: Sean Kelly <smkelly@FreeBSD.org>
To: freebsd-gnats-submit@FreeBSD.org,
	KS Braunsdorf <m4@ksb.npcguild.org>
Cc: petef@freebsd.org
Subject: Re: bin/60914: m4 C operators are not
Date: Wed, 28 Apr 2004 03:29:34 -0500

 Okay. Nobody seems to have an opinion on what m4 should do regarding
 short-circuiting, so I plan to do it. Here is a patch that is derived from
 yours against m4 in -CURRENT. Does this meet your expectations? I am
 looking for some m4 tests to ensure that I didn't break it somehow. It
 passes /usr/src/usr.bin/m4/TEST/... I guess Sendmail .mc files is my
 next test.
 
 Note that I am not using a global 'mayeval' variable, but instead am
 passing it as a parameter through the parser. This saves from having
 to continuously having to save and restore the global variable.
 
 Index: expr.c
 ===================================================================
 RCS file: /usr/home/ncvs/src/usr.bin/m4/expr.c,v
 retrieving revision 1.13
 diff -u -r1.13 expr.c
 --- expr.c	30 Oct 2002 15:50:00 -0000	1.13
 +++ expr.c	28 Apr 2004 08:17:42 -0000
 @@ -67,31 +67,24 @@
   *      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 }
 + *      land    :       bor { "&&" bor }
 + *      bor     :       xor { "|" xor }
 + *      xor     :       band { "^" eqrel }
 + *      band    :       eqrel { "&" eqrel }
 + *      eqrel   :       nerel { ("==" | "!=") nerel }
 + *      nerel   :       shift { ("<" | ">" | "<=" | ">=") shift }
 + *      shift   :       primary { ("<<" | ">>") primary }
 + *      primary :       term { ("+" | "-") term }
 + *      term    :       exp { ("*" | "/" | "%") exp }
 + *      exp     :       unary { "**" unary }
   *      unary   :       factor
 - *              |       unop unary
 + *              |       ("+" | "-" | "~" | "!") unary
   *      factor  :       constant
   *              |       "(" query ")"
   *      constant:       num
   *              |       "'" CHAR "'"
   *      num     :       DIGIT
   *              |       DIGIT num
 - *      shop    :       "<<"
 - *              |       ">>"
 - *      eqrel   :       "="
 - *              |       "=="
 - *              |       "!="
 - *      	|       "<"
 - *              |       ">"
 - *              |       "<="
 - *              |       ">="
   *
   *
   *      This expression evaluator is lifted from a public-domain
 @@ -115,20 +108,23 @@
  static const char *nxtch;		       /* Parser scan pointer */
  static const char *where;
  
 -static int query(void);
 -static int lor(void);
 -static int land(void);
 -static int not(void);
 -static int eqrel(void);
 -static int shift(void);
 -static int primary(void);
 -static int term(void);
 -static int exp(void);
 -static int unary(void);
 -static int factor(void);
 -static int constant(void);
 -static int num(void);
 -static int geteqrel(void);
 +static int query(int mayeval);
 +static int lor(int mayeval);
 +static int land(int mayeval);
 +static int bor(int mayeval);
 +static int xor(int mayeval);
 +static int band(int mayeval);
 +static int eqrel(int mayeval);
 +static int nerel(int mayeval);
 +static int shift(int mayeval);
 +static int primary(int mayeval);
 +static int term(int mayeval);
 +static int exp(int mayeval);
 +static int unary(int mayeval);
 +static int factor(int mayeval);
 +static int constant(int mayeval);
 +static int num(int mayeval);
 +static int geteqrel(int mayeval);
  static int skipws(void);
  static void experr(const char *);
  
 @@ -156,7 +152,7 @@
  	if (setjmp(expjump) != 0)
  		return FALSE;
  
 -	rval = query();
 +	rval = query(1);
  	if (skipws() == EOS)
  		return rval;
  
 @@ -168,21 +164,21 @@
   * query : lor | lor '?' query ':' query
   */
  static int
 -query(void)
 +query(int mayeval)
  {
  	int result, true_val, false_val;
  
 -	result = lor();
 +	result = lor(mayeval);
  	if (skipws() != '?') {
  		ungetch();
  		return result;
  	}
  
 -	true_val = query();
 +	true_val = query(result);
  	if (skipws() != ':')
 -		experr("bad query");
 +		experr("bad query: missing \":\"");
  
 -	false_val = query();
 +	false_val = query(!result);
  	return result ? true_val : false_val;
  }
  
 @@ -190,15 +186,19 @@
   * lor : land { '||' land }
   */
  static int
 -lor(void)
 +lor(int mayeval)
  {
  	int c, vl, vr;
  
 -	vl = land();
 +	vl = land(mayeval);
  	while ((c = skipws()) == '|') {
 -		if (getch() != '|')
 +		if (getch() != '|') {
  			ungetch();
 -		vr = land();
 +			break;
 +		}
 +		if (vl != 0)
 +			mayeval = 0;
 +		vr = land(mayeval);
  		vl = vl || vr;
  	}
  
 @@ -210,15 +210,19 @@
   * land : not { '&&' not }
   */
  static int
 -land(void)
 +land(int mayeval)
  {
  	int c, vl, vr;
  
 -	vl = not();
 +	vl = bor(mayeval);
  	while ((c = skipws()) == '&') {
 -		if (getch() != '&')
 +		if (getch() != '&') {
  			ungetch();
 -		vr = not();
 +			break;
 +		}
 +		if (vl == 0)
 +			mayeval = 0;
 +		vr = bor(mayeval);
  		vl = vl && vr;
  	}
  
 @@ -227,74 +231,131 @@
  }
  
  /*
 - * not : eqrel | '!' not
 + * bor : xor { "|" xor }
   */
  static int
 -not(void)
 +bor(int mayeval)
  {
 -	int val, c;
 +	int vl, vr, c, cr;
  
 -	if ((c = skipws()) == '!' && getch() != '=') {
 +	vl = xor(mayeval);
 +	while ((c = skipws()) == '|') {
 +		cr = getch();
  		ungetch();
 -		val = not();
 -		return !val;
 +		if (cr == '|')
 +			break;
 +		vr = xor(mayeval);
 +		vl |= vr;
  	}
 +	ungetch();
 +	return (vl);
 +}
  
 -	if (c == '!')
 -		ungetch();
 +/*
 + * xor : band { "^" band }
 + */
 +static int
 +xor(int mayeval)
 +{
 +	int vl, vr, c;
 +
 +	vl = band(mayeval);
 +	while ((c = skipws()) == '^') {
 +		vr = band(mayeval);
 +		vl ^= vr;
 +	}
  	ungetch();
 -	return eqrel();
 +	return (vl);
  }
  
  /*
 - * eqrel : shift { eqrelop shift }
 + * band : eqrel { "&" eqrel }
   */
  static int
 -eqrel(void)
 +band(int mayeval)
  {
 -	int vl, vr, op;
 +	int c, cr, vl, vr;
  
 -	vl = shift();
 -	while ((op = geteqrel()) != -1) {
 -		vr = shift();
 +	vl = eqrel(mayeval);
 +	while ((c = skipws()) == '&') {
 +		cr = getch();
 +		ungetch();
 +		if (cr == '&')
 +			break;
 +		vr = eqrel(mayeval);
 +		vl &= vr;
 +	}
 +	ungetch();
 +	return vl;
 +}
  
 -		switch (op) {
 +/*
 + * eqrel : nerel { ("==" | "!=" ) nerel }
 + */
 +static int
 +eqrel(int mayeval)
 +{
 +	int vl, vr, c, cr;
  
 -		case EQL:
 +	vl = nerel(mayeval);
 +	while ((c = skipws()) == '!' || c == '=') {
 +		if ((cr = getch()) != '=') {
 +			ungetch();
 +			break;
 +		}
 +		vr = nerel(mayeval);
 +		switch (c) {
 +		case '=':
  			vl = (vl == vr);
  			break;
 -		case NEQ:
 +		case '!':
  			vl = (vl != vr);
  			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(int mayeval)
 +{
 +	int vl, vr, c, cr;
 +
 +	vl = shift(mayeval);
 +	while ((c = skipws()) == '<' || c == '>') {
 +		if ((cr = getch()) != '=') {
 +			ungetch();
 +			cr = '\0';
 +		}
 +		vr = shift(mayeval);
 +		switch (c) {
 +		case '<':
 +			vl = (cr == '\0') ? (vl < vr) : (vl <= vr);
  			break;
 -		case GEQ:
 -			vl = (vl >= vr);
 +		case '>':
 +			vl = (cr == '\0') ? (vl > vr) : (vl >= vr);
  			break;
  		}
  	}
 +	ungetch();
  	return vl;
  }
  
  /*
 - * shift : primary { shop primary }
 + * shift : primary { ("<<" | ">>") primary }
   */
  static int
 -shift(void)
 +shift(int mayeval)
  {
  	int vl, vr, c;
  
 -	vl = primary();
 +	vl = primary(mayeval);
  	while (((c = skipws()) == '<' || c == '>') && getch() == c) {
 -		vr = primary();
 +		vr = primary(mayeval);
  
  		if (c == '<')
  			vl <<= vr;
 @@ -309,16 +370,16 @@
  }
  
  /*
 - * primary : term { addop term }
 + * primary : term { ("+" | "-") term }
   */
  static int
 -primary(void)
 +primary(int mayeval)
  {
  	int c, vl, vr;
  
 -	vl = term();
 +	vl = term(mayeval);
  	while ((c = skipws()) == '+' || c == '-') {
 -		vr = term();
 +		vr = term(mayeval);
  
  		if (c == '+')
  			vl += vr;
 @@ -331,29 +392,33 @@
  }
  
  /*
 - * <term> := <exp> { <mulop> <exp> }
 + * term : exp { ("*" | "/" | "%") exp }
   */
  static int
 -term(void)
 +term(int mayeval)
  {
  	int c, vl, vr;
  
 -	vl = exp();
 +	vl = exp(mayeval);
  	while ((c = skipws()) == '*' || c == '/' || c == '%') {
 -		vr = exp();
 +		vr = exp(mayeval);
  
  		switch (c) {
  		case '*':
  			vl *= vr;
  			break;
  		case '/':
 -			if (vr == 0)
 +			if (!mayeval)
 +				/* short-circuit */;
 +			else if (vr == 0)
  				errx(1, "division by zero in eval.");
  			else
  				vl /= vr;
  			break;
  		case '%':
 -			if (vr == 0)
 +			if (!mayeval)
 +				/* short-circuit */;
 +			else if (vr == 0)
  				errx(1, "modulo zero in eval.");
  			else
  				vl %= vr;
 @@ -365,24 +430,20 @@
  }
  
  /*
 - * <term> := <unary> { <expop> <unary> }
 + * exp : unary { "**" exp }
   */
  static int
 -exp(void)
 +exp(int mayeval)
  {
  	int c, vl, vr, n;
  
 -	vl = unary();
 -	switch (c = skipws()) {
 -
 -	case '*':
 +	vl = unary(mayeval);
 +	while ((c = skipws()) == '*') {
  		if (getch() != '*') {
  			ungetch();
  			break;
  		}
 -
 -	case '^':
 -		vr = exp();
 +		vr = unary(mayeval);
  		n = 1;
  		while (vr-- > 0)
  			n *= vl;
 @@ -394,15 +455,15 @@
  }
  
  /*
 - * unary : factor | unop unary
 + * unary : factor | ("+" | "-" | "~" | "!") unary
   */
  static int
 -unary(void)
 +unary(int mayeval)
  {
  	int val, c;
  
 -	if ((c = skipws()) == '+' || c == '-' || c == '~') {
 -		val = unary();
 +	if ((c = skipws()) == '+' || c == '-' || c == '~' || c == '!') {
 +		val = unary(mayeval);
  
  		switch (c) {
  		case '+':
 @@ -411,30 +472,32 @@
  			return -val;
  		case '~':
  			return ~val;
 +		case '!':
 +			return !val;
  		}
  	}
  
  	ungetch();
 -	return factor();
 +	return factor(mayeval);
  }
  
  /*
   * factor : constant | '(' query ')'
   */
  static int
 -factor(void)
 +factor(int mayeval)
  {
  	int val;
  
  	if (skipws() == '(') {
 -		val = query();
 +		val = query(mayeval);
  		if (skipws() != ')')
 -			experr("bad factor");
 +			experr("bad factor: missing \")\"");
  		return val;
  	}
  
  	ungetch();
 -	return constant();
 +	return constant(mayeval);
  }
  
  /*
 @@ -442,7 +505,7 @@
   * Note: constant() handles multi-byte constants
   */
  static int
 -constant(void)
 +constant(int mayeval)
  {
  	int i;
  	int value;
 @@ -451,7 +514,7 @@
  
  	if (skipws() != '\'') {
  		ungetch();
 -		return num();
 +		return num(mayeval);
  	}
  	for (i = 0; i < (ssize_t)sizeof(int); i++) {
  		if ((c = getch()) == '\'') {
 @@ -469,7 +532,7 @@
  			case '6':
  			case '7':
  				ungetch();
 -				c = num();
 +				c = num(mayeval);
  				break;
  			case 'n':
  				c = 012;
 @@ -503,7 +566,7 @@
   * num : digit | num digit
   */
  static int
 -num(void)
 +num(int mayeval)
  {
  	int rval, c, base;
  	int ndig;
 @@ -555,50 +618,6 @@
  		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;
 -
 -	default:
 -		ungetch();
 -		ungetch();
 -		return -1;
 -	}
  }
  
  /*
 
 -- 
 Sean Kelly          | PGP KeyID: D2E5E296
 smkelly@FreeBSD.org | http://www.sean-kelly.org/

From: Pete Fritchman <petef@FreeBSD.org>
To: Sean Kelly <smkelly@FreeBSD.org>
Cc: freebsd-gnats-submit@FreeBSD.org,
	KS Braunsdorf <m4@ksb.npcguild.org>
Subject: Re: bin/60914: m4 C operators are not 
Date: Thu, 29 Apr 2004 10:40:57 -0500

 ksb & I wrote a regression test for C operators in m4. example out:
 
 knap.sac(src) [1133] > /usr/bin/m4 math.m4
 failed &
 failed ^
 failed ^
 failed |
 failed |
 precedence wrong, ! *
 precedence wrong < ==
 precedence wrong > !=
 precedence wrong & |
 precedence wrong & ^
 precedence wrong ^ |
 knap.sac(src) [1134] > /usr/bin/m4 -DSHORTCIRCUIT=yes math.m4
 failed &
 failed ^
 failed ^
 failed |
 failed |
 m4: division by zero in eval.
 knap.sac(src) [1135] > m4/m4 -DSHORTCIRCUIT=yes math.m4
 knap.sac(src) [1136] > 
 
 Much better.  http://absolutbsd.org/petef/tmp/math.m4.
 
 knap.sac(src) [1136] > openssl md5 math.m4
 MD5(math.m4)= 001f598caf18c51757156ca0e828dd44
 knap.sac(src) [1137] > wc -l math.m4
      181 math.m4
 
 --pete
State-Changed-From-To: open->patched 
State-Changed-By: smkelly 
State-Changed-When: Fri Apr 30 21:13:55 PDT 2004 
State-Changed-Why:  
This has been committed to HEAD. It is on my MFC list after 4.10 settles down and we make sure it didn't break anything. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=60914 
State-Changed-From-To: patched->closed 
State-Changed-By: smkelly 
State-Changed-When: Mon Feb 7 20:01:18 GMT 2005 
State-Changed-Why:  
This was fixed in RELENG_5. There are possible POLA issues with 
MFC'ing it, and there are also likely to be no more RELENG_4 releases. 
Might as well let this one go. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=60914 
>Unformatted:
