ref: db1eb844461b07a25ca49117851fa874fd88e065
dir: /appl/cmd/fc.b/
implement Fc; include "sys.m"; sys: Sys; include "draw.m"; include "math.m"; math: Math; include "string.m"; str: String; include "regex.m"; regex: Regex; Fc: module { init: fn(nil: ref Draw->Context, argv: list of string); }; UNARY, BINARY, SPECIAL: con iota; oSWAP, oDUP, oREP, oSUM, oPRNUM, oMULT, oPLUS, oMINUS, oDIV, oDIVIDE, oMOD, oSHIFTL, oSHIFTR, oAND, oOR, oXOR, oNOT, oUMINUS, oFACTORIAL, oPOW, oHYPOT, oATAN2, oJN, oYN, oSCALBN, oCOPYSIGN, oFDIM, oFMIN, oFMAX, oNEXTAFTER, oREMAINDER, oFMOD, oPOW10, oSQRT, oEXP, oEXPM1, oLOG, oLOG10, oLOG1P, oCOS, oCOSH, oSIN, oSINH, oTAN, oTANH, oACOS, oASIN, oACOSH, oASINH, oATAN, oATANH, oERF, oERFC, oJ0, oJ1, oY0, oY1, oILOGB, oFABS, oCEIL, oFLOOR, oFINITE, oISNAN, oRINT, oLGAMMA, oMODF, oDEG, oRAD: con iota; Op: adt { name: string; kind: int; op: int; }; ops := array[] of { Op ("swap", SPECIAL, oSWAP), ("dup", SPECIAL, oDUP), ("rep", SPECIAL, oREP), ("sum", SPECIAL, oSUM), ("p", SPECIAL, oPRNUM), ("x", BINARY, oMULT), ("×", BINARY, oMULT), ("pow", BINARY, oPOW), ("xx", BINARY, oPOW), ("+", BINARY, oPLUS), ("-", BINARY, oMINUS), ("/", BINARY, oDIVIDE), ("div", BINARY, oDIV), ("%", BINARY, oMOD), ("shl", BINARY, oSHIFTL), ("shr", BINARY, oSHIFTR), ("and", BINARY, oAND), ("or", BINARY, oOR), ("⋀", BINARY, oAND), ("⋁", BINARY, oOR), ("xor", BINARY, oXOR), ("not", UNARY, oNOT), ("_", UNARY, oUMINUS), ("factorial", UNARY, oFACTORIAL), ("!", UNARY, oFACTORIAL), ("pow", BINARY, oPOW), ("hypot", BINARY, oHYPOT), ("atan2", BINARY, oATAN2), ("jn", BINARY, oJN), ("yn", BINARY, oYN), ("scalbn", BINARY, oSCALBN), ("copysign", BINARY, oCOPYSIGN), ("fdim", BINARY, oFDIM), ("fmin", BINARY, oFMIN), ("fmax", BINARY, oFMAX), ("nextafter", BINARY, oNEXTAFTER), ("remainder", BINARY, oREMAINDER), ("fmod", BINARY, oFMOD), ("pow10", UNARY, oPOW10), ("sqrt", UNARY, oSQRT), ("exp", UNARY, oEXP), ("expm1", UNARY, oEXPM1), ("log", UNARY, oLOG), ("log10", UNARY, oLOG10), ("log1p", UNARY, oLOG1P), ("cos", UNARY, oCOS), ("cosh", UNARY, oCOSH), ("sin", UNARY, oSIN), ("sinh", UNARY, oSINH), ("tan", UNARY, oTAN), ("tanh", UNARY, oTANH), ("acos", UNARY, oACOS), ("asin", UNARY, oASIN), ("acosh", UNARY, oACOSH), ("asinh", UNARY, oASINH), ("atan", UNARY, oATAN), ("atanh", UNARY, oATANH), ("erf", UNARY, oERF), ("erfc", UNARY, oERFC), ("j0", UNARY, oJ0), ("j1", UNARY, oJ1), ("y0", UNARY, oY0), ("y1", UNARY, oY1), ("ilogb", UNARY, oILOGB), ("fabs", UNARY, oFABS), ("ceil", UNARY, oCEIL), ("floor", UNARY, oFLOOR), ("finite", UNARY, oFINITE), ("isnan", UNARY, oISNAN), ("rint", UNARY, oRINT), ("rad", UNARY, oRAD), ("deg", UNARY, oDEG), ("lgamma", SPECIAL, oLGAMMA), ("modf", SPECIAL, oMODF), }; nHEX, nBINARY, nOCTAL, nRADIX1, nRADIX2, nREAL, nCHAR: con iota; pats0 := array[] of { nHEX => "-?0[xX][0-9a-fA-F]+", nBINARY => "-?0[bB][01]+", nOCTAL => "-?0[0-7]+", nRADIX1 => "-?[0-9][rR][0-8]+", nRADIX2 => "-?[0-3][0-9][rR][0-9a-zA-Z]+", nREAL => "-?(([0-9]+(\\.[0-9]+)?)|([0-9]*(\\.[0-9]+)))([eE]-?[0-9]+)?", nCHAR => "@.", }; RADIX, ANNOTATE, CHAR: con 1 << (iota + 10); outbase := 10; pats: array of Regex->Re; stack: list of real; last_op: Op; stderr: ref Sys->FD; usage() { sys->fprint(stderr, "usage: fc [-xdbB] [-r radix] <postfix expression>\n" + "option specifies output format:\n" + "\t-d decimal (default)\n" + "\t-x hex\n" + "\t-o octal\n" + "\t-b binary\n" + "\t-B annotated binary\n" + "\t-c character\n" + "\t-r <radix> specified base in Limbo 99r9999 format\n" + "operands are decimal(default), hex(0x), octal(0), binary(0b), radix(99r)\n"); sys->fprint(stderr, "operators are:\n"); for (i := 0; i < len ops; i++) sys->fprint(stderr, "%s ", ops[i].name); sys->fprint(stderr, "\n"); raise "fail:usage"; } init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; stderr = sys->fildes(2); math = load Math Math->PATH; regex = load Regex Regex->PATH; if (regex == nil) { sys->fprint(stderr, "fc: cannot load %s: %r\n", Regex->PATH); raise "fail:error"; } initpats(); if (argv == nil || tl argv == nil) return; argv = tl argv; a := hd argv; if (len a > 1 && a[0] == '-' && number(a).t0 == 0) { case a[1] { 'd' => outbase = 10; 'x' => outbase = 16; 'o' => outbase = 8; 'b' => outbase = 2; 'c' => outbase = CHAR; 'r' => r := 0; if (len a > 2) r = int a[2:]; else if (tl argv == nil) usage(); else { argv = tl argv; r = int hd argv; } if (r < 2 || r > 36) usage(); outbase = r | RADIX; 'B' => outbase = 2 | ANNOTATE; * => sys->fprint(stderr, "fc: unknown option -%c\n", a[1]); usage(); } argv = tl argv; } math->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX); for (; argv != nil; argv = tl argv) { (ok, x) := number(hd argv); if (ok) stack = x :: stack; else { op := find(hd argv); exec(op); last_op = op; } } sp: list of real; for (; stack != nil; stack = tl stack) sp = hd stack :: sp; # print stack bottom first for (; sp != nil; sp = tl sp) printnum(hd sp); } printnum(n: real) { case outbase { CHAR => sys->print("@%c\n", int n); 2 => sys->print("%s\n", binary(big n)); 2 | ANNOTATE => sys->print("%s\n", annotatebinary(big n)); 8 => sys->print("%#bo\n", big n); 10 => sys->print("%g\n", n); 16 => sys->print("%#bx\n", big n); * => if ((outbase & RADIX) == 0) error("unknown output base " + string outbase); sys->print("%s\n", big2string(big n, outbase & ~RADIX)); } } # convert to binary string, keeping multiples of 8 digits. binary(n: big): string { s := "0b"; for (j := 7; j > 0; j--) if ((n & (big 16rff << (j * 8))) != big 0) break; for (i := 63; i >= 0; i--) if (i / 8 <= j) s[len s] = (int (n >> i) & 1) + '0'; return s; } annotatebinary(n: big): string { s := binary(n); a := s + "\n "; ndig := len s - 2; for (i := ndig - 1; i >= 0; i--) a[len a] = (i % 10) + '0'; if (ndig < 10) return a; a += "\n "; for (i = ndig - 1; i >= 10; i--) { if (i % 10 == 0) a[len a] = (i / 10) + '0'; else a[len a] = ' '; } return a; } find(name: string): Op { # XXX could do binary search here if we weren't a lousy performer anyway for (i := 0; i < len ops; i++) if (name == ops[i].name) break; if (i == len ops) error("invalid operator '" + name + "'"); return ops[i]; } exec(op: Op) { case op.kind { UNARY => unaryop(op.name, op.op); BINARY => binaryop(op.name, op.op); SPECIAL => specialop(op.name, op.op); } } unaryop(name: string, op: int) { assure(1, name); v := hd stack; case op { oNOT => v = real !(int v); oUMINUS => v = -v; oFACTORIAL => n := int v; v = 1.0; while (n > 0) v *= real n--; oPOW10 => v = math->pow10(int v); oSQRT => v = math->sqrt(v); oEXP => v = math->exp(v); oEXPM1 => v = math->expm1(v); oLOG => v = math->log(v); oLOG10 => v = math->log10(v); oLOG1P => v = math->log1p(v); oCOS => v = math->cos(v); oCOSH => v = math->cosh(v); oSIN => v = math->sin(v); oSINH => v = math->sinh(v); oTAN => v = math->tan(v); oTANH => v = math->tanh(v); oACOS => v = math->acos(v); oASIN => v = math->asin(v); oACOSH => v = math->acosh(v); oASINH => v = math->asinh(v); oATAN => v = math->atan(v); oATANH => v = math->atanh(v); oERF => v = math->erf(v); oERFC => v = math->erfc(v); oJ0 => v = math->j0(v); oJ1 => v = math->j1(v); oY0 => v = math->y0(v); oY1 => v = math->y1(v); oILOGB => v = real math->ilogb(v); oFABS => v = math->fabs(v); oCEIL => v = math->ceil(v); oFLOOR => v = math->floor(v); oFINITE => v = real math->finite(v); oISNAN => v = real math->isnan(v); oRINT => v = math->rint(v); oRAD => v = (v / 360.0) * 2.0 * Math->Pi; oDEG => v = v / (2.0 * Math->Pi) * 360.0; * => error("unknown unary operator '" + name + "'"); } stack = v :: tl stack; } binaryop(name: string, op: int) { assure(2, name); v1 := hd stack; v0 := hd tl stack; case op { oMULT => v0 = v0 * v1; oPLUS => v0 = v0 + v1; oMINUS => v0 = v0 - v1; oDIVIDE => v0 = v0 / v1; oDIV => v0 = real (big v0 / big v1); oMOD => v0 = real (big v0 % big v1); oSHIFTL => v0 = real (big v0 << int v1); oSHIFTR => v0 = real (big v0 >> int v1); oAND => v0 = real (big v0 & big v1); oOR => v0 = real (big v0 | big v1); oXOR => v0 = real (big v0 ^ big v1); oPOW => v0 = math->pow(v0, v1); oHYPOT => v0 = math->hypot(v0, v1); oATAN2 => v0 = math->atan2(v0, v1); oJN => v0 = math->jn(int v0, v1); oYN => v0 = math->yn(int v0, v1); oSCALBN => v0 = math->scalbn(v0, int v1); oCOPYSIGN => v0 = math->copysign(v0, v1); oFDIM => v0 = math->fdim(v0, v1); oFMIN => v0 = math->fmin(v0, v1); oFMAX => v0 = math->fmax(v0, v1); oNEXTAFTER => v0 = math->nextafter(v0, v1); oREMAINDER => v0 = math->remainder(v0, v1); oFMOD => v0 = math->fmod(v0, v1); * => error("unknown binary operator '" + name + "'"); } stack = v0 :: tl tl stack; } specialop(name: string, op: int) { case op { oSWAP => assure(2, name); stack = hd tl stack :: hd stack :: tl tl stack; oDUP => assure(1, name); stack = hd stack :: stack; oREP => if (last_op.kind != BINARY) error("invalid operator '" + last_op.name + "' for rep"); while (stack != nil && tl stack != nil) exec(last_op); oSUM => for (sum := 0.0; stack != nil; stack = tl stack) sum += hd stack; stack = sum :: nil; oPRNUM => assure(1, name); printnum(hd stack); stack = tl stack; oLGAMMA => assure(1, name); (s, lg) := math->lgamma(hd stack); stack = lg :: real s :: tl stack; oMODF => assure(1, name); (i, r) := math->modf(hd stack); stack = r :: real i :: tl stack; * => error("unknown operator '" + name + "'"); } } initpats() { pats = array[len pats0] of Regex->Re; for (i := 0; i < len pats0; i++) { (re, e) := regex->compile("^" + pats0[i] + "$", 0); if (re == nil) { sys->fprint(stderr, "fc: bad number pattern '^%s$': %s\n", pats0[i], e); raise "fail:error"; } pats[i] = re; } } number(s: string): (int, real) { case s { "pi" or "π" => return (1, Math->Pi); "e" => return (1, 2.71828182845904509); "nan" or "NaN" => return (1, Math->NaN); "-nan" or "-NaN" => return (1, -Math->NaN); "infinity" or "Infinity" or "∞" => return (1, Math->Infinity); "-infinity" or "-Infinity" or "-∞" => return (1, -Math->Infinity); "eps" or "macheps" => return (1, Math->MachEps); } for (i := 0; i < len pats; i++) { if (regex->execute(pats[i], s) != nil) break; } case i { nHEX => return base(s, 2, 16); nBINARY => return base(s, 2, 2); nOCTAL => return base(s, 1, 8); nRADIX1 => return base(s, 2, int s); nRADIX2 => return base(s, 3, int s); nREAL => return (1, real s); nCHAR => return (1, real s[1]); } return (0, Math->NaN); } base(s: string, i: int, radix: int): (int, real) { neg := s[0] == '-'; if (neg) i++; n := big 0; if (radix == 10) n = big s[i:]; else if (radix == 0 || radix > 36) return (0, Math->NaN); else { for (; i < len s; i++) { c := s[i]; if ('0' <= c && c <= '9') n = (n * big radix) + big(c - '0'); else if ('a' <= c && c < 'a' + radix - 10) n = (n * big radix) + big(c - 'a' + 10); else if ('A' <= c && c < 'A' + radix - 10) n = (n * big radix) + big(c - 'A' + 10); else return (0, Math->NaN); } } if (neg) n = -n; return (1, real n); } # stolen from /appl/cmd/sh/expr.b 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; } error(e: string) { sys->fprint(stderr, "fc: %s\n", e); raise "fail:error"; } assure(n: int, opname: string) { if (len stack < n) error("stack too small for op '" + opname + "'"); }