diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2014-09-19 02:53:32 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2014-09-19 02:53:32 +0000 |
commit | bea0037dbdd979603fb0b5be8b43f5478c1f6fec (patch) | |
tree | 5776ae3af5a3e83d41c7087f70713952b6360988 /src/skaembutils/s6-expr.c | |
download | s6-portable-utils-bea0037dbdd979603fb0b5be8b43f5478c1f6fec.tar.xz |
initial commit
Diffstat (limited to 'src/skaembutils/s6-expr.c')
-rw-r--r-- | src/skaembutils/s6-expr.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/skaembutils/s6-expr.c b/src/skaembutils/s6-expr.c new file mode 100644 index 0000000..4404d44 --- /dev/null +++ b/src/skaembutils/s6-expr.c @@ -0,0 +1,211 @@ +/* ISC license. */ + +#include <unistd.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint.h> +#include <skalibs/fmtscan.h> +#include <skalibs/strerr2.h> + +#define USAGE "s6-expr arithmetic expression" + +enum opnum +{ + T_DATA, + T_AND, + T_OR, + T_LEFTP, + T_RIGHTP, + T_EQUAL, + T_NEQUAL, + T_GREATER, + T_GREATERE, + T_LESSER, + T_LESSERE, + T_PLUS, + T_MINUS, + T_TIMES, + T_DIV, + T_MOD +} ; + +struct token +{ + char const *string ; + enum opnum op ; + unsigned int type ; +} ; + +struct node +{ + enum opnum op ; + unsigned int type ; + unsigned int arg1 ; + unsigned int arg2 ; + int data ; +} ; + +static unsigned int lex (struct node *tree, char const *const *argv) +{ + static struct token const tokens[16] = + { + { "+", T_PLUS, 3 }, + { "-", T_MINUS, 3 }, + { "*", T_TIMES, 2 }, + { "/", T_DIV, 2 }, + { "%", T_MOD, 2 }, + { "(", T_LEFTP, 7 }, + { ")", T_RIGHTP, 8 }, + { "=", T_EQUAL, 4 }, + { "!=", T_NEQUAL, 4 }, + { "<", T_LESSER, 4 }, + { "<=", T_LESSERE, 4 }, + { ">", T_GREATER, 4 }, + { ">=", T_GREATERE, 4 }, + { "|", T_OR, 6 }, + { "&", T_AND, 5 }, + { 0, 0, 0 } + } ; + register unsigned int pos = 0 ; + + for (; argv[pos] ; pos++) + { + register unsigned int i = 0 ; + for (i = 0 ; tokens[i].string ; i++) + if (!str_diff(argv[pos], tokens[i].string)) + { + tree[pos].op = tokens[i].op ; + tree[pos].type = tokens[i].type ; + break ; + } + if (!tokens[i].string) + { + tree[pos].op = T_DATA ; + tree[pos].type = 0 ; + if (!int_scan(argv[pos], &tree[pos].data)) + strerr_dief1x(2, "invalid expression") ; + } + } + return pos ; +} + +static void reduce (struct node *tree, register unsigned int *stack, register unsigned int *sp, unsigned int type) +{ + if (tree[stack[*sp-1]].type == type) + { + tree[stack[*sp-1]].arg1 = stack[*sp-2] ; + tree[stack[*sp-1]].arg2 = stack[*sp] ; + stack[*sp-2] = stack[*sp-1] ; + *sp -= 2 ; + } + tree[stack[*sp]].type = type + 7 ; +} + +static unsigned int parse (struct node *tree, unsigned int n) +{ + static char const table[9][15] = + { + "xsssssssxzzzzzz", + "xxxxxxxx!zzzzzz", + "mxxxxxxxMszzzzz", + "mxxxxxxxMaszzzz", + "mxxxxxxxMacszzz", + "mxxxxxxxMacAszz", + "mxxxxxxxMacAOsz", + "xsssssssxzzzzzz", + "mxxxxxxxMacAOEs" + } ; + unsigned int stack[n] ; + unsigned int sp = 0, pos = 0 ; + char cont = 1 ; + stack[0] = n + 1 ; + tree[n].type = 8 ; /* add ) for the final reduce */ + tree[n+1].type = 1 ; /* add EOF */ + while (cont) + { + switch (table[tree[pos].type][tree[stack[sp]].type]) + { + case 'x' : _exit(2) ; + case '!' : cont = 0 ; break ; + case 's' : stack[++sp] = pos++ ; break ; + case 'm' : reduce(tree, stack, &sp, 2) ; break ; + case 'a' : reduce(tree, stack, &sp, 3) ; break ; + case 'c' : reduce(tree, stack, &sp, 4) ; break ; + case 'A' : reduce(tree, stack, &sp, 5) ; break ; + case 'O' : reduce(tree, stack, &sp, 6) ; break ; + case 'E' : tree[stack[sp]].type = 14 ; break ; + case 'M' : + { + if (tree[stack[sp-2]].type != 7) _exit(2) ; + stack[sp-2] = stack[sp-1] ; + sp -= 2 ; + reduce(tree, stack, &sp, 2) ; + break ; + } + case 'z' : + default : strerr_dief1x(101, "internal error in parse, please submit a bug-report.") ; /* can't happen */ + } + } + if (sp != 2) strerr_dief1x(2, "invalid expression") ; + return stack[1] ; +} + +static int run (struct node const *tree, unsigned int root) +{ + switch (tree[root].op) + { + case T_DATA : + return tree[root].data ; + case T_OR : + { + int r = run(tree, tree[root].arg1) ; + return r ? r : run(tree, tree[root].arg2) ; + } + case T_AND : + { + int r = run(tree, tree[root].arg1) ; + return r ? run(tree, tree[root].arg2) ? r : 0 : 0 ; + } + case T_EQUAL : + return run(tree, tree[root].arg1) == run(tree, tree[root].arg2) ; + case T_NEQUAL : + return run(tree, tree[root].arg1) != run(tree, tree[root].arg2) ; + case T_GREATER : + return run(tree, tree[root].arg1) > run(tree, tree[root].arg2) ; + case T_GREATERE : + return run(tree, tree[root].arg1) >= run(tree, tree[root].arg2) ; + case T_LESSER : + return run(tree, tree[root].arg1) < run(tree, tree[root].arg2) ; + case T_LESSERE : + return run(tree, tree[root].arg1) <= run(tree, tree[root].arg2) ; + case T_PLUS : + return run(tree, tree[root].arg1) + run(tree, tree[root].arg2) ; + case T_MINUS : + return run(tree, tree[root].arg1) - run(tree, tree[root].arg2) ; + case T_TIMES : + return run(tree, tree[root].arg1) * run(tree, tree[root].arg2) ; + case T_DIV : + return run(tree, tree[root].arg1) / run(tree, tree[root].arg2) ; + case T_MOD : + return run(tree, tree[root].arg1) % run(tree, tree[root].arg2) ; + default: strerr_dief1x(101, "internal error in run, please submit a bug-report") ; + } +} + +int main (int argc, char const *const *argv) +{ + char fmt[UINT_FMT+1] ; + int val ; + unsigned int len ; + PROG = "s6-expr" ; + if (argc <= 1) return 2 ; + { + struct node tree[argc + 1] ; + val = run(tree, parse(tree, lex(tree, argv+1))) ; + } + len = int_fmt(fmt, val) ; + fmt[len++] = '\n' ; + if (allwrite(1, fmt, len) < len) + strerr_diefu1sys(111, "write to stdout") ; + return !val ; +} |