[BACK]Return to expr.y CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / bin / expr

Annotation of src/bin/expr/expr.y, Revision 1.37

1.37    ! joerg       1: /* $NetBSD: expr.y,v 1.36 2009/01/20 14:22:37 joerg Exp $ */
1.17      jdolecek    2:
                      3: /*_
                      4:  * Copyright (c) 2000 The NetBSD Foundation, Inc.
                      5:  * All rights reserved.
1.2       cgd         6:  *
1.17      jdolecek    7:  * This code is derived from software contributed to The NetBSD Foundation
1.29      grant       8:  * by Jaromir Dolecek <jdolecek@NetBSD.org> and J.T. Conklin <jtc@NetBSD.org>.
1.11      jtc         9:  *
1.17      jdolecek   10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
1.34      martin     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     21:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     22:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     23:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     24:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     25:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     26:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     27:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     28:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     29:  * POSSIBILITY OF SUCH DAMAGE.
1.1       cgd        30:  */
1.12      jtc        31:
1.17      jdolecek   32: %{
                     33: #include <sys/cdefs.h>
                     34: #ifndef lint
1.37    ! joerg      35: __RCSID("$NetBSD: expr.y,v 1.36 2009/01/20 14:22:37 joerg Exp $");
1.17      jdolecek   36: #endif /* not lint */
                     37:
                     38: #include <sys/types.h>
1.28      wiz        39:
1.17      jdolecek   40: #include <err.h>
                     41: #include <errno.h>
                     42: #include <limits.h>
                     43: #include <locale.h>
                     44: #include <regex.h>
                     45: #include <stdarg.h>
1.1       cgd        46: #include <stdio.h>
1.4       cgd        47: #include <stdlib.h>
                     48: #include <string.h>
1.1       cgd        49:
1.23      jdolecek   50: static const char * const *av;
1.1       cgd        51:
1.37    ! joerg      52: static void yyerror(const char *, ...) __dead;
1.17      jdolecek   53: static int yylex(void);
                     54: static int is_zero_or_null(const char *);
                     55: static int is_integer(const char *);
1.26      jmc        56: static int64_t perform_arith_op(const char *, const char *, const char *);
                     57:
1.23      jdolecek   58: int main(int, const char * const *);
1.1       cgd        59:
1.17      jdolecek   60: #define YYSTYPE        const char *
1.1       cgd        61:
1.17      jdolecek   62: %}
1.20      jdolecek   63: %token STRING
1.22      thorpej    64: %left SPEC_OR
                     65: %left SPEC_AND
1.26      jmc        66: %left COMPARE
                     67: %left ADD_SUB_OPERATOR
                     68: %left MUL_DIV_MOD_OPERATOR
                     69: %left SPEC_REG
1.31      jdolecek   70: %left LENGTH
1.17      jdolecek   71: %left LEFT_PARENT RIGHT_PARENT
1.1       cgd        72:
                     73: %%
                     74:
1.17      jdolecek   75: exp:   expr = {
                     76:                (void) printf("%s\n", $1);
                     77:                return (is_zero_or_null($1));
                     78:                }
1.1       cgd        79:        ;
                     80:
