1 /* $OpenBSD: expr.c,v 1.33 2018/04/09 17:53:36 tobias Exp $ */
4 * Korn expression evaluation
7 * todo: better error handling: if in builtin, should be builtin error, etc.
16 /* The order of these enums is constrained by the order of opinfo[] */
18 /* some (long) unary operators */
19 O_PLUSPLUS
= 0, O_MINUSMINUS
,
20 /* binary operators */
22 /* assignments are assumed to be in range O_ASN .. O_BORASN */
23 O_ASN
, O_TIMESASN
, O_DIVASN
, O_MODASN
, O_PLUSASN
, O_MINUSASN
,
24 O_LSHIFTASN
, O_RSHIFTASN
, O_BANDASN
, O_BXORASN
, O_BORASN
,
26 O_LE
, O_GE
, O_LT
, O_GT
,
29 O_TIMES
, O_DIV
, O_MOD
,
36 /* things after this aren't used as binary operators */
37 /* unary that are not also binaries */
40 OPEN_PAREN
, CLOSE_PAREN
, CTERN
,
41 /* things that don't appear in the opinfo[] table */
44 #define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
45 #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
48 P_PRIMARY
= 0, /* VAR, LIT, (), ~ ! - + */
52 P_RELATION
, /* < <= > >= */
53 P_EQUALITY
, /* == != */
60 P_ASSIGN
, /* = *= /= %= += -= <<= >>= &= ^= |= */
63 #define MAX_PREC P_COMMA
67 int len
; /* name length */
68 enum prec prec
; /* precedence: lower is higher */
71 /* Tokens in this table must be ordered so the longest are first
72 * (eg, += before +). If you change something, change the order
75 static const struct opinfo opinfo
[] = {
76 { "++", 2, P_PRIMARY
}, /* before + */
77 { "--", 2, P_PRIMARY
}, /* before - */
78 { "==", 2, P_EQUALITY
}, /* before = */
79 { "!=", 2, P_EQUALITY
}, /* before ! */
80 { "=", 1, P_ASSIGN
}, /* keep assigns in a block */
81 { "*=", 2, P_ASSIGN
},
82 { "/=", 2, P_ASSIGN
},
83 { "%=", 2, P_ASSIGN
},
84 { "+=", 2, P_ASSIGN
},
85 { "-=", 2, P_ASSIGN
},
86 { "<<=", 3, P_ASSIGN
},
87 { ">>=", 3, P_ASSIGN
},
88 { "&=", 2, P_ASSIGN
},
89 { "^=", 2, P_ASSIGN
},
90 { "|=", 2, P_ASSIGN
},
93 { "<=", 2, P_RELATION
},
94 { ">=", 2, P_RELATION
},
95 { "<", 1, P_RELATION
},
96 { ">", 1, P_RELATION
},
109 { "~", 1, P_PRIMARY
},
110 { "!", 1, P_PRIMARY
},
111 { "(", 1, P_PRIMARY
},
112 { ")", 1, P_PRIMARY
},
113 { ":", 1, P_PRIMARY
},
114 { "", 0, P_PRIMARY
} /* end of table */
118 typedef struct expr_state Expr_state
;
120 const char *expression
; /* expression being evaluated */
121 const char *tokp
; /* lexical position */
122 enum token tok
; /* token from token() */
123 int noassign
; /* don't do assigns (for ?:,&&,||) */
124 bool arith
; /* true if evaluating an $(())
127 struct tbl
*val
; /* value from token() */
128 struct tbl
*evaling
; /* variable that is being recursively
129 * expanded (EXPRINEVAL flag set)
134 ET_UNEXPECTED
, ET_BADLIT
, ET_RECURSIVE
,
135 ET_LVALUE
, ET_RDONLY
, ET_STR
138 static void evalerr(Expr_state
*, enum error_type
, const char *)
139 __attribute__((__noreturn__
));
140 static struct tbl
*evalexpr(Expr_state
*, enum prec
);
141 static void token(Expr_state
*);
142 static struct tbl
*do_ppmm(Expr_state
*, enum token
, struct tbl
*, bool);
143 static void assign_check(Expr_state
*, enum token
, struct tbl
*);
144 static struct tbl
*tempvar(void);
145 static struct tbl
*intvar(Expr_state
*, struct tbl
*);
148 * parse and evaluate expression
151 evaluate(const char *expr
, int64_t *rval
, int error_ok
, bool arith
)
156 v
.flag
= DEFINED
|INTEGER
;
158 ret
= v_evaluate(&v
, expr
, error_ok
, arith
);
164 * parse and evaluate expression, storing result in vp.
167 v_evaluate(struct tbl
*vp
, const char *expr
, volatile int error_ok
,
172 Expr_state
* const es
= &curstate
;
175 /* save state to allow recursive calls */
176 curstate
.expression
= curstate
.tokp
= expr
;
177 curstate
.noassign
= 0;
178 curstate
.arith
= arith
;
179 curstate
.evaling
= NULL
;
183 i
= sigsetjmp(genv
->jbuf
, 0);
185 /* Clear EXPRINEVAL in of any variables we were playing with */
186 if (curstate
.evaling
)
187 curstate
.evaling
->flag
&= ~EXPRINEVAL
;
190 if (error_ok
== KSH_RETURN_ERROR
)
199 #if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */
200 if (es
->tok
== END
) {
205 v
= intvar(es
, evalexpr(es
, MAX_PREC
));
208 evalerr(es
, ET_UNEXPECTED
, NULL
);
210 if (vp
->flag
& INTEGER
)
211 setint_v(vp
, v
, es
->arith
);
213 /* can fail if readonly */
214 setstr(vp
, str_val(v
), error_ok
);
222 evalerr(Expr_state
*es
, enum error_type type
, const char *str
)
235 s
= str_val(es
->val
);
238 s
= "end of expression";
246 s
= opinfo
[(int)es
->tok
].name
;
248 warningf(true, "%s: unexpected `%s'", es
->expression
, s
);
252 warningf(true, "%s: bad number `%s'", es
->expression
, str
);
256 warningf(true, "%s: expression recurses on parameter `%s'",
257 es
->expression
, str
);
261 warningf(true, "%s: %s requires lvalue",
262 es
->expression
, str
);
266 warningf(true, "%s: %s applied to read only variable",
267 es
->expression
, str
);
270 default: /* keep gcc happy */
272 warningf(true, "%s: %s", es
->expression
, str
);
279 evalexpr(Expr_state
*es
, enum prec prec
)
281 struct tbl
*vl
, *vr
= NULL
, *vasn
;
285 if (prec
== P_PRIMARY
) {
287 if (op
== O_BNOT
|| op
== O_LNOT
|| op
== O_MINUS
||
290 vl
= intvar(es
, evalexpr(es
, P_PRIMARY
));
292 vl
->val
.i
= ~vl
->val
.i
;
293 else if (op
== O_LNOT
)
294 vl
->val
.i
= !vl
->val
.i
;
295 else if (op
== O_MINUS
)
296 vl
->val
.i
= -vl
->val
.i
;
297 /* op == O_PLUS is a no-op */
298 } else if (op
== OPEN_PAREN
) {
300 vl
= evalexpr(es
, MAX_PREC
);
301 if (es
->tok
!= CLOSE_PAREN
)
302 evalerr(es
, ET_STR
, "missing )");
304 } else if (op
== O_PLUSPLUS
|| op
== O_MINUSMINUS
) {
306 vl
= do_ppmm(es
, op
, es
->val
, true);
308 } else if (op
== VAR
|| op
== LIT
) {
312 evalerr(es
, ET_UNEXPECTED
, NULL
);
315 if (es
->tok
== O_PLUSPLUS
|| es
->tok
== O_MINUSMINUS
) {
316 vl
= do_ppmm(es
, es
->tok
, vl
, false);
321 vl
= evalexpr(es
, ((int) prec
) - 1);
322 for (op
= es
->tok
; IS_BINOP(op
) && opinfo
[(int) op
].prec
== prec
;
326 if (op
!= O_ASN
) /* vl may not have a value yet */
328 if (IS_ASSIGNOP(op
)) {
329 assign_check(es
, op
, vasn
);
330 vr
= intvar(es
, evalexpr(es
, P_ASSIGN
));
331 } else if (op
!= O_TERN
&& op
!= O_LAND
&& op
!= O_LOR
)
332 vr
= intvar(es
, evalexpr(es
, ((int) prec
) - 1));
333 if ((op
== O_DIV
|| op
== O_MOD
|| op
== O_DIVASN
||
334 op
== O_MODASN
) && vr
->val
.i
== 0) {
338 evalerr(es
, ET_STR
, "zero divisor");
343 res
= vl
->val
.i
* vr
->val
.i
;
347 if (vl
->val
.i
== LONG_MIN
&& vr
->val
.i
== -1)
350 res
= vl
->val
.i
/ vr
->val
.i
;
354 if (vl
->val
.i
== LONG_MIN
&& vr
->val
.i
== -1)
357 res
= vl
->val
.i
% vr
->val
.i
;
361 res
= vl
->val
.i
+ vr
->val
.i
;
365 res
= vl
->val
.i
- vr
->val
.i
;
369 res
= vl
->val
.i
<< vr
->val
.i
;
373 res
= vl
->val
.i
>> vr
->val
.i
;
376 res
= vl
->val
.i
< vr
->val
.i
;
379 res
= vl
->val
.i
<= vr
->val
.i
;
382 res
= vl
->val
.i
> vr
->val
.i
;
385 res
= vl
->val
.i
>= vr
->val
.i
;
388 res
= vl
->val
.i
== vr
->val
.i
;
391 res
= vl
->val
.i
!= vr
->val
.i
;
395 res
= vl
->val
.i
& vr
->val
.i
;
399 res
= vl
->val
.i
^ vr
->val
.i
;
403 res
= vl
->val
.i
| vr
->val
.i
;
408 vr
= intvar(es
, evalexpr(es
, ((int) prec
) - 1));
409 res
= vl
->val
.i
&& vr
->val
.i
;
416 vr
= intvar(es
, evalexpr(es
, ((int) prec
) - 1));
417 res
= vl
->val
.i
|| vr
->val
.i
;
423 int e
= vl
->val
.i
!= 0;
427 vl
= evalexpr(es
, MAX_PREC
);
430 if (es
->tok
!= CTERN
)
431 evalerr(es
, ET_STR
, "missing :");
435 vr
= evalexpr(es
, P_TERN
);
448 if (IS_ASSIGNOP(op
)) {
450 if (vasn
->flag
& INTEGER
)
451 setint_v(vasn
, vr
, es
->arith
);
455 } else if (op
!= O_TERN
)
462 token(Expr_state
*es
)
468 /* skip white space */
469 for (cp
= es
->tokp
; (c
= *cp
), isspace((unsigned char)c
); cp
++)
475 else if (letter(c
)) {
476 for (; letnum(c
); c
= *cp
)
481 len
= array_ref_len(cp
);
483 evalerr(es
, ET_STR
, "missing ]");
485 } else if (c
== '(' /*)*/ ) {
486 /* todo: add math functions (all take single argument):
487 * abs acos asin atan cos cosh exp int log sin sinh sqrt
494 es
->val
->flag
|= EXPRLVALUE
;
496 tvar
= str_nsave(es
->tokp
, cp
- es
->tokp
, ATEMP
);
497 es
->val
= global(tvar
);
501 } else if (digit(c
)) {
502 for (; c
!= '_' && (letnum(c
) || c
== '#'); c
= *cp
++)
504 tvar
= str_nsave(es
->tokp
, --cp
- es
->tokp
, ATEMP
);
506 es
->val
->flag
&= ~INTEGER
;
508 es
->val
->val
.s
= tvar
;
509 if (setint_v(es
->val
, es
->val
, es
->arith
) == NULL
)
510 evalerr(es
, ET_BADLIT
, tvar
);
516 for (i
= 0; (n0
= opinfo
[i
].name
[0]); i
++)
518 strncmp(cp
, opinfo
[i
].name
, opinfo
[i
].len
) == 0) {
519 es
->tok
= (enum token
) i
;
529 /* Do a ++ or -- operation */
531 do_ppmm(Expr_state
*es
, enum token op
, struct tbl
*vasn
, bool is_prefix
)
536 assign_check(es
, op
, vasn
);
538 vl
= intvar(es
, vasn
);
539 oval
= op
== O_PLUSPLUS
? vl
->val
.i
++ : vl
->val
.i
--;
540 if (vasn
->flag
& INTEGER
)
541 setint_v(vasn
, vl
, es
->arith
);
543 setint(vasn
, vl
->val
.i
);
544 if (!is_prefix
) /* undo the inc/dec */
551 assign_check(Expr_state
*es
, enum token op
, struct tbl
*vasn
)
553 if (es
->tok
== END
|| vasn
== NULL
||
554 (vasn
->name
[0] == '\0' && !(vasn
->flag
& EXPRLVALUE
)))
555 evalerr(es
, ET_LVALUE
, opinfo
[(int) op
].name
);
556 else if (vasn
->flag
& RDONLY
)
557 evalerr(es
, ET_RDONLY
, opinfo
[(int) op
].name
);
565 vp
= alloc(sizeof(struct tbl
), ATEMP
);
566 vp
->flag
= ISSET
|INTEGER
;
574 /* cast (string) variable to temporary integer variable */
576 intvar(Expr_state
*es
, struct tbl
*vp
)
580 /* try to avoid replacing a temp var with another temp var */
581 if (vp
->name
[0] == '\0' &&
582 (vp
->flag
& (ISSET
|INTEGER
|EXPRLVALUE
)) == (ISSET
|INTEGER
))
586 if (setint_v(vq
, vp
, es
->arith
) == NULL
) {
587 if (vp
->flag
& EXPRINEVAL
)
588 evalerr(es
, ET_RECURSIVE
, vp
->name
);
590 vp
->flag
|= EXPRINEVAL
;
591 v_evaluate(vq
, str_val(vp
), KSH_UNWIND_ERROR
, es
->arith
);
592 vp
->flag
&= ~EXPRINEVAL
;