diff options
author | jilles <jilles@FreeBSD.org> | 2011-02-08 23:18:06 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2011-02-08 23:18:06 +0000 |
commit | 1cbab8a321b4fa3392f1ed1ec381b3fadbc41558 (patch) | |
tree | bb9c25c32a33a77e92cd3306937e474e7efe2da0 /bin/sh | |
parent | 6543d03b931354fdbab3e83e273b87c539c81231 (diff) | |
download | FreeBSD-src-1cbab8a321b4fa3392f1ed1ec381b3fadbc41558.zip FreeBSD-src-1cbab8a321b4fa3392f1ed1ec381b3fadbc41558.tar.gz |
sh: Import arithmetic expression code from dash.
New features:
* proper lazy evaluation of || and &&
* ?: ternary operator
* executable is considerably smaller (8K on i386) because lex and yacc are
no longer used
Differences from dash:
* arith_t instead of intmax_t
* imaxdiv() not used
* unset or null variables default to 0
* let/exp builtin (undocumented, will probably be removed later)
Obtained from: dash
Diffstat (limited to 'bin/sh')
-rw-r--r-- | bin/sh/Makefile | 10 | ||||
-rw-r--r-- | bin/sh/arith.h | 2 | ||||
-rw-r--r-- | bin/sh/arith.y | 366 | ||||
-rw-r--r-- | bin/sh/arith_lex.l | 143 | ||||
-rw-r--r-- | bin/sh/arith_yacc.c | 376 | ||||
-rw-r--r-- | bin/sh/arith_yacc.h | 91 | ||||
-rw-r--r-- | bin/sh/arith_yylex.c | 244 |
7 files changed, 716 insertions, 516 deletions
diff --git a/bin/sh/Makefile b/bin/sh/Makefile index 47642df..afca8a3 100644 --- a/bin/sh/Makefile +++ b/bin/sh/Makefile @@ -3,22 +3,22 @@ PROG= sh INSTALLFLAGS= -S -SHSRCS= alias.c arith.y arith_lex.l cd.c echo.c error.c eval.c exec.c expand.c \ +SHSRCS= alias.c arith_yacc.c arith_yylex.c cd.c echo.c error.c eval.c \ + exec.c expand.c \ histedit.c input.c jobs.c kill.c mail.c main.c memalloc.c miscbltin.c \ mystring.c options.c output.c parser.c printf.c redir.c show.c \ test.c trap.c var.c GENSRCS= builtins.c init.c nodes.c syntax.c GENHDRS= builtins.h nodes.h syntax.h token.h -SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS} y.tab.h +SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS} # MLINKS for Shell built in commands for which there are no userland # utilities of the same name are handled with the associated manpage, # builtin.1 in share/man/man1/. -DPADD= ${LIBL} ${LIBEDIT} ${LIBTERMCAP} -LDADD= -ll -ledit -ltermcap +DPADD= ${LIBEDIT} ${LIBTERMCAP} +LDADD= -ledit -ltermcap -LFLAGS= -8 # 8-bit lex scanner for arithmetic CFLAGS+=-DSHELL -I. -I${.CURDIR} # for debug: # DEBUG_FLAGS+= -g -DDEBUG=2 -fno-inline diff --git a/bin/sh/arith.h b/bin/sh/arith.h index 6a143a4..3a7db9e 100644 --- a/bin/sh/arith.h +++ b/bin/sh/arith.h @@ -34,8 +34,6 @@ #define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3) -extern const char *arith_buf, *arith_startbuf; - arith_t arith(const char *); void arith_lex_reset(void); int expcmd(int, char **); diff --git a/bin/sh/arith.y b/bin/sh/arith.y deleted file mode 100644 index 28046ba..0000000 --- a/bin/sh/arith.y +++ /dev/null @@ -1,366 +0,0 @@ -%{ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#ifndef lint -static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <limits.h> -#include <stdio.h> - -#include "arith.h" -#include "shell.h" -#include "var.h" -%} -%union { - arith_t l_value; - char* s_value; -} -%token <l_value> ARITH_NUM ARITH_LPAREN ARITH_RPAREN -%token <s_value> ARITH_VAR - -%type <l_value> expr -%right ARITH_ASSIGN -%right ARITH_ADDASSIGN ARITH_SUBASSIGN -%right ARITH_MULASSIGN ARITH_DIVASSIGN ARITH_REMASSIGN -%right ARITH_RSHASSIGN ARITH_LSHASSIGN -%right ARITH_BANDASSIGN ARITH_BXORASSIGN ARITH_BORASSIGN -%left ARITH_OR -%left ARITH_AND -%left ARITH_BOR -%left ARITH_BXOR -%left ARITH_BAND -%left ARITH_EQ ARITH_NE -%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE -%left ARITH_LSHIFT ARITH_RSHIFT -%left ARITH_ADD ARITH_SUB -%left ARITH_MUL ARITH_DIV ARITH_REM -%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT -%% - -exp: - expr - { - *YYPARSE_PARAM = $1; - return (0); - } - ; - -expr: - ARITH_LPAREN expr ARITH_RPAREN - { $$ = $2; } | - expr ARITH_OR expr - { $$ = $1 || $3; } | - expr ARITH_AND expr - { $$ = $1 && $3; } | - expr ARITH_BOR expr - { $$ = $1 | $3; } | - expr ARITH_BXOR expr - { $$ = $1 ^ $3; } | - expr ARITH_BAND expr - { $$ = $1 & $3; } | - expr ARITH_EQ expr - { $$ = $1 == $3; } | - expr ARITH_GT expr - { $$ = $1 > $3; } | - expr ARITH_GE expr - { $$ = $1 >= $3; } | - expr ARITH_LT expr - { $$ = $1 < $3; } | - expr ARITH_LE expr - { $$ = $1 <= $3; } | - expr ARITH_NE expr - { $$ = $1 != $3; } | - expr ARITH_LSHIFT expr - { $$ = $1 << $3; } | - expr ARITH_RSHIFT expr - { $$ = $1 >> $3; } | - expr ARITH_ADD expr - { $$ = $1 + $3; } | - expr ARITH_SUB expr - { $$ = $1 - $3; } | - expr ARITH_MUL expr - { $$ = $1 * $3; } | - expr ARITH_DIV expr - { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 / $3; - } | - expr ARITH_REM expr - { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 % $3; - } | - ARITH_NOT expr - { $$ = !($2); } | - ARITH_BNOT expr - { $$ = ~($2); } | - ARITH_SUB expr %prec ARITH_UNARYMINUS - { $$ = -($2); } | - ARITH_ADD expr %prec ARITH_UNARYPLUS - { $$ = $2; } | - ARITH_NUM | - ARITH_VAR - { - char *p; - arith_t arith_val; - char *str_val; - - if (lookupvar($1) == NULL) - setvarsafe($1, "0", 0); - str_val = lookupvar($1); - arith_val = strtoarith_t(str_val, &p, 0); - /* - * Conversion is successful only in case - * we've converted _all_ characters. - */ - if (*p != '\0') - yyerror("variable conversion error"); - $$ = arith_val; - } | - ARITH_VAR ARITH_ASSIGN expr - { - if (arith_assign($1, $3) != 0) - yyerror("variable assignment error"); - $$ = $3; - } | - ARITH_VAR ARITH_ADDASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) + $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_SUBASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) - $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_MULASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) * $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_DIVASSIGN expr - { - arith_t value; - - if ($3 == 0) - yyerror("division by zero"); - - value = atoarith_t(lookupvar($1)) / $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_REMASSIGN expr - { - arith_t value; - - if ($3 == 0) - yyerror("division by zero"); - - value = atoarith_t(lookupvar($1)) % $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_RSHASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) >> $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_LSHASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) << $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_BANDASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) & $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_BXORASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) ^ $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_BORASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) | $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } ; -%% -#include "error.h" -#include "output.h" -#include "memalloc.h" - -#define YYPARSE_PARAM_TYPE arith_t * -#define YYPARSE_PARAM result - -const char *arith_buf, *arith_startbuf; - -int yylex(void); -int yyparse(YYPARSE_PARAM_TYPE); - -static int -arith_assign(char *name, arith_t value) -{ - char *str; - int ret; - - str = (char *)ckmalloc(DIGITS(value)); - sprintf(str, ARITH_FORMAT_STR, value); - ret = setvarsafe(name, str, 0); - free(str); - return ret; -} - -arith_t -arith(const char *s) -{ - arith_t result; - struct stackmark smark; - - setstackmark(&smark); - arith_buf = arith_startbuf = s; - - INTOFF; - yyparse(&result); - arith_lex_reset(); /* Reprime lex. */ - INTON; - - popstackmark(&smark); - - return result; -} - -static void -yyerror(const char *s) -{ - - yyerrok; - yyclearin; - arith_lex_reset(); /* Reprime lex. */ - error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); -} - -/* - * The exp(1) builtin. - */ -int -expcmd(int argc, char **argv) -{ - const char *p; - char *concat; - char **ap; - arith_t i; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - /* - * Concatenate arguments. - */ - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - } else - p = ""; - - i = arith(p); - - out1fmt(ARITH_FORMAT_STR "\n", i); - return !i; -} - -/*************************/ -#ifdef TEST_ARITH -#include <stdio.h> -main(int argc, char *argv[]) -{ - printf("%d\n", exp(argv[1])); -} - -error(const char *s) -{ - fprintf(stderr, "exp: %s\n", s); - exit(1); -} -#endif diff --git a/bin/sh/arith_lex.l b/bin/sh/arith_lex.l deleted file mode 100644 index f93608a..0000000 --- a/bin/sh/arith_lex.l +++ /dev/null @@ -1,143 +0,0 @@ -%{ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#ifndef lint -static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <string.h> - -#include "arith.h" -#include "shell.h" -#include "y.tab.h" -#include "error.h" -#include "memalloc.h" -#include "var.h" - -int yylex(void); - -#undef YY_INPUT -#define YY_INPUT(buf,result,max) \ - do { \ - result = strnlen(arith_buf, max); \ - if (result == 0) \ - result = YY_NULL; \ - else { \ - memcpy(buf, arith_buf, result); \ - arith_buf += result; \ - } \ - } while (0); -#define YY_NO_UNPUT -#define YY_NO_INPUT -%} - -%% -[ \t\n] { ; } - -0x[a-fA-F0-9]+ { - yylval.l_value = strtoarith_t(yytext, NULL, 16); - return ARITH_NUM; - } - -0[0-7]* { - yylval.l_value = strtoarith_t(yytext, NULL, 8); - return ARITH_NUM; - } - -[1-9][0-9]* { - yylval.l_value = strtoarith_t(yytext, NULL, 10); - return ARITH_NUM; - } - -[A-Za-z][A-Za-z0-9_]* { - /* - * If variable doesn't exist, we should initialize - * it to zero. - */ - char *temp; - if (lookupvar(yytext) == NULL) - setvarsafe(yytext, "0", 0); - temp = stalloc(strlen(yytext) + 1); - yylval.s_value = strcpy(temp, yytext); - - return ARITH_VAR; - } - -"(" { return ARITH_LPAREN; } -")" { return ARITH_RPAREN; } -"||" { return ARITH_OR; } -"&&" { return ARITH_AND; } -"|" { return ARITH_BOR; } -"^" { return ARITH_BXOR; } -"&" { return ARITH_BAND; } -"==" { return ARITH_EQ; } -"!=" { return ARITH_NE; } -">" { return ARITH_GT; } -">=" { return ARITH_GE; } -"<" { return ARITH_LT; } -"<=" { return ARITH_LE; } -"<<" { return ARITH_LSHIFT; } -">>" { return ARITH_RSHIFT; } -"*" { return ARITH_MUL; } -"/" { return ARITH_DIV; } -"%" { return ARITH_REM; } -"+" { return ARITH_ADD; } -"-" { return ARITH_SUB; } -"~" { return ARITH_BNOT; } -"!" { return ARITH_NOT; } -"=" { return ARITH_ASSIGN; } -"+=" { return ARITH_ADDASSIGN; } -"-=" { return ARITH_SUBASSIGN; } -"*=" { return ARITH_MULASSIGN; } -"/=" { return ARITH_DIVASSIGN; } -"%=" { return ARITH_REMASSIGN; } -">>=" { return ARITH_RSHASSIGN; } -"<<=" { return ARITH_LSHASSIGN; } -"&=" { return ARITH_BANDASSIGN; } -"^=" { return ARITH_BXORASSIGN; } -"|=" { return ARITH_BORASSIGN; } -. { - error("arith: syntax error: \"%s\"\n", arith_startbuf); - } -%% - -void -arith_lex_reset(void) -{ - YY_NEW_FILE; -} diff --git a/bin/sh/arith_yacc.c b/bin/sh/arith_yacc.c new file mode 100644 index 0000000..423e4e2 --- /dev/null +++ b/bin/sh/arith_yacc.c @@ -0,0 +1,376 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/limits.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "arith.h" +#include "arith_yacc.h" +#include "expand.h" +#include "shell.h" +#include "error.h" +#include "memalloc.h" +#include "output.h" +#include "options.h" +#include "var.h" + +#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ +#error Arithmetic tokens are out of order. +#endif + +static const char *arith_startbuf; + +const char *arith_buf; +union yystype yylval; + +static int last_token; + +#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec + +static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = { + ARITH_PRECEDENCE(ARITH_MUL, 0), + ARITH_PRECEDENCE(ARITH_DIV, 0), + ARITH_PRECEDENCE(ARITH_REM, 0), + ARITH_PRECEDENCE(ARITH_ADD, 1), + ARITH_PRECEDENCE(ARITH_SUB, 1), + ARITH_PRECEDENCE(ARITH_LSHIFT, 2), + ARITH_PRECEDENCE(ARITH_RSHIFT, 2), + ARITH_PRECEDENCE(ARITH_LT, 3), + ARITH_PRECEDENCE(ARITH_LE, 3), + ARITH_PRECEDENCE(ARITH_GT, 3), + ARITH_PRECEDENCE(ARITH_GE, 3), + ARITH_PRECEDENCE(ARITH_EQ, 4), + ARITH_PRECEDENCE(ARITH_NE, 4), + ARITH_PRECEDENCE(ARITH_BAND, 5), + ARITH_PRECEDENCE(ARITH_BXOR, 6), + ARITH_PRECEDENCE(ARITH_BOR, 7), +}; + +#define ARITH_MAX_PREC 8 + +static __dead2 void yyerror(const char *s) +{ + error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); + /* NOTREACHED */ +} + +static arith_t arith_lookupvarint(char *varname) +{ + const char *str; + char *p; + arith_t result; + + str = lookupvar(varname); + if (str == NULL || *str == '\0') + str = "0"; + errno = 0; + result = strtoarith_t(str, &p, 0); + if (errno != 0 || *p != '\0') + yyerror("variable conversion error"); + return result; +} + +static inline int arith_prec(int op) +{ + return prec[op - ARITH_BINOP_MIN]; +} + +static inline int higher_prec(int op1, int op2) +{ + return arith_prec(op1) < arith_prec(op2); +} + +static arith_t do_binop(int op, arith_t a, arith_t b) +{ + + switch (op) { + default: + case ARITH_REM: + case ARITH_DIV: + if (!b) + yyerror("division by zero"); + return op == ARITH_REM ? a % b : a / b; + case ARITH_MUL: + return a * b; + case ARITH_ADD: + return a + b; + case ARITH_SUB: + return a - b; + case ARITH_LSHIFT: + return a << b; + case ARITH_RSHIFT: + return a >> b; + case ARITH_LT: + return a < b; + case ARITH_LE: + return a <= b; + case ARITH_GT: + return a > b; + case ARITH_GE: + return a >= b; + case ARITH_EQ: + return a == b; + case ARITH_NE: + return a != b; + case ARITH_BAND: + return a & b; + case ARITH_BXOR: + return a ^ b; + case ARITH_BOR: + return a | b; + } +} + +static arith_t assignment(int var, int noeval); + +static arith_t primary(int token, union yystype *val, int op, int noeval) +{ + arith_t result; + +again: + switch (token) { + case ARITH_LPAREN: + result = assignment(op, noeval); + if (last_token != ARITH_RPAREN) + yyerror("expecting ')'"); + last_token = yylex(); + return result; + case ARITH_NUM: + last_token = op; + return val->val; + case ARITH_VAR: + last_token = op; + return noeval ? val->val : arith_lookupvarint(val->name); + case ARITH_ADD: + token = op; + *val = yylval; + op = yylex(); + goto again; + case ARITH_SUB: + *val = yylval; + return -primary(op, val, yylex(), noeval); + case ARITH_NOT: + *val = yylval; + return !primary(op, val, yylex(), noeval); + case ARITH_BNOT: + *val = yylval; + return ~primary(op, val, yylex(), noeval); + default: + yyerror("expecting primary"); + } +} + +static arith_t binop2(arith_t a, int op, int prec, int noeval) +{ + for (;;) { + union yystype val; + arith_t b; + int op2; + int token; + + token = yylex(); + val = yylval; + + b = primary(token, &val, yylex(), noeval); + + op2 = last_token; + if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX && + higher_prec(op2, op)) { + b = binop2(b, op2, arith_prec(op), noeval); + op2 = last_token; + } + + a = noeval ? b : do_binop(op, a, b); + + if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX || + arith_prec(op2) >= prec) + return a; + + op = op2; + } +} + +static arith_t binop(int token, union yystype *val, int op, int noeval) +{ + arith_t a = primary(token, val, op, noeval); + + op = last_token; + if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX) + return a; + + return binop2(a, op, ARITH_MAX_PREC, noeval); +} + +static arith_t and(int token, union yystype *val, int op, int noeval) +{ + arith_t a = binop(token, val, op, noeval); + arith_t b; + + op = last_token; + if (op != ARITH_AND) + return a; + + token = yylex(); + *val = yylval; + + b = and(token, val, yylex(), noeval | !a); + + return a && b; +} + +static arith_t or(int token, union yystype *val, int op, int noeval) +{ + arith_t a = and(token, val, op, noeval); + arith_t b; + + op = last_token; + if (op != ARITH_OR) + return a; + + token = yylex(); + *val = yylval; + + b = or(token, val, yylex(), noeval | !!a); + + return a || b; +} + +static arith_t cond(int token, union yystype *val, int op, int noeval) +{ + arith_t a = or(token, val, op, noeval); + arith_t b; + arith_t c; + + if (last_token != ARITH_QMARK) + return a; + + b = assignment(yylex(), noeval | !a); + + if (last_token != ARITH_COLON) + yyerror("expecting ':'"); + + token = yylex(); + *val = yylval; + + c = cond(token, val, yylex(), noeval | !!a); + + return a ? b : c; +} + +static arith_t assignment(int var, int noeval) +{ + union yystype val = yylval; + int op = yylex(); + arith_t result; + char sresult[DIGITS(result) + 1]; + + if (var != ARITH_VAR) + return cond(var, &val, op, noeval); + + if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX)) + return cond(var, &val, op, noeval); + + result = assignment(yylex(), noeval); + if (noeval) + return result; + + if (op != ARITH_ASS) + result = do_binop(op - 11, arith_lookupvarint(val.name), result); + snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result); + setvar(val.name, sresult, 0); + return result; +} + +arith_t arith(const char *s) +{ + struct stackmark smark; + arith_t result; + + setstackmark(&smark); + + arith_buf = arith_startbuf = s; + + result = assignment(yylex(), 0); + + if (last_token) + yyerror("expecting EOF"); + + popstackmark(&smark); + + return result; +} + +/* + * The exp(1) builtin. + */ +int +expcmd(int argc, char **argv) +{ + const char *p; + char *concat; + char **ap; + arith_t i; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + /* + * Concatenate arguments. + */ + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + } else + p = ""; + + i = arith(p); + + out1fmt(ARITH_FORMAT_STR "\n", i); + return !i; +} + diff --git a/bin/sh/arith_yacc.h b/bin/sh/arith_yacc.h new file mode 100644 index 0000000..5d8354f --- /dev/null +++ b/bin/sh/arith_yacc.h @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define ARITH_ASS 1 + +#define ARITH_OR 2 +#define ARITH_AND 3 +#define ARITH_BAD 4 +#define ARITH_NUM 5 +#define ARITH_VAR 6 +#define ARITH_NOT 7 + +#define ARITH_BINOP_MIN 8 +#define ARITH_LE 8 +#define ARITH_GE 9 +#define ARITH_LT 10 +#define ARITH_GT 11 +#define ARITH_EQ 12 +#define ARITH_REM 13 +#define ARITH_BAND 14 +#define ARITH_LSHIFT 15 +#define ARITH_RSHIFT 16 +#define ARITH_MUL 17 +#define ARITH_ADD 18 +#define ARITH_BOR 19 +#define ARITH_SUB 20 +#define ARITH_BXOR 21 +#define ARITH_DIV 22 +#define ARITH_NE 23 +#define ARITH_BINOP_MAX 24 + +#define ARITH_ASS_MIN 24 +#define ARITH_REMASS 24 +#define ARITH_BANDASS 25 +#define ARITH_LSHIFTASS 26 +#define ARITH_RSHIFTASS 27 +#define ARITH_MULASS 28 +#define ARITH_ADDASS 29 +#define ARITH_BORASS 30 +#define ARITH_SUBASS 31 +#define ARITH_BXORASS 32 +#define ARITH_DIVASS 33 +#define ARITH_ASS_MAX 34 + +#define ARITH_LPAREN 34 +#define ARITH_RPAREN 35 +#define ARITH_BNOT 36 +#define ARITH_QMARK 37 +#define ARITH_COLON 38 + +union yystype { + arith_t val; + char *name; +}; + +extern union yystype yylval; + +int yylex(void); diff --git a/bin/sh/arith_yylex.c b/bin/sh/arith_yylex.c new file mode 100644 index 0000000..0d08a83 --- /dev/null +++ b/bin/sh/arith_yylex.c @@ -0,0 +1,244 @@ +/*- + * Copyright (c) 2002 + * Herbert Xu. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include "shell.h" +#include "arith_yacc.h" +#include "expand.h" +#include "error.h" +#include "memalloc.h" +#include "parser.h" +#include "syntax.h" + +#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ +#error Arithmetic tokens are out of order. +#endif + +extern const char *arith_buf; + +int +yylex() +{ + int value; + const char *buf = arith_buf; + const char *p; + + for (;;) { + value = *buf; + switch (value) { + case ' ': + case '\t': + case '\n': + buf++; + continue; + default: + return ARITH_BAD; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + yylval.val = strtoarith_t(buf, (char **)&arith_buf, 0); + return ARITH_NUM; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + p = buf; + while (buf++, is_in_name(*buf)) + ; + yylval.name = stalloc(buf - p + 1); + memcpy(yylval.name, p, buf - p); + yylval.name[buf - p] = '\0'; + value = ARITH_VAR; + goto out; + case '=': + value += ARITH_ASS - '='; +checkeq: + buf++; +checkeqcur: + if (*buf != '=') + goto out; + value += 11; + break; + case '>': + switch (*++buf) { + case '=': + value += ARITH_GE - '>'; + break; + case '>': + value += ARITH_RSHIFT - '>'; + goto checkeq; + default: + value += ARITH_GT - '>'; + goto out; + } + break; + case '<': + switch (*++buf) { + case '=': + value += ARITH_LE - '<'; + break; + case '<': + value += ARITH_LSHIFT - '<'; + goto checkeq; + default: + value += ARITH_LT - '<'; + goto out; + } + break; + case '|': + if (*++buf != '|') { + value += ARITH_BOR - '|'; + goto checkeqcur; + } + value += ARITH_OR - '|'; + break; + case '&': + if (*++buf != '&') { + value += ARITH_BAND - '&'; + goto checkeqcur; + } + value += ARITH_AND - '&'; + break; + case '!': + if (*++buf != '=') { + value += ARITH_NOT - '!'; + goto out; + } + value += ARITH_NE - '!'; + break; + case 0: + goto out; + case '(': + value += ARITH_LPAREN - '('; + break; + case ')': + value += ARITH_RPAREN - ')'; + break; + case '*': + value += ARITH_MUL - '*'; + goto checkeq; + case '/': + value += ARITH_DIV - '/'; + goto checkeq; + case '%': + value += ARITH_REM - '%'; + goto checkeq; + case '+': + value += ARITH_ADD - '+'; + goto checkeq; + case '-': + value += ARITH_SUB - '-'; + goto checkeq; + case '~': + value += ARITH_BNOT - '~'; + break; + case '^': + value += ARITH_BXOR - '^'; + goto checkeq; + case '?': + value += ARITH_QMARK - '?'; + break; + case ':': + value += ARITH_COLON - ':'; + break; + } + break; + } + + buf++; +out: + arith_buf = buf; + return value; +} |