[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.21

1.21    ! jdolecek    1: /* $NetBSD: expr.y,v 1.20 2000/10/26 23:10:21 jdolecek 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.21    ! jdolecek    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.
                     18:  * 3. All advertising materials mentioning features or use of this software
                     19:  *    must display the following acknowledgement:
                     20:  *      This product includes software developed by the NetBSD
                     21:  *      Foundation, Inc. and its contributors.
                     22:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     23:  *    contributors may be used to endorse or promote products derived
                     24:  *    from this software without specific prior written permission.
                     25:  *
                     26:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     27:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     28:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     29:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     30:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     31:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     32:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     33:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     35:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1       cgd        36:  */
1.12      jtc        37:
1.17      jdolecek   38: %{
                     39: #include <sys/cdefs.h>
                     40: #ifndef lint
1.21    ! jdolecek   41: __RCSID("$NetBSD: expr.y,v 1.20 2000/10/26 23:10:21 jdolecek Exp $");
1.17      jdolecek   42: #endif /* not lint */
                     43:
                     44: #include <sys/types.h>
                     45: #include <err.h>
                     46: #include <errno.h>
                     47: #include <limits.h>
                     48: #include <locale.h>
                     49: #include <regex.h>
                     50: #include <stdarg.h>
1.1       cgd        51: #include <stdio.h>
1.4       cgd        52: #include <stdlib.h>
                     53: #include <string.h>
1.1       cgd        54:
1.17      jdolecek   55: const char **av;
1.1       cgd        56:
1.17      jdolecek   57: static void yyerror(const char *, ...);
                     58: static int yylex(void);
                     59: static int is_zero_or_null(const char *);
                     60: static int is_integer(const char *);
                     61: static int yyparse(void);
1.1       cgd        62:
1.17      jdolecek   63: #define YYSTYPE        const char *
1.1       cgd        64:
1.17      jdolecek   65: %}
1.20      jdolecek   66: %token STRING
                     67: %left SPEC_OR SPEC_AND
                     68: %left COMPARE ARITH_OPERATOR SPEC_REG
1.17      jdolecek   69: %left LEFT_PARENT RIGHT_PARENT
1.1       cgd        70:
                     71: %%
                     72:
1.17      jdolecek   73: exp:   expr = {
                     74:                (void) printf("%s\n", $1);
                     75:                return (is_zero_or_null($1));
                     76:                }
1.1       cgd        77:        ;
                     78:
1.17      jdolecek   79: expr:  item    { $$ = $1; }
                     80:        | expr SPEC_OR expr = {
                     81:                /*
                     82:                 * Return evaluation of first expression if it is neither
                     83:                 * an empty string nor zero; otherwise, returns the evaluation
                     84:                 * of second expression.
                     85:                 */
                     86:                if (!is_zero_or_null($1))
                     87:                        $$ = $1;
                     88:                else
                     89:                        $$ = $3;
                     90:                }
                     91:        | expr SPEC_AND expr = {
                     92:                /*
                     93:                 * Returns the evaluation of first expr if neither expression
                     94:                 * evaluates to an empty string or zero; otherwise, returns
                     95:                 * zero.
                     96:                 */
                     97:                if (!is_zero_or_null($1) && !is_zero_or_null($3))
                     98:                        $$ = $1;
                     99:                else
                    100:                        $$ = "0";
                    101:                }
                    102:        | expr SPEC_REG expr = {
                    103:                /*
                    104:                 * The ``:'' operator matches first expr against the second,
                    105:                 * which must be a regular expression.
                    106:                 */
                    107:                regex_t rp;
                    108:                regmatch_t rm[2];
                    109:                int eval;
                    110:
                    111:                /* compile regular expression */
                    112:                if ((eval = regcomp(&rp, $3, 0)) != 0) {
1.18      jdolecek  113:                        char errbuf[256];
1.17      jdolecek  114:                        (void)regerror(eval, &rp, errbuf, sizeof(errbuf));
                    115:                        yyerror("%s", errbuf);
                    116:                        /* NOT REACHED */
1.1       cgd       117:                }
                    118:
1.17      jdolecek  119:                /* compare string against pattern --  remember that patterns
                    120:                   are anchored to the beginning of the line */
                    121:                if (regexec(&rp, $1, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
                    122:                        char *val;
                    123:                        if (rm[1].rm_so >= 0) {
1.19      jdolecek  124:                                (void) asprintf(&val, "%.*s",
1.18      jdolecek  125:                                        (int) (rm[1].rm_eo - rm[1].rm_so),
                    126:                                        $1 + rm[1].rm_so);
1.17      jdolecek  127:                        } else {
                    128:                                (void) asprintf(&val, "%d",
                    129:                                        (int)(rm[0].rm_eo - rm[0].rm_so));
                    130:                        }
                    131:                        $$ = val;
                    132:                } else {
                    133:                        if (rp.re_nsub == 0) {
                    134:                                $$ = "0";
                    135:                        } else {
                    136:                                $$ = "";
                    137:                        }
                    138:                }
1.1       cgd       139:
1.17      jdolecek  140:                }
                    141:        | expr ARITH_OPERATOR expr = {
                    142:                /*
                    143:                 * Returns the results of multiplication, division,
                    144:                 * addition, subtraction, remainder of numeric-valued arguments.
                    145:                 */
                    146:                char *val;
                    147:                int64_t res, l, r;
                    148:
                    149:                if (!is_integer($1)) {
                    150:                        yyerror("non-integer argument '%s'", $1);
                    151:                        /* NOTREACHED */
                    152:                }
                    153:                if (!is_integer($3)) {
                    154:                        yyerror("non-integer argument '%s'", $3);
                    155:                        /* NOTREACHED */
                    156:                }
1.4       cgd       157:
1.17      jdolecek  158:                errno = 0;
                    159:                l = strtoll($1, NULL, 10);
                    160:                if (errno == ERANGE) {
                    161:                        yyerror("value '%s' is %s is %lld", $1,
                    162:                                (l > 0)
                    163:                                ? "too big, maximum" : "too small, minimum",
                    164:                                (l > 0) ? LLONG_MAX : LLONG_MIN
                    165:                        );
                    166:                        /* NOTREACHED */
                    167:                }
1.4       cgd       168:
1.17      jdolecek  169:                errno = 0;
                    170:                r = strtoll($3, NULL, 10);
                    171:                if (errno == ERANGE) {
                    172:                        yyerror("value '%s' is %s is %lld", $3,
                    173:                                (l > 0)
                    174:                                ? "too big, maximum" : "too small, minimum",
                    175:                                (l > 0) ? LLONG_MAX : LLONG_MIN
                    176:                        );
                    177:                        /* NOTREACHED */
                    178:                }
1.1       cgd       179:
1.17      jdolecek  180:                switch($2[0]) {
                    181:                case '+':
                    182:                        res = l + r;
                    183:                        /* very simplistic check for over-& underflow */
                    184:                        if ((res < 0 && l > 0 && r > 0 && l > r)
                    185:                                || (res > 0 && l < 0 && r < 0 && l < r)) {
                    186:                                yyerror("integer overflow or underflow occured\
                    187:  for operation '%s %s %s'", $1, $2, $3);
                    188:                                /* NOTREACHED */
                    189:                        }
                    190:                        break;
                    191:                case '-':
                    192:                        res = l - r;
                    193:                        /* very simplistic check for over-& underflow */
                    194:                        if ((res < 0 && l > 0 && l > r)
                    195:                                || (res > 0 && l < 0 && l < r) ) {
                    196:                                yyerror("integer overflow or underflow occured\
                    197:  for operation '%s %s %s'", $1, $2, $3);
                    198:                                /* NOTREACHED */
                    199:                        }
                    200:                        break;
                    201:                case '/':
                    202:                        if (r == 0) {
                    203:                                yyerror("second argument to '%s' must not be\
                    204:  zero", $2);
                    205:                                /* NOTREACHED */
                    206:                        }
                    207:                        res = l / r;
                    208:
                    209:                        break;
                    210:                case '%':
                    211:                        if (r == 0) {
                    212:                                yyerror("second argument to '%s' must not be zero", $2);
                    213:                                /* NOTREACHED */
                    214:                        }
                    215:                        res = l % r;
                    216:                        break;
                    217:                case '*':
                    218:                        if (r == 0) {
                    219:                                res = 0;
                    220:                                break;
                    221:                        }
                    222:
                    223:                        /* check if the result would over- or underflow */
                    224:                        if ((l > 0 && l > (LLONG_MAX / r))
                    225:                                || (l < 0 && l < (LLONG_MIN / r))) {
                    226:                                yyerror("operation '%s %s %s' would cause over-\
                    227:  or underflow",
                    228:                                        $1, $2, $3);
                    229:                                /* NOTREACHED */
                    230:                        }
1.4       cgd       231:
1.17      jdolecek  232:                        res = l * r;
                    233:                        break;
                    234:                }
1.4       cgd       235:
1.17      jdolecek  236:                (void) asprintf(&val, "%lld", (long long int) res);
                    237:                $$ = val;
1.1       cgd       238:
1.17      jdolecek  239:                }
                    240:        | expr COMPARE expr = {
                    241:                /*
                    242:                 * Returns the results of integer comparison if both arguments
                    243:                 * are integers; otherwise, returns the results of string
                    244:                 * comparison using the locale-specific collation sequence.
                    245:                 * The result of each comparison is 1 if the specified relation
                    246:                 * is true, or 0 if the relation is false.
                    247:                 */
                    248:
                    249:                int64_t l, r;
                    250:                int res;
                    251:
                    252:                /*
                    253:                 * Slight hack to avoid differences in the compare code
                    254:                 * between string and numeric compare.
                    255:                 */
                    256:                if (is_integer($1) && is_integer($3)) {
                    257:                        /* numeric comparison */
                    258:                        l = strtoll($1, NULL, 10);
                    259:                        r = strtoll($3, NULL, 10);
                    260:                } else {
                    261:                        /* string comparison */
                    262:                        l = strcoll($1, $3);
                    263:                        r = 0;
                    264:                }
1.4       cgd       265:
1.17      jdolecek  266:                switch($2[0]) {
                    267:                case '=': /* equal */
                    268:                        res = (l == r);
                    269:                        break;
                    270:                case '>': /* greater or greater-equal */
                    271:                        if ($2[1] == '=')
                    272:                                res = (l >= r);
                    273:                        else
                    274:                                res = (l > r);
                    275:                        break;
                    276:                case '<': /* lower or lower-equal */
                    277:                        if ($2[1] == '=')
                    278:                                res = (l <= r);
                    279:                        else
                    280:                                res = (l < r);
                    281:                        break;
                    282:                case '!': /* not equal */
                    283:                        /* the check if this is != was done in yylex() */
                    284:                        res = (l != r);
                    285:                }
1.1       cgd       286:
1.17      jdolecek  287:                $$ = (res) ? "1" : "0";
1.4       cgd       288:
1.17      jdolecek  289:                }
                    290:        | LEFT_PARENT expr RIGHT_PARENT { $$ = $2; }
                    291:        ;
1.4       cgd       292:
1.17      jdolecek  293: item:  STRING
                    294:        | ARITH_OPERATOR
                    295:        | COMPARE
                    296:        | SPEC_OR
                    297:        | SPEC_AND
                    298:        | SPEC_REG
                    299:        ;
                    300: %%
1.1       cgd       301:
1.17      jdolecek  302: /*
                    303:  * Returns 1 if the string is empty or contains only numeric zero.
                    304:  */
                    305: static int
                    306: is_zero_or_null(const char *str)
1.1       cgd       307: {
1.17      jdolecek  308:        char *endptr;
1.4       cgd       309:
1.17      jdolecek  310:        return str[0] == '\0'
                    311:                || ( strtoll(str, &endptr, 10) == 0LL
                    312:                        && endptr[0] == '\0');
1.1       cgd       313: }
                    314:
1.17      jdolecek  315: /*
                    316:  * Returns 1 if the string is an integer.
                    317:  */
                    318: static int
                    319: is_integer(const char *str)
1.1       cgd       320: {
1.17      jdolecek  321:        char *endptr;
1.4       cgd       322:
1.17      jdolecek  323:        (void) strtoll(str, &endptr, 10);
                    324:        /* note we treat empty string as valid number */
                    325:        return (endptr[0] == '\0');
1.1       cgd       326: }
                    327:
1.4       cgd       328:
1.17      jdolecek  329: const char *x = "|&=<>+-*/%:()";
                    330: const int x_token[] = {
                    331:        SPEC_OR, SPEC_AND, COMPARE, COMPARE, COMPARE, ARITH_OPERATOR,
                    332:        ARITH_OPERATOR, ARITH_OPERATOR, ARITH_OPERATOR, ARITH_OPERATOR,
                    333:        SPEC_REG, LEFT_PARENT, RIGHT_PARENT
                    334: };
1.1       cgd       335:
1.17      jdolecek  336: int
                    337: yylex(void)
1.1       cgd       338: {
1.17      jdolecek  339:        const char *p = *av++;
                    340:        int retval = 0;
1.4       cgd       341:
1.17      jdolecek  342:        if (!p) {
                    343:                return 0;
1.4       cgd       344:        }
1.1       cgd       345:
1.17      jdolecek  346:        if (p[1] == '\0') {
                    347:                const char *w = strchr(x, p[0]);
                    348:                if (w) {
                    349:                        retval = x_token[w-x];
                    350:                } else {
                    351:                        retval = STRING;
                    352:                }
                    353:        } else if (p[1] == '=' && p[2] == '\0'
                    354:                        && (p[0] == '>' || p[0] == '<' || p[0] == '!'))
                    355:                retval = COMPARE;
                    356:        else
                    357:                retval = STRING;
1.1       cgd       358:
1.17      jdolecek  359:        yylval = p;
1.4       cgd       360:
1.17      jdolecek  361:        return retval;
1.1       cgd       362: }
1.4       cgd       363:
1.17      jdolecek  364: /*
                    365:  * Print error message and exit with error 2 (syntax error).
                    366:  */
                    367: static void
                    368: yyerror(const char *fmt, ...)
1.1       cgd       369: {
1.17      jdolecek  370:        va_list arg;
1.4       cgd       371:
1.17      jdolecek  372:        va_start(arg, fmt);
                    373:        verrx(2, fmt, arg);
                    374:        va_end(arg);
1.1       cgd       375: }
                    376:
1.17      jdolecek  377: int
                    378: main(int argc, const char **argv)
1.1       cgd       379: {
1.17      jdolecek  380:        (void) setlocale(LC_ALL, "");
1.6       jtc       381:
1.17      jdolecek  382:        if (argc == 1) {
                    383:                (void) fprintf(stderr, "usage: expr expression\n");
                    384:                exit(2);
1.1       cgd       385:        }
1.6       jtc       386:
1.17      jdolecek  387:        av = argv + 1;
1.5       cgd       388:
1.17      jdolecek  389:        exit(yyparse());
                    390:        /* NOTREACHED */
1.1       cgd       391: }

CVSweb <webmaster@jp.NetBSD.org>