// DC deskcalculator - Copyright 1997,2006 J.L. Bezemer // You can redistribute this file and/or modify it under // the terms of the GNU General Public License // This program is part of Arena // To make a RPN (HP style) deskcalculator you don't need very much: // Just a simple parser, a stack and an interpreter that follows // these three simple rules: // - if it is a command, execute it; // - if it isn't a command, try to convert it to a number; // - if it isn't a number, throw an exception. // The interpreter is not much more than a simple program // that receives tokens from the parser and tries to find them // in a table. // This is the equivalent of the basic parsing system in Forth. // It features a Terminal Input Buffer, called TIB, that is // filled by the word REFILL. The word PARSE returns a token // delimited by delimiter d. tib = ""; // This is the terminal input buffer string parse (string d) { do { p = strchr (tib, d); if (p == ()) { str = tib; tib = ""; } else { str = left (tib, p); tib = right (tib, strlen (tib) - (p + 1)); } } while ((str == "") && (strlen (tib) > 0)); global ("tib"); return (str); } void refill (s) { tib = trim (fgets (s)); global ("tib"); } // This is the equivalent of the Data Stack in Forth. // Values are pushed on the stack by push() and popped // from the stack by pop(). In Forth this is usually // a transparent process. The stackpointer is stored in sp. stack = mkarray (); // this is the stack sp = 0; // this is the stack pointer float pop () { if (sp > 0) { f = (stack [--sp]); global ("stack", "sp"); return (f); } else { throw ("Stack empty"); return (0.0); } } void push (forced float f) { stack [sp++] = f; global ("stack", "sp"); } // These are all commands of the interpreter. They can be reduced to // push() and pop() calls. // These words do basic aritmethic void add () { push (pop () + pop ()); } void sub () { a = pop (); push (pop () - a); } void div () { a = pop (); if (a == 0.0) throw ("Divide by zero"); else push (pop () / a); } void mul () { push (pop () * pop ()); } // These words do extended arithmatic void mod () { a = pop (); if (a == 0.0) throw ("Divide by zero"); else push (pop () % a); } void abs () { a = pop (); push (a < 0 ? -a : a); } void negate () { push (0 - pop ()); } void min () { a = pop (); b = pop (); push (a > b ? b : a); } void max () { a = pop (); b = pop (); push (a > b ? a : b); } // These words do binary aritmatic void invert () { a = pop (); push (~a); } void or () { push (pop () | pop ()); } void and () { push (pop () & pop ()); } void xor () { push (pop () ^ pop ()); } void lshift () { a = pop (); push (pop () << a); } void rshift () { a = pop (); push (pop () >> a); } // These words do quick (binary) aritmatic void twotimes () { push ((pop () << 1)); } void twodiv () { push ((pop () >> 1)); } void increment () { push (pop () + 1); } void decrement () { push (pop () - 1); } // These words handle built-in variables vars = mkarray (0, 0, 0, 0, 0, 0, 0, 0); void var_a () { push (0.0); } void var_b () { push (1.0); } void var_c () { push (2.0); } void var_d () { push (3.0); } void var_e () { push (4.0); } void var_f () { push (5.0); } void var_g () { push (6.0); } void var_h () { push (7.0); } void fetch () { a = (int) pop (); if ((a < 0) || (a > 7)) throw ("Bad variable"); else push (vars [a]); } void store () { a = (int) pop (); if ((a < 0) || (a > 7)) throw ("Bad variable"); else vars [a] = pop (); global ("vars"); } void plusstore () { a = (int) pop (); if ((a < 0) || (a > 7)) throw ("Bad variable"); else vars [a] += pop (); global ("vars"); } void question () { fetch (); dot (); } // These words do basic stack operations void drop () { a = pop (); } void dup () { a = pop (); push (a); push (a); } void rot () { a = pop (); b = pop (); c = pop (); push (b); push (c); push (a); } void swap () { a = pop (); b = pop (); push (a); push (b); } void over () { a = pop (); b = pop (); push (b); push (a); push (b); } void dots () { for (x = 0; x < sp; x++) printf ("%f ", stack [x]); print ("(TOS)\n"); } // These are I/O words void emit () { print (chr ((int) pop ())); } void dot () { printf ("%f ", pop ()); } void dotparen () { print (parse (")")); } void cr () { print ("\n"); } void space () { print (" "); } void spaces () { a = pop (); for (x = 0; x < a; x++) space (); } // These are miscellenious words void paren () { a = parse (")"); } void char () { push (ord (parse (" "))); } void times () { push ((float) time()); } void random () { push ((float) rand (0, RAND_MAX)); } void quit () { exit (0); } void depth () { push ((float) sp); } // This is the basic interpreter. Here the tokens are mapped // to the actual words we defined before. words = mkstruct ( "+", add, "-", sub, "/", div, "*", mul, ".", dot, "n", dot, "mod", mod, "%", mod, "abs", abs, "negate", negate, "invert", invert, "min", min, "max", max, "or", or, "and", and, "xor", xor, "lshift", lshift, "rshift", rshift, "2*", twotimes, "2/", twodiv, "1+", increment, "1-", decrement, "a.", var_a, "b.", var_b, "c.", var_c, "d.", var_d, "e.", var_e, "f.", var_f, "g.", var_g, "h.", var_h, "!", store, "+!", plusstore, "@", fetch, "?", question, "char", char, "emit", emit, "space", space, "spaces", spaces, ".(", dotparen, "cr", cr, "drop", drop, "dup", dup, "d", dup, "rot", rot, "swap", swap, "r", swap, "over", over, "time", times, "random", random, "(", paren, ".s", dots, "f", dots, "depth", depth, "z", depth, "q", quit, "quit", quit, "bye", quit, "exit", quit ); // This routine tries to figure out whether this is a number or a misspelled // command. bool is_number (string w) { if (strlen (w) == 0) return (false); else { s = left (w, 1); if ((s != "-") && (s != "+") && (! isdigit (s))) return (false); else { n = 0; for (x = 1; x < strlen (w); x++) { d = substr (w, x, 1); if ((! isdigit (d)) && (d != ".")) n++; } return (n == 0); } } } // execute() takes the actual string and parses it. Every token is mapped to // the table and any equivalent words are executed. If a token does not // contain a word, it tries to convert it to a float. If that fails too, // it is an unsupported token. void execute () { do { token = tolower (parse (" ")); // get a token if (token != "") { // if it isn't empty, look it up word = struct_get (words, token); if (word == ()) // if it wasn't found, convert it if (is_number (token)) push (token); else throw ("Undefined name"); else word (); // execute the word we found } } while (token != ""); // continue until no more words } // in the input buffer // The interpreter shows the prompt, refills the buffer and tries to execute // whatever has been entered. If this causes an error, it is caught. void interpret (ch, prompt) { for (;;) { if (prompt) print ("OK\n"); // show a prompt if interactive refill (ch); // get a line try execute (); // now try to execute it catch (e) printf ("%s ", e); // an exception was thrown } } // Use the keyboard, unless you've issued a valid file at the prompt. prompt = false; if ((argc > 1) && ((ch = fopen (argv [1], "r")) != ())); else { ch = stdin; prompt = true; } interpret (ch, prompt);