summaryrefslogtreecommitdiff
path: root/src/skaembutils/s6-expr.c
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2014-09-19 02:53:32 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2014-09-19 02:53:32 +0000
commitbea0037dbdd979603fb0b5be8b43f5478c1f6fec (patch)
tree5776ae3af5a3e83d41c7087f70713952b6360988 /src/skaembutils/s6-expr.c
downloads6-portable-utils-bea0037dbdd979603fb0b5be8b43f5478c1f6fec.tar.xz
initial commit
Diffstat (limited to 'src/skaembutils/s6-expr.c')
-rw-r--r--src/skaembutils/s6-expr.c211
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 ;
+}