diff options
author | Jari Aalto <jari.aalto@cante.net> | 2000-03-17 21:46:59 +0000 |
---|---|---|
committer | Jari Aalto <jari.aalto@cante.net> | 2009-09-12 16:46:53 +0000 |
commit | bb70624e964126b7ac4ff085ba163a9c35ffa18f (patch) | |
tree | ba2dd4add13ada94b1899c6d4aca80195b80b74b /expr.c | |
parent | b72432fdcc59300c6fe7c9d6c8a31ad3447933f5 (diff) | |
download | bash-bb70624e964126b7ac4ff085ba163a9c35ffa18f.tar.gz |
Imported from ../bash-2.04.tar.gz.
Diffstat (limited to 'expr.c')
-rw-r--r-- | expr.c | 188 |
1 files changed, 135 insertions, 53 deletions
@@ -6,7 +6,7 @@ Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) + the Free Software Foundation; either version 2, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free - Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ /* All arithmetic is done as long integers with no checking for overflow @@ -25,6 +25,8 @@ The following operators are handled, grouped into a set of levels in order of decreasing precedence. + "id++", "id--" [post-increment and post-decrement] + "++id", "--id" [pre-increment and pre-decrement] "-", "+" [(unary operators)] "!", "~" "**" [(exponentiation)] @@ -75,6 +77,8 @@ # include <unistd.h> #endif +#include <ctype.h> + #include "shell.h" /* Because of the $((...)) construct, expressions may include newlines. @@ -104,6 +108,10 @@ #define OP_ASSIGN 11 /* op= expassign as in Posix.2 */ #define COND 12 /* exp1 ? exp2 : exp3 */ #define POWER 13 /* exp1**exp2 */ +#define PREINC 14 /* ++var */ +#define PREDEC 15 /* --var */ +#define POSTINC 16 /* var++ */ +#define POSTDEC 17 /* var-- */ #define EQ '=' #define GT '>' #define LT '<' @@ -121,6 +129,11 @@ #define BNOT '~' /* Bitwise NOT; Two's complement. */ #define QUES '?' #define COL ':' +#define COMMA ',' + +/* This should be the function corresponding to the operator with the + highest precedence. */ +#define EXP_HIGHEST expcomma static char *expression; /* The current expression */ static char *tp; /* token lexical position */ @@ -136,18 +149,25 @@ static procenv_t evalbuf; static void readtok (); /* lexical analyzer */ static long subexpr (), expassign (), exp0 (), exp1 (), exp2 (), exp3 (), exp4 (), exp5 (), expshift (), expland (), explor (), - expband (), expbor (), expbxor (), expcond (), exppower (); + expband (), expbor (), expbxor (), expcond (), exppower (), + expcomma (); static long strlong (); static void evalerror (); /* A structure defining a single expression context. */ typedef struct { int curtok, lasttok; - char *expression, *tp; + char *expression, *tp, *lasttp; int tokval; char *tokstr; + int noeval; } EXPR_CONTEXT; +typedef struct { + char *tokstr; + int tokval; +} LVALUE; + /* Global var which contains the stack of expression contexts. */ static EXPR_CONTEXT **expr_stack; static int expr_depth; /* Location in the stack. */ @@ -155,6 +175,28 @@ static int expr_stack_size; /* Number of slots already allocated. */ extern char *this_command_name; +#define SAVETOK(X) \ + do { \ + (X)->curtok = curtok; \ + (X)->lasttok = lasttok; \ + (X)->tp = tp; \ + (X)->lasttp = lasttp; \ + (X)->tokval = tokval; \ + (X)->tokstr = tokstr; \ + (X)->noeval = noeval; \ + } while (0) + +#define RESTORETOK(X) \ + do { \ + curtok = (X)->curtok; \ + lasttok = (X)->lasttok; \ + tp = (X)->tp; \ + lasttp = (X)->lasttp; \ + tokval = (X)->tokval; \ + tokstr = (X)->tokstr; \ + noeval = (X)->noeval; \ + } while (0) + /* Push and save away the contents of the globals describing the current expression context. */ static void @@ -174,12 +216,9 @@ pushexp () context = (EXPR_CONTEXT *)xmalloc (sizeof (EXPR_CONTEXT)); - context->curtok = curtok; - context->lasttok = lasttok; context->expression = expression; - context->tp = tp; - context->tokval = tokval; - context->tokstr = tokstr; + SAVETOK(context); + expr_stack[expr_depth++] = context; } @@ -194,12 +233,10 @@ popexp () evalerror ("recursion stack underflow"); context = expr_stack[--expr_depth]; - curtok = context->curtok; - lasttok = context->lasttok; + expression = context->expression; - tp = context->tp; - tokval = context->tokval; - tokstr = context->tokstr; + RESTORETOK (context); + free (context); } @@ -294,7 +331,7 @@ subexpr (expr) readtok (); - val = expassign (); + val = EXP_HIGHEST (); if (curtok != 0) evalerror ("syntax error in expression"); @@ -307,35 +344,21 @@ subexpr (expr) return val; } -/* Bind/create a shell variable with the name LHS to the RHS. - This creates or modifies a variable such that it is an integer. - - This should really be in variables.c, but it is here so that all of the - expression evaluation stuff is localized. Since we don't want any - recursive evaluation from bind_variable() (possible without this code, - since bind_variable() calls the evaluator for variables with the integer - attribute set), we temporarily turn off the integer attribute for each - variable we set here, then turn it back on after binding as necessary. */ - -void -bind_int_variable (lhs, rhs) - char *lhs, *rhs; +static long +expcomma () { - register SHELL_VAR *v; - int isint = 0; + register long value; - v = find_variable (lhs); - if (v) + value = expassign (); + while (curtok == COMMA) { - isint = integer_p (v); - v->attributes &= ~att_integer; + readtok (); + value = expassign (); } - v = bind_variable (lhs, rhs); - if (isint) - v->attributes |= att_integer; + return value; } - + static long expassign () { @@ -404,7 +427,7 @@ expassign () rhs = itos (value); if (noeval == 0) - bind_int_variable (lhs, rhs); + (void)bind_int_variable (lhs, rhs); free (rhs); free (lhs); FREE (tokstr); @@ -435,7 +458,7 @@ expcond () #if 0 val1 = explor (); #else - val1 = expassign (); + val1 = EXP_HIGHEST (); #endif if (set_noeval) noeval--; @@ -706,6 +729,8 @@ exppower () val2 = exp1 (); if (val2 == 0) return (1L); + if (val2 < 0) + evalerror ("exponent less than 0"); for (c = 1; val2--; c *= val1) ; val1 = c; @@ -737,9 +762,31 @@ exp1 () static long exp0 () { - register long val = 0L; + register long val = 0L, v2; + char *vincdec; + int stok; + + /* XXX - might need additional logic here to decide whether or not + pre-increment or pre-decrement is legal at this point. */ + if (curtok == PREINC || curtok == PREDEC) + { + stok = lasttok = curtok; + readtok (); + if (curtok != STR) + /* readtok() catches this */ + evalerror ("identifier expected after pre-increment or pre-decrement"); - if (curtok == MINUS) + v2 = tokval + ((stok == PREINC) ? 1 : -1); + vincdec = itos (v2); + if (noeval == 0) + (void)bind_int_variable (tokstr, vincdec); + free (vincdec); + val = v2; + + curtok = NUM; /* make sure --x=7 is flagged as an error */ + readtok (); + } + else if (curtok == MINUS) { readtok (); val = - exp0 (); @@ -752,7 +799,7 @@ exp0 () else if (curtok == LPAR) { readtok (); - val = expassign (); + val = EXP_HIGHEST (); if (curtok != RPAR) evalerror ("missing `)'"); @@ -763,6 +810,19 @@ exp0 () else if ((curtok == NUM) || (curtok == STR)) { val = tokval; + if (curtok == STR && (*tp == '+' || *tp == '-') && tp[1] == *tp && + (tp[2] == '\0' || (isalnum (tp[2]) == 0))) + { + /* post-increment or post-decrement */ + v2 = val + ((*tp == '+') ? 1 : -1); + vincdec = itos (v2); + if (noeval == 0) + (void)bind_int_variable (tokstr, vincdec); + free (vincdec); + tp += 2; + curtok = NUM; /* make sure x++=7 is flagged as an error */ + } + readtok (); } else @@ -802,9 +862,10 @@ readtok () if (legal_variable_starter (c)) { - /* Semi-bogus ksh compatibility feature -- variable names - not preceded with a dollar sign are shell variables. */ - char *value; + /* variable names not preceded with a dollar sign are shell variables. */ + char *value, *savecp; + EXPR_CONTEXT ec; + int peektok; while (legal_variable_char (c)) c = *cp++; @@ -827,24 +888,41 @@ readtok () #endif /* ARRAY_VARS */ *cp = '\0'; - FREE (tokstr); tokstr = savestring (tp); + *cp = c; + SAVETOK (&ec); + tokstr = (char *)NULL; /* keep it from being freed */ + tp = savecp = cp; + noeval = 1; + readtok (); + peektok = curtok; + if (peektok == STR) /* free new tokstr before old one is restored */ + FREE (tokstr); + RESTORETOK (&ec); + cp = savecp; + + /* The tests for PREINC and PREDEC aren't strictly correct, but they + preserve old behavior if a construct like --x=9 is given. */ + if (lasttok == PREINC || lasttok == PREDEC || peektok != EQ) + { #if defined (ARRAY_VARS) - value = (e == ']') ? get_array_value (tokstr, 0) : get_string_value (tokstr); + value = (e == ']') ? get_array_value (tokstr, 0) : get_string_value (tokstr); #else - value = get_string_value (tokstr); + value = get_string_value (tokstr); #endif - tokval = (value && *value) ? subexpr (value) : 0; + tokval = (value && *value) ? subexpr (value) : 0; #if defined (ARRAY_VARS) - if (e == ']') - FREE (value); /* get_array_value returns newly-allocated memory */ + if (e == ']') + FREE (value); /* get_array_value returns newly-allocated memory */ #endif + } + else + tokval = 0; - *cp = c; lasttok = curtok; curtok = STR; } @@ -900,6 +978,10 @@ readtok () c = LOR; else if ((c == '*') && (c1 == '*')) c = POWER; + else if ((c == '-') && (c1 == '-') && legal_variable_starter (*cp)) + c = PREDEC; + else if ((c == '+') && (c1 == '+') && legal_variable_starter (*cp)) + c = PREINC; else if (c1 == EQ && member(c, "*/%+-&^|")) { assigntok = c; /* a OP= b */ |