summaryrefslogtreecommitdiff
path: root/appl/cmd/sh/expr.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/sh/expr.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/sh/expr.b')
-rw-r--r--appl/cmd/sh/expr.b281
1 files changed, 281 insertions, 0 deletions
diff --git a/appl/cmd/sh/expr.b b/appl/cmd/sh/expr.b
new file mode 100644
index 00000000..d613dce2
--- /dev/null
+++ b/appl/cmd/sh/expr.b
@@ -0,0 +1,281 @@
+implement Shellbuiltin;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Listnode, Context: import sh;
+ myself: Shellbuiltin;
+
+initbuiltin(ctxt: ref Context, shmod: Sh): string
+{
+ sys = load Sys Sys->PATH;
+ sh = shmod;
+ myself = load Shellbuiltin "$self";
+ if (myself == nil)
+ ctxt.fail("bad module", sys->sprint("expr: cannot load self: %r"));
+
+ ctxt.addsbuiltin("expr", myself);
+ ctxt.addbuiltin("ntest", myself);
+ return nil;
+}
+
+whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string
+{
+ return nil;
+}
+
+getself(): Shellbuiltin
+{
+ return myself;
+}
+
+EQ, GT, LT, GE, LE, PLUS, MINUS, DIVIDE, AND, TIMES, MOD,
+OR, XOR, UMINUS, SHL, SHR, NOT, BNOT, NEQ, REP, SEQ: con iota;
+
+runbuiltin(ctxt: ref Sh->Context, nil: Sh,
+ cmd: list of ref Sh->Listnode, nil: int): string
+{
+ case (hd cmd).word {
+ "ntest" =>
+ if (len cmd != 2)
+ ctxt.fail("usage", "usage: ntest n");
+ if (big (hd tl cmd).word == big 0)
+ return "false";
+ }
+ return nil;
+}
+
+runsbuiltin(ctxt: ref Sh->Context, nil: Sh,
+ cmd: list of ref Sh->Listnode): list of ref Listnode
+{
+ # only one sbuiltin: expr.
+ stk: list of big;
+ lastop := -1;
+ lastn := -1;
+ lastname := "";
+ radix: int;
+ (cmd, radix) = opts(ctxt, tl cmd);
+ for (; cmd != nil; cmd = tl cmd) {
+ w := (hd cmd).word;
+ op := -1;
+ nops := 2;
+ case w {
+ "+" =>
+ op = PLUS;
+ "-" =>
+ op = MINUS;
+ "x" or "*" or "×" =>
+ op = TIMES;
+ "/" =>
+ op = DIVIDE;
+ "%" =>
+ op = MOD;
+ "and" =>
+ op = AND;
+ "or" =>
+ op = OR;
+ "xor" =>
+ op = XOR;
+ "_"=>
+ (op, nops) = (UMINUS, 1);
+ "<<" or "shl" =>
+ op = SHL;
+ ">>" or "shr" =>
+ op = SHR;
+ "=" or "==" or "eq" =>
+ op = EQ;
+ "!=" or "neq" =>
+ op = NEQ;
+ ">" or "gt" =>
+ op = GT;
+ "<" or "lt" =>
+ op = LT;
+ ">=" or "ge" =>
+ op = GE;
+ "<=" or "le" =>
+ op = LE;
+ "!" or "not" =>
+ (op, nops) = (NOT, 1);
+ "~" =>
+ (op, nops) = (BNOT, 1);
+ "rep" =>
+ (op, nops) = (REP, 0);
+ "seq" =>
+ (op, nops) = (SEQ, 2);
+ }
+ if (op == -1)
+ stk = makenum(ctxt, w) :: stk;
+ else
+ stk = operator(ctxt, stk, op, nops, lastop, lastn, w, lastname);
+ lastop = op;
+ lastn = nops;
+ lastname = w;
+ }
+ r: list of ref Listnode;
+ for (; stk != nil; stk = tl stk)
+ r = ref Listnode(nil, big2string(hd stk, radix)) :: r;
+ return r;
+}
+
+opts(ctxt: ref Context, cmd: list of ref Listnode): (list of ref Listnode, int)
+{
+ radix := 10;
+ if (cmd == nil)
+ return (nil, 10);
+ w := (hd cmd).word;
+ if (len w < 2)
+ return (cmd, 10);
+ if (w[0] != '-' || (w[1] >= '0' && w[1] <= '9'))
+ return (cmd, 10);
+ if (w[1] != 'r')
+ ctxt.fail("usage", "usage: expr [-r radix] [arg...]");
+ if (len w > 2)
+ w = w[2:];
+ else {
+ if (tl cmd == nil)
+ ctxt.fail("usage", "usage: expr [-r radix] [arg...]");
+ cmd = tl cmd;
+ w = (hd cmd).word;
+ }
+ r := int w;
+ if (r <= 0 || r > 36)
+ ctxt.fail("usage", "expr: invalid radix " + string r);
+ return (tl cmd, int w);
+}
+
+operator(ctxt: ref Context, stk: list of big, op, nops, lastop, lastn: int,
+ opname, lastopname: string): list of big
+{
+ al: list of big;
+ for (i := 0; i < nops; i++) {
+ if (stk == nil)
+ ctxt.fail("empty stack",
+ sys->sprint("expr: empty stack on op '%s'", opname));
+ al = hd stk :: al;
+ stk = tl stk;
+ }
+ return oper(ctxt, al, op, lastop, lastn, lastopname, stk);
+}
+
+# args are in reverse order
+oper(ctxt: ref Context, args: list of big, op, lastop, lastn: int,
+ lastopname: string, stk: list of big): list of big
+{
+ if (op == REP) {
+ if (lastop == -1 || lastop == SEQ || lastn != 2)
+ ctxt.fail("usage", "expr: bad operator for rep");
+ if (stk == nil || tl stk == nil)
+ return stk;
+ while (tl stk != nil)
+ stk = operator(ctxt, stk, lastop, 2, -1, -1, lastopname, nil);
+ return stk;
+ }
+ n2 := big 0;
+ n1 := hd args;
+ if (tl args != nil)
+ n2 = hd tl args;
+ r := big 0;
+ case op {
+ EQ => r = big(n1 == n2);
+ NEQ => r = big(n1 != n2);
+ GT => r = big(n1 > n2);
+ LT => r = big(n1 < n2);
+ GE => r = big(n1 >= n2);
+ LE => r = big(n1 <= n2);
+ PLUS => r = big(n1 + n2);
+ MINUS => r = big(n1 - n2);
+ NOT => r = big(n1 != big 0);
+ DIVIDE =>
+ if (n2 == big 0)
+ ctxt.fail("divide by zero", "expr: division by zero");
+ r = n1 / n2;
+ MOD =>
+ if (n2 == big 0)
+ ctxt.fail("divide by zero", "expr: division by zero");
+ r = n1 % n2;
+ TIMES => r = n1 * n2;
+ AND => r = n1 & n2;
+ OR => r = n1 | n2;
+ XOR => r = n1 ^ n2;
+ UMINUS => r = -n1;
+ BNOT => r = ~n1;
+ SHL => r = n1 << int n2;
+ SHR => r = n1 >> int n2;
+ SEQ => return seq(n1, n2, stk);
+ }
+ return r :: stk;
+}
+
+seq(n1, n2: big, stk: list of big): list of big
+{
+ incr := big 1;
+ if (n2 < n1)
+ incr = big -1;
+ for (; n1 != n2; n1 += incr)
+ stk = n1 :: stk;
+ return n1 :: stk;
+}
+
+makenum(ctxt: ref Context, s: string): big
+{
+ if (s == nil || (s[0] != '-' && (s[0] < '0' || s[0] > '9')))
+ ctxt.fail("usage", sys->sprint("expr: unknown operator '%s'", s));
+
+ t := s;
+ if (neg := s[0] == '-')
+ s = s[1:];
+ radix := 10;
+ for (i := 0; i < len s && i < 3; i++) {
+ if (s[i] == 'r') {
+ radix = int s;
+ s = s[i+1:];
+ break;
+ }
+ }
+ if (radix == 10)
+ return big t;
+ if (radix == 0 || radix > 36)
+ ctxt.fail("usage", "expr: bad number " + t);
+ n := big 0;
+ for (i = 0; i < len s; i++) {
+ if ('0' <= s[i] && s[i] <= '9')
+ n = (n * big radix) + big(s[i] - '0');
+ else if ('a' <= s[i] && s[i] < 'a' + radix - 10)
+ n = (n * big radix) + big(s[i] - 'a' + 10);
+ else if ('A' <= s[i] && s[i] < 'A' + radix - 10)
+ n = (n * big radix) + big(s[i] - 'A' + 10);
+ else
+ break;
+ }
+ if (neg)
+ return -n;
+ return n;
+}
+
+big2string(n: big, radix: int): string
+{
+ if (neg := n < big 0) {
+ n = -n;
+ }
+ s := "";
+ do {
+ c: int;
+ d := int (n % big radix);
+ if (d < 10)
+ c = '0' + d;
+ else
+ c = 'a' + d - 10;
+ s[len s] = c;
+ n /= big radix;
+ } while (n > big 0);
+ t := s;
+ for (i := len s - 1; i >= 0; i--)
+ t[len s - 1 - i] = s[i];
+ if (radix != 10)
+ t = string radix + "r" + t;
+ if (neg)
+ return "-" + t;
+ return t;
+}