#!/usr/bin/gawk -f # # # calc # # This is a simple interactive numeric calculator, you enter # expressions and calc prints the results. # function skipws(string) { sub(/^[ \t]+/, "", string); return (string); } function getval(name, val) { val = ""; if (name in var) val = var[name]; else val = ENVIRON[name]; return (val); } function interpret(arr, i, n, y, name) { sp = 0; n = arr["cp"]; for (i=1; i<=n; i++) { opc = arr[i]; if (opc == "number" || opc =="string") cell[++sp] = arr[++i]; else if (opc == "variable") cell[++sp] = getval(arr[++i]); # var[arr[++i]]; else if (opc == "assign") var[arr[++i]] = cell[sp]; else if (opc == "+") { y = cell[sp--]; cell[sp] += y; } else if (opc == "-") { y = cell[sp--]; cell[sp] -= y; } else if (opc == "*") { y = cell[sp--]; cell[sp] *= y; } else if (opc == "/") { y = cell[sp--]; cell[sp] /= y; } else if (opc == "%") { y = cell[sp--]; cell[sp] %= y; } else if (opc == "**") { y = cell[sp--]; x = cell[sp]; cell[sp] = exp(log(x) * y); } else if (opc == "||") { y = cell[sp--]; cell[sp] = (cell[sp] != 0 || y != 0); } else if (opc == "&&") { y = cell[sp--]; cell[sp] = (cell[sp] != 0 && y != 0); } else if (opc == "^^") { y = cell[sp--]; cell[sp] = (((cell[sp] != 0) + (y != 0)) == 1); } else if (opc == "^") { y = cell[sp--]; cell[sp] = cell[sp] y; } else if (opc == "unary") { name = arr[++i]; if (name == "-") cell[sp] = -cell[sp]; else if (name == "!") cell[sp] = (cell[sp] == 0)? 1: 0; } else if (opc == "function") { x = cell[sp]; name = arr[++i]; if (name == "sqrt") cell[sp] = (x > 0)? sqrt(x): sqrt(-x); else if (name == "exp") cell[sp] = exp(x); else if (name == "log") cell[sp] = (x > 0)? log(x): 0; } else if (opc == ",") ; # nothing } return (sp); } function getvar( pattern, token, expr) { pattern = "[A-Za-z_][A-Za-z_0-9]*"; sub(/^[ \t]+/, "", EXPR); if (EXPR == "") return ("\001"); if (match(EXPR, pattern) != 1) return (""); token = substr(EXPR, 1, RLENGTH); EXPR = skipws(substr(EXPR, RLENGTH+1)); return (token); } function getnext(get, c, q, i, pattern, token, expr) { pattern = "[.0-9]+(e[+-]?[0-9])?|['\"]" \ "|[A-Za-z_][A-Za-z_0-9]*([ \t]*\\()?" \ "|[-+\\*\\/%=\\(\\),!\\^]|\\*\\*" \ "|\\|\\||\\^\\^|&&"; sub(/^[ \t]+/, "", EXPR); if (EXPR == "") return ("\001"); if (match(EXPR, pattern) != 1) return (""); if ((q = substr(EXPR, 1, 1)) == "'" || q == "\"") { token = q; for (i=2; i<=length(EXPR); i++) { if ((c = substr(EXPR, i, 1)) == q) { if (substr(EXPR, i+1, 1) != q) break; else i++; } token = token c; } token = token q; RLENGTH = i; } else { token = substr(EXPR, 1, RLENGTH); } if (get == 0) EXPR = skipws(substr(EXPR, RLENGTH+1)); return (token); } function code1(arr, opc) { arr["cp"]++; arr[arr["cp"]] = opc; return (0); } function code(arr, type, par) { code1(arr, type); if (par != "") code1(arr, par); return (0); } function codestring(arr, type, par) { code1(arr, type); code1(arr, par); return (0); } function level1(arr, token, x) { token = level2a(arr, token); while ((x = token) == ",") { token = level2a(arr, token); code(arr, x); } return (token); } function level2a(arr, token, x) { token = level2b(arr, token); while ((x = token) == "||") { token = level2b(arr, token); code(arr, x); } return (token); } function level2b(arr, token, x) { token = level2c(arr, token); while ((x = token) == "&&") { token = level2c(arr, token); code(arr, x); } return (token); } function level2c(arr, token, x) { token = level3(arr, token); while ((x = token) == "^^") { token = level3(arr, token); code(arr, x); } return (token); } function level3(arr, token, x) { token = level4(arr, token); while ((x = token) == "^") { token = level4(arr, token); code(arr, x); } return (token); } function level4(arr, token, x) { token = level5(arr, token); while ((x = token) == "+" || x == "-") { token = level5(arr, token); code(arr, x); } return (token); } function level5(arr, token, x) { token = level6(arr, token); while ((x = token) == "*" || x == "/" || x == "%") { token = level6(arr, token); code(arr, x); } return (token); } function level6(arr, token, x) { token = level7(arr, token); while ((x = token) == "**") { token = level7(arr, token); code(arr, x); } return (token); } function level7(arr, token, x) { token = getnext(1); if (index("+-!", token) == 0) token = level9(arr, token); else { x = getnext(0); token = level9(arr, token); code(arr, "unary", x); } return (token); } function level9(arr, token, count, x, varname, name, i, type, list, n, p, argv) { token = getnext(0); count = 0; while (1) { if (token == "(") { token = level2a(arr, ""); if (token != ")") { printf ("expression error: %s\n", EXPR); return ("\002"); } } else if ((p = substr(token, 1, 1)) == "'" || p == "\"") { codestring(arr, "string", substr(token, 2, length(token) - 2)); } else if (token ~ /^[.0-9]/) { code(arr, "number", token); } else if (token ~ /^[A-Za-z_]/) { name = token; gsub(/[^A-Z0-9a-z_]+/, "", name); if (token ~ /\($/) { name = token; sub(/[ \t]*\(.*$/, "", name); if (! (name in partab)) { printf ("? %s\n", name) >>STDERR; return ("\002"); } list = partab[name]; for (i=1; i<=length(list); i++) { if (i > 1) { if (getnext(0) != ",") { printf ("? %s, par= %d\n", name, i) >>STDERR; return ("\002"); } } type = substr(list, i, 1); if (type == "v") { if ((varname = getvar()) == "" || varname == "\001") { printf ("? %s, par= %d\n", name, i) >>STDERR; return ("\002"); } code(arr, "string", varname); token = getnext(0) != ")"; } else { token = level2a(arr, ""); } } if (token != ")") { printf ("? %s, par= %d\n", name, i) >>STDERR; return ("\002"); } code(arr, "function", name); } else if (EXPR ~ /^[ \t]?=[^=<>]/) { varname = token; x = getnext(0); token = level2a(arr, ""); code(arr, "assign", varname); } else code(arr, "variable", token); } else break; if (token != "") { count++; if (count > 1) code(arr, "^"); } token = getnext(0); } if (count == 0) { printf ("? %s %s\n", token, EXPR) >>STDERR; return ("\002"); } return (token); } function compile(arr, expression, token) { partab["sqrt"] = "e"; partab["exp"] = "e"; partab["log"] = "e"; EXPR = expression; if ((token = level1(arr, "")) != "\001") { if (token != "\002") printf ("? %s %s\n", program, token, EXPR) >>STDERR; return (-1); } return (0); } function nextarg(par, arg) { if (argi >= ARGC) { printf ("%s: missing argument: %s\n", program, par) >>STDERR; exit (1); } arg = ARGV[argi]; ARGV[argi++] = ""; return (arg); } BEGIN { STDERR = "/dev/stderr"; program = "calc"; argi = 1; while (1) { printf (": "); if (getline line <= 0) break; vm["cp"] = 0; rc = compile(vm, line); if (rc != 0) rc = "\001"; else { sp = interpret(vm); for (i=1; i<=sp; i++) { val = sprintf ("%.8g", cell[i]); if (match(val, /e[+-]/) > 0) { num = substr(val, 1, RSTART-1); ee = substr(val, RSTART+RLENGTH); while ((ee % 3) != 0) { num = num * 10; ee--; } val = num substr(val, RSTART, RLENGTH) ee; } if (i > 1) printf (" "); printf ("%s", val); } printf ("\n"); var["last"] = cell[sp]; } } printf ("\n"); exit (0); }