aboutsummaryrefslogtreecommitdiff
path: root/shell/math.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/math.c')
-rw-r--r--shell/math.c684
1 files changed, 361 insertions, 323 deletions
diff --git a/shell/math.c b/shell/math.c
index a4c55a4d0..760645d0f 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * arithmetic code ripped out of ash shell for code sharing 2 * Arithmetic code ripped out of ash shell for code sharing.
3 * 3 *
4 * This code is derived from software contributed to Berkeley by 4 * This code is derived from software contributed to Berkeley by
5 * Kenneth Almquist. 5 * Kenneth Almquist.
@@ -26,43 +26,41 @@
26 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 26 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
27 */ 27 */
28/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> 28/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
29 29 *
30 Permission is hereby granted, free of charge, to any person obtaining 30 * Permission is hereby granted, free of charge, to any person obtaining
31 a copy of this software and associated documentation files (the 31 * a copy of this software and associated documentation files (the
32 "Software"), to deal in the Software without restriction, including 32 * "Software"), to deal in the Software without restriction, including
33 without limitation the rights to use, copy, modify, merge, publish, 33 * without limitation the rights to use, copy, modify, merge, publish,
34 distribute, sublicense, and/or sell copies of the Software, and to 34 * distribute, sublicense, and/or sell copies of the Software, and to
35 permit persons to whom the Software is furnished to do so, subject to 35 * permit persons to whom the Software is furnished to do so, subject to
36 the following conditions: 36 * the following conditions:
37 37 *
38 The above copyright notice and this permission notice shall be 38 * The above copyright notice and this permission notice shall be
39 included in all copies or substantial portions of the Software. 39 * included in all copies or substantial portions of the Software.
40 40 *
41 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
42 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 42 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
43 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 43 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
44 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 44 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
45 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 45 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
46 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 46 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
47 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 47 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48*/ 48 */
49 49
50/* This is my infix parser/evaluator. It is optimized for size, intended 50/* This is my infix parser/evaluator. It is optimized for size, intended
51 * as a replacement for yacc-based parsers. However, it may well be faster 51 * as a replacement for yacc-based parsers. However, it may well be faster
52 * than a comparable parser written in yacc. The supported operators are 52 * than a comparable parser written in yacc. The supported operators are
53 * listed in #defines below. Parens, order of operations, and error handling 53 * listed in #defines below. Parens, order of operations, and error handling
54 * are supported. This code is thread safe. The exact expression format should 54 * are supported. This code is thread safe. The exact expression format should
55 * be that which POSIX specifies for shells. */ 55 * be that which POSIX specifies for shells.
56 56 *
57/* The code uses a simple two-stack algorithm. See 57 * The code uses a simple two-stack algorithm. See
58 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html 58 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
59 * for a detailed explanation of the infix-to-postfix algorithm on which 59 * for a detailed explanation of the infix-to-postfix algorithm on which
60 * this is based (this code differs in that it applies operators immediately 60 * this is based (this code differs in that it applies operators immediately
61 * to the stack instead of adding them to a queue to end up with an 61 * to the stack instead of adding them to a queue to end up with an
62 * expression). */ 62 * expression).
63 63 */
64/* To use the routine, call it with an expression string and error return
65 * pointer */
66 64
67/* 65/*
68 * Aug 24, 2001 Manuel Novoa III 66 * Aug 24, 2001 Manuel Novoa III
@@ -104,28 +102,23 @@
104 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> 102 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
105 * 103 *
106 * - allow access to variable, 104 * - allow access to variable,
107 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6) 105 * use recursive value indirection: c="2*2"; a="c"; echo $((a+=2)) produce 6
108 * - realize assign syntax (VAR=expr, +=, *= etc) 106 * - implement assign syntax (VAR=expr, +=, *= etc)
109 * - realize exponentiation (** operator) 107 * - implement exponentiation (** operator)
110 * - realize comma separated - expr, expr 108 * - implement comma separated - expr, expr
111 * - realise ++expr --expr expr++ expr-- 109 * - implement ++expr --expr expr++ expr--
112 * - realise expr ? expr : expr (but, second expr calculate always) 110 * - implement expr ? expr : expr (but second expr is always calculated)
113 * - allow hexadecimal and octal numbers 111 * - allow hexadecimal and octal numbers
114 * - was restored loses XOR operator 112 * - restore lost XOR operator
115 * - remove one goto label, added three ;-) 113 * - protect $((num num)) as true zero expr (Manuel's error)
116 * - protect $((num num)) as true zero expr (Manuel`s error)
117 * - always use special isspace(), see comment from bash ;-) 114 * - always use special isspace(), see comment from bash ;-)
118 */ 115 */
119#include "libbb.h" 116#include "libbb.h"
120#include "math.h" 117#include "math.h"
121 118
122#define a_e_h_t arith_eval_hooks_t 119#define lookupvar (math_state->lookupvar)
123#define lookupvar (math_hooks->lookupvar) 120#define setvar (math_state->setvar )
124#define setvar (math_hooks->setvar ) 121//#define endofname (math_state->endofname)
125//#define endofname (math_hooks->endofname)
126
127#define arith_isspace(arithval) \
128 (arithval == ' ' || arithval == '\n' || arithval == '\t')
129 122
130typedef unsigned char operator; 123typedef unsigned char operator;
131 124
@@ -133,181 +126,199 @@ typedef unsigned char operator;
133 * precedence, and 3 high bits are an ID unique across operators of that 126 * precedence, and 3 high bits are an ID unique across operators of that
134 * precedence. The ID portion is so that multiple operators can have the 127 * precedence. The ID portion is so that multiple operators can have the
135 * same precedence, ensuring that the leftmost one is evaluated first. 128 * same precedence, ensuring that the leftmost one is evaluated first.
136 * Consider * and /. */ 129 * Consider * and /
137 130 */
138#define tok_decl(prec,id) (((id)<<5)|(prec)) 131#define tok_decl(prec,id) (((id)<<5) | (prec))
139#define PREC(op) ((op) & 0x1F) 132#define PREC(op) ((op) & 0x1F)
140
141#define TOK_LPAREN tok_decl(0,0)
142 133
143#define TOK_COMMA tok_decl(1,0) 134#define TOK_LPAREN tok_decl(0,0)
144 135
145#define TOK_ASSIGN tok_decl(2,0) 136#define TOK_COMMA tok_decl(1,0)
146#define TOK_AND_ASSIGN tok_decl(2,1)
147#define TOK_OR_ASSIGN tok_decl(2,2)
148#define TOK_XOR_ASSIGN tok_decl(2,3)
149#define TOK_PLUS_ASSIGN tok_decl(2,4)
150#define TOK_MINUS_ASSIGN tok_decl(2,5)
151#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
152#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
153 137
154#define TOK_MUL_ASSIGN tok_decl(3,0) 138/* All assignments are right associative and have the same precedence,
155#define TOK_DIV_ASSIGN tok_decl(3,1) 139 * but there are 11 of them, which doesn't fit into 3 bits for unique id.
156#define TOK_REM_ASSIGN tok_decl(3,2) 140 * Abusing another precedence level:
141 */
142#define TOK_ASSIGN tok_decl(2,0)
143#define TOK_AND_ASSIGN tok_decl(2,1)
144#define TOK_OR_ASSIGN tok_decl(2,2)
145#define TOK_XOR_ASSIGN tok_decl(2,3)
146#define TOK_PLUS_ASSIGN tok_decl(2,4)
147#define TOK_MINUS_ASSIGN tok_decl(2,5)
148#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
149#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
157 150
158/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */ 151#define TOK_MUL_ASSIGN tok_decl(3,0)
159#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0) 152#define TOK_DIV_ASSIGN tok_decl(3,1)
153#define TOK_REM_ASSIGN tok_decl(3,2)
160 154
161/* conditional is right associativity too */ 155#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0)
162#define TOK_CONDITIONAL tok_decl(4,0)
163#define TOK_CONDITIONAL_SEP tok_decl(4,1)
164 156
165#define TOK_OR tok_decl(5,0) 157/* Ternary conditional operator is right associative too */
158#define TOK_CONDITIONAL tok_decl(4,0)
159#define TOK_CONDITIONAL_SEP tok_decl(4,1)
166 160
167#define TOK_AND tok_decl(6,0) 161#define TOK_OR tok_decl(5,0)
168 162
169#define TOK_BOR tok_decl(7,0) 163#define TOK_AND tok_decl(6,0)
170 164
171#define TOK_BXOR tok_decl(8,0) 165#define TOK_BOR tok_decl(7,0)
172 166
173#define TOK_BAND tok_decl(9,0) 167#define TOK_BXOR tok_decl(8,0)
174 168
175#define TOK_EQ tok_decl(10,0) 169#define TOK_BAND tok_decl(9,0)
176#define TOK_NE tok_decl(10,1)
177 170
178#define TOK_LT tok_decl(11,0) 171#define TOK_EQ tok_decl(10,0)
179#define TOK_GT tok_decl(11,1) 172#define TOK_NE tok_decl(10,1)
180#define TOK_GE tok_decl(11,2)
181#define TOK_LE tok_decl(11,3)
182 173
183#define TOK_LSHIFT tok_decl(12,0) 174#define TOK_LT tok_decl(11,0)
184#define TOK_RSHIFT tok_decl(12,1) 175#define TOK_GT tok_decl(11,1)
176#define TOK_GE tok_decl(11,2)
177#define TOK_LE tok_decl(11,3)
185 178
186#define TOK_ADD tok_decl(13,0) 179#define TOK_LSHIFT tok_decl(12,0)
187#define TOK_SUB tok_decl(13,1) 180#define TOK_RSHIFT tok_decl(12,1)
188 181
189#define TOK_MUL tok_decl(14,0) 182#define TOK_ADD tok_decl(13,0)
190#define TOK_DIV tok_decl(14,1) 183#define TOK_SUB tok_decl(13,1)
191#define TOK_REM tok_decl(14,2)
192 184
193/* exponent is right associativity */ 185#define TOK_MUL tok_decl(14,0)
194#define TOK_EXPONENT tok_decl(15,1) 186#define TOK_DIV tok_decl(14,1)
187#define TOK_REM tok_decl(14,2)
195 188
196/* For now unary operators. */ 189/* Exponent is right associative */
197#define UNARYPREC 16 190#define TOK_EXPONENT tok_decl(15,1)
198#define TOK_BNOT tok_decl(UNARYPREC,0)
199#define TOK_NOT tok_decl(UNARYPREC,1)
200 191
201#define TOK_UMINUS tok_decl(UNARYPREC+1,0) 192/* Unary operators */
202#define TOK_UPLUS tok_decl(UNARYPREC+1,1) 193#define UNARYPREC 16
194#define TOK_BNOT tok_decl(UNARYPREC,0)
195#define TOK_NOT tok_decl(UNARYPREC,1)
203 196
204#define PREC_PRE (UNARYPREC+2) 197#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
198#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
205 199
206#define TOK_PRE_INC tok_decl(PREC_PRE, 0) 200#define PREC_PRE (UNARYPREC+2)
207#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
208 201
209#define PREC_POST (UNARYPREC+3) 202#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
203#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
210 204
211#define TOK_POST_INC tok_decl(PREC_POST, 0) 205#define PREC_POST (UNARYPREC+3)
212#define TOK_POST_DEC tok_decl(PREC_POST, 1)
213 206
214#define SPEC_PREC (UNARYPREC+4) 207#define TOK_POST_INC tok_decl(PREC_POST, 0)
208#define TOK_POST_DEC tok_decl(PREC_POST, 1)
215 209
216#define TOK_NUM tok_decl(SPEC_PREC, 0) 210#define SPEC_PREC (UNARYPREC+4)
217#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
218 211
219#define NUMPTR (*numstackptr) 212#define TOK_NUM tok_decl(SPEC_PREC, 0)
213#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
220 214
221static int 215static int
222tok_have_assign(operator op) 216is_assign_op(operator op)
223{ 217{
224 operator prec = PREC(op); 218 operator prec = PREC(op);
225 219 fix_assignment_prec(prec);
226 convert_prec_is_assing(prec); 220 return prec == PREC(TOK_ASSIGN)
227 return (prec == PREC(TOK_ASSIGN) || 221 || prec == PREC_PRE
228 prec == PREC_PRE || prec == PREC_POST); 222 || prec == PREC_POST;
229} 223}
230 224
231static int 225static int
232is_right_associativity(operator prec) 226is_right_associative(operator prec)
233{ 227{
234 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) 228 return prec == PREC(TOK_ASSIGN)
235 || prec == PREC(TOK_CONDITIONAL)); 229 || prec == PREC(TOK_EXPONENT)
230 || prec == PREC(TOK_CONDITIONAL);
236} 231}
237 232
233
238typedef struct { 234typedef struct {
239 arith_t val; 235 arith_t val;
240 arith_t contidional_second_val; 236 /* We acquire second_val only when "expr1 : expr2" part
241 char contidional_second_val_initialized; 237 * of ternary ?: op is evaluated.
242 char *var; /* if NULL then is regular number, 238 * We treat ?: as two binary ops: (expr ? (expr1 : expr2)).
243 else is variable name */ 239 * ':' produces a new value which has two parts, val and second_val;
244} v_n_t; 240 * then '?' selects one of them based on its left side.
245 241 */
246typedef struct chk_var_recursive_looped_t { 242 arith_t second_val;
243 char second_val_present;
244 /* If NULL then it's just a number, else it's a named variable */
245 char *var;
246} var_or_num_t;
247
248typedef struct remembered_name {
249 struct remembered_name *next;
247 const char *var; 250 const char *var;
248 struct chk_var_recursive_looped_t *next; 251} remembered_name;
249} chk_var_recursive_looped_t;
250 252
251static chk_var_recursive_looped_t *prev_chk_var_recursive;
252 253
253static int 254static arith_t FAST_FUNC
254arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks) 255evaluate_string(arith_state_t *math_state, const char *expr);
256
257static const char*
258arith_lookup_val(arith_state_t *math_state, var_or_num_t *t)
255{ 259{
256 if (t->var) { 260 if (t->var) {
257 const char *p = lookupvar(t->var); 261 const char *p = lookupvar(t->var);
258
259 if (p) { 262 if (p) {
260 int errcode; 263 remembered_name *cur;
261 264 remembered_name cur_save;
262 /* recursive try as expression */
263 chk_var_recursive_looped_t *cur;
264 chk_var_recursive_looped_t cur_save;
265 265
266 for (cur = prev_chk_var_recursive; cur; cur = cur->next) { 266 /* did we already see this name?
267 * testcase: a=b; b=a; echo $((a))
268 */
269 for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) {
267 if (strcmp(cur->var, t->var) == 0) { 270 if (strcmp(cur->var, t->var) == 0) {
268 /* expression recursion loop detected */ 271 /* Yes */
269 return -5; 272 return "expression recursion loop detected";
270 } 273 }
271 } 274 }
272 /* save current lookuped var name */ 275
273 cur = prev_chk_var_recursive; 276 /* push current var name */
277 cur = math_state->list_of_recursed_names;
274 cur_save.var = t->var; 278 cur_save.var = t->var;
275 cur_save.next = cur; 279 cur_save.next = cur;
276 prev_chk_var_recursive = &cur_save; 280 math_state->list_of_recursed_names = &cur_save;
281
282 /* recursively evaluate p as expression */
283 t->val = evaluate_string(math_state, p);
277 284
278 t->val = arith (p, &errcode, math_hooks); 285 /* pop current var name */
279 /* restore previous ptr after recursiving */ 286 math_state->list_of_recursed_names = cur;
280 prev_chk_var_recursive = cur; 287
281 return errcode; 288 return math_state->errmsg;
282 } 289 }
283 /* allow undefined var as 0 */ 290 /* treat undefined var as 0 */
284 t->val = 0; 291 t->val = 0;
285 } 292 }
286 return 0; 293 return 0;
287} 294}
288 295
289/* "applying" a token means performing it on the top elements on the integer 296/* "Applying" a token means performing it on the top elements on the integer
290 * stack. For a unary operator it will only change the top element, but a 297 * stack. For an unary operator it will only change the top element, but a
291 * binary operator will pop two arguments and push a result */ 298 * binary operator will pop two arguments and push the result */
292static NOINLINE int 299static NOINLINE const char*
293arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks) 300arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr)
294{ 301{
295 v_n_t *numptr_m1; 302#define NUMPTR (*numstackptr)
296 arith_t numptr_val, rez; 303
297 int ret_arith_lookup_val; 304 var_or_num_t *top_of_stack;
305 arith_t rez;
306 const char *err;
298 307
299 /* There is no operator that can work without arguments */ 308 /* There is no operator that can work without arguments */
300 if (NUMPTR == numstack) goto err; 309 if (NUMPTR == numstack)
301 numptr_m1 = NUMPTR - 1; 310 goto err;
311
312 top_of_stack = NUMPTR - 1;
302 313
303 /* check operand is var with noninteger value */ 314 /* Resolve name to value, if needed */
304 ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks); 315 err = arith_lookup_val(math_state, top_of_stack);
305 if (ret_arith_lookup_val) 316 if (err)
306 return ret_arith_lookup_val; 317 return err;
307 318
308 rez = numptr_m1->val; 319 rez = top_of_stack->val;
309 if (op == TOK_UMINUS) 320 if (op == TOK_UMINUS)
310 rez *= -1; 321 rez = -rez;
311 else if (op == TOK_NOT) 322 else if (op == TOK_NOT)
312 rez = !rez; 323 rez = !rez;
313 else if (op == TOK_BNOT) 324 else if (op == TOK_BNOT)
@@ -318,118 +329,123 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hoo
318 rez--; 329 rez--;
319 else if (op != TOK_UPLUS) { 330 else if (op != TOK_UPLUS) {
320 /* Binary operators */ 331 /* Binary operators */
332 arith_t right_side_val;
333 char bad_second_val;
321 334
322 /* check and binary operators need two arguments */ 335 /* Binary operators need two arguments */
323 if (numptr_m1 == numstack) goto err; 336 if (top_of_stack == numstack)
324
325 /* ... and they pop one */
326 --NUMPTR;
327 numptr_val = rez;
328 if (op == TOK_CONDITIONAL) {
329 if (!numptr_m1->contidional_second_val_initialized) {
330 /* protect $((expr1 ? expr2)) without ": expr" */
331 goto err;
332 }
333 rez = numptr_m1->contidional_second_val;
334 } else if (numptr_m1->contidional_second_val_initialized) {
335 /* protect $((expr1 : expr2)) without "expr ? " */
336 goto err; 337 goto err;
338 /* ...and they pop one */
339 NUMPTR = top_of_stack; /* this decrements NUMPTR */
340
341 bad_second_val = top_of_stack->second_val_present;
342 if (op == TOK_CONDITIONAL) { /* ? operation */
343 /* Make next if (...) protect against
344 * $((expr1 ? expr2)) - that is, missing ": expr" */
345 bad_second_val = !bad_second_val;
346 }
347 if (bad_second_val) {
348 /* Protect against $((expr <not_?_op> expr1 : expr2)) */
349 return "malformed ?: operator";
337 } 350 }
338 numptr_m1 = NUMPTR - 1; 351
352 top_of_stack--; /* now points to left side */
353
339 if (op != TOK_ASSIGN) { 354 if (op != TOK_ASSIGN) {
340 /* check operand is var with noninteger value for not '=' */ 355 /* Resolve left side value (unless the op is '=') */
341 ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks); 356 err = arith_lookup_val(math_state, top_of_stack);
342 if (ret_arith_lookup_val) 357 if (err)
343 return ret_arith_lookup_val; 358 return err;
344 } 359 }
345 if (op == TOK_CONDITIONAL) { 360
346 numptr_m1->contidional_second_val = rez; 361 right_side_val = rez;
362 rez = top_of_stack->val;
363 if (op == TOK_CONDITIONAL) /* ? operation */
364 rez = (rez ? right_side_val : top_of_stack[1].second_val);
365 else if (op == TOK_CONDITIONAL_SEP) { /* : operation */
366 if (top_of_stack == numstack) {
367 /* Protect against $((expr : expr)) */
368 return "malformed ?: operator";
369 }
370 top_of_stack->second_val_present = op;
371 top_of_stack->second_val = right_side_val;
347 } 372 }
348 rez = numptr_m1->val; 373 else if (op == TOK_BOR || op == TOK_OR_ASSIGN)
349 if (op == TOK_BOR || op == TOK_OR_ASSIGN) 374 rez |= right_side_val;
350 rez |= numptr_val;
351 else if (op == TOK_OR) 375 else if (op == TOK_OR)
352 rez = numptr_val || rez; 376 rez = right_side_val || rez;
353 else if (op == TOK_BAND || op == TOK_AND_ASSIGN) 377 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
354 rez &= numptr_val; 378 rez &= right_side_val;
355 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) 379 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
356 rez ^= numptr_val; 380 rez ^= right_side_val;
357 else if (op == TOK_AND) 381 else if (op == TOK_AND)
358 rez = rez && numptr_val; 382 rez = rez && right_side_val;
359 else if (op == TOK_EQ) 383 else if (op == TOK_EQ)
360 rez = (rez == numptr_val); 384 rez = (rez == right_side_val);
361 else if (op == TOK_NE) 385 else if (op == TOK_NE)
362 rez = (rez != numptr_val); 386 rez = (rez != right_side_val);
363 else if (op == TOK_GE) 387 else if (op == TOK_GE)
364 rez = (rez >= numptr_val); 388 rez = (rez >= right_side_val);
365 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) 389 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
366 rez >>= numptr_val; 390 rez >>= right_side_val;
367 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) 391 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
368 rez <<= numptr_val; 392 rez <<= right_side_val;
369 else if (op == TOK_GT) 393 else if (op == TOK_GT)
370 rez = (rez > numptr_val); 394 rez = (rez > right_side_val);
371 else if (op == TOK_LT) 395 else if (op == TOK_LT)
372 rez = (rez < numptr_val); 396 rez = (rez < right_side_val);
373 else if (op == TOK_LE) 397 else if (op == TOK_LE)
374 rez = (rez <= numptr_val); 398 rez = (rez <= right_side_val);
375 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) 399 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
376 rez *= numptr_val; 400 rez *= right_side_val;
377 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) 401 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
378 rez += numptr_val; 402 rez += right_side_val;
379 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) 403 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
380 rez -= numptr_val; 404 rez -= right_side_val;
381 else if (op == TOK_ASSIGN || op == TOK_COMMA) 405 else if (op == TOK_ASSIGN || op == TOK_COMMA)
382 rez = numptr_val; 406 rez = right_side_val;
383 else if (op == TOK_CONDITIONAL_SEP) { 407 else if (op == TOK_EXPONENT) {
384 if (numptr_m1 == numstack) { 408 arith_t c;
385 /* protect $((expr : expr)) without "expr ? " */ 409 if (right_side_val < 0)
386 goto err; 410 return "exponent less than 0";
387 } 411 c = 1;
388 numptr_m1->contidional_second_val_initialized = op; 412 while (--right_side_val >= 0)
389 numptr_m1->contidional_second_val = numptr_val; 413 c *= rez;
390 } else if (op == TOK_CONDITIONAL) { 414 rez = c;
391 rez = rez ? 415 }
392 numptr_val : numptr_m1->contidional_second_val; 416 else if (right_side_val == 0)
393 } else if (op == TOK_EXPONENT) { 417 return "divide by zero";
394 if (numptr_val < 0)
395 return -3; /* exponent less than 0 */
396 else {
397 arith_t c = 1;
398
399 if (numptr_val)
400 while (numptr_val--)
401 c *= rez;
402 rez = c;
403 }
404 } else if (numptr_val==0) /* zero divisor check */
405 return -2;
406 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) 418 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
407 rez /= numptr_val; 419 rez /= right_side_val;
408 else if (op == TOK_REM || op == TOK_REM_ASSIGN) 420 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
409 rez %= numptr_val; 421 rez %= right_side_val;
410 } 422 }
411 if (tok_have_assign(op)) { 423
424 if (is_assign_op(op)) {
412 char buf[sizeof(arith_t)*3 + 2]; 425 char buf[sizeof(arith_t)*3 + 2];
413 426
414 if (numptr_m1->var == NULL) { 427 if (top_of_stack->var == NULL) {
415 /* Hmm, 1=2 ? */ 428 /* Hmm, 1=2 ? */
429//TODO: actually, bash allows ++7 but for some reason it evals to 7, not 8
416 goto err; 430 goto err;
417 } 431 }
418 /* save to shell variable */ 432 /* Save to shell variable */
419 sprintf(buf, arith_t_fmt, rez); 433 sprintf(buf, ARITH_FMT, rez);
420 setvar(numptr_m1->var, buf); 434 setvar(top_of_stack->var, buf);
421 /* after saving, make previous value for v++ or v-- */ 435 /* After saving, make previous value for v++ or v-- */
422 if (op == TOK_POST_INC) 436 if (op == TOK_POST_INC)
423 rez--; 437 rez--;
424 else if (op == TOK_POST_DEC) 438 else if (op == TOK_POST_DEC)
425 rez++; 439 rez++;
426 } 440 }
427 numptr_m1->val = rez; 441
428 /* protect geting var value, is number now */ 442 top_of_stack->val = rez;
429 numptr_m1->var = NULL; 443 /* Erase var name, it is just a number now */
430 return 0; 444 top_of_stack->var = NULL;
445 return NULL;
431 err: 446 err:
432 return -1; 447 return "arithmetic syntax error";
448#undef NUMPTR
433} 449}
434 450
435/* longest must be first */ 451/* longest must be first */
@@ -476,8 +492,7 @@ static const char op_tokens[] ALIGN1 = {
476 '(', 0, TOK_LPAREN, 492 '(', 0, TOK_LPAREN,
477 0 493 0
478}; 494};
479/* ptr to ")" */ 495#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
480#define endexpression (&op_tokens[sizeof(op_tokens)-7])
481 496
482const char* FAST_FUNC 497const char* FAST_FUNC
483endofname(const char *name) 498endofname(const char *name)
@@ -491,35 +506,40 @@ endofname(const char *name)
491 return name; 506 return name;
492} 507}
493 508
494arith_t 509static arith_t FAST_FUNC
495arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) 510evaluate_string(arith_state_t *math_state, const char *expr)
496{ 511{
497 char arithval; /* Current character under analysis */ 512 operator lasttok;
498 operator lasttok, op; 513 const char *errmsg;
499 operator prec; 514 const char *start_expr = expr = skip_whitespace(expr);
500 operator *stack, *stackptr; 515 unsigned expr_len = strlen(expr) + 2;
501 const char *p = endexpression;
502 int errcode;
503 v_n_t *numstack, *numstackptr;
504 unsigned datasizes = strlen(expr) + 2;
505
506 /* Stack of integers */ 516 /* Stack of integers */
507 /* The proof that there can be no more than strlen(startbuf)/2+1 integers 517 /* The proof that there can be no more than strlen(startbuf)/2+1
508 * in any given correct or incorrect expression is left as an exercise to 518 * integers in any given correct or incorrect expression
509 * the reader. */ 519 * is left as an exercise to the reader. */
510 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0])); 520 var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0]));
521 var_or_num_t *numstackptr = numstack;
511 /* Stack of operator tokens */ 522 /* Stack of operator tokens */
512 stackptr = stack = alloca(datasizes * sizeof(stack[0])); 523 operator *const stack = alloca(expr_len * sizeof(stack[0]));
524 operator *stackptr = stack;
513 525
514 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ 526 /* Start with a left paren */
515 *perrcode = errcode = 0; 527 *stackptr++ = lasttok = TOK_LPAREN;
528 errmsg = NULL;
516 529
517 while (1) { 530 while (1) {
531 const char *p;
532 operator op;
533 operator prec;
534 char arithval;
535
536 expr = skip_whitespace(expr);
518 arithval = *expr; 537 arithval = *expr;
519 if (arithval == 0) { 538 if (arithval == '\0') {
520 if (p == endexpression) { 539 if (expr == start_expr) {
521 /* Null expression. */ 540 /* Null expression */
522 return 0; 541 numstack->val = 0;
542 goto ret;
523 } 543 }
524 544
525 /* This is only reached after all tokens have been extracted from the 545 /* This is only reached after all tokens have been extracted from the
@@ -527,77 +547,80 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
527 * are to be applied in order. At the end, there should be a final 547 * are to be applied in order. At the end, there should be a final
528 * result on the integer stack */ 548 * result on the integer stack */
529 549
530 if (expr != endexpression + 1) { 550 if (expr != ptr_to_rparen + 1) {
531 /* If we haven't done so already, */ 551 /* If we haven't done so already,
532 /* append a closing right paren */ 552 * append a closing right paren
533 expr = endexpression; 553 * and let the loop process it */
534 /* and let the loop process it. */ 554 expr = ptr_to_rparen;
535 continue; 555 continue;
536 } 556 }
537 /* At this point, we're done with the expression. */ 557 /* At this point, we're done with the expression */
538 if (numstackptr != numstack+1) { 558 if (numstackptr != numstack + 1) {
539 /* ... but if there isn't, it's bad */ 559 /* ...but if there isn't, it's bad */
540 err: 560 goto err;
541 *perrcode = -1;
542 return *perrcode;
543 } 561 }
544 if (numstack->var) { 562 if (numstack->var) {
545 /* expression is $((var)) only, lookup now */ 563 /* expression is $((var)) only, lookup now */
546 errcode = arith_lookup_val(numstack, math_hooks); 564 errmsg = arith_lookup_val(math_state, numstack);
547 } 565 }
548 ret: 566 goto ret;
549 *perrcode = errcode;
550 return numstack->val;
551 } 567 }
552 568
553 /* Continue processing the expression. */
554 if (arith_isspace(arithval)) {
555 /* Skip whitespace */
556 goto prologue;
557 }
558 p = endofname(expr); 569 p = endofname(expr);
559 if (p != expr) { 570 if (p != expr) {
560 size_t var_name_size = (p-expr) + 1; /* trailing zero */ 571 /* Name */
561 572 size_t var_name_size = (p-expr) + 1; /* +1 for NUL */
562 numstackptr->var = alloca(var_name_size); 573 numstackptr->var = alloca(var_name_size);
563 safe_strncpy(numstackptr->var, expr, var_name_size); 574 safe_strncpy(numstackptr->var, expr, var_name_size);
564 expr = p; 575 expr = p;
565 num: 576 num:
566 numstackptr->contidional_second_val_initialized = 0; 577 numstackptr->second_val_present = 0;
567 numstackptr++; 578 numstackptr++;
568 lasttok = TOK_NUM; 579 lasttok = TOK_NUM;
569 continue; 580 continue;
570 } 581 }
582
571 if (isdigit(arithval)) { 583 if (isdigit(arithval)) {
584 /* Number */
572 numstackptr->var = NULL; 585 numstackptr->var = NULL;
573 errno = 0; 586 errno = 0;
574 /* call strtoul[l]: */ 587 numstackptr->val = strto_arith_t(expr, (char**) &expr, 0);
575 numstackptr->val = strto_arith_t(expr, (char **) &expr, 0);
576 if (errno) 588 if (errno)
577 numstackptr->val = 0; /* bash compat */ 589 numstackptr->val = 0; /* bash compat */
578 goto num; 590 goto num;
579 } 591 }
580 for (p = op_tokens; ; p++) {
581 const char *o;
582 592
583 if (*p == 0) { 593 /* Should be an operator */
584 /* strange operator not found */ 594 p = op_tokens;
585 goto err; 595 while (1) {
586 } 596// TODO: bash allows 7+++v, treats it as 7 + ++v
587 for (o = expr; *p && *o == *p; p++) 597// we treat it as 7++ + v and reject
588 o++; 598 /* Compare expr to current op_tokens[] element */
589 if (!*p) { 599 const char *e = expr;
590 /* found */ 600 while (1) {
591 expr = o - 1; 601 if (*p == '\0') {
592 break; 602 /* Match: operator is found */
603 expr = e;
604 goto tok_found;
605 }
606 if (*p != *e)
607 break;
608 p++;
609 e++;
593 } 610 }
594 /* skip tail uncompared token */ 611 /* No match, go to next element of op_tokens[] */
595 while (*p) 612 while (*p)
596 p++; 613 p++;
597 /* skip zero delim */ 614 p += 2; /* skip NUL and TOK_foo bytes */
598 p++; 615 if (*p == '\0') {
616 /* No next element, operator not found */
617 //math_state->syntax_error_at = expr;
618 goto err;
619 }
599 } 620 }
600 op = p[1]; 621 tok_found:
622 op = p[1]; /* fetch TOK_foo value */
623 /* NB: expr now points past the operator */
601 624
602 /* post grammar: a++ reduce to num */ 625 /* post grammar: a++ reduce to num */
603 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) 626 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
@@ -626,13 +649,13 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
626 /* We don't want an unary operator to cause recursive descent on the 649 /* We don't want an unary operator to cause recursive descent on the
627 * stack, because there can be many in a row and it could cause an 650 * stack, because there can be many in a row and it could cause an
628 * operator to be evaluated before its argument is pushed onto the 651 * operator to be evaluated before its argument is pushed onto the
629 * integer stack. */ 652 * integer stack.
630 /* But for binary operators, "apply" everything on the operator 653 * But for binary operators, "apply" everything on the operator
631 * stack until we find an operator with a lesser priority than the 654 * stack until we find an operator with a lesser priority than the
632 * one we have just extracted. */ 655 * one we have just extracted. If op is right-associative,
633 /* Left paren is given the lowest priority so it will never be 656 * then stop "applying" on the equal priority too.
657 * Left paren is given the lowest priority so it will never be
634 * "applied" in this way. 658 * "applied" in this way.
635 * if associativity is right and priority eq, applied also skip
636 */ 659 */
637 prec = PREC(op); 660 prec = PREC(op);
638 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { 661 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
@@ -642,41 +665,56 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
642 goto err; 665 goto err;
643 } 666 }
644 while (stackptr != stack) { 667 while (stackptr != stack) {
668 operator prev_op = *--stackptr;
645 if (op == TOK_RPAREN) { 669 if (op == TOK_RPAREN) {
646 /* The algorithm employed here is simple: while we don't 670 /* The algorithm employed here is simple: while we don't
647 * hit an open paren nor the bottom of the stack, pop 671 * hit an open paren nor the bottom of the stack, pop
648 * tokens and apply them */ 672 * tokens and apply them */
649 if (stackptr[-1] == TOK_LPAREN) { 673 if (prev_op == TOK_LPAREN) {
650 --stackptr; 674 /* Any operator directly after a
651 /* Any operator directly after a */ 675 * close paren should consider itself binary */
652 lasttok = TOK_NUM; 676 lasttok = TOK_NUM;
653 /* close paren should consider itself binary */ 677 goto next;
654 goto prologue;
655 } 678 }
656 } else { 679 } else {
657 operator prev_prec = PREC(stackptr[-1]); 680 operator prev_prec = PREC(prev_op);
658 681 fix_assignment_prec(prec);
659 convert_prec_is_assing(prec); 682 fix_assignment_prec(prev_prec);
660 convert_prec_is_assing(prev_prec); 683 if (prev_prec < prec
661 if (prev_prec < prec) 684 || (prev_prec == prec && is_right_associative(prec))
662 break; 685 ) {
663 /* check right assoc */ 686 stackptr++;
664 if (prev_prec == prec && is_right_associativity(prec))
665 break; 687 break;
688 }
666 } 689 }
667 errcode = arith_apply(*--stackptr, numstack, &numstackptr, math_hooks); 690 errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr);
668 if (errcode) goto ret; 691 if (errmsg)
692 goto err_with_custom_msg;
669 } 693 }
670 if (op == TOK_RPAREN) { 694 if (op == TOK_RPAREN)
671 goto err; 695 goto err;
672 }
673 } 696 }
674 697
675 /* Push this operator to the stack and remember it. */ 698 /* Push this operator to the stack and remember it */
676 *stackptr++ = lasttok = op; 699 *stackptr++ = lasttok = op;
677 prologue: 700 next: ;
678 ++expr; 701 } /* while (1) */
679 } /* while */ 702
703 err:
704 errmsg = "arithmetic syntax error";
705 err_with_custom_msg:
706 numstack->val = -1;
707 ret:
708 math_state->errmsg = errmsg;
709 return numstack->val;
710}
711
712arith_t FAST_FUNC
713arith(arith_state_t *math_state, const char *expr)
714{
715 math_state->errmsg = NULL;
716 math_state->list_of_recursed_names = NULL;
717 return evaluate_string(math_state, expr);
680} 718}
681 719
682/* 720/*