aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-09-13 11:11:40 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-09-13 11:11:40 +0200
commitbd14770b0c8594ce5b0ab9b0b1249b72fc781dd3 (patch)
tree8907f85fa7657f613c50ac1e01ad6a3f2f855783
parent51850c818cf909cde0e07091a8015532cc645b7a (diff)
downloadbusybox-w32-bd14770b0c8594ce5b0ab9b0b1249b72fc781dd3.tar.gz
busybox-w32-bd14770b0c8594ce5b0ab9b0b1249b72fc781dd3.tar.bz2
busybox-w32-bd14770b0c8594ce5b0ab9b0b1249b72fc781dd3.zip
shell/math.c: small code shrink; fixed incomprehensible comments
function old new delta arith_apply 1334 1304 -30 Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r--shell/math.c225
1 files changed, 113 insertions, 112 deletions
diff --git a/shell/math.c b/shell/math.c
index c698a442b..a9fbc8910 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -52,17 +52,18 @@
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 64 * To use the routine, call it with an expression string and error return
65 * pointer */ 65 * pointer
66 */
66 67
67/* 68/*
68 * Aug 24, 2001 Manuel Novoa III 69 * Aug 24, 2001 Manuel Novoa III
@@ -104,22 +105,21 @@
104 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> 105 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
105 * 106 *
106 * - allow access to variable, 107 * - allow access to variable,
107 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6) 108 * use recursive value indirection: c="2*2"; a="c"; echo $((a+=2)) produce 6
108 * - realize assign syntax (VAR=expr, +=, *= etc) 109 * - implement assign syntax (VAR=expr, +=, *= etc)
109 * - realize exponentiation (** operator) 110 * - implement exponentiation (** operator)
110 * - realize comma separated - expr, expr 111 * - implement comma separated - expr, expr
111 * - realise ++expr --expr expr++ expr-- 112 * - implement ++expr --expr expr++ expr--
112 * - realise expr ? expr : expr (but, second expr calculate always) 113 * - implement expr ? expr : expr (but second expr is always calculated)
113 * - allow hexadecimal and octal numbers 114 * - allow hexadecimal and octal numbers
114 * - was restored loses XOR operator 115 * - restore lost XOR operator
115 * - remove one goto label, added three ;-) 116 * - 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 ;-) 117 * - always use special isspace(), see comment from bash ;-)
118 */ 118 */
119#include "libbb.h" 119#include "libbb.h"
120#include "math.h" 120#include "math.h"
121 121
122#define a_e_h_t arith_eval_hooks_t 122#define a_e_h_t arith_eval_hooks_t
123#define lookupvar (math_hooks->lookupvar) 123#define lookupvar (math_hooks->lookupvar)
124#define setvar (math_hooks->setvar ) 124#define setvar (math_hooks->setvar )
125//#define endofname (math_hooks->endofname) 125//#define endofname (math_hooks->endofname)
@@ -130,103 +130,104 @@ typedef unsigned char operator;
130 * precedence, and 3 high bits are an ID unique across operators of that 130 * precedence, and 3 high bits are an ID unique across operators of that
131 * precedence. The ID portion is so that multiple operators can have the 131 * precedence. The ID portion is so that multiple operators can have the
132 * same precedence, ensuring that the leftmost one is evaluated first. 132 * same precedence, ensuring that the leftmost one is evaluated first.
133 * Consider * and /. */ 133 * Consider * and /
134 134 */
135#define tok_decl(prec,id) (((id)<<5)|(prec)) 135#define tok_decl(prec,id) (((id)<<5) | (prec))
136#define PREC(op) ((op) & 0x1F) 136#define PREC(op) ((op) & 0x1F)
137
138#define TOK_LPAREN tok_decl(0,0)
139 137
140#define TOK_COMMA tok_decl(1,0) 138#define TOK_LPAREN tok_decl(0,0)
141 139
142#define TOK_ASSIGN tok_decl(2,0) 140#define TOK_COMMA tok_decl(1,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)
150 141
151#define TOK_MUL_ASSIGN tok_decl(3,0) 142/* All assignments are right associative and have the same precedence,
152#define TOK_DIV_ASSIGN tok_decl(3,1) 143 * but there are 11 of them, which doesn't fit into 3 bits for unique id.
153#define TOK_REM_ASSIGN tok_decl(3,2) 144 * Abusing another precedence level:
145 */
146#define TOK_ASSIGN tok_decl(2,0)
147#define TOK_AND_ASSIGN tok_decl(2,1)
148#define TOK_OR_ASSIGN tok_decl(2,2)
149#define TOK_XOR_ASSIGN tok_decl(2,3)
150#define TOK_PLUS_ASSIGN tok_decl(2,4)
151#define TOK_MINUS_ASSIGN tok_decl(2,5)
152#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
153#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
154 154
155/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */ 155#define TOK_MUL_ASSIGN tok_decl(3,0)
156#define convert_prec_is_assign(prec) do { if (prec == 3) prec = 2; } while (0) 156#define TOK_DIV_ASSIGN tok_decl(3,1)
157#define TOK_REM_ASSIGN tok_decl(3,2)
157 158
158/* conditional is right associativity too */ 159#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0)
159#define TOK_CONDITIONAL tok_decl(4,0)
160#define TOK_CONDITIONAL_SEP tok_decl(4,1)
161 160
162#define TOK_OR tok_decl(5,0) 161/* ternary conditional operator is right associative too */
162#define TOK_CONDITIONAL tok_decl(4,0)
163#define TOK_CONDITIONAL_SEP tok_decl(4,1)
163 164
164#define TOK_AND tok_decl(6,0) 165#define TOK_OR tok_decl(5,0)
165 166
166#define TOK_BOR tok_decl(7,0) 167#define TOK_AND tok_decl(6,0)
167 168
168#define TOK_BXOR tok_decl(8,0) 169#define TOK_BOR tok_decl(7,0)
169 170
170#define TOK_BAND tok_decl(9,0) 171#define TOK_BXOR tok_decl(8,0)
171 172
172#define TOK_EQ tok_decl(10,0) 173#define TOK_BAND tok_decl(9,0)
173#define TOK_NE tok_decl(10,1)
174 174
175#define TOK_LT tok_decl(11,0) 175#define TOK_EQ tok_decl(10,0)
176#define TOK_GT tok_decl(11,1) 176#define TOK_NE tok_decl(10,1)
177#define TOK_GE tok_decl(11,2)
178#define TOK_LE tok_decl(11,3)
179 177
180#define TOK_LSHIFT tok_decl(12,0) 178#define TOK_LT tok_decl(11,0)
181#define TOK_RSHIFT tok_decl(12,1) 179#define TOK_GT tok_decl(11,1)
180#define TOK_GE tok_decl(11,2)
181#define TOK_LE tok_decl(11,3)
182 182
183#define TOK_ADD tok_decl(13,0) 183#define TOK_LSHIFT tok_decl(12,0)
184#define TOK_SUB tok_decl(13,1) 184#define TOK_RSHIFT tok_decl(12,1)
185 185
186#define TOK_MUL tok_decl(14,0) 186#define TOK_ADD tok_decl(13,0)
187#define TOK_DIV tok_decl(14,1) 187#define TOK_SUB tok_decl(13,1)
188#define TOK_REM tok_decl(14,2)
189 188
190/* exponent is right associativity */ 189#define TOK_MUL tok_decl(14,0)
191#define TOK_EXPONENT tok_decl(15,1) 190#define TOK_DIV tok_decl(14,1)
191#define TOK_REM tok_decl(14,2)
192 192
193/* For now unary operators. */ 193/* exponent is right associative */
194#define UNARYPREC 16 194#define TOK_EXPONENT tok_decl(15,1)
195#define TOK_BNOT tok_decl(UNARYPREC,0)
196#define TOK_NOT tok_decl(UNARYPREC,1)
197 195
198#define TOK_UMINUS tok_decl(UNARYPREC+1,0) 196/* unary operators */
199#define TOK_UPLUS tok_decl(UNARYPREC+1,1) 197#define UNARYPREC 16
198#define TOK_BNOT tok_decl(UNARYPREC,0)
199#define TOK_NOT tok_decl(UNARYPREC,1)
200 200
201#define PREC_PRE (UNARYPREC+2) 201#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
202#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
202 203
203#define TOK_PRE_INC tok_decl(PREC_PRE, 0) 204#define PREC_PRE (UNARYPREC+2)
204#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
205 205
206#define PREC_POST (UNARYPREC+3) 206#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
207#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
207 208
208#define TOK_POST_INC tok_decl(PREC_POST, 0) 209#define PREC_POST (UNARYPREC+3)
209#define TOK_POST_DEC tok_decl(PREC_POST, 1)
210 210
211#define SPEC_PREC (UNARYPREC+4) 211#define TOK_POST_INC tok_decl(PREC_POST, 0)
212#define TOK_POST_DEC tok_decl(PREC_POST, 1)
212 213
213#define TOK_NUM tok_decl(SPEC_PREC, 0) 214#define SPEC_PREC (UNARYPREC+4)
214#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
215 215
216#define NUMPTR (*numstackptr) 216#define TOK_NUM tok_decl(SPEC_PREC, 0)
217#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
217 218
218static int 219static int
219tok_have_assign(operator op) 220tok_have_assign(operator op)
220{ 221{
221 operator prec = PREC(op); 222 operator prec = PREC(op);
222 223
223 convert_prec_is_assign(prec); 224 fix_assignment_prec(prec);
224 return (prec == PREC(TOK_ASSIGN) || 225 return (prec == PREC(TOK_ASSIGN) ||
225 prec == PREC_PRE || prec == PREC_POST); 226 prec == PREC_PRE || prec == PREC_POST);
226} 227}
227 228
228static int 229static int
229is_right_associativity(operator prec) 230is_right_associative(operator prec)
230{ 231{
231 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) 232 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
232 || prec == PREC(TOK_CONDITIONAL)); 233 || prec == PREC(TOK_CONDITIONAL));
@@ -255,25 +256,25 @@ arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks)
255 256
256 if (p) { 257 if (p) {
257 int errcode; 258 int errcode;
258
259 /* recursive try as expression */
260 chk_var_recursive_looped_t *cur; 259 chk_var_recursive_looped_t *cur;
261 chk_var_recursive_looped_t cur_save; 260 chk_var_recursive_looped_t cur_save;
262 261
262 /* recursively try p as expression */
263
263 for (cur = prev_chk_var_recursive; cur; cur = cur->next) { 264 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
264 if (strcmp(cur->var, t->var) == 0) { 265 if (strcmp(cur->var, t->var) == 0) {
265 /* expression recursion loop detected */ 266 /* expression recursion loop detected */
266 return -5; 267 return -5;
267 } 268 }
268 } 269 }
269 /* save current lookuped var name */ 270 /* save current var name */
270 cur = prev_chk_var_recursive; 271 cur = prev_chk_var_recursive;
271 cur_save.var = t->var; 272 cur_save.var = t->var;
272 cur_save.next = cur; 273 cur_save.next = cur;
273 prev_chk_var_recursive = &cur_save; 274 prev_chk_var_recursive = &cur_save;
274 275
275 t->val = arith (p, &errcode, math_hooks); 276 t->val = arith(p, &errcode, math_hooks);
276 /* restore previous ptr after recursiving */ 277 /* restore previous ptr after recursion */
277 prev_chk_var_recursive = cur; 278 prev_chk_var_recursive = cur;
278 return errcode; 279 return errcode;
279 } 280 }
@@ -283,21 +284,24 @@ arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks)
283 return 0; 284 return 0;
284} 285}
285 286
286/* "applying" a token means performing it on the top elements on the integer 287/* "Applying" a token means performing it on the top elements on the integer
287 * stack. For a unary operator it will only change the top element, but a 288 * stack. For an unary operator it will only change the top element, but a
288 * binary operator will pop two arguments and push a result */ 289 * binary operator will pop two arguments and push the result */
289static NOINLINE int 290static NOINLINE int
290arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks) 291arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks)
291{ 292{
293#define NUMPTR (*numstackptr)
294
292 v_n_t *numptr_m1; 295 v_n_t *numptr_m1;
293 arith_t numptr_val, rez; 296 arith_t numptr_val, rez;
294 int ret_arith_lookup_val; 297 int ret_arith_lookup_val;
295 298
296 /* There is no operator that can work without arguments */ 299 /* There is no operator that can work without arguments */
297 if (NUMPTR == numstack) goto err; 300 if (NUMPTR == numstack)
301 goto err;
298 numptr_m1 = NUMPTR - 1; 302 numptr_m1 = NUMPTR - 1;
299 303
300 /* check operand is var with noninteger value */ 304 /* Check operand is var with noninteger value */
301 ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks); 305 ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks);
302 if (ret_arith_lookup_val) 306 if (ret_arith_lookup_val)
303 return ret_arith_lookup_val; 307 return ret_arith_lookup_val;
@@ -388,16 +392,13 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hoo
388 rez = rez ? 392 rez = rez ?
389 numptr_val : numptr_m1->contidional_second_val; 393 numptr_val : numptr_m1->contidional_second_val;
390 } else if (op == TOK_EXPONENT) { 394 } else if (op == TOK_EXPONENT) {
395 arith_t c;
391 if (numptr_val < 0) 396 if (numptr_val < 0)
392 return -3; /* exponent less than 0 */ 397 return -3; /* exponent less than 0 */
393 else { 398 c = 1;
394 arith_t c = 1; 399 while (--numptr_val >= 0)
395 400 c *= rez;
396 if (numptr_val) 401 rez = c;
397 while (numptr_val--)
398 c *= rez;
399 rez = c;
400 }
401 } else if (numptr_val==0) /* zero divisor check */ 402 } else if (numptr_val==0) /* zero divisor check */
402 return -2; 403 return -2;
403 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) 404 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
@@ -422,11 +423,12 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hoo
422 rez++; 423 rez++;
423 } 424 }
424 numptr_m1->val = rez; 425 numptr_m1->val = rez;
425 /* protect geting var value, is number now */ 426 /* erase var name, it is just a number now */
426 numptr_m1->var = NULL; 427 numptr_m1->var = NULL;
427 return 0; 428 return 0;
428 err: 429 err:
429 return -1; 430 return -1;
431#undef NUMPTR
430} 432}
431 433
432/* longest must be first */ 434/* longest must be first */
@@ -473,7 +475,6 @@ static const char op_tokens[] ALIGN1 = {
473 '(', 0, TOK_LPAREN, 475 '(', 0, TOK_LPAREN,
474 0 476 0
475}; 477};
476/* ptr to ")" */
477#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) 478#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
478 479
479const char* FAST_FUNC 480const char* FAST_FUNC
@@ -529,15 +530,15 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
529 * result on the integer stack */ 530 * result on the integer stack */
530 531
531 if (expr != ptr_to_rparen + 1) { 532 if (expr != ptr_to_rparen + 1) {
532 /* If we haven't done so already, */ 533 /* If we haven't done so already,
533 /* append a closing right paren */ 534 * append a closing right paren
535 * and let the loop process it */
534 expr = ptr_to_rparen; 536 expr = ptr_to_rparen;
535 /* and let the loop process it. */
536 continue; 537 continue;
537 } 538 }
538 /* At this point, we're done with the expression. */ 539 /* At this point, we're done with the expression */
539 if (numstackptr != numstack + 1) { 540 if (numstackptr != numstack + 1) {
540 /* ... but if there isn't, it's bad */ 541 /* ...but if there isn't, it's bad */
541 goto err; 542 goto err;
542 } 543 }
543 if (numstack->var) { 544 if (numstack->var) {
@@ -619,11 +620,11 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
619 /* We don't want an unary operator to cause recursive descent on the 620 /* We don't want an unary operator to cause recursive descent on the
620 * stack, because there can be many in a row and it could cause an 621 * stack, because there can be many in a row and it could cause an
621 * operator to be evaluated before its argument is pushed onto the 622 * operator to be evaluated before its argument is pushed onto the
622 * integer stack. */ 623 * integer stack.
623 /* But for binary operators, "apply" everything on the operator 624 * But for binary operators, "apply" everything on the operator
624 * stack until we find an operator with a lesser priority than the 625 * stack until we find an operator with a lesser priority than the
625 * one we have just extracted. */ 626 * one we have just extracted.
626 /* Left paren is given the lowest priority so it will never be 627 * Left paren is given the lowest priority so it will never be
627 * "applied" in this way. 628 * "applied" in this way.
628 * if associativity is right and priority eq, applied also skip 629 * if associativity is right and priority eq, applied also skip
629 */ 630 */
@@ -641,17 +642,17 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
641 * hit an open paren nor the bottom of the stack, pop 642 * hit an open paren nor the bottom of the stack, pop
642 * tokens and apply them */ 643 * tokens and apply them */
643 if (prev_op == TOK_LPAREN) { 644 if (prev_op == TOK_LPAREN) {
644 /* Any operator directly after a */ 645 /* Any operator directly after a
646 * close paren should consider itself binary */
645 lasttok = TOK_NUM; 647 lasttok = TOK_NUM;
646 /* close paren should consider itself binary */
647 goto next; 648 goto next;
648 } 649 }
649 } else { 650 } else {
650 operator prev_prec = PREC(prev_op); 651 operator prev_prec = PREC(prev_op);
651 convert_prec_is_assign(prec); 652 fix_assignment_prec(prec);
652 convert_prec_is_assign(prev_prec); 653 fix_assignment_prec(prev_prec);
653 if (prev_prec < prec 654 if (prev_prec < prec
654 || (prev_prec == prec && is_right_associativity(prec)) 655 || (prev_prec == prec && is_right_associative(prec))
655 ) { 656 ) {
656 stackptr++; 657 stackptr++;
657 break; 658 break;