diff options
author | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-02-01 22:55:37 +0000 |
---|---|---|
committer | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-02-03 18:22:05 -0600 |
commit | 5b57d28ffb6e1ef86b50f7d05d977826eae89bfe (patch) | |
tree | 154a22fe556b49e6927197336f8bf91b12eacd5e /bin/expr/expr.y | |
download | userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.gz userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.bz2 userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.xz userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.zip |
initial population
Diffstat (limited to 'bin/expr/expr.y')
-rw-r--r-- | bin/expr/expr.y | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/bin/expr/expr.y b/bin/expr/expr.y new file mode 100644 index 0000000..2bc4ad1 --- /dev/null +++ b/bin/expr/expr.y @@ -0,0 +1,467 @@ +/* $NetBSD: expr.y,v 1.45 2018/06/27 17:23:36 kamil Exp $ */ + +/*_ + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek <jdolecek@NetBSD.org> and J.T. Conklin <jtc@NetBSD.org>. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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> +#ifndef lint +__RCSID("$NetBSD: expr.y,v 1.45 2018/06/27 17:23:36 kamil Exp $"); +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <locale.h> +#include <regex.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static const char * const *av; + +static void yyerror(const char *, ...) __dead; +static int yylex(void); +static int is_zero_or_null(const char *); +static int is_integer(const char *); +static int64_t perform_arith_op(const char *, const char *, const char *); + +#define YYSTYPE const char * + +%} +%token STRING +%left SPEC_OR +%left SPEC_AND +%left COMPARE +%left ADD_SUB_OPERATOR +%left MUL_DIV_MOD_OPERATOR +%left SPEC_REG +%left LENGTH +%left LEFT_PARENT RIGHT_PARENT + +%% + +exp: expr = { + (void) printf("%s\n", $1); + return (is_zero_or_null($1)); + } + ; + +expr: item { $$ = $1; } + | expr SPEC_OR expr = { + /* + * Return evaluation of first expression if it is neither + * an empty string nor zero; otherwise, returns the evaluation + * of second expression. + */ + if (!is_zero_or_null($1)) + $$ = $1; + else + $$ = $3; + } + | expr SPEC_AND expr = { + /* + * Returns the evaluation of first expr if neither expression + * evaluates to an empty string or zero; otherwise, returns + * zero. + */ + if (!is_zero_or_null($1) && !is_zero_or_null($3)) + $$ = $1; + else + $$ = "0"; + } + | expr SPEC_REG expr = { + /* + * The ``:'' operator matches first expr against the second, + * which must be a regular expression. + */ + regex_t rp; + regmatch_t rm[2]; + int eval; + + /* compile regular expression */ + if ((eval = regcomp(&rp, $3, REG_BASIC)) != 0) { + char errbuf[256]; + (void)regerror(eval, &rp, errbuf, sizeof(errbuf)); + yyerror("%s", errbuf); + /* NOT REACHED */ + } + + /* compare string against pattern -- remember that patterns + are anchored to the beginning of the line */ + if (regexec(&rp, $1, 2, rm, 0) == 0 && rm[0].rm_so == 0) { + char *val; + if (rm[1].rm_so >= 0) { + (void) asprintf(&val, "%.*s", + (int) (rm[1].rm_eo - rm[1].rm_so), + $1 + rm[1].rm_so); + } else { + (void) asprintf(&val, "%d", + (int)(rm[0].rm_eo - rm[0].rm_so)); + } + if (val == NULL) + err(1, NULL); + $$ = val; + } else { + if (rp.re_nsub == 0) { + $$ = "0"; + } else { + $$ = ""; + } + } + + } + | expr ADD_SUB_OPERATOR expr = { + /* Returns the results of addition, subtraction */ + char *val; + int64_t res; + + res = perform_arith_op($1, $2, $3); + (void) asprintf(&val, "%lld", (long long int) res); + if (val == NULL) + err(1, NULL); + $$ = val; + } + + | expr MUL_DIV_MOD_OPERATOR expr = { + /* + * Returns the results of multiply, divide or remainder of + * numeric-valued arguments. + */ + char *val; + int64_t res; + + res = perform_arith_op($1, $2, $3); + (void) asprintf(&val, "%lld", (long long int) res); + if (val == NULL) + err(1, NULL); + $$ = val; + + } + | expr COMPARE expr = { + /* + * Returns the results of integer comparison if both arguments + * are integers; otherwise, returns the results of string + * comparison using the locale-specific collation sequence. + * The result of each comparison is 1 if the specified relation + * is true, or 0 if the relation is false. + */ + + int64_t l, r; + int res; + + res = 0; + + /* + * Slight hack to avoid differences in the compare code + * between string and numeric compare. + */ + if (is_integer($1) && is_integer($3)) { + /* numeric comparison */ + l = strtoll($1, NULL, 10); + r = strtoll($3, NULL, 10); + } else { + /* string comparison */ + l = strcoll($1, $3); + r = 0; + } + + switch($2[0]) { + case '=': /* equal */ + res = (l == r); + break; + case '>': /* greater or greater-equal */ + if ($2[1] == '=') + res = (l >= r); + else + res = (l > r); + break; + case '<': /* lower or lower-equal */ + if ($2[1] == '=') + res = (l <= r); + else + res = (l < r); + break; + case '!': /* not equal */ + /* the check if this is != was done in yylex() */ + res = (l != r); + } + + $$ = (res) ? "1" : "0"; + + } + | LEFT_PARENT expr RIGHT_PARENT { $$ = $2; } + | LENGTH expr { + /* + * Return length of 'expr' in bytes. + */ + char *ln; + + asprintf(&ln, "%ld", (long) strlen($2)); + if (ln == NULL) + err(1, NULL); + $$ = ln; + } + ; + +item: STRING + | ADD_SUB_OPERATOR + | MUL_DIV_MOD_OPERATOR + | COMPARE + | SPEC_OR + | SPEC_AND + | SPEC_REG + | LENGTH + ; +%% + +/* + * Returns 1 if the string is empty or contains only numeric zero. + */ +static int +is_zero_or_null(const char *str) +{ + char *endptr; + + return str[0] == '\0' + || ( strtoll(str, &endptr, 10) == 0LL + && endptr[0] == '\0'); +} + +/* + * Returns 1 if the string is an integer. + */ +static int +is_integer(const char *str) +{ + char *endptr; + + (void) strtoll(str, &endptr, 10); + /* note we treat empty string as valid number */ + return (endptr[0] == '\0'); +} + +static int64_t +perform_arith_op(const char *left, const char *op, const char *right) +{ + int64_t res, l, r; + + res = 0; + + if (!is_integer(left)) { + yyerror("non-integer argument '%s'", left); + /* NOTREACHED */ + } + if (!is_integer(right)) { + yyerror("non-integer argument '%s'", right); + /* NOTREACHED */ + } + + errno = 0; + l = strtoll(left, NULL, 10); + if (errno == ERANGE) { + yyerror("value '%s' is %s is %lld", left, + (l > 0) ? "too big, maximum" : "too small, minimum", + (l > 0) ? LLONG_MAX : LLONG_MIN); + /* NOTREACHED */ + } + + errno = 0; + r = strtoll(right, NULL, 10); + if (errno == ERANGE) { + yyerror("value '%s' is %s is %lld", right, + (l > 0) ? "too big, maximum" : "too small, minimum", + (l > 0) ? LLONG_MAX : LLONG_MIN); + /* NOTREACHED */ + } + + switch(op[0]) { + case '+': + /* + * Check for over-& underflow. + */ + if ((l >= 0 && r <= INT64_MAX - l) || + (l <= 0 && r >= INT64_MIN - l)) { + res = l + r; + } else { + yyerror("integer overflow or underflow occurred for " + "operation '%s %s %s'", left, op, right); + } + break; + case '-': + /* + * Check for over-& underflow. + */ + if ((r > 0 && l < INT64_MIN + r) || + (r < 0 && l > INT64_MAX + r)) { + yyerror("integer overflow or underflow occurred for " + "operation '%s %s %s'", left, op, right); + } else { + res = l - r; + } + break; + case '/': + if (r == 0) + yyerror("second argument to '%s' must not be zero", op); + if (l == INT64_MIN && r == -1) + yyerror("integer overflow or underflow occurred for " + "operation '%s %s %s'", left, op, right); + res = l / r; + + break; + case '%': + if (r == 0) + yyerror("second argument to '%s' must not be zero", op); + if (l == INT64_MIN && r == -1) + yyerror("integer overflow or underflow occurred for " + "operation '%s %s %s'", left, op, right); + res = l % r; + break; + case '*': + /* + * Check for over-& underflow. + */ + + /* + * Simplify the conditions: + * - remove the case of both negative arguments + * unless the operation will cause an overflow + */ + if (l < 0 && r < 0 && l != INT64_MIN && r != INT64_MIN) { + l = -l; + r = -r; + } + + /* - remove the case of legative l and positive r */ + if (l < 0 && r >= 0) { + /* Use res as a temporary variable */ + res = l; + l = r; + r = res; + } + + if ((l < 0 && r < 0) || + (r > 0 && l > INT64_MAX / r) || + (r <= 0 && l != 0 && r < INT64_MIN / l)) { + yyerror("integer overflow or underflow occurred for " + "operation '%s %s %s'", left, op, right); + /* NOTREACHED */ + } else { + res = l * r; + } + break; + } + return res; +} + +static const char *x = "|&=<>+-*/%:()"; +static const int x_token[] = { + SPEC_OR, SPEC_AND, COMPARE, COMPARE, COMPARE, ADD_SUB_OPERATOR, + ADD_SUB_OPERATOR, MUL_DIV_MOD_OPERATOR, MUL_DIV_MOD_OPERATOR, + MUL_DIV_MOD_OPERATOR, SPEC_REG, LEFT_PARENT, RIGHT_PARENT +}; + +static int handle_ddash = 1; + +int +yylex(void) +{ + const char *p = *av++; + int retval; + + if (!p) + retval = 0; + else if (p[1] == '\0') { + const char *w = strchr(x, p[0]); + if (w) { + retval = x_token[w-x]; + } else { + retval = STRING; + } + } else if (p[1] == '=' && p[2] == '\0' + && (p[0] == '>' || p[0] == '<' || p[0] == '!')) + retval = COMPARE; + else if (handle_ddash && p[0] == '-' && p[1] == '-' && p[2] == '\0') { + /* ignore "--" if passed as first argument and isn't followed + * by another STRING */ + retval = yylex(); + if (retval != STRING && retval != LEFT_PARENT + && retval != RIGHT_PARENT) { + /* is not followed by string or parenthesis, use as + * STRING */ + retval = STRING; + av--; /* was increased in call to yylex() above */ + p = "--"; + } else { + /* "--" is to be ignored */ + p = yylval; + } + } else if (strcmp(p, "length") == 0) + retval = LENGTH; + else + retval = STRING; + + handle_ddash = 0; + yylval = p; + + return retval; +} + +/* + * Print error message and exit with error 2 (syntax error). + */ +static __printflike(1, 2) void +yyerror(const char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + verrx(2, fmt, arg); + va_end(arg); +} + +int +main(int argc, const char * const *argv) +{ + setprogname(argv[0]); + (void)setlocale(LC_ALL, ""); + + if (argc == 1) { + (void)fprintf(stderr, "usage: %s expression\n", + getprogname()); + exit(2); + } + + av = argv + 1; + + return yyparse(); +} |