[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
improve eval
From: |
Eric Blake |
Subject: |
improve eval |
Date: |
Wed, 24 Dec 2008 21:49:20 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
I noticed a FIXME in master's modules/evalparse.c which can be addressed now
that POSIX 2008 is released. Basically, POSIX no longer forbids the use of
extension operators, and therefore it is sufficient to warn (for consistency)
rather than fail on an operator that we recognize but don't support. While I
was modifying the message strings, I also added the ?: operator to branch-1.6.
I'm applying these two patches (the first to branch-1.6, the second to master):
From: Eric Blake <address@hidden>
Date: Wed, 24 Dec 2008 14:15:13 -0700
Subject: [PATCH] Enhance eval, as allowed by POSIX 2008.
* src/eval.c (enum eval_token): Add QUESTION and COLON.
(enum eval_error): Add MISSING_COLON.
(condition_term): New function.
(eval_lex, simple_term): Support new operator.
(evaluate): Likewise. Warn, not error, on invalid operator.
* doc/m4.texinfo (Eval): Update documentation.
(Improved forloop): Adjust test.
* NEWS: Document the change.
Signed-off-by: Eric Blake <address@hidden>
---
ChangeLog | 12 +++++++++
NEWS | 4 +++
doc/m4.texinfo | 30 ++++++++++++++--------
src/eval.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
4 files changed, 103 insertions(+), 18 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 7f3bde2..d15e52a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2008-12-24 Eric Blake <address@hidden>
+
+ Enhance eval, as allowed by POSIX 2008.
+ * src/eval.c (enum eval_token): Add QUESTION and COLON.
+ (enum eval_error): Add MISSING_COLON.
+ (condition_term): New function.
+ (eval_lex, simple_term): Support new operator.
+ (evaluate): Likewise. Warn, not error, on invalid operator.
+ * doc/m4.texinfo (Eval): Update documentation.
+ (Improved forloop): Adjust test.
+ * NEWS: Document the change.
+
2008-12-23 Eric Blake <address@hidden>
Issue deprecation warning for -o/--error-output.
diff --git a/NEWS b/NEWS
index ebd2cbb..2e1a286 100644
--- a/NEWS
+++ b/NEWS
@@ -107,6 +107,10 @@ Foundation, Inc.
context of a macro name, rather than acting on the empty string. This
was already done for `define', `pushdef', `builtin', and `indir'.
+** Enhance the `eval' builtin to understand the `?:' operator, and
+ downgrade a failed parse due to an unknown operator from an error to a
+ warning.
+
** A number of portability improvements inherited from gnulib.
* Noteworthy changes in Version 1.4.10b (2008-02-25) [beta]
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 2ab7290..93adb64 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -6670,6 +6670,8 @@ Eval
Logical and
@item ||
Logical or
address@hidden ?:
+Conditional ternary
@end table
The macro @code{eval} is recognized only with parameters.
@@ -6678,7 +6680,7 @@ Eval
All binary operators, except exponentiation, are left associative. C
operators that perform variable assignment, such as @samp{+=} or
@samp{--}, are not implemented, since @code{eval} only operates on
-constants, not variables. Attempting to use them results in an error.
+constants, not variables. Attempting to use them results in a warning.
However, since traditional implementations treated @samp{=} as an
undocumented alias for @samp{==} as opposed to an assignment operator,
this usage is supported as a special case. Be aware that a future
@@ -6686,16 +6688,15 @@ Eval
extension when @acronym{POSIX} mode is not requested, and that using
@samp{=} to check equality is not portable.
address@hidden status: 1
@example
eval(`2 = 2')
@error{}m4:stdin:1: Warning: eval: recommend ==, not =, for equality
@result{}1
eval(`++0')
address@hidden:stdin:2: eval: invalid operator: ++0
address@hidden:stdin:2: Warning: eval: invalid operator: `++0'
@result{}
eval(`0 |= 1')
address@hidden:stdin:3: eval: invalid operator: 0 |= 1
address@hidden:stdin:3: Warning: eval: invalid operator: `0 |= 1'
@result{}
@end example
@@ -6738,12 +6739,12 @@ Eval
eval(`2 || 1 / 0')
@result{}1
eval(`0 || 1 / 0')
address@hidden:stdin:9: Warning: eval: divide by zero: 0 || 1 / 0
address@hidden:stdin:9: Warning: eval: divide by zero: `0 || 1 / 0'
@result{}
eval(`0 && 1 % 0')
@result{}0
eval(`2 && 1 % 0')
address@hidden:stdin:11: Warning: eval: modulo by zero: 2 && 1 % 0
address@hidden:stdin:11: Warning: eval: modulo by zero: `2 && 1 % 0'
@result{}
@end example
@@ -6751,7 +6752,8 @@ Eval
As a @acronym{GNU} extension, the operator @samp{**} performs integral
exponentiation. The operator is right-associative, and if evaluated,
the exponent must be non-negative, and at least one of the arguments
-must be non-zero, or a warning is issued.
+must be non-zero, or a warning is issued. Also, the C operator
address@hidden:} is supported.
@example
eval(`2 ** 3 ** 2')
@@ -6764,10 +6766,16 @@ Eval
@result{}1
eval(`0 ** 0')
@result{}
address@hidden:stdin:5: Warning: eval: divide by zero: 0 ** 0
address@hidden:stdin:5: Warning: eval: divide by zero: `0 ** 0'
eval(`4 ** -2')
address@hidden:stdin:6: Warning: eval: negative exponent: 4 ** -2
address@hidden:stdin:6: Warning: eval: negative exponent: `4 ** -2'
@result{}
+eval(`0 ? 2 : 3')
address@hidden
+eval(`1 ? 2 : 1/0')
address@hidden
+eval(`0 ? 1/0 : 3')
address@hidden
@end example
Within @var{expression}, (but not @var{radix} or @var{width}), numbers
@@ -6811,7 +6819,7 @@ Eval
define(`foo', `666')
@result{}
eval(`foo / 6')
address@hidden:stdin:11: Warning: eval: bad expression: foo / 6
address@hidden:stdin:11: Warning: eval: bad expression: `foo / 6'
@result{}
eval(foo / 6)
@result{}111
@@ -8254,7 +8262,7 @@ Improved forloop
forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')')
@result{} 0xa 0xb 0xc
forloop(`i', `a', `b', `non-numeric bounds')
address@hidden:stdin:6: Warning: eval: bad expression (bad input): (a) <= (b)
address@hidden:stdin:6: Warning: eval: bad input: `(a) <= (b)'
@result{}
@end example
diff --git a/src/eval.c b/src/eval.c
index 1b617ed..c5ad30d 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -39,6 +39,7 @@ typedef enum eval_token
LNOT, LAND, LOR,
NOT, AND, OR, XOR,
LEFTP, RIGHTP,
+ QUESTION, COLON,
NUMBER, EOTEXT
}
eval_token;
@@ -56,6 +57,7 @@ typedef enum eval_error
about a syntax error. */
SYNTAX_ERROR,
MISSING_RIGHT,
+ MISSING_COLON,
UNKNOWN_INPUT,
EXCESS_INPUT,
INVALID_OPERATOR,
@@ -63,6 +65,7 @@ typedef enum eval_error
}
eval_error;
+static eval_error condition_term (const call_info *, eval_token, int32_t *);
static eval_error logical_or_term (const call_info *, eval_token, int32_t *);
static eval_error logical_and_term (const call_info *, eval_token, int32_t *);
static eval_error or_term (const call_info *, eval_token, int32_t *);
@@ -283,6 +286,10 @@ eval_lex (int32_t *val)
return LEFTP;
case ')':
return RIGHTP;
+ case '?':
+ return QUESTION;
+ case ':':
+ return COLON;
default:
return ERROR;
}
@@ -303,7 +310,7 @@ evaluate (const call_info *me, const char *expr, size_t
len, int32_t *val)
if (et == EOTEXT)
err = EMPTY_ARGUMENT;
else
- err = logical_or_term (me, et, val);
+ err = condition_term (me, et, val);
if (err == NO_ERROR && *eval_text != '\0')
{
@@ -313,6 +320,8 @@ evaluate (const call_info *me, const char *expr, size_t
len, int32_t *val)
err = EXCESS_INPUT;
}
+ if (err != NO_ERROR)
+ expr = quotearg_style_mem (locale_quoting_style, expr, len);
switch (err)
{
/* Cases where result is printed. */
@@ -325,8 +334,11 @@ evaluate (const call_info *me, const char *expr, size_t
len, int32_t *val)
/* Cases where error makes result meaningless. */
case MISSING_RIGHT:
- m4_warn (0, me, _("bad expression (missing right parenthesis): %s"),
- expr);
+ m4_warn (0, me, _("missing right parenthesis: %s"), expr);
+ break;
+
+ case MISSING_COLON:
+ m4_warn (0, me, _("missing colon: %s"), expr);
break;
case SYNTAX_ERROR:
@@ -334,15 +346,15 @@ evaluate (const call_info *me, const char *expr, size_t
len, int32_t *val)
break;
case UNKNOWN_INPUT:
- m4_warn (0, me, _("bad expression (bad input): %s"), expr);
+ m4_warn (0, me, _("bad input: %s"), expr);
break;
case EXCESS_INPUT:
- m4_warn (0, me, _("bad expression (excess input): %s"), expr);
+ m4_warn (0, me, _("excess input: %s"), expr);
break;
case INVALID_OPERATOR:
- m4_error (0, 0, me, _("invalid operator: %s"), expr);
+ m4_warn (0, me, _("invalid operator: %s"), expr);
break;
case DIVIDE_ZERO:
@@ -370,6 +382,55 @@ evaluate (const call_info *me, const char *expr, size_t
len, int32_t *val)
`---------------------------*/
static eval_error
+condition_term (const call_info *me, eval_token et, int32_t *v1)
+{
+ int32_t v2;
+ int32_t v3;
+ eval_error er;
+
+ if ((er = logical_or_term (me, et, v1)) != NO_ERROR)
+ return er;
+
+ if ((et = eval_lex (&v2)) == QUESTION)
+ {
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ /* Implement short-circuiting of valid syntax. */
+ /* C requires 'logical_or_term ? expression : condition_term';
+ if we ever introduce assignment_term or comma_term, then
+ condition_term and expression are no longer synonymous. */
+ er = condition_term (me, et, &v2);
+ if (er != NO_ERROR
+ && !(*v1 == 0 && er < SYNTAX_ERROR))
+ return er;
+
+ et = eval_lex (&v3);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+ if (et != COLON)
+ return MISSING_COLON;
+
+ et = eval_lex (&v3);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ er = condition_term (me, et, &v3);
+ if (er != NO_ERROR
+ && !(*v1 != 0 && er < SYNTAX_ERROR))
+ return er;
+
+ *v1 = *v1 ? v2 : v3;
+ }
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
logical_or_term (const call_info *me, eval_token et, int32_t *v1)
{
int32_t v2;
@@ -832,7 +893,7 @@ simple_term (const call_info *me, eval_token et, int32_t
*v1)
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = logical_or_term (me, et, v1)) != NO_ERROR)
+ if ((er = condition_term (me, et, v1)) != NO_ERROR)
return er;
et = eval_lex (&v2);
--
1.6.0.4
From: Eric Blake <address@hidden>
Date: Wed, 24 Dec 2008 14:34:06 -0700
Subject: [PATCH] Relax eval as allowed by POSIX 2008.
* modules/evalparse.c (m4_evaluate): Warn, not error, on invalid
operator. Quote expression in warning.
* modules/mpeval.c (includes): Add quotearg.h.
* doc/m4.texinfo (Eval, Improved forloop): Update tests.
* NEWS: Update to reflect 1.6 support for `?:'.
Signed-off-by: Eric Blake <address@hidden>
---
ChangeLog | 9 +++++++++
NEWS | 6 +++++-
doc/m4.texinfo | 19 +++++++++----------
modules/evalparse.c | 5 +++--
modules/mpeval.c | 2 ++
5 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 4321384..9d1c1c8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2008-12-24 Eric Blake <address@hidden>
+
+ Relax eval as allowed by POSIX 2008.
+ * modules/evalparse.c (m4_evaluate): Warn, not error, on invalid
+ operator. Quote expression in warning.
+ * modules/mpeval.c (includes): Add quotearg.h.
+ * doc/m4.texinfo (Eval, Improved forloop): Update tests.
+ * NEWS: Update to reflect 1.6 support for `?:'.
+
2008-12-23 Eric Blake <address@hidden>
Add debugmode(o) to control dumpdef output location.
diff --git a/NEWS b/NEWS
index 4180190..ca4e0b0 100644
--- a/NEWS
+++ b/NEWS
@@ -158,7 +158,7 @@ promoted to 2.0.
first line to show the definition of the macro being expanded.
*** The `eval' and `mpeval' builtins now support the following new
- operators: `>>>', `\', `?:', and `,'.
+ operators: `>>>', `\', and `,'.
*** The `maketemp' builtin now always warns that it is obsolete, even in GNU
mode where it uses the same secure algorithm as `mkstemp', because of
@@ -296,6 +296,10 @@ promoted to 2.0.
context of a macro name, rather than acting on the empty string. This
was already done for `define', `pushdef', `builtin', and `indir'.
+** Enhance the `eval' builtin to understand the `?:' operator, and
+ downgrade a failed parse due to an unknown operator from an error to a
+ warning.
+
** A number of portability improvements inherited from gnulib.
* Noteworthy changes in Version 1.4.10b (2008-02-25) [beta]
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 3d039f6..c5e36dd 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -7506,7 +7506,6 @@ Eval
implementations of @code{m4} require explicit parentheses to get the
correct result:
address@hidden status: 1
@example
eval(`1 == 2 > 0')
@result{}1
@@ -7523,23 +7522,23 @@ Eval
eval(`+ + - ~ ! ~ 0')
@result{}1
eval(`++0')
address@hidden:stdin:8: eval: invalid operator: ++0
address@hidden:stdin:8: Warning: eval: invalid operator: `++0'
@result{}
eval(`1 = 1')
address@hidden:stdin:9: eval: invalid operator: 1 = 1
address@hidden:stdin:9: Warning: eval: invalid operator: `1 = 1'
@result{}
eval(`0 |= 1')
address@hidden:stdin:10: eval: invalid operator: 0 |= 1
address@hidden:stdin:10: Warning: eval: invalid operator: `0 |= 1'
@result{}
eval(`2 || 1 / 0')
@result{}1
eval(`0 || 1 / 0')
address@hidden:stdin:12: Warning: eval: divide by zero: 0 || 1 / 0
address@hidden:stdin:12: Warning: eval: divide by zero: `0 || 1 / 0'
@result{}
eval(`0 && 1 % 0')
@result{}0
eval(`2 && 1 % 0')
address@hidden:stdin:14: Warning: eval: modulo by zero: 2 && 1 % 0
address@hidden:stdin:14: Warning: eval: modulo by zero: `2 && 1 % 0'
@result{}
@end example
@@ -7567,9 +7566,9 @@ Eval
@result{}1
eval(`0 ** 0')
@result{}
address@hidden:stdin:5: Warning: eval: divide by zero: 0 ** 0
address@hidden:stdin:5: Warning: eval: divide by zero: `0 ** 0'
eval(`4 ** -2')
address@hidden:stdin:6: Warning: eval: negative exponent: 4 ** -2
address@hidden:stdin:6: Warning: eval: negative exponent: `4 ** -2'
@result{}
eval(`2 || 4 ** -2')
@result{}1
@@ -7632,7 +7631,7 @@ Eval
define(`foo', `666')
@result{}
eval(`foo / 6')
address@hidden:stdin:11: Warning: eval: bad expression: foo / 6
address@hidden:stdin:11: Warning: eval: bad expression: `foo / 6'
@result{}
eval(foo / 6)
@result{}111
@@ -9317,7 +9316,7 @@ Improved forloop
forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')')
@result{} 0xa 0xb 0xc
forloop(`i', `a', `b', `non-numeric bounds')
address@hidden:stdin:6: Warning: eval: bad input: (a) <= (b)
address@hidden:stdin:6: Warning: eval: bad input: `(a) <= (b)'
@result{}
@end example
diff --git a/modules/evalparse.c b/modules/evalparse.c
index 9927e13..ac30cfe 100644
--- a/modules/evalparse.c
+++ b/modules/evalparse.c
@@ -940,6 +940,8 @@ m4_evaluate (m4 *context, m4_obstack *obs, size_t argc,
m4_macro_args *argv)
err = EXCESS_INPUT;
}
+ if (err != NO_ERROR)
+ str = quotearg_style_mem (locale_quoting_style, str, M4ARGLEN (1));
switch (err)
{
case NO_ERROR:
@@ -967,8 +969,7 @@ m4_evaluate (m4 *context, m4_obstack *obs, size_t argc,
m4_macro_args *argv)
break;
case INVALID_OPERATOR:
- /* POSIX requires an error here, unless XCU ERN 137 is approved. */
- m4_error (context, 0, 0, me, _("invalid operator: %s"), str);
+ m4_warn (context, 0, me, _("invalid operator: %s"), str);
break;
case DIVIDE_ZERO:
diff --git a/modules/mpeval.c b/modules/mpeval.c
index 63cd56a..2d63d6e 100644
--- a/modules/mpeval.c
+++ b/modules/mpeval.c
@@ -32,6 +32,8 @@
# include <gmp.h>
#endif
+#include "quotearg.h"
+
/* Rename exported symbols for dlpreload()ing. */
#define m4_builtin_table mpeval_LTX_m4_builtin_table
#define m4_macro_table mpeval_LTX_m4_macro_table
--
1.6.0.4
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- improve eval,
Eric Blake <=