diff options
Diffstat (limited to 'appl/cmd/sh/expr.b')
| -rw-r--r-- | appl/cmd/sh/expr.b | 281 |
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; +} |
