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>
This commit is contained in:
121
shell/math.c
121
shell/math.c
@ -52,17 +52,18 @@
|
|||||||
* than a comparable parser written in yacc. The supported operators are
|
* than a comparable parser written in yacc. The supported operators are
|
||||||
* listed in #defines below. Parens, order of operations, and error handling
|
* listed in #defines below. Parens, order of operations, and error handling
|
||||||
* are supported. This code is thread safe. The exact expression format should
|
* are supported. This code is thread safe. The exact expression format should
|
||||||
* be that which POSIX specifies for shells. */
|
* be that which POSIX specifies for shells.
|
||||||
|
*
|
||||||
/* The code uses a simple two-stack algorithm. See
|
* The code uses a simple two-stack algorithm. See
|
||||||
* http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
|
* http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
|
||||||
* for a detailed explanation of the infix-to-postfix algorithm on which
|
* for a detailed explanation of the infix-to-postfix algorithm on which
|
||||||
* this is based (this code differs in that it applies operators immediately
|
* this is based (this code differs in that it applies operators immediately
|
||||||
* to the stack instead of adding them to a queue to end up with an
|
* to the stack instead of adding them to a queue to end up with an
|
||||||
* expression). */
|
* expression).
|
||||||
|
*
|
||||||
/* To use the routine, call it with an expression string and error return
|
* To use the routine, call it with an expression string and error return
|
||||||
* pointer */
|
* pointer
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Aug 24, 2001 Manuel Novoa III
|
* Aug 24, 2001 Manuel Novoa III
|
||||||
@ -104,16 +105,15 @@
|
|||||||
* (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
|
* (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
|
||||||
*
|
*
|
||||||
* - allow access to variable,
|
* - allow access to variable,
|
||||||
* used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
|
* use recursive value indirection: c="2*2"; a="c"; echo $((a+=2)) produce 6
|
||||||
* - realize assign syntax (VAR=expr, +=, *= etc)
|
* - implement assign syntax (VAR=expr, +=, *= etc)
|
||||||
* - realize exponentiation (** operator)
|
* - implement exponentiation (** operator)
|
||||||
* - realize comma separated - expr, expr
|
* - implement comma separated - expr, expr
|
||||||
* - realise ++expr --expr expr++ expr--
|
* - implement ++expr --expr expr++ expr--
|
||||||
* - realise expr ? expr : expr (but, second expr calculate always)
|
* - implement expr ? expr : expr (but second expr is always calculated)
|
||||||
* - allow hexadecimal and octal numbers
|
* - allow hexadecimal and octal numbers
|
||||||
* - was restored loses XOR operator
|
* - restore lost XOR operator
|
||||||
* - remove one goto label, added three ;-)
|
* - protect $((num num)) as true zero expr (Manuel's error)
|
||||||
* - protect $((num num)) as true zero expr (Manuel`s error)
|
|
||||||
* - always use special isspace(), see comment from bash ;-)
|
* - always use special isspace(), see comment from bash ;-)
|
||||||
*/
|
*/
|
||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
@ -130,15 +130,19 @@ typedef unsigned char operator;
|
|||||||
* precedence, and 3 high bits are an ID unique across operators of that
|
* precedence, and 3 high bits are an ID unique across operators of that
|
||||||
* precedence. The ID portion is so that multiple operators can have the
|
* precedence. The ID portion is so that multiple operators can have the
|
||||||
* same precedence, ensuring that the leftmost one is evaluated first.
|
* same precedence, ensuring that the leftmost one is evaluated first.
|
||||||
* Consider * and /. */
|
* Consider * and /
|
||||||
|
*/
|
||||||
#define tok_decl(prec,id) (((id)<<5)|(prec))
|
#define tok_decl(prec,id) (((id)<<5) | (prec))
|
||||||
#define PREC(op) ((op) & 0x1F)
|
#define PREC(op) ((op) & 0x1F)
|
||||||
|
|
||||||
#define TOK_LPAREN tok_decl(0,0)
|
#define TOK_LPAREN tok_decl(0,0)
|
||||||
|
|
||||||
#define TOK_COMMA tok_decl(1,0)
|
#define TOK_COMMA tok_decl(1,0)
|
||||||
|
|
||||||
|
/* All assignments are right associative and have the same precedence,
|
||||||
|
* but there are 11 of them, which doesn't fit into 3 bits for unique id.
|
||||||
|
* Abusing another precedence level:
|
||||||
|
*/
|
||||||
#define TOK_ASSIGN tok_decl(2,0)
|
#define TOK_ASSIGN tok_decl(2,0)
|
||||||
#define TOK_AND_ASSIGN tok_decl(2,1)
|
#define TOK_AND_ASSIGN tok_decl(2,1)
|
||||||
#define TOK_OR_ASSIGN tok_decl(2,2)
|
#define TOK_OR_ASSIGN tok_decl(2,2)
|
||||||
@ -152,10 +156,9 @@ typedef unsigned char operator;
|
|||||||
#define TOK_DIV_ASSIGN tok_decl(3,1)
|
#define TOK_DIV_ASSIGN tok_decl(3,1)
|
||||||
#define TOK_REM_ASSIGN tok_decl(3,2)
|
#define TOK_REM_ASSIGN tok_decl(3,2)
|
||||||
|
|
||||||
/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
|
#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0)
|
||||||
#define convert_prec_is_assign(prec) do { if (prec == 3) prec = 2; } while (0)
|
|
||||||
|
|
||||||
/* conditional is right associativity too */
|
/* ternary conditional operator is right associative too */
|
||||||
#define TOK_CONDITIONAL tok_decl(4,0)
|
#define TOK_CONDITIONAL tok_decl(4,0)
|
||||||
#define TOK_CONDITIONAL_SEP tok_decl(4,1)
|
#define TOK_CONDITIONAL_SEP tok_decl(4,1)
|
||||||
|
|
||||||
@ -187,10 +190,10 @@ typedef unsigned char operator;
|
|||||||
#define TOK_DIV tok_decl(14,1)
|
#define TOK_DIV tok_decl(14,1)
|
||||||
#define TOK_REM tok_decl(14,2)
|
#define TOK_REM tok_decl(14,2)
|
||||||
|
|
||||||
/* exponent is right associativity */
|
/* exponent is right associative */
|
||||||
#define TOK_EXPONENT tok_decl(15,1)
|
#define TOK_EXPONENT tok_decl(15,1)
|
||||||
|
|
||||||
/* For now unary operators. */
|
/* unary operators */
|
||||||
#define UNARYPREC 16
|
#define UNARYPREC 16
|
||||||
#define TOK_BNOT tok_decl(UNARYPREC,0)
|
#define TOK_BNOT tok_decl(UNARYPREC,0)
|
||||||
#define TOK_NOT tok_decl(UNARYPREC,1)
|
#define TOK_NOT tok_decl(UNARYPREC,1)
|
||||||
@ -213,20 +216,18 @@ typedef unsigned char operator;
|
|||||||
#define TOK_NUM tok_decl(SPEC_PREC, 0)
|
#define TOK_NUM tok_decl(SPEC_PREC, 0)
|
||||||
#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
|
#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
|
||||||
|
|
||||||
#define NUMPTR (*numstackptr)
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
tok_have_assign(operator op)
|
tok_have_assign(operator op)
|
||||||
{
|
{
|
||||||
operator prec = PREC(op);
|
operator prec = PREC(op);
|
||||||
|
|
||||||
convert_prec_is_assign(prec);
|
fix_assignment_prec(prec);
|
||||||
return (prec == PREC(TOK_ASSIGN) ||
|
return (prec == PREC(TOK_ASSIGN) ||
|
||||||
prec == PREC_PRE || prec == PREC_POST);
|
prec == PREC_PRE || prec == PREC_POST);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
is_right_associativity(operator prec)
|
is_right_associative(operator prec)
|
||||||
{
|
{
|
||||||
return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
|
return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
|
||||||
|| prec == PREC(TOK_CONDITIONAL));
|
|| prec == PREC(TOK_CONDITIONAL));
|
||||||
@ -255,25 +256,25 @@ arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks)
|
|||||||
|
|
||||||
if (p) {
|
if (p) {
|
||||||
int errcode;
|
int errcode;
|
||||||
|
|
||||||
/* recursive try as expression */
|
|
||||||
chk_var_recursive_looped_t *cur;
|
chk_var_recursive_looped_t *cur;
|
||||||
chk_var_recursive_looped_t cur_save;
|
chk_var_recursive_looped_t cur_save;
|
||||||
|
|
||||||
|
/* recursively try p as expression */
|
||||||
|
|
||||||
for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
|
for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
|
||||||
if (strcmp(cur->var, t->var) == 0) {
|
if (strcmp(cur->var, t->var) == 0) {
|
||||||
/* expression recursion loop detected */
|
/* expression recursion loop detected */
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* save current lookuped var name */
|
/* save current var name */
|
||||||
cur = prev_chk_var_recursive;
|
cur = prev_chk_var_recursive;
|
||||||
cur_save.var = t->var;
|
cur_save.var = t->var;
|
||||||
cur_save.next = cur;
|
cur_save.next = cur;
|
||||||
prev_chk_var_recursive = &cur_save;
|
prev_chk_var_recursive = &cur_save;
|
||||||
|
|
||||||
t->val = arith (p, &errcode, math_hooks);
|
t->val = arith(p, &errcode, math_hooks);
|
||||||
/* restore previous ptr after recursiving */
|
/* restore previous ptr after recursion */
|
||||||
prev_chk_var_recursive = cur;
|
prev_chk_var_recursive = cur;
|
||||||
return errcode;
|
return errcode;
|
||||||
}
|
}
|
||||||
@ -283,21 +284,24 @@ arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "applying" a token means performing it on the top elements on the integer
|
/* "Applying" a token means performing it on the top elements on the integer
|
||||||
* stack. For a unary operator it will only change the top element, but a
|
* stack. For an unary operator it will only change the top element, but a
|
||||||
* binary operator will pop two arguments and push a result */
|
* binary operator will pop two arguments and push the result */
|
||||||
static NOINLINE int
|
static NOINLINE int
|
||||||
arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks)
|
arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks)
|
||||||
{
|
{
|
||||||
|
#define NUMPTR (*numstackptr)
|
||||||
|
|
||||||
v_n_t *numptr_m1;
|
v_n_t *numptr_m1;
|
||||||
arith_t numptr_val, rez;
|
arith_t numptr_val, rez;
|
||||||
int ret_arith_lookup_val;
|
int ret_arith_lookup_val;
|
||||||
|
|
||||||
/* There is no operator that can work without arguments */
|
/* There is no operator that can work without arguments */
|
||||||
if (NUMPTR == numstack) goto err;
|
if (NUMPTR == numstack)
|
||||||
|
goto err;
|
||||||
numptr_m1 = NUMPTR - 1;
|
numptr_m1 = NUMPTR - 1;
|
||||||
|
|
||||||
/* check operand is var with noninteger value */
|
/* Check operand is var with noninteger value */
|
||||||
ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks);
|
ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks);
|
||||||
if (ret_arith_lookup_val)
|
if (ret_arith_lookup_val)
|
||||||
return ret_arith_lookup_val;
|
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
|
|||||||
rez = rez ?
|
rez = rez ?
|
||||||
numptr_val : numptr_m1->contidional_second_val;
|
numptr_val : numptr_m1->contidional_second_val;
|
||||||
} else if (op == TOK_EXPONENT) {
|
} else if (op == TOK_EXPONENT) {
|
||||||
|
arith_t c;
|
||||||
if (numptr_val < 0)
|
if (numptr_val < 0)
|
||||||
return -3; /* exponent less than 0 */
|
return -3; /* exponent less than 0 */
|
||||||
else {
|
c = 1;
|
||||||
arith_t c = 1;
|
while (--numptr_val >= 0)
|
||||||
|
|
||||||
if (numptr_val)
|
|
||||||
while (numptr_val--)
|
|
||||||
c *= rez;
|
c *= rez;
|
||||||
rez = c;
|
rez = c;
|
||||||
}
|
|
||||||
} else if (numptr_val==0) /* zero divisor check */
|
} else if (numptr_val==0) /* zero divisor check */
|
||||||
return -2;
|
return -2;
|
||||||
else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
|
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
|
|||||||
rez++;
|
rez++;
|
||||||
}
|
}
|
||||||
numptr_m1->val = rez;
|
numptr_m1->val = rez;
|
||||||
/* protect geting var value, is number now */
|
/* erase var name, it is just a number now */
|
||||||
numptr_m1->var = NULL;
|
numptr_m1->var = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
return -1;
|
return -1;
|
||||||
|
#undef NUMPTR
|
||||||
}
|
}
|
||||||
|
|
||||||
/* longest must be first */
|
/* longest must be first */
|
||||||
@ -473,7 +475,6 @@ static const char op_tokens[] ALIGN1 = {
|
|||||||
'(', 0, TOK_LPAREN,
|
'(', 0, TOK_LPAREN,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
/* ptr to ")" */
|
|
||||||
#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
|
#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
|
||||||
|
|
||||||
const char* FAST_FUNC
|
const char* FAST_FUNC
|
||||||
@ -529,15 +530,15 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
|
|||||||
* result on the integer stack */
|
* result on the integer stack */
|
||||||
|
|
||||||
if (expr != ptr_to_rparen + 1) {
|
if (expr != ptr_to_rparen + 1) {
|
||||||
/* If we haven't done so already, */
|
/* If we haven't done so already,
|
||||||
/* append a closing right paren */
|
* append a closing right paren
|
||||||
|
* and let the loop process it */
|
||||||
expr = ptr_to_rparen;
|
expr = ptr_to_rparen;
|
||||||
/* and let the loop process it. */
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* At this point, we're done with the expression. */
|
/* At this point, we're done with the expression */
|
||||||
if (numstackptr != numstack + 1) {
|
if (numstackptr != numstack + 1) {
|
||||||
/* ... but if there isn't, it's bad */
|
/* ...but if there isn't, it's bad */
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if (numstack->var) {
|
if (numstack->var) {
|
||||||
@ -619,11 +620,11 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
|
|||||||
/* We don't want an unary operator to cause recursive descent on the
|
/* We don't want an unary operator to cause recursive descent on the
|
||||||
* stack, because there can be many in a row and it could cause an
|
* stack, because there can be many in a row and it could cause an
|
||||||
* operator to be evaluated before its argument is pushed onto the
|
* operator to be evaluated before its argument is pushed onto the
|
||||||
* integer stack. */
|
* integer stack.
|
||||||
/* But for binary operators, "apply" everything on the operator
|
* But for binary operators, "apply" everything on the operator
|
||||||
* stack until we find an operator with a lesser priority than the
|
* stack until we find an operator with a lesser priority than the
|
||||||
* one we have just extracted. */
|
* one we have just extracted.
|
||||||
/* Left paren is given the lowest priority so it will never be
|
* Left paren is given the lowest priority so it will never be
|
||||||
* "applied" in this way.
|
* "applied" in this way.
|
||||||
* if associativity is right and priority eq, applied also skip
|
* if associativity is right and priority eq, applied also skip
|
||||||
*/
|
*/
|
||||||
@ -641,17 +642,17 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
|
|||||||
* hit an open paren nor the bottom of the stack, pop
|
* hit an open paren nor the bottom of the stack, pop
|
||||||
* tokens and apply them */
|
* tokens and apply them */
|
||||||
if (prev_op == TOK_LPAREN) {
|
if (prev_op == TOK_LPAREN) {
|
||||||
/* Any operator directly after a */
|
/* Any operator directly after a
|
||||||
|
* close paren should consider itself binary */
|
||||||
lasttok = TOK_NUM;
|
lasttok = TOK_NUM;
|
||||||
/* close paren should consider itself binary */
|
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
operator prev_prec = PREC(prev_op);
|
operator prev_prec = PREC(prev_op);
|
||||||
convert_prec_is_assign(prec);
|
fix_assignment_prec(prec);
|
||||||
convert_prec_is_assign(prev_prec);
|
fix_assignment_prec(prev_prec);
|
||||||
if (prev_prec < prec
|
if (prev_prec < prec
|
||||||
|| (prev_prec == prec && is_right_associativity(prec))
|
|| (prev_prec == prec && is_right_associative(prec))
|
||||||
) {
|
) {
|
||||||
stackptr++;
|
stackptr++;
|
||||||
break;
|
break;
|
||||||
|
Reference in New Issue
Block a user