From f6e3b2f8a59922405f42c8bc283e0f5546c25d0e Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 11 Oct 2007 22:36:28 +0800 Subject: [ARITH] Add assignment and intmax_t support This patch adds assignment operator support in arithmetic expansions. It also changes the type used to intmax_t. --- ChangeLog | 4 + src/Makefile.am | 8 +- src/arith.y | 155 ---------------------------- src/arith_lex.l | 83 --------------- src/arith_yacc.c | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/arith_yacc.h | 89 ++++++++++++++++ src/arith_yylex.c | 114 +++++++++++++++++---- src/expand.c | 23 +++-- src/expand.h | 4 +- src/mystring.c | 9 +- src/mystring.h | 1 + src/shell.h | 10 ++ src/var.c | 20 ++++ src/var.h | 4 + 14 files changed, 549 insertions(+), 273 deletions(-) delete mode 100644 src/arith.y delete mode 100644 src/arith_lex.l create mode 100644 src/arith_yacc.c create mode 100644 src/arith_yacc.h diff --git a/ChangeLog b/ChangeLog index 69ba464..895c607 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2007-10-11 Herbert Xu + + * Add assignment support in arithmetic expansions. + 2007-10-08 Herbert Xu * Report substition errors at expansion time. diff --git a/src/Makefile.am b/src/Makefile.am index 37d6d3c..49026a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,12 +18,12 @@ COMPILE_FOR_BUILD = \ bin_PROGRAMS = dash dash_CFILES = \ - alias.c arith_yylex.c cd.c error.c eval.c exec.c expand.c \ + alias.c arith_yacc.c arith_yylex.c cd.c error.c eval.c exec.c expand.c \ histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \ mystring.c options.c parser.c redir.c show.c trap.c output.c \ bltin/printf.c system.c bltin/test.c bltin/times.c var.c dash_SOURCES = \ - $(dash_CFILES) arith.y \ + $(dash_CFILES) \ alias.h bltin/bltin.h cd.h error.h eval.h exec.h expand.h hetio.h \ init.h input.h jobs.h machdep.h mail.h main.h memalloc.h miscbltin.h \ myhistedit.h mystring.h options.h output.h parser.h redir.h shell.h \ @@ -32,10 +32,10 @@ dash_LDADD = builtins.o init.o nodes.o signames.o syntax.o HELPERS = mkinit mksyntax mknodes mksignames -BUILT_SOURCES = arith.h builtins.h nodes.h syntax.h token.h +BUILT_SOURCES = builtins.h nodes.h syntax.h token.h CLEANFILES = \ $(BUILT_SOURCES) $(patsubst %.o,%.c,$(dash_LDADD)) \ - arith.c $(HELPERS) builtins.def + $(HELPERS) builtins.def man_MANS = dash.1 EXTRA_DIST = \ diff --git a/src/arith.y b/src/arith.y deleted file mode 100644 index 07b0b39..0000000 --- a/src/arith.y +++ /dev/null @@ -1,155 +0,0 @@ -%{ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1997-2005 - * Herbert Xu . 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 -#include "expand.h" -#include "shell.h" -#include "error.h" -#include "output.h" -#include "memalloc.h" - -const char *arith_buf, *arith_startbuf; - -#ifndef YYBISON -int yyparse(void); -#endif -void yyerror(const char *); -#ifdef TESTARITH -int main(int , char *[]); -int sh_error(char *); -#endif - -%} -%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN - -%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 { - return ($1); - } - ; - - -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 - ; -%% -int -arith(s) - const char *s; -{ - long result; - - arith_buf = arith_startbuf = s; - - INTOFF; - result = yyparse(); - arith_lex_reset(); /* reprime lex */ - INTON; - - return (result); -} - - -/*************************/ -#ifdef TEST_ARITH -#include -main(argc, argv) - char *argv[]; -{ - printf("%d\n", exp(argv[1])); -} -sh_error(s) - char *s; -{ - fprintf(stderr, "exp: %s\n", s); - exit(1); -} -#endif - -void -yyerror(s) - const char *s; -{ - -#ifndef YYBISON - yyerrok; -#endif - yyclearin; - arith_lex_reset(); /* reprime lex */ - sh_error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); - /* NOTREACHED */ -} diff --git a/src/arith_lex.l b/src/arith_lex.l deleted file mode 100644 index 85e170c..0000000 --- a/src/arith_lex.l +++ /dev/null @@ -1,83 +0,0 @@ -%{ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1997-2005 - * Herbert Xu . 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 -#include -#include "arith.h" -#include "error.h" -#include "expand.h" - -extern int yylval; -extern const char *arith_buf, *arith_startbuf; -#undef YY_INPUT -#define YY_INPUT(buf,result,max) \ - result = (*buf = *arith_buf++) ? 1 : YY_NULL; -#define YY_NO_UNPUT -%} - -%% -[ \t\n] { ; } -[0-9]+ { yylval = strtoll(yytext, 0, 0); return(ARITH_NUM); } -"(" { 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); } -. { error("arith: syntax error: \"%s\"", arith_startbuf); } -%% - -void -arith_lex_reset() { -#ifdef YY_NEW_FILE - YY_NEW_FILE; -#endif -} diff --git a/src/arith_yacc.c b/src/arith_yacc.c new file mode 100644 index 0000000..ad653ed --- /dev/null +++ b/src/arith_yacc.c @@ -0,0 +1,298 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu . 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 +#include +#include +#include "arith_yacc.h" +#include "expand.h" +#include "shell.h" +#include "error.h" +#include "output.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), +}; + +static void yyerror(const char *s) __attribute__ ((noreturn)); +static void yyerror(const char *s) +{ + sh_error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); + /* NOTREACHED */ +} + +static inline int higher_prec(int op1, int op2) +{ + return prec[op1 - ARITH_BINOP_MIN] < prec[op2 - ARITH_BINOP_MIN]; +} + +static intmax_t do_binop(int op, intmax_t a, intmax_t b) +{ + imaxdiv_t div; + + switch (op) { + default: + case ARITH_REM: + case ARITH_DIV: + if (!b) + yyerror("division by zero"); + div = imaxdiv(a, b); + return op == ARITH_REM ? div.rem : div.quot; + 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 intmax_t assignment(int var, int noeval); + +static intmax_t primary(int token, union yystype *val, int op, int noeval) +{ + intmax_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 : 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 intmax_t binop2(intmax_t a, int op, int noeval) +{ + for (;;) { + union yystype val; + intmax_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) + return noeval ? b : do_binop(op, a, b); + + if (higher_prec(op2, op)) { + b = binop2(b, op2, noeval); + return noeval ? b : do_binop(op, a, b); + } + + a = do_binop(op, a, b); + op = op2; + } +} + +static intmax_t binop(int token, union yystype *val, int op, int noeval) +{ + intmax_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, noeval); +} + +static intmax_t and(int token, union yystype *val, int op, int noeval) +{ + intmax_t a = binop(token, val, op, noeval); + intmax_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 intmax_t or(int token, union yystype *val, int op, int noeval) +{ + intmax_t a = and(token, val, op, noeval); + intmax_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 intmax_t cond(int token, union yystype *val, int op, int noeval) +{ + intmax_t a = or(token, val, op, noeval); + intmax_t b; + intmax_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 intmax_t assignment(int var, int noeval) +{ + union yystype val = yylval; + int op = yylex(); + intmax_t result; + + 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; + + return setvarint(val.name, + op == ARITH_ASS ? result : + do_binop(op - 11, lookupvarint(val.name), result)); +} + +intmax_t arith(const char *s) +{ + intmax_t result; + + arith_buf = arith_startbuf = s; + + result = assignment(yylex(), 0); + + if (last_token) + yyerror("expecting EOF"); + + return result; +} diff --git a/src/arith_yacc.h b/src/arith_yacc.h new file mode 100644 index 0000000..ff34d52 --- /dev/null +++ b/src/arith_yacc.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu . 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. + */ + +#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 { + intmax_t val; + char *name; +}; + +extern union yystype yylval; + +int yylex(void); diff --git a/src/arith_yylex.c b/src/arith_yylex.c index 4fa2051..0f46990 100644 --- a/src/arith_yylex.c +++ b/src/arith_yylex.c @@ -32,19 +32,28 @@ * SUCH DAMAGE. */ +#include #include -#include "arith.h" +#include +#include "arith_yacc.h" #include "expand.h" #include "error.h" +#include "shell.h" +#include "memalloc.h" +#include "syntax.h" -extern int yylval; -extern const char *arith_buf, *arith_startbuf; +#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 (;;) { switch (*buf) { @@ -54,9 +63,7 @@ yylex() buf++; continue; default: -err: - sh_error("arith: syntax error: \"%s\"", arith_startbuf); - /* NOTREACHED */ + return ARITH_BAD; case '0': case '1': case '2': @@ -67,13 +74,74 @@ err: case '7': case '8': case '9': - yylval = strtoll(buf, (char **) &arith_buf, 0); + yylval.val = strtoimax(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); + *(char *)mempcpy(yylval.name, p, buf - p) = 0; + value = ARITH_VAR; + goto out; case '=': - if (*++buf != '=') { - goto err; - } - value = ARITH_EQ; + value = ARITH_ASS; +checkeq: + if (*++buf != '=') + goto out; + value += 11; break; case '>': switch (*++buf) { @@ -82,7 +150,7 @@ err: break; case '>': value = ARITH_RSHIFT; - break; + goto checkeq; default: value = ARITH_GT; goto out; @@ -95,7 +163,7 @@ err: break; case '<': value = ARITH_LSHIFT; - break; + goto checkeq; default: value = ARITH_LT; goto out; @@ -104,14 +172,14 @@ err: case '|': if (*++buf != '|') { value = ARITH_BOR; - goto out; + goto checkeq; } value = ARITH_OR; break; case '&': if (*++buf != '&') { value = ARITH_BAND; - goto out; + goto checkeq; } value = ARITH_AND; break; @@ -133,24 +201,30 @@ err: break; case '*': value = ARITH_MUL; - break; + goto checkeq; case '/': value = ARITH_DIV; - break; + goto checkeq; case '%': value = ARITH_REM; - break; + goto checkeq; case '+': value = ARITH_ADD; - break; + goto checkeq; case '-': value = ARITH_SUB; - break; + goto checkeq; case '~': value = ARITH_BNOT; break; case '^': value = ARITH_BXOR; + goto checkeq; + case '?': + value = ARITH_QMARK; + break; + case ':': + value = ARITH_COLON; break; } break; diff --git a/src/expand.c b/src/expand.c index 54fe908..9cb8eab 100644 --- a/src/expand.c +++ b/src/expand.c @@ -42,6 +42,7 @@ #endif #include #include +#include #include #include #if defined(__GLIBC__) @@ -142,7 +143,7 @@ STATIC int pmatch(const char *, const char *); #else #define pmatch(a, b) !fnmatch((a), (b), 0) #endif -STATIC int cvtnum(long); +STATIC int cvtnum(intmax_t); STATIC size_t esclen(const char *, const char *); STATIC char *scanleft(char *, char *, char *, char *, int, int); STATIC char *scanright(char *, char *, char *, char *, int, int); @@ -478,9 +479,11 @@ removerecordregions(int endoff) void expari(int flag) { + struct stackmark sm; char *p, *start; int begoff; int len; + intmax_t result; /* ifsfree(); */ @@ -490,8 +493,9 @@ expari(int flag) * start of arithmetic. */ start = stackblock(); - p = expdest - 1; - *p = '\0'; + p = expdest; + pushstackmark(&sm, p - start); + *--p = '\0'; p--; do { int esc; @@ -522,7 +526,10 @@ expari(int flag) if (flag & QUOTES_ESC) rmescapes(p + 1); - len = cvtnum(arith(p + 1)); + result = arith(p + 1); + popstackmark(&sm); + + len = cvtnum(result); if (!(flag & EXP_QUOTED)) recordregion(begoff, begoff + len, 0); @@ -1707,12 +1714,12 @@ casematch(union node *pattern, char *val) */ STATIC int -cvtnum(long num) +cvtnum(intmax_t num) { - int len; + int len = max_int_length(sizeof(num)); - expdest = makestrspace(32, expdest); - len = fmtstr(expdest, 32, "%ld", num); + expdest = makestrspace(len, expdest); + len = fmtstr(expdest, len, "%jd", num); STADJUST(len, expdest); return len; } diff --git a/src/expand.h b/src/expand.h index 4dfbc43..225b004 100644 --- a/src/expand.h +++ b/src/expand.h @@ -34,6 +34,8 @@ * @(#)expand.h 8.2 (Berkeley) 5/4/95 */ +#include + struct strlist { struct strlist *next; char *text; @@ -68,7 +70,7 @@ char *_rmescapes(char *, int); int casematch(union node *, char *); /* From arith.y */ -int arith(const char *); +intmax_t arith(const char *); int expcmd(int , char **); #ifdef USE_LEX void arith_lex_reset(void); diff --git a/src/mystring.c b/src/mystring.c index df1691b..b84b7e2 100644 --- a/src/mystring.c +++ b/src/mystring.c @@ -112,13 +112,13 @@ prefix(const char *string, const char *pfx) /* * Convert a string into an integer of type intmax_t. Alow trailing spaces. */ -intmax_t atomax10(const char *s) +intmax_t atomax(const char *s, int base) { char *p; intmax_t r; errno = 0; - r = strtoimax(s, &p, 10); + r = strtoimax(s, &p, base); if (errno != 0) sh_error(illnum, s); @@ -132,6 +132,11 @@ intmax_t atomax10(const char *s) return r; } +intmax_t atomax10(const char *s) +{ + return atomax(s, 10); +} + /* * Convert a string of digits to an integer, printing an error message on * failure. diff --git a/src/mystring.h b/src/mystring.h index c9cade6..477cd16 100644 --- a/src/mystring.h +++ b/src/mystring.h @@ -48,6 +48,7 @@ extern const char homestr[]; void scopyn(const char *, char *, int); #endif char *prefix(const char *, const char *); +intmax_t atomax(const char *, int); intmax_t atomax10(const char *); int number(const char *); int is_number(const char *); diff --git a/src/shell.h b/src/shell.h index 9b67696..98edc8b 100644 --- a/src/shell.h +++ b/src/shell.h @@ -92,3 +92,13 @@ extern char nullstr[1]; /* null string */ #define likely(x) __builtin_expect(!!(x),1) #define unlikely(x) __builtin_expect(!!(x),0) + +/* + * Hack to calculate maximum length. + * (length * 8 - 1) * log10(2) + 1 + 1 + 12 + * The second 1 is for the minus sign and the 12 is a safety margin. + */ +static inline int max_int_length(int bytes) +{ + return (bytes * 8 - 1) * 0.30102999566398119521 + 14; +} diff --git a/src/var.c b/src/var.c index 501a279..17d3637 100644 --- a/src/var.c +++ b/src/var.c @@ -202,6 +202,21 @@ setvar(const char *name, const char *val, int flags) INTON; } +/* + * Set the given integer as the value of a variable. The flags argument is + * ored with the flags of the variable. + */ + +intmax_t setvarint(const char *name, intmax_t val) +{ + int len = max_int_length(sizeof(val)); + char buf[len]; + + fmtstr(buf, len, "%jd", val); + setvar(name, buf, 0); + return val; +} + /* @@ -293,6 +308,11 @@ lookupvar(const char *name) return NULL; } +intmax_t lookupvarint(const char *name) +{ + return atomax(lookupvar(name) ?: nullstr, 0); +} + /* diff --git a/src/var.h b/src/var.h index ae58c6c..66443df 100644 --- a/src/var.h +++ b/src/var.h @@ -34,6 +34,8 @@ * @(#)var.h 8.2 (Berkeley) 5/4/95 */ +#include + /* * Shell variables. */ @@ -125,10 +127,12 @@ extern const char defpathvar[]; void initvar(void); void setvar(const char *, const char *, int); +intmax_t setvarint(const char *, intmax_t); void setvareq(char *, int); struct strlist; void listsetvar(struct strlist *, int); char *lookupvar(const char *); +intmax_t lookupvarint(const char *); char *bltinlookup(const char *); char **listvars(int, int, char ***); #define environment() listvars(VEXPORT, VUNSET, 0) -- cgit 1.4.1