Date: Tue, 8 Feb 2011 23:18:07 +0000 (UTC) From: Jilles Tjoelker <jilles@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r218466 - head/bin/sh Message-ID: <201102082318.p18NI72i063357@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jilles Date: Tue Feb 8 23:18:06 2011 New Revision: 218466 URL: http://svn.freebsd.org/changeset/base/218466 Log: 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 Added: head/bin/sh/arith_yacc.c (contents, props changed) head/bin/sh/arith_yacc.h (contents, props changed) head/bin/sh/arith_yylex.c (contents, props changed) Deleted: head/bin/sh/arith.y head/bin/sh/arith_lex.l Modified: head/bin/sh/Makefile head/bin/sh/arith.h Modified: head/bin/sh/Makefile ============================================================================== --- head/bin/sh/Makefile Tue Feb 8 23:16:19 2011 (r218465) +++ head/bin/sh/Makefile Tue Feb 8 23:18:06 2011 (r218466) @@ -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 Modified: head/bin/sh/arith.h ============================================================================== --- head/bin/sh/arith.h Tue Feb 8 23:16:19 2011 (r218465) +++ head/bin/sh/arith.h Tue Feb 8 23:18:06 2011 (r218466) @@ -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 **); Added: head/bin/sh/arith_yacc.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/bin/sh/arith_yacc.c Tue Feb 8 23:18:06 2011 (r218466) @@ -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; +} + Added: head/bin/sh/arith_yacc.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/bin/sh/arith_yacc.h Tue Feb 8 23:18:06 2011 (r218466) @@ -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); Added: head/bin/sh/arith_yylex.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/bin/sh/arith_yylex.c Tue Feb 8 23:18:06 2011 (r218466) @@ -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; +}
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201102082318.p18NI72i063357>