2 * Korn expression evaluation
5 * todo: better error handling: if in builtin, should be builtin error, etc.
12 /* The order of these enums is constrained by the order of opinfo[] */
14 /* some (long) unary operators */
15 O_PLUSPLUS
= 0, O_MINUSMINUS
,
16 /* binary operators */
18 /* assignments are assumed to be in range O_ASN .. O_BORASN */
19 O_ASN
, O_TIMESASN
, O_DIVASN
, O_MODASN
, O_PLUSASN
, O_MINUSASN
,
20 O_LSHIFTASN
, O_RSHIFTASN
, O_BANDASN
, O_BXORASN
, O_BORASN
,
22 O_LE
, O_GE
, O_LT
, O_GT
,
25 O_TIMES
, O_DIV
, O_MOD
,
32 /* things after this aren't used as binary operators */
33 /* unary that are not also binaries */
36 OPEN_PAREN
, CLOSE_PAREN
, CTERN
,
37 /* things that don't appear in the opinfo[] table */
40 #define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
41 #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
44 P_PRIMARY
= 0, /* VAR, LIT, (), ~ ! - + */
48 P_RELATION
, /* < <= > >= */
49 P_EQUALITY
, /* == != */
56 P_ASSIGN
, /* = *= /= %= += -= <<= >>= &= ^= |= */
59 #define MAX_PREC P_COMMA
63 int len
; /* name length */
64 enum prec prec
; /* precidence: lower is higher */
67 /* Tokens in this table must be ordered so the longest are first
68 * (eg, += before +). If you change something, change the order
71 static const struct opinfo opinfo
[] = {
72 { "++", 2, P_PRIMARY
}, /* before + */
73 { "--", 2, P_PRIMARY
}, /* before - */
74 { "==", 2, P_EQUALITY
}, /* before = */
75 { "!=", 2, P_EQUALITY
}, /* before ! */
76 { "=", 1, P_ASSIGN
}, /* keep assigns in a block */
77 { "*=", 2, P_ASSIGN
},
78 { "/=", 2, P_ASSIGN
},
79 { "%=", 2, P_ASSIGN
},
80 { "+=", 2, P_ASSIGN
},
81 { "-=", 2, P_ASSIGN
},
82 { "<<=", 3, P_ASSIGN
},
83 { ">>=", 3, P_ASSIGN
},
84 { "&=", 2, P_ASSIGN
},
85 { "^=", 2, P_ASSIGN
},
86 { "|=", 2, P_ASSIGN
},
89 { "<=", 2, P_RELATION
},
90 { ">=", 2, P_RELATION
},
91 { "<", 1, P_RELATION
},
92 { ">", 1, P_RELATION
},
105 { "~", 1, P_PRIMARY
},
106 { "!", 1, P_PRIMARY
},
107 { "(", 1, P_PRIMARY
},
108 { ")", 1, P_PRIMARY
},
109 { ":", 1, P_PRIMARY
},
110 { "", 0, P_PRIMARY
} /* end of table */
114 typedef struct expr_state Expr_state
;
116 const char *expression
; /* expression being evaluated */
117 const char *tokp
; /* lexical position */
118 enum token tok
; /* token from token() */
119 int noassign
; /* don't do assigns (for ?:,&&,||) */
120 struct tbl
*val
; /* value from token() */
121 struct tbl
*evaling
; /* variable that is being recursively
122 * expanded (EXPRINEVAL flag set)
126 enum error_type
{ ET_UNEXPECTED
, ET_BADLIT
, ET_RECURSIVE
,
127 ET_LVALUE
, ET_RDONLY
, ET_STR
};
129 static void evalerr
ARGS((Expr_state
*es
, enum error_type type
,
130 const char *str
)) GCC_FUNC_ATTR(noreturn
);
131 static struct tbl
*evalexpr
ARGS((Expr_state
*es
, enum prec prec
));
132 static void token
ARGS((Expr_state
*es
));
133 static struct tbl
*do_ppmm
ARGS((Expr_state
*es
, enum token op
,
134 struct tbl
*vasn
, bool_t is_prefix
));
135 static void assign_check
ARGS((Expr_state
*es
, enum token op
,
137 static struct tbl
*tempvar
ARGS((void));
138 static struct tbl
*intvar
ARGS((Expr_state
*es
, struct tbl
*vp
));
141 * parse and evalute expression
144 evaluate(const char *expr
, long *rval
, int error_ok
)
149 v
.flag
= DEFINED
|INTEGER
;
151 ret
= v_evaluate(&v
, expr
, error_ok
);
157 * parse and evalute expression, storing result in vp.
160 v_evaluate(struct tbl
*vp
, const char *expr
, int error_ok
)
164 Expr_state
* const es
= &curstate
;
167 /* save state to allow recursive calls */
168 curstate
.expression
= curstate
.tokp
= expr
;
169 curstate
.noassign
= 0;
170 curstate
.evaling
= (struct tbl
*) 0;
173 i
= sigsetjmp(e
->jbuf
, 0);
175 /* Clear EXPRINEVAL in of any variables we were playing with */
176 if (curstate
.evaling
)
177 curstate
.evaling
->flag
&= ~EXPRINEVAL
;
180 if (error_ok
== KSH_RETURN_ERROR
)
189 #if 0 /* ifdef-out to disallow empty expressions to be treated as 0 */
190 if (es
->tok
== END
) {
195 v
= intvar(es
, evalexpr(es
, MAX_PREC
));
198 evalerr(es
, ET_UNEXPECTED
, (char *) 0);
200 if (vp
->flag
& INTEGER
)
203 /* can fail if readony */
204 setstr(vp
, str_val(v
), error_ok
);
212 evalerr(es
, type
, str
)
214 enum error_type type
;
227 s
= str_val(es
->val
);
230 s
= "end of expression";
238 s
= opinfo
[(int)es
->tok
].name
;
240 warningf(TRUE
, "%s: unexpected `%s'", es
->expression
, s
);
244 warningf(TRUE
, "%s: bad number `%s'", es
->expression
, str
);
248 warningf(TRUE
, "%s: expression recurses on parameter `%s'",
249 es
->expression
, str
);
253 warningf(TRUE
, "%s: %s requires lvalue",
254 es
->expression
, str
);
258 warningf(TRUE
, "%s: %s applied to read only variable",
259 es
->expression
, str
);
262 default: /* keep gcc happy */
264 warningf(TRUE
, "%s: %s", es
->expression
, str
);
271 evalexpr(Expr_state
*es
, enum prec prec
)
273 struct tbl
*vl
, UNINITIALIZED(*vr
), *vasn
;
275 long UNINITIALIZED(res
);
277 if (prec
== P_PRIMARY
) {
279 if (op
== O_BNOT
|| op
== O_LNOT
|| op
== O_MINUS
283 vl
= intvar(es
, evalexpr(es
, P_PRIMARY
));
285 vl
->val
.i
= ~vl
->val
.i
;
286 else if (op
== O_LNOT
)
287 vl
->val
.i
= !vl
->val
.i
;
288 else if (op
== O_MINUS
)
289 vl
->val
.i
= -vl
->val
.i
;
290 /* op == O_PLUS is a no-op */
291 } else if (op
== OPEN_PAREN
) {
293 vl
= evalexpr(es
, MAX_PREC
);
294 if (es
->tok
!= CLOSE_PAREN
)
295 evalerr(es
, ET_STR
, "missing )");
297 } else if (op
== O_PLUSPLUS
|| op
== O_MINUSMINUS
) {
299 vl
= do_ppmm(es
, op
, es
->val
, TRUE
);
301 } else if (op
== VAR
|| op
== LIT
) {
305 evalerr(es
, ET_UNEXPECTED
, (char *) 0);
308 if (es
->tok
== O_PLUSPLUS
|| es
->tok
== O_MINUSMINUS
) {
309 vl
= do_ppmm(es
, es
->tok
, vl
, FALSE
);
314 vl
= evalexpr(es
, ((int) prec
) - 1);
315 for (op
= es
->tok
; IS_BINOP(op
) && opinfo
[(int) op
].prec
== prec
;
320 if (op
!= O_ASN
) /* vl may not have a value yet */
322 if (IS_ASSIGNOP(op
)) {
323 assign_check(es
, op
, vasn
);
324 vr
= intvar(es
, evalexpr(es
, P_ASSIGN
));
325 } else if (op
!= O_TERN
&& op
!= O_LAND
&& op
!= O_LOR
)
326 vr
= intvar(es
, evalexpr(es
, ((int) prec
) - 1));
327 if ((op
== O_DIV
|| op
== O_MOD
|| op
== O_DIVASN
328 || op
== O_MODASN
) && vr
->val
.i
== 0)
333 evalerr(es
, ET_STR
, "zero divisor");
338 res
= vl
->val
.i
* vr
->val
.i
;
342 res
= vl
->val
.i
/ vr
->val
.i
;
346 res
= vl
->val
.i
% vr
->val
.i
;
350 res
= vl
->val
.i
+ vr
->val
.i
;
354 res
= vl
->val
.i
- vr
->val
.i
;
358 res
= vl
->val
.i
<< vr
->val
.i
;
362 res
= vl
->val
.i
>> vr
->val
.i
;
365 res
= vl
->val
.i
< vr
->val
.i
;
368 res
= vl
->val
.i
<= vr
->val
.i
;
371 res
= vl
->val
.i
> vr
->val
.i
;
374 res
= vl
->val
.i
>= vr
->val
.i
;
377 res
= vl
->val
.i
== vr
->val
.i
;
380 res
= vl
->val
.i
!= vr
->val
.i
;
384 res
= vl
->val
.i
& vr
->val
.i
;
388 res
= vl
->val
.i
^ vr
->val
.i
;
392 res
= vl
->val
.i
| vr
->val
.i
;
397 vr
= intvar(es
, evalexpr(es
, ((int) prec
) - 1));
398 res
= vl
->val
.i
&& vr
->val
.i
;
405 vr
= intvar(es
, evalexpr(es
, ((int) prec
) - 1));
406 res
= vl
->val
.i
|| vr
->val
.i
;
412 int e
= vl
->val
.i
!= 0;
415 vl
= evalexpr(es
, MAX_PREC
);
418 if (es
->tok
!= CTERN
)
419 evalerr(es
, ET_STR
, "missing :");
423 vr
= evalexpr(es
, P_TERN
);
436 if (IS_ASSIGNOP(op
)) {
439 if (vasn
->flag
& INTEGER
)
445 } else if (op
!= O_TERN
)
459 /* skip white space */
460 for (cp
= es
->tokp
; (c
= *cp
), isspace(c
); cp
++)
466 else if (isalpha(c
) || c
=='_') {
467 for (; isalnum(c
) || c
=='_'; c
= *cp
)
472 len
= array_ref_len(cp
);
474 evalerr(es
, ET_STR
, "missing ]");
478 else if (c
== '(' /*)*/ ) {
479 /* todo: add math functions (all take single argument):
480 * abs acos asin atan cos cosh exp int log sin sinh sqrt
488 es
->val
->flag
|= EXPRLVALUE
;
490 tvar
= str_nsave(es
->tokp
, cp
- es
->tokp
, ATEMP
);
491 es
->val
= global(tvar
);
495 } else if (isdigit(c
)) {
496 for (; (isalnum(c
) || c
== '#'); c
= *cp
++)
498 tvar
= str_nsave(es
->tokp
, --cp
- es
->tokp
, ATEMP
);
500 es
->val
->flag
&= ~INTEGER
;
502 es
->val
->val
.s
= tvar
;
503 if (setint_v(es
->val
, es
->val
) == NULL
)
504 evalerr(es
, ET_BADLIT
, tvar
);
510 for (i
= 0; (n0
= opinfo
[i
].name
[0]); i
++)
512 && strncmp(cp
, opinfo
[i
].name
, opinfo
[i
].len
) == 0)
514 es
->tok
= (enum token
) i
;
524 /* Do a ++ or -- operation */
526 do_ppmm(es
, op
, vasn
, is_prefix
)
535 assign_check(es
, op
, vasn
);
537 vl
= intvar(es
, vasn
);
538 oval
= op
== O_PLUSPLUS
? vl
->val
.i
++ : vl
->val
.i
--;
539 if (vasn
->flag
& INTEGER
)
542 setint(vasn
, vl
->val
.i
);
543 if (!is_prefix
) /* undo the inc/dec */
550 assign_check(es
, op
, vasn
)
555 if (vasn
->name
[0] == '\0' && !(vasn
->flag
& EXPRLVALUE
))
556 evalerr(es
, ET_LVALUE
, opinfo
[(int) op
].name
);
557 else if (vasn
->flag
& RDONLY
)
558 evalerr(es
, ET_RDONLY
, opinfo
[(int) op
].name
);
566 vp
= (struct tbl
*) alloc(sizeof(struct tbl
), ATEMP
);
567 vp
->flag
= ISSET
|INTEGER
;
575 /* cast (string) variable to temporary integer variable */
583 /* try to avoid replacing a temp var with another temp var */
584 if (vp
->name
[0] == '\0'
585 && (vp
->flag
& (ISSET
|INTEGER
|EXPRLVALUE
)) == (ISSET
|INTEGER
))
589 if (setint_v(vq
, vp
) == NULL
) {
590 if (vp
->flag
& EXPRINEVAL
)
591 evalerr(es
, ET_RECURSIVE
, vp
->name
);
593 vp
->flag
|= EXPRINEVAL
;
594 v_evaluate(vq
, str_val(vp
), KSH_UNWIND_ERROR
);
595 vp
->flag
&= ~EXPRINEVAL
;
596 es
->evaling
= (struct tbl
*) 0;