1implement Shellbuiltin; 2 3include "sys.m"; 4 sys: Sys; 5include "draw.m"; 6include "sh.m"; 7 sh: Sh; 8 Listnode, Context: import sh; 9 myself: Shellbuiltin; 10 11initbuiltin(ctxt: ref Context, shmod: Sh): string 12{ 13 sys = load Sys Sys->PATH; 14 sh = shmod; 15 myself = load Shellbuiltin "$self"; 16 if (myself == nil) 17 ctxt.fail("bad module", sys->sprint("expr: cannot load self: %r")); 18 19 ctxt.addsbuiltin("expr", myself); 20 ctxt.addbuiltin("ntest", myself); 21 return nil; 22} 23 24whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string 25{ 26 return nil; 27} 28 29getself(): Shellbuiltin 30{ 31 return myself; 32} 33 34EQ, GT, LT, GE, LE, PLUS, MINUS, DIVIDE, AND, TIMES, MOD, 35OR, XOR, UMINUS, SHL, SHR, NOT, BNOT, NEQ, REP, SEQ: con iota; 36 37runbuiltin(ctxt: ref Sh->Context, nil: Sh, 38 cmd: list of ref Sh->Listnode, nil: int): string 39{ 40 case (hd cmd).word { 41 "ntest" => 42 if (len cmd != 2) 43 ctxt.fail("usage", "usage: ntest n"); 44 if (big (hd tl cmd).word == big 0) 45 return "false"; 46 } 47 return nil; 48} 49 50runsbuiltin(ctxt: ref Sh->Context, nil: Sh, 51 cmd: list of ref Sh->Listnode): list of ref Listnode 52{ 53 # only one sbuiltin: expr. 54 stk: list of big; 55 lastop := -1; 56 lastn := -1; 57 lastname := ""; 58 radix: int; 59 (cmd, radix) = opts(ctxt, tl cmd); 60 for (; cmd != nil; cmd = tl cmd) { 61 w := (hd cmd).word; 62 op := -1; 63 nops := 2; 64 case w { 65 "+" => 66 op = PLUS; 67 "-" => 68 op = MINUS; 69 "x" or "*" or "×" => 70 op = TIMES; 71 "/" => 72 op = DIVIDE; 73 "%" => 74 op = MOD; 75 "and" => 76 op = AND; 77 "or" => 78 op = OR; 79 "xor" => 80 op = XOR; 81 "_"=> 82 (op, nops) = (UMINUS, 1); 83 "<<" or "shl" => 84 op = SHL; 85 ">>" or "shr" => 86 op = SHR; 87 "=" or "==" or "eq" => 88 op = EQ; 89 "!=" or "neq" => 90 op = NEQ; 91 ">" or "gt" => 92 op = GT; 93 "<" or "lt" => 94 op = LT; 95 ">=" or "ge" => 96 op = GE; 97 "<=" or "le" => 98 op = LE; 99 "!" or "not" => 100 (op, nops) = (NOT, 1); 101 "~" => 102 (op, nops) = (BNOT, 1); 103 "rep" => 104 (op, nops) = (REP, 0); 105 "seq" => 106 (op, nops) = (SEQ, 2); 107 } 108 if (op == -1) 109 stk = makenum(ctxt, w) :: stk; 110 else 111 stk = operator(ctxt, stk, op, nops, lastop, lastn, w, lastname); 112 lastop = op; 113 lastn = nops; 114 lastname = w; 115 } 116 r: list of ref Listnode; 117 for (; stk != nil; stk = tl stk) 118 r = ref Listnode(nil, big2string(hd stk, radix)) :: r; 119 return r; 120} 121 122opts(ctxt: ref Context, cmd: list of ref Listnode): (list of ref Listnode, int) 123{ 124 radix := 10; 125 if (cmd == nil) 126 return (nil, 10); 127 w := (hd cmd).word; 128 if (len w < 2) 129 return (cmd, 10); 130 if (w[0] != '-' || (w[1] >= '0' && w[1] <= '9')) 131 return (cmd, 10); 132 if (w[1] != 'r') 133 ctxt.fail("usage", "usage: expr [-r radix] [arg...]"); 134 if (len w > 2) 135 w = w[2:]; 136 else { 137 if (tl cmd == nil) 138 ctxt.fail("usage", "usage: expr [-r radix] [arg...]"); 139 cmd = tl cmd; 140 w = (hd cmd).word; 141 } 142 r := int w; 143 if (r <= 0 || r > 36) 144 ctxt.fail("usage", "expr: invalid radix " + string r); 145 return (tl cmd, int w); 146} 147 148operator(ctxt: ref Context, stk: list of big, op, nops, lastop, lastn: int, 149 opname, lastopname: string): list of big 150{ 151 al: list of big; 152 for (i := 0; i < nops; i++) { 153 if (stk == nil) 154 ctxt.fail("empty stack", 155 sys->sprint("expr: empty stack on op '%s'", opname)); 156 al = hd stk :: al; 157 stk = tl stk; 158 } 159 return oper(ctxt, al, op, lastop, lastn, lastopname, stk); 160} 161 162# args are in reverse order 163oper(ctxt: ref Context, args: list of big, op, lastop, lastn: int, 164 lastopname: string, stk: list of big): list of big 165{ 166 if (op == REP) { 167 if (lastop == -1 || lastop == SEQ || lastn != 2) 168 ctxt.fail("usage", "expr: bad operator for rep"); 169 if (stk == nil || tl stk == nil) 170 return stk; 171 while (tl stk != nil) 172 stk = operator(ctxt, stk, lastop, 2, -1, -1, lastopname, nil); 173 return stk; 174 } 175 n2 := big 0; 176 n1 := hd args; 177 if (tl args != nil) 178 n2 = hd tl args; 179 r := big 0; 180 case op { 181 EQ => r = big(n1 == n2); 182 NEQ => r = big(n1 != n2); 183 GT => r = big(n1 > n2); 184 LT => r = big(n1 < n2); 185 GE => r = big(n1 >= n2); 186 LE => r = big(n1 <= n2); 187 PLUS => r = big(n1 + n2); 188 MINUS => r = big(n1 - n2); 189 NOT => r = big(n1 != big 0); 190 DIVIDE => 191 if (n2 == big 0) 192 ctxt.fail("divide by zero", "expr: division by zero"); 193 r = n1 / n2; 194 MOD => 195 if (n2 == big 0) 196 ctxt.fail("divide by zero", "expr: division by zero"); 197 r = n1 % n2; 198 TIMES => r = n1 * n2; 199 AND => r = n1 & n2; 200 OR => r = n1 | n2; 201 XOR => r = n1 ^ n2; 202 UMINUS => r = -n1; 203 BNOT => r = ~n1; 204 SHL => r = n1 << int n2; 205 SHR => r = n1 >> int n2; 206 SEQ => return seq(n1, n2, stk); 207 } 208 return r :: stk; 209} 210 211seq(n1, n2: big, stk: list of big): list of big 212{ 213 incr := big 1; 214 if (n2 < n1) 215 incr = big -1; 216 for (; n1 != n2; n1 += incr) 217 stk = n1 :: stk; 218 return n1 :: stk; 219} 220 221makenum(ctxt: ref Context, s: string): big 222{ 223 if (s == nil || (s[0] != '-' && (s[0] < '0' || s[0] > '9'))) 224 ctxt.fail("usage", sys->sprint("expr: unknown operator '%s'", s)); 225 226 t := s; 227 if (neg := s[0] == '-') 228 s = s[1:]; 229 radix := 10; 230 for (i := 0; i < len s && i < 3; i++) { 231 if (s[i] == 'r') { 232 radix = int s; 233 s = s[i+1:]; 234 break; 235 } 236 } 237 if (radix == 10) 238 return big t; 239 if (radix == 0 || radix > 36) 240 ctxt.fail("usage", "expr: bad number " + t); 241 n := big 0; 242 for (i = 0; i < len s; i++) { 243 if ('0' <= s[i] && s[i] <= '9') 244 n = (n * big radix) + big(s[i] - '0'); 245 else if ('a' <= s[i] && s[i] < 'a' + radix - 10) 246 n = (n * big radix) + big(s[i] - 'a' + 10); 247 else if ('A' <= s[i] && s[i] < 'A' + radix - 10) 248 n = (n * big radix) + big(s[i] - 'A' + 10); 249 else 250 break; 251 } 252 if (neg) 253 return -n; 254 return n; 255} 256 257big2string(n: big, radix: int): string 258{ 259 if (neg := n < big 0) { 260 n = -n; 261 } 262 s := ""; 263 do { 264 c: int; 265 d := int (n % big radix); 266 if (d < 10) 267 c = '0' + d; 268 else 269 c = 'a' + d - 10; 270 s[len s] = c; 271 n /= big radix; 272 } while (n > big 0); 273 t := s; 274 for (i := len s - 1; i >= 0; i--) 275 t[len s - 1 - i] = s[i]; 276 if (radix != 10) 277 t = string radix + "r" + t; 278 if (neg) 279 return "-" + t; 280 return t; 281} 282