1.23      jdolecek   81: expr:  item { $$ = $1; }
1.17      jdolecek   82:        | expr SPEC_OR expr = {
                     83:                /*
                     84:                 * Return evaluation of first expression if it is neither
                     85:                 * an empty string nor zero; otherwise, returns the evaluation
                     86:                 * of second expression.
                     87:                 */
                     88:                if (!is_zero_or_null($1))
                     89:                        $$ = $1;
                     90:                else
                     91:                        $$ = $3;
                     92:                }
                     93:        | expr SPEC_AND expr = {
                     94:                /*
                     95:                 * Returns the evaluation of first expr if neither expression
                     96:                 * evaluates to an empty string or zero; otherwise, returns
                     97:                 * zero.
                     98:                 */
                     99:                if (!is_zero_or_null($1) && !is_zero_or_null($3))
                    100:                        $$ = $1;
                    101:                else
                    102:                        $$ = "0";
                    103:                }
                    104:        | expr SPEC_REG expr = {
                    105:                /*
                    106:                 * The ``:'' operator matches first expr against the second,
                    107:                 * which must be a regular expression.
                    108:                 */
                    109:                regex_t rp;
                    110:                regmatch_t rm[2];
                    111:                int eval;
                    112:
                    113:                /* compile regular expression */
1.30      jdolecek  114:                if ((eval = regcomp(&rp, $3, REG_BASIC)) != 0) {
1.18      jdolecek  115:                        char errbuf[256];
1.17      jdolecek  116:                        (void)regerror(eval, &rp, errbuf, sizeof(errbuf));
                    117:                        yyerror("%s", errbuf);
                    118:                        /* NOT REACHED */
1.1       cgd       119:                }
                    120:
1.17      jdolecek  121:                /* compare string against pattern --  remember that patterns
                    122:                   are anchored to the beginning of the line */
                    123:                if (regexec(&rp, $1, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
                    124:                        char *val;
                    125:                        if (rm[1].rm_so >= 0) {
1.19      jdolecek  126:                                (void) asprintf(&val, "%.*s",
1.18      jdolecek  127:                                        (int) (rm[1].rm_eo - rm[1].rm_so),
                    128:                                        $1 + rm[1].rm_so);
1.17      jdolecek  129:                        } else {
                    130:                                (void) asprintf(&val, "%d",
                    131:                                        (int)(rm[0].rm_eo - rm[0].rm_so));
                    132:                        }
1.33      rumble    133:                        if (val == NULL)
                    134:                                err(1, NULL);
1.17      jdolecek  135:                        $$ = val;
                    136:                } else {
                    137:                        if (rp.re_nsub == 0) {
                    138:                                $$ = "0";
                    139:                        } else {
                    140:                                $$ = "";
                    141:                        }
                    142:                }
1.1       cgd       143:
1.17      jdolecek  144:                }
1.26      jmc       145:        | expr ADD_SUB_OPERATOR expr = {
                    146:                /* Returns the results of addition, subtraction */
                    147:                char *val;
                    148:                int64_t res;
                    149:
                    150:                res = perform_arith_op($1, $2, $3);
                    151:                (void) asprintf(&val, "%lld", (long long int) res);
1.33      rumble    152:                if (val == NULL)
                    153:                        err(1, NULL);
1.26      jmc       154:                $$ = val;
                    155:                 }
                    156:
                    157:        | expr MUL_DIV_MOD_OPERATOR expr = {
                    158:                /*
                    159:                 * Returns the results of multiply, divide or remainder of
                    160:                 * numeric-valued arguments.
1.17      jdolecek  161:                 */
                    162:                char *val;
1.26      jmc       163:                int64_t res;
1.4       cgd       164:
1.26      jmc       165:                res = perform_arith_op($1, $2, $3);
1.17      jdolecek  166:                (void) asprintf(&val, "%lld", (long long int) res);
1.33      rumble    167:                if (val == NULL)
                    168:                        err(1, NULL);
1.17      jdolecek  169:                $$ = val;
1.1       cgd       170:
1.17      jdolecek  171:                }
                    172:        | expr COMPARE expr = {
                    173:                /*
                    174:                 * Returns the results of integer comparison if both arguments
                    175:                 * are integers; otherwise, returns the results of string
                    176:                 * comparison using the locale-specific collation sequence.
                    177:                 * The result of each comparison is 1 if the specified relation
                    178:                 * is true, or 0 if the relation is false.
                    179:                 */
                    180:
                    181:                int64_t l, r;
                    182:                int res;
                    183:
1.32      lukem     184:                res = 0;
                    185:
1.17      jdolecek  186:                /*
                    187:                 * Slight hack to avoid differences in the compare code
                    188:                 * between string and numeric compare.
                    189:                 */
                    190:                if (is_integer($1) && is_integer($3)) {
                    191:                        /* numeric comparison */
                    192:                        l = strtoll($1, NULL, 10);
                    193:                        r = strtoll($3, NULL, 10);
                    194:                } else {
                    195:                        /* string comparison */
                    196:                        l = strcoll($1, $3);
                    197:                        r = 0;
                    198:                }
1.4       cgd       199:
1.17      jdolecek  200:                switch($2[0]) {
                    201:                case '=': /* equal */
                    202:                        res = (l == r);
                    203:                        break;
                    204:                case '>': /* greater or greater-equal */
                    205:                        if ($2[1] == '=')
                    206:                                res = (l >= r);
                    207:                        else
                    208:                                res = (l > r);
                    209:                        break;
                    210:                case '<': /* lower or lower-equal */
                    211:                        if ($2[1] == '=')
                    212:                                res = (l <= r);
                    213:                        else
                    214:                                res = (l < r);
                    215:                        break;
                    216:                case '!': /* not equal */
                    217:                        /* the check if this is != was done in yylex() */
                    218:                        res = (l != r);
                    219:                }
1.1       cgd       220:
1.17      jdolecek  221:                $$ = (res) ? "1" : "0";
1.4       cgd       222:
1.17      jdolecek  223:                }
                    224:        | LEFT_PARENT expr RIGHT_PARENT { $$ = $2; }
1.31      jdolecek  225:        | LENGTH expr {
                    226:                /*
                    227:                 * Return length of 'expr' in bytes.
                    228:                 */
                    229:                char *ln;
                    230:
                    231:                asprintf(&ln, "%ld", (long) strlen($2));
1.33      rumble    232:                if (ln == NULL)
                    233:                        err(1, NULL);
1.31      jdolecek  234:                $$ = ln;
                    235:                }
1.17      jdolecek  236:        ;
1.4       cgd       237:
1.17      jdolecek  238: item:  STRING
1.26      jmc       239:        | ADD_SUB_OPERATOR
                    240:        | MUL_DIV_MOD_OPERATOR
1.17      jdolecek  241:        | COMPARE
                    242:        | SPEC_OR
                    243:        | SPEC_AND
                    244:        | SPEC_REG
1.31      jdolecek  245:        | LENGTH
1.17      jdolecek  246:        ;
                    247: %%
1.1       cgd       248:
1.17      jdolecek  249: /*
                    250:  * Returns 1 if the string is empty or contains only numeric zero.
                    251:  */
                    252: static int
                    253: is_zero_or_null(const char *str)
1.1       cgd       254: {
1.17      jdolecek  255:        char *endptr;
1.4       cgd       256:
1.17      jdolecek  257:        return str[0] == '\0'
                    258:                || ( strtoll(str, &endptr, 10) == 0LL
                    259:                        && endptr[0] == '\0');
1.1       cgd       260: }
                    261:
1.17      jdolecek  262: /*
                    263:  * Returns 1 if the string is an integer.
                    264:  */
                    265: static int
                    266: is_integer(const char *str)
1.1       cgd       267: {
1.17      jdolecek  268:        char *endptr;
1.4       cgd       269:
1.17      jdolecek  270:        (void) strtoll(str, &endptr, 10);
                    271:        /* note we treat empty string as valid number */
                    272:        return (endptr[0] == '\0');
1.1       cgd       273: }
                    274:
1.26      jmc       275: static int64_t
                    276: perform_arith_op(const char *left, const char *op, const char *right)
                    277: {
1.27      jmc       278:        int64_t res, sign, l, r;
                    279:        u_int64_t temp;
1.26      jmc       280:
1.32      lukem     281:        res = 0;
                    282:
1.26      jmc       283:        if (!is_integer(left)) {
                    284:                yyerror("non-integer argument '%s'", left);
                    285:                /* NOTREACHED */
                    286:        }
                    287:        if (!is_integer(right)) {
                    288:                yyerror("non-integer argument '%s'", right);
                    289:                /* NOTREACHED */
                    290:        }
                    291:
                    292:        errno = 0;
                    293:        l = strtoll(left, NULL, 10);
                    294:        if (errno == ERANGE) {
                    295:                yyerror("value '%s' is %s is %lld", left,
                    296:                    (l > 0) ? "too big, maximum" : "too small, minimum",
                    297:                    (l > 0) ? LLONG_MAX : LLONG_MIN);
                    298:                /* NOTREACHED */
                    299:        }
                    300:
                    301:        errno = 0;
                    302:        r = strtoll(right, NULL, 10);
                    303:        if (errno == ERANGE) {
                    304:                yyerror("value '%s' is %s is %lld", right,
                    305:                    (l > 0) ? "too big, maximum" : "too small, minimum",
                    306:                    (l > 0) ? LLONG_MAX : LLONG_MIN);
                    307:                /* NOTREACHED */
                    308:        }
                    309:
                    310:        switch(op[0]) {
                    311:        case '+':
1.27      jmc       312:                /*
                    313:                 * Do the op into an unsigned to avoid overflow and then cast
                    314:                 * back to check the resulting signage.
                    315:                 */
                    316:                temp = l + r;
                    317:                res = (int64_t) temp;
1.26      jmc       318:                /* very simplistic check for over-& underflow */
1.27      jmc       319:                if ((res < 0 && l > 0 && r > 0)
                    320:                    || (res > 0 && l < 0 && r < 0))
1.26      jmc       321:                        yyerror("integer overflow or underflow occurred for "
                    322:                             "operation '%s %s %s'", left, op, right);
                    323:                break;
                    324:        case '-':
1.27      jmc       325:                /*
                    326:                 * Do the op into an unsigned to avoid overflow and then cast
                    327:                 * back to check the resulting signage.
                    328:                 */
                    329:                temp = l - r;
                    330:                res = (int64_t) temp;
1.26      jmc       331:                /* very simplistic check for over-& underflow */
                    332:                if ((res < 0 && l > 0 && l > r)
1.27      jmc       333:                    || (res > 0 && l < 0 && l < r) )
1.26      jmc       334:                        yyerror("integer overflow or underflow occurred for "
                    335:                            "operation '%s %s %s'", left, op, right);
                    336:                break;
                    337:        case '/':
1.27      jmc       338:                if (r == 0)
1.26      jmc       339:                        yyerror("second argument to '%s' must not be zero", op);
                    340:                res = l / r;
                    341:
                    342:                break;
                    343:        case '%':
1.27      jmc       344:                if (r == 0)
1.26      jmc       345:                        yyerror("second argument to '%s' must not be zero", op);
                    346:                res = l % r;
                    347:                break;
                    348:        case '*':
1.27      jmc       349:                /* shortcut */
                    350:                if ((l == 0) || (r == 0)) {
1.26      jmc       351:                        res = 0;
                    352:                        break;
                    353:                }
                    354:
1.27      jmc       355:                sign = 1;
                    356:                if (l < 0)
                    357:                        sign *= -1;
                    358:                if (r < 0)
                    359:                        sign *= -1;
1.26      jmc       360:
                    361:                res = l * r;
1.27      jmc       362:                /*
                    363:                 * XXX: not the most portable but works on anything with 2's
                    364:                 * complement arithmetic. If the signs don't match or the
                    365:                 * result was 0 on 2's complement this overflowed.
                    366:                 */
                    367:                if ((res < 0 && sign > 0) || (res > 0 && sign < 0) ||
                    368:                    (res == 0))
                    369:                        yyerror("integer overflow or underflow occurred for "
                    370:                            "operation '%s %s %s'", left, op, right);
                    371:                        /* NOTREACHED */
1.26      jmc       372:                break;
                    373:        }
                    374:        return res;
                    375: }
1.4       cgd       376:
1.23      jdolecek  377: static const char *x = "|&=<>+-*/%:()";
                    378: static const int x_token[] = {
1.26      jmc       379:        SPEC_OR, SPEC_AND, COMPARE, COMPARE, COMPARE, ADD_SUB_OPERATOR,
                    380:        ADD_SUB_OPERATOR, MUL_DIV_MOD_OPERATOR, MUL_DIV_MOD_OPERATOR,
                    381:        MUL_DIV_MOD_OPERATOR, SPEC_REG, LEFT_PARENT, RIGHT_PARENT
1.17      jdolecek  382: };
1.1       cgd       383:
1.23      jdolecek  384: static int handle_ddash = 1;
                    385:
1.17      jdolecek  386: int
                    387: yylex(void)
1.1       cgd       388: {
1.17      jdolecek  389:        const char *p = *av++;
1.23      jdolecek  390:        int retval;
1.4       cgd       391:
1.23      jdolecek  392:        if (!p)
                    393:                retval = 0;
                    394:        else if (p[1] == '\0') {
1.17      jdolecek  395:                const char *w = strchr(x, p[0]);
                    396:                if (w) {
                    397:                        retval = x_token[w-x];
                    398:                } else {
                    399:                        retval = STRING;
                    400:                }
                    401:        } else if (p[1] == '=' && p[2] == '\0'
                    402:                        && (p[0] == '>' || p[0] == '<' || p[0] == '!'))
                    403:                retval = COMPARE;
1.23      jdolecek  404:        else if (handle_ddash && p[0] == '-' && p[1] == '-' && p[2] == '\0') {
                    405:                /* ignore "--" if passed as first argument and isn't followed
                    406:                 * by another STRING */
                    407:                retval = yylex();
                    408:                if (retval != STRING && retval != LEFT_PARENT
                    409:                    && retval != RIGHT_PARENT) {
                    410:                        /* is not followed by string or parenthesis, use as
                    411:                         * STRING */
                    412:                        retval = STRING;
                    413:                        av--;   /* was increased in call to yylex() above */
                    414:                        p = "--";
                    415:                } else {
                    416:                        /* "--" is to be ignored */
                    417:                        p = yylval;
                    418:                }
1.31      jdolecek  419:        } else if (strcmp(p, "length") == 0)
                    420:                retval = LENGTH;
                    421:        else
1.17      jdolecek  422:                retval = STRING;
1.1       cgd       423:
1.23      jdolecek  424:        handle_ddash = 0;
1.17      jdolecek  425:        yylval = p;
1.4       cgd       426:
1.17      jdolecek  427:        return retval;
1.1       cgd       428: }
1.4       cgd       429:
1.17      jdolecek  430: /*
                    431:  * Print error message and exit with error 2 (syntax error).
                    432:  */
                    433: static void
                    434: yyerror(const char *fmt, ...)
1.1       cgd       435: {
1.17      jdolecek  436:        va_list arg;
1.4       cgd       437:
1.17      jdolecek  438:        va_start(arg, fmt);
                    439:        verrx(2, fmt, arg);
                    440:        va_end(arg);
1.1       cgd       441: }
                    442:
1.17      jdolecek  443: int
1.23      jdolecek  444: main(int argc, const char * const *argv)
1.1       cgd       445: {
1.28      wiz       446:        setprogname(argv[0]);
                    447:        (void)setlocale(LC_ALL, "");
1.6       jtc       448:
1.36      joerg     449:        if (argc == 1) {
                    450:                (void)fprintf(stderr, "usage: %s expression\n",
                    451:                    getprogname());
                    452:                exit(2);
                    453:        }
                    454:
                    455:        av = argv + 1;
1.5       cgd       456:
1.17      jdolecek  457:        exit(yyparse());
                    458:        /* NOTREACHED */
1.1       cgd       459: }

CVSweb <webmaster@jp.NetBSD.org>