Floating point programmable calculator
Keith Packard
keith at reed.UUCP
Sat Mar 15 11:30:30 AEST 1986
I know this seems like an elementary programming assignment given to all
first year CS students, but I have found it quite useful in day-to-day
work. This program compiles a simple language to expression trees and
executes them. The language includes functions, arrays and most of the C
language control structures.
I have stuck this compiler into *many* other programs, it immediately adds
programmability to many utilities. For example, I have written a
programmable graphics editor using this compiler which lets the user
define functions to draw arbitrarily complicated shapes at the touch of
a key.
Send wisdom, fixes, bug reports to:
keith packard
...!tektronix!reed!keith
-------------------------------CUT HERE------------------------------
#!/bin/sh
# shar: Shell Archiver
# Run the following text with /bin/sh to create:
# Makefile
# README
# builtin.c
# expr.c
# func.c
# gram.y
# ic.1
# ic.h
# lex.l
# main.c
# symbol.c
# util.c
sed 's/^X//' << 'SHAR_EOF' > Makefile
X#
X# makefile for ic
X#
XCFLAGS=-O
XOFILES=gram.o lex.o symbol.o \
X expr.o main.o func.o builtin.o\
X util.o
X
Xic: $(OFILES)
X cc $(CFLAGS) -o ic $(OFILES) -lm
X
Xclean:
X rm -f $(OFILES) gram.c lex.c y.tab.h ic
X
Xgram.c: gram.y
X yacc -d gram.y
X mv y.tab.c gram.c
X
Xlex.c: lex.l
X lex lex.l
X mv lex.yy.c lex.c
Xbuiltin.o: ic.h
Xexpr.o: ic.h
Xexpr.o: y.tab.h
Xfunc.o: ic.h
Xgram.o: ic.h
Xlex.o: ic.h
Xlex.o: y.tab.h
Xmain.o: ic.h
Xsymbol.o: ic.h
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > README
XThis contains the sources for 'ic' an interpretive calculator.
X
XThe files involved are:
X
XREADME - this file
XMakefile - makefile for 'ic'
Xbuiltin.c - builtin functions and glue to math functions
Xexpr.c - build and execute expression trees
Xfunc.c - build function definition expression trees
Xgram.y - yacc grammar
Xic.1 - man page
Xic.h - global include file
Xlex.l - lexical analysis and file handling
Xmain.c - main line, argument parsing mostly
Xsymbol.c - symbol table management
Xutil.c - general purpose utilities
X
XThis program compiles on 4.2BSD and 2.9BSD systems. I don't use
Xany terminal driver features nor any 4.2 special open(2) features
Xso it should compile on practically anything. It also contains no
Xidentifiers not unique in 7 chars. It does not assume
Xsizeof (long) == sizeof (int) nor does it use void.
X
XHave fun!
X
XKeith Packard
X...!tektronix!reed!keith
X(503) 771-1305 (home)
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > builtin.c
X/*
X * builtin.c
X *
X * initialize builtin functions
X */
X
X# include "ic.h"
X# include <math.h>
X
X# define PI 3.14159265358979323846
X
Xstruct fbuiltin {
X double (*bf_func)();
X char *bf_name;
X int bf_argc;
X};
X
Xstruct vbuiltin {
X double bv_value;
X char *bv_name;
X};
X
Xdouble dowrt(), dowrtln(), doprintf(), doscanf();
Xdouble sinD(), cosD(), tanD(), asinD(), acosD(), atanD(), atan2D();
Xdouble dist();
X
Xstruct fbuiltin funcs[] = {
X dowrt, "write", -1,
X dowrtln, "writeln", -1,
X doprintf, "printf", -1,
X doscanf, "scanf", -1,
X exp, "exp", 1,
X log, "log", 1,
X log10, "log10", 1,
X pow, "pow", 2,
X sqrt, "sqrt", 1,
X fabs, "abs", 1,
X floor, "floor", 1,
X ceil, "ceil", 1,
X hypot, "hypot", 2,
X j0, "j0", 1,
X j1, "j1", 1,
X jn, "jn", 256 | 2,
X y0, "y0", 1,
X y1, "y1", 1,
X yn, "yn", 256 | 2,
X sinD, "sin", 1,
X cosD, "cos", 1,
X tanD, "tan", 1,
X asinD, "asin", 1,
X acosD, "acos", 1,
X atanD, "atan", 1,
X atan2D, "atan2", 2,
X sinh, "sinh", 1,
X cosh, "cosh", 1,
X tanh, "tanh", 1,
X dist, "dist", 4,
X 0, 0, 0,
X};
X
Xstruct vbuiltin vars[] = {
X 3.1415926535897932384626433, "pi",
X 2.7182818284590452353602874, "e",
X 0.0, 0,
X};
X
Xinitbuiltin ()
X{
X register struct fbuiltin *f;
X register struct vbuiltin *v;
X register symbol *s;
X symbol *insertSym();
X
X for (f = funcs; f->bf_name; f++) {
X s = insertSym (f->bf_name);
X s->s_type = BUILTIN;
X s->s_level = -1;
X s->s_builtin = f->bf_func;
X s->s_argc = f->bf_argc;
X }
X for (v = vars; v->bv_name; v++) {
X s = insertSym (v->bv_name);
X s->s_type = VARTYPE;
X s->s_level = 0;
X s->s_value = v->bv_value;
X }
X}
X
Xdouble
Xdowrt (n, p)
Xint n;
Xdouble *p;
X{
X while (n--) {
X printf ("%.15g ", *p++);
X }
X return 1.0;
X}
X
Xdouble
Xdowrtln (n, p)
Xint n;
Xdouble *p;
X{
X dowrt (n, p);
X putchar ('\n');
X return 1.0;
X}
X
Xdouble
Xdoprintf (n, p)
Xint n;
Xdouble *p;
X{
X char *fmt;
X char **strings;
X extern char **stringsp;
X
X strings = stringsp;
X ++p;
X for (fmt = *strings++; *fmt; ++fmt) {
X switch (*fmt) {
X case '%':
X switch (*++fmt) {
X case 'd':
X printf ("%.0f", *p);
X break;
X case 's':
X printf ("%s", *strings++);
X break;
X case 'f':
X printf ("%f", *p);
X break;
X case 'e':
X printf ("%e", *p);
X break;
X case 'g':
X printf ("%g", *p);
X break;
X case 'c':
X printf ("%c", (char) *p);
X break;
X case 'o':
X printf ("%lo", (long) *p);
X break;
X case 'x':
X printf ("%lx", (long) *p);
X break;
X default:
X putchar (*fmt);
X continue;
X }
X ++p;
X break;
X default:
X putchar (*fmt);
X }
X }
X}
X
Xdouble
Xdoscanf (n, p)
X{
X return 0.0;
X}
X
Xdouble
XsinD(a)
Xdouble a;
X{
X return sin (a * PI / 180);
X}
X
Xdouble
XcosD(a)
Xdouble a;
X{
X return cos (a * PI / 180);
X}
X
Xdouble
XtanD(a)
Xdouble a;
X{
X return tan (a * PI / 180);
X}
X
Xdouble
XasinD(a)
Xdouble a;
X{
X return asin (a) * 180/PI;
X}
X
Xdouble
XacosD(a)
Xdouble a;
X{
X return acos (a) * 180/PI;
X}
X
Xdouble
XatanD(a)
Xdouble a;
X{
X return atan (a) * 180/PI;
X}
X
Xdouble
Xatan2D(a,b)
Xdouble a,b;
X{
X return atan2 (a,b) * 180/PI;
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > expr.c
X
X/*
X * expr.c
X *
X * handle expression trees
X */
X
X# include "ic.h"
X# include "y.tab.h"
X
Xextern double pow();
X
X# define NSTACK 200
X
Xdouble stack[NSTACK];
Xdouble *fstack[NSTACK];
Xdouble *stackp = stack + NSTACK;
Xdouble **fstackp = fstack + NSTACK;
Xdouble *framep;
X
X# define N_NODES 200
X
Xint usedfirst = 0;
Xexpr firstblock[N_NODES];
X
Xexpr *exprhead;
X
Xexpr *
Xallocexpr()
X{
X expr *e;
X
X if (!exprhead) {
X if (!usedfirst) {
X exprhead = firstblock;
X ++usedfirst;
X } else
X exprhead = (expr *) malloc (N_NODES * sizeof (expr));
X e = exprhead;
X while (e < exprhead + N_NODES - 1) {
X e->e_left = e + 1;
X ++e;
X }
X e->e_left = 0;
X }
X e = exprhead;
X exprhead = e->e_left;
X e->e_tag = 0;
X return e;
X}
X
Xfreeexpr (e)
Xexpr *e;
X{
X if (!e)
X return;
X if (e->e_tag == STRING)
X free (e->e_string);
X e->e_left = exprhead;
X exprhead = e;
X}
X
Xfreetree (e)
Xexpr *e;
X{
X if (!e)
X return;
X switch (e->e_tag) {
X case NAME:
X case NUMBER:
X case STRING:
X break;
X default:
X freetree (e->e_left);
X freetree (e->e_right);
X }
X freeexpr (e);
X}
X
Xexpr *
XbuildOp(val, left, right)
Xexpr *left, *right;
X{
X register expr *foo = allocexpr();
X
X foo->e_tag = val;
X foo->e_left = left;
X foo->e_right = right;
X return foo;
X}
X
Xexpr *
XbuildNum(val)
Xdouble val;
X{
X register expr *foo = allocexpr();
X
X foo->e_tag = NUMBER;
X foo->e_number = val;
X return foo;
X}
X
Xexpr *
XbuildStr(s)
Xchar *s;
X{
X register expr *foo = allocexpr();
X foo->e_tag = STRING;
X foo->e_string = s;
X return foo;
X}
X
Xexpr *
XbuildVar(val)
Xsymbol *val;
X{
X register expr *foo = allocexpr();
X
X foo->e_tag = NAME;
X foo->e_name = val;
X return foo;
X}
X
Xdouble result;
X# define NSTRINGS 100
Xchar *stringstack[NSTRINGS];
Xchar **stringsp = stringstack + NSTRINGS;
X# define MAXARGS 10
X
Xdouble
Xeeval(f)
Xregister expr *f;
X{
X register symbol *s;
X register double r;
X register int args;
X register char **strsp;
X double argt[MAXARGS];
X double *argp;
X
X if (!f)
X return 1.0;
X switch (f->e_tag) {
X case NAME:
X s = f->e_name;
X switch (s->s_type) {
X case UNDEF:
X s->s_type = VARTYPE;
X s->s_value = 0.0;
X case VARTYPE:
X return s->s_value;
X case STACKTYPE:
X return framep[s->s_offset];
X default:
X eerror ("illegal use of identifier");
X return 0.0;
X }
X case OP: /* call function */
X s = f->e_left->e_name;
X if (s->s_type != FUNCTYPE && s->s_type != BUILTIN) {
X eerror ("illegal use of identifier");
X return 0.0;
X }
X f = f->e_right;
X argp = argt + MAXARGS;
X args = 0;
X strsp = stringsp;
X while (f) {
X *--argp = eeval (f->e_left);
X f = f->e_right;
X ++args;
X }
X result = call (s, args, argp);
X stringsp = strsp;
X return result;
X case NUMBER:
X return f->e_number;
X case STRING:
X *--stringsp = f->e_string;
X return 0.0;
X case PLUS:
X return eeval(f->e_left) + eeval(f->e_right);
X case MINUS:
X return eeval(f->e_left) - eeval(f->e_right);
X case DIVIDE:
X return eeval(f->e_left) / eeval(f->e_right);
X case TIMES:
X return eeval(f->e_left) * eeval(f->e_right);
X case MOD:
X return (double) (((int) eeval(f->e_left)) % ((int) eeval(f->e_right)));
X case POW:
X return pow (eeval (f->e_left), eeval (f->e_right));
X case EQ:
X return eeval(f->e_left) == eeval(f->e_right);
X case NE:
X return eeval(f->e_left) != eeval(f->e_right);
X case LT:
X return eeval(f->e_left) < eeval(f->e_right);
X case GT:
X return eeval(f->e_left) > eeval(f->e_right);
X case LE:
X return eeval(f->e_left) <= eeval(f->e_right);
X case GE:
X return eeval(f->e_left) >= eeval(f->e_right);
X case UMINUS:
X return -eeval(f->e_left);
X case FACT:
X args = eeval (f->e_right);
X r = 1;
X while (args > 0)
X r *= args--;
X return r;
X case BANG:
X return !eeval(f->e_left);
X case QUEST:
X return (eeval(f->e_left) ?
X eeval(f->e_right->e_left) :
X eeval(f->e_right->e_right));
X case AND:
X return eeval(f->e_left) && eeval(f->e_right);
X case OR:
X return eeval(f->e_left) || eeval(f->e_right);
X case ASSIGN:
X s = f->e_left->e_name;
X r = eeval(f->e_right);
X switch (s->s_type) {
X case UNDEF:
X s->s_type = VARTYPE;
X case VARTYPE:
X s->s_value = r;
X break;
X case STACKTYPE:
X framep[s->s_offset] = r;
X break;
X default:
X eerror ("illegal use of identifier");
X }
X return r;
X case INC:
X if (f->e_left == 0) {
X s = f->e_right->e_name;
X switch (s->s_type) {
X case UNDEF:
X s->s_type = VARTYPE;
X case VARTYPE:
X r = s->s_value;
X s->s_value += 1;
X break;
X case STACKTYPE:
X r = framep[s->s_offset];
X framep[s->s_offset] += 1;
X }
X return r;
X } else {
X s = f->e_right->e_name;
X switch (s->s_type) {
X case UNDEF:
X s->s_type = VARTYPE;
X case VARTYPE:
X return (s->s_value += 1);
X case STACKTYPE:
X return (framep[s->s_offset] += 1);
X }
X }
X case DEC:
X if (f->e_left == 0) {
X s = f->e_right->e_name;
X switch (s->s_type) {
X case UNDEF:
X s->s_type = VARTYPE;
X case VARTYPE:
X r = s->s_value;
X s->s_value -= 1.0;
X break;
X case STACKTYPE:
X r = framep[s->s_offset];
X framep[s->s_offset] -= 1.0;
X }
X return r;
X } else {
X s = f->e_right->e_name;
X switch (s->s_type) {
X case UNDEF:
X s->s_type = VARTYPE;
X case VARTYPE:
X return (s->s_value -= 1.0);
X case STACKTYPE:
X return (framep[s->s_offset] -= 1.0);
X }
X }
X }
X}
X
Xdouble
Xcall(s, argc, argv)
Xregister symbol *s;
Xregister double *argv;
X{
X int c;
X
X if (argc != (s->s_argc & 255) && s->s_argc != -1) {
X char buf[256];
X
X sprintf (buf,
X "function %s requiring %d arguments was called with %d",
X s->s_name, s->s_argc, argc);
X eerror (buf);
X return 0.0;
X }
X if (s->s_type == FUNCTYPE) {
X argv += argc;
X c = argc;
X while (c-- > 0) {
X *--stackp = *--argv;
X }
X *--fstackp = framep;
X framep = stackp;
X if (!s->s_expr) {
X eerror ("function is not compiled yet");
X return 0.0;
X }
X eval (s->s_expr);
X framep = *fstackp++;
X stackp += argc;
X return result;
X } else {
X switch (s->s_argc) {
X case -1:
X return (*s->s_builtin)(argc, argv);
X case 0:
X return (*s->s_builtin)();
X case 1:
X return (*s->s_builtin)(argv[0]);
X case 2:
X if (s->s_argc & 256)
X return (*s->s_builtin)((int) argv[0], argv[1]);
X else
X return (*s->s_builtin)(argv[0], argv[1]);
X case 3:
X return (*s->s_builtin)(argv[0], argv[1],
X argv[2]);
X case 4:
X return (*s->s_builtin)(argv[0], argv[1],
X argv[2], argv[3]);
X }
X }
X}
X
Xeval(f)
Xexpr *f;
X{
X register int tmp;
X
X switch (f->e_tag) {
X case EXPR:
X eeval(f->e_left);
X break;
X case IF:
X if (eeval(f->e_left))
X return eval(f->e_right);
X break;
X case ELSE:
X if (eeval(f->e_left))
X return eval(f->e_right->e_left);
X else
X return eval(f->e_right->e_right);
X case WHILE:
X while (eeval(f->e_left))
X switch (eval(f->e_right)) {
X case BRK:
X return 0;
X case RET:
X return RET;
X }
X break;
X case DO:
X do
X switch (eval(f->e_right)) {
X case BRK:
X return 0;
X case RET:
X return RET;
X }
X while (eeval(f->e_right));
X break;
X case FOR:
X for (eeval(f->e_left->e_left); eeval(f->e_left->e_right);
X eeval(f->e_right->e_left))
X switch (eval(f->e_right->e_right)) {
X case BRK:
X return 0;
X case RET:
X return RET;
X }
X break;
X case OC:
X do {
X switch (tmp = eval(f->e_left)) {
X case CONT:
X case BRK:
X case RET:
X return tmp;
X }
X f = f->e_right;
X } while (f != 0);
X break;
X case BREAK:
X return BRK;
X case CONTINUE:
X return CONT;
X case RETURN:
X result = eeval (f->e_right);
X return RET;
X }
X return 0;
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > func.c
X/*
X * func.c
X *
X * handle function definition
X */
X
X# include "ic.h"
X
Xstatic char *errs[] = {
X# define NOTFUNC 0
X "Non-function used as function name",
X};
X
Xdefinefunc (sym, args, autos, stat)
Xsymbol *sym;
Xexpr *args, *autos, *stat;
X{
X int offset, argc;
X expr *a;
X symbol *s, *tmp;
X
X if (sym->s_type != UNDEF && sym->s_type != FUNCTYPE) {
X eerror (errs[NOTFUNC]);
X return 0;
X }
X if (sym->s_expr) {
X freetree (sym->s_expr);
X freesyms (sym->s_local);
X }
X offset = 0;
X tmp = 0;
X argc = 0;
X for (a = args; a; a = a->e_right) {
X s = a->e_left->e_name;
X extractSym (s);
X s->s_next = tmp;
X tmp = s;
X s->s_type = STACKTYPE;
X s->s_offset = offset++;
X ++argc;
X }
X sym->s_argc = argc;
X offset = 0;
X for (a = autos; a; a = a->e_right) {
X s = a->e_left->e_name;
X extractSym (s);
X s->s_next = tmp;
X tmp = s;
X s->s_type = STACKTYPE;
X s->s_offset = --offset;
X }
X sym->s_local = tmp;
X sym->s_expr = stat;
X}
X
Xfixstack (e)
Xexpr *e;
X{
X symbol *s, *insertSym();
X char *malloc (), *strcpy();
X
X while (e) {
X s = e->e_left->e_name;
X if (s->s_level == 0)
X e->e_left->e_name = insertSym (
X strcpy (malloc (strlen (s->s_name) + 1),
X s->s_name));
X e = e->e_right;
X }
X}
X
Xfreesyms (s)
Xsymbol *s;
X{
X symbol *t;
X
X while (s) {
X t = s->s_next;
X symFree (s);
X s = t;
X }
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > gram.y
X/*
X * grammar for interpreter
X */
X
X%{
X
X# include <math.h>
X# include "ic.h"
X
Xexpr *buildOp();
Xexpr *buildNum();
Xexpr *buildVar();
Xexpr *buildConst();
Xexpr *buildStr();
Xdouble eeval();
Xint eval();
Xint ignorenl;
Xdouble dotval;
Xextern int yyfiledeep;
X
X%}
X
X%union {
X int ival;
X char *cval;
X double dval;
X expr *eval;
X symbol *nval;
X}
X
X%token <cval> STRING
X%token <dval> NUMBER
X%token <ival> NL ALL DOWN UP
X%token <ival> DEFINE QUIT READ SHELL EDIT
X%token <ival> WHILE IF ELSE FOR DO BREAK CONTINUE EXPR RETURN
X%token <ival> OP CP OS CS OC CC FUNC COMMA SEMI
X%token <nval> NAME AUTO
X%type <eval> expr var stat optexpr statlist primary arglist oarglist
X%type <eval> auto names fargs ofargs aexpr
X
X%nonassoc <ival> POUND
X%right <ival> ASSIGN
X%right <ival> QUEST COLON
X%left <ival> OR
X%left <ival> AND
X%left <ival> EQ NE
X%left <ival> LT GT LE GE
X%left <ival> PLUS MINUS
X%left <ival> TIMES DIVIDE MOD
X%right <ival> POW
X%right <ival> UMINUS BANG FACT
X%nonassoc <ival> INC DEC
X
X%%
Xlines : lines pcommand
X |
X { ignorenl = 0; }
X ;
Xpcommand: command
X | error
X { ignorenl = 0; } NL
X ;
Xcommand : QUIT NL
X { YYACCEPT; }
X | expr NL
X {
X if ($1->e_tag != ASSIGN)
X printf ("%.15g\n", dotval = eeval($1));
X else
X eeval ($1);
X freetree ($1);
X }
X | expr POUND expr NL
X {
X double base;
X
X base = eeval ($3);
X dotval = eeval ($1);
X freetree ($1);
X freetree ($3);
X printinbase (base, dotval);
X }
X | stat { eval ($1); freetree ($1); ignorenl = 0; } optnl
X | DEFINE { ignorenl = 1; } func { ignorenl = 0; } optnl
X | READ STRING
X {
X pushinput ($2);
X }
X | NL
X ;
Xoptnl : NL
X |
X ;
Xfunc : NAME OP { pushlevel(); } ofargs CP OC auto statlist CC
X {
X definefunc ($1, $4, $7, $8);
X poplevel();
X }
X ;
Xofargs : fargs
X { fixstack ($1); $$ = $1; }
X |
X { $$ = 0; }
Xfargs : NAME COMMA fargs
X { $$ = buildOp ($2, buildVar($1), $3); }
X | NAME
X { $$ = buildOp (COMMA, buildVar ($1), (expr *) 0); }
X ;
Xauto : AUTO names
X { fixstack ($2); $$ = $2; }
X |
X { $$ = 0; }
X ;
Xnames : NAME COMMA names
X { $$ = buildOp (AUTO, buildVar ($1), $3); }
X | NAME SEMI auto
X { $$ = buildOp (AUTO, buildVar ($1), $3); }
X ;
Xstat : IF ignorenl OP expr CP stat
X { $$ = buildOp(IF, $4, $6); }
X | IF ignorenl OP expr CP stat ELSE stat
X { $$ = buildOp(ELSE, $4, buildOp(ELSE, $6, $8)); }
X | WHILE ignorenl OP expr CP stat
X { $$ = buildOp(WHILE, $4, $6); }
X | DO ignorenl stat WHILE OP expr CP
X { $$ = buildOp(DO, $3, $6); }
X | FOR ignorenl OP optexpr SEMI optexpr SEMI optexpr CP stat
X {
X $$ = buildOp(FOR, buildOp(FOR, $4, $6),
X buildOp(FOR, $8, $10));
X }
X | BREAK ignorenl SEMI
X { $$ = buildOp(BREAK, (expr *) 0, (expr *) 0); }
X | CONTINUE ignorenl SEMI
X { $$ = buildOp(CONTINUE, (expr *) 0, (expr *) 0); }
X | RETURN ignorenl expr SEMI
X { $$ = buildOp (RETURN, (expr *) 0, $3); }
X | expr ignorenl SEMI
X { $$ = buildOp(EXPR, $1, (expr *) 0); }
X | OC ignorenl statlist CC
X { $$ = $3; }
X | SEMI ignorenl
X { $$ = buildOp((expr *) 0, (expr *) 0, (expr *) 0); }
X ;
Xignorenl: { ignorenl = 1; }
X ;
Xoptexpr : expr
X { $$ = $1; }
X |
X { $$ = 0; }
X ;
Xstatlist: stat statlist
X { $$ = buildOp(OC, $1, $2); }
X | stat
X { $$ = buildOp(OC, $1, (expr *) 0); }
X ;
Xvar : NAME
X { $$ = buildVar($1); }
X | var OS expr CS
X { $$ = buildOp ($2, $1, $3); }
X ;
Xexpr : primary
X | expr PLUS expr
X {
X binop:
X $$ = buildOp($2, $1, $3);
X }
X | expr MINUS expr
X { goto binop; }
X | expr TIMES expr
X { goto binop; }
X | expr DIVIDE expr
X { goto binop; }
X | expr MOD expr
X { goto binop; }
X | expr POW expr
X { goto binop; }
X | expr QUEST expr COLON expr
X { $$ = buildOp(QUEST, $1, buildOp(COLON, $3, $5)); }
X | expr AND expr
X { goto binop; }
X | expr OR expr
X { goto binop; }
X | var ASSIGN expr
X { goto binop; }
X | expr EQ expr
X { goto binop; }
X | expr NE expr
X { goto binop; }
X | expr LT expr
X { goto binop; }
X | expr GT expr
X { goto binop; }
X | expr LE expr
X { goto binop; }
X | expr GE expr
X { goto binop; }
X ;
Xprimary : MINUS primary %prec UMINUS
X { $$ = buildOp(UMINUS, $2, (expr *) 0); }
X | BANG primary
X { $$ = buildOp(BANG, $2, (expr *) 0); }
X | primary BANG %prec FACT
X { $$ = buildOp(FACT, (expr *) 0, $1); }
X | INC var
X { $$ = buildOp(INC, $2, (expr *) 0); }
X | var INC
X { $$ = buildOp(INC, (expr *) 0, $1); }
X | DEC var
X { $$ = buildOp(DEC, $2, (expr *) 0); }
X | var DEC
X { $$ = buildOp(DEC, (expr *) 0, $1); }
X | NUMBER
X { $$ = buildNum($1); }
X | var
X { $$ = $1; }
X | OP expr CP
X { $$ = $2; }
X | NAME OP oarglist CP
X {
X switch ($1->s_type) {
X case UNDEF:
X $1->s_level = 0;
X $1->s_type = FUNCTYPE;
X case FUNCTYPE:
X case BUILTIN:
X break;
X default:
X yyerror ("illegal use of identifier as function");
X YYERROR;
X }
X $$ = buildOp ($2, buildVar ($1), $3);
X }
X ;
Xoarglist: arglist
X |
X { $$ = 0; }
X ;
Xarglist : arglist COMMA aexpr
X { $$ = buildOp ($2, $3, $1); }
X | aexpr
X { $$ = buildOp (COMMA, $1, (expr *) 0); }
X ;
Xaexpr : expr
X { $$ = $1; }
X | STRING
X { $$ = buildStr ($1); }
X ;
X%%
X
X# include <stdio.h>
X
Xyywrap ()
X{
X return 1;
X}
X
Xyyerror (s)
Xchar *s;
X{
X extern char *yyfile;
X extern int yylineno;
X if (yyfiledeep)
X fprintf (stderr, "\"%s\": line %d, %s\n", yyfile, yylineno, s);
X else
X fprintf (stderr, "%s\n", s);
X}
X
Xeerror (s)
Xchar *s;
X{
X fprintf (stderr, "%s\n", s);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > ic.1
X.TH IC 1 motel6
X.SH NAME
Xic \- interpretive calculator, yet another desk calculator
X.SH SYNOPSIS
Xic [ file ... ]
X.SH DESCRIPTION
X\fIIc\fP is an attempt at a more useful calculator than provided by
X\fIbc\fP(1). Instead of using arbitrary precision integers (or fixed
Xpoint numbers), \fIic\fP uses simple floating point numbers.
X.PP
XAs a further aid, \fIic\fP has many standard mathematical functions
Xpre-programmed and, of course, it can be programed by the user as
Xwell.
X.PP
XThe input language more closely resembles C than \fIbc\fP; \fIelse\fP,
X\fI&&\fP and \fI||\fP are supported while \fBnewline\fP only terminates
Xstatements at ``reasonable'' times. \fBNewline\fP terminates either
Xexpressions or single statements typed by the user, inside compound
Xstatements or function definitions, only a \fB;\fP terminates.
XThis is designed to be more ``natural'' than \fIbc\fP was when
Xwriting function definitions.
X.PP
XThe syntax for \fIic\fP programs is as follows; name means
Xa sequence of letters, digits and _ not starting with a digit; E means
Xexpression; S means statement.
X
X.nf
XComments are enclosed in /* and */
X
XNames
X simple variables: name
X array elements: name[E]([E]...)
X
XOther operands
X floating point numbers - can include exponent, need not
X include decimal point nor sign.
X octal numbers - start with a 0, eg 014 is the same as 12.
X hexdecimal numbers - start with "0x", eg 0x1a is the same as 26.
X
X (E)
X
X name (E)
X
XOperators
X ++ -- (prefix and postfix, apply to names)
X - ! (unary minus, logical not and factorial)
X ^ (power)
X * / % (% is modulus)
X + -
X <= >= < >
X == !=
X || &&
X ?:
X =
X
XStat
X E;
X {S ... S}
X if (E) S
X if (E) S then S
X while (E) S
X do S while (E);
X for (opt-E;opt-E; opt-E) S
X ;
X break;
X continue;
X return E;
X
XFunction definitions
X define name (name,...,name)
X {
X auto name, name;
X
X S ... S
X }
X
XBuiltin functions
X exp, log, log10, pow, sqrt, fabs, floor, ceil,
X hypot, j0, j1, jn, y0, y1, yn, sin, cos, tan,
X asin, acos, atan, atan2, sinh, cosh, tanh, printf
X
X Note: trig functions take and return arguments in
X degrees - not radians!
X
X Printf accepts a reasonable sub-set of the stdio
X library version: %d, %e, %c, %g, %f, %s, %o work
X as expected.
X
X These functions are adapted from the C math library and,
X further questions about algorithm and argument usage
X should be directed to the manual.
X
XOther commands:
X quit exit ic
X read "file" read commands from a file
X expr1 # expr2 print expr1 in base expr2
X
X.fi
XAll function arguments are passed by value.
X
XFor example (taken from the \fIbc\fP manual:
X
X.nf
Xdefine exponent(x)
X{
X auto a, b, c, i, s;
X
X a = 1;
X b = 1;
X s = 1;
X for (i = 1;; i++) {
X a = a * x;
X b = b * i;
X c = a/b;
X if (abs(c) < 1e-6 == 0)
X return s;
X s = s + c;
X }
X}
X.fi
Xdefines a functino to compute an approximate value of the exponential
Xfunction and
X
X.nf
X for (i = 1; i < 10; i++)
X printf ("%g\n", exponent (i));
X.fi
X
Xprints approximate values of the exponential function of the first
Xten integers.
X.SH BUGS
XHa!
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > ic.h
X/*
X * ic.h
X *
X */
X
X# define UNDEF 0
X# define VARTYPE 1
X# define FUNCTYPE 2
X# define ARRAYTYPE 3
X# define STACKTYPE 4
X# define BUILTIN 5
X
Xtypedef struct symbol {
X struct symbol *s_next; /* linked hash chains */
X struct symbol *s_back; /* doubly linked for deleting */
X char *s_name;
X int s_type;
X int s_level;
X union {
X double S_value;
X int S_offset;
X struct {
X double *S_data;
X int S_size;
X } S_array;
X struct {
X int S_argc;
X union {
X struct {
X struct symbol *S_local;
X struct expr *S_expr;
X } S_user;
X double (*S_builtin)();
X } S_f;
X } S_func;
X } Su;
X} symbol;
X
X# define s_value Su.S_value
X# define s_offset Su.S_offset
X# define s_data Su.S_array.S_data
X# define s_size Su.S_array.S_size
X# define s_local Su.S_func.S_f.S_user.S_local
X# define s_expr Su.S_func.S_f.S_user.S_expr
X# define s_builtin Su.S_func.S_f.S_builtin
X# define s_argc Su.S_func.S_argc
X
X# define NOTHING 0
X# define CONT 1
X# define BRK 2
X# define RET 3
X
Xtypedef struct expr {
X int e_tag;
X union {
X struct {
X struct expr *Left;
X struct expr *Right;
X } Es;
X double Number;
X symbol *Name;
X char *String;
X } Eu;
X} expr;
X
X# define e_left Eu.Es.Left
X# define e_right Eu.Es.Right
X# define e_number Eu.Number
X# define e_name Eu.Name
X# define e_string Eu.String
X
Xdouble call();
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > lex.l
X%{
X# include "ic.h"
X# include "y.tab.h"
Xextern char *strcpy(), *malloc();
Xextern symbol *lookUp();
Xextern double atof();
Xextern int ignorenl;
Xextern int noprompt;
Xextern double dotval;
X#undef input
X#undef unput
X# define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==10?\
X (yylineno++,yytchar):yytchar)==EOF?popinput():yytchar)
X# define unput(c) {yytchar= (c);if(yytchar=='\n')yylineno--;*yysptr++=yytchar;}
X#define YYINDEEP 20
XFILE *yyinstack[YYINDEEP];
XFILE **yyinpt = yyinstack + YYINDEEP;
Xint linenos[YYINDEEP];
Xint *linenopt = linenos + YYINDEEP;
Xchar *yyfile = "";
Xchar *fnames[YYINDEEP];
Xchar **fnamept = fnames + YYINDEEP;
Xint yyfiledeep = 0;
X
X%}
X%%
X"/*" skipcomment();
Xauto return AUTO;
Xdefine return DEFINE;
Xquit return QUIT;
Xexit return QUIT;
Xshell return SHELL;
Xedit return EDIT;
Xread return READ;
Xwhile { yylval.ival = WHILE; return WHILE; }
Xfor { yylval.ival = FOR; return FOR; }
Xdo { yylval.ival = DO; return DO; }
Xif { yylval.ival = IF; return IF; }
Xelse { yylval.ival = ELSE; return ELSE; }
Xbreak { yylval.ival = BREAK; return BREAK; }
Xcontinue { yylval.ival = CONTINUE; return CONTINUE; }
Xreturn { yylval.ival = RETURN; return RETURN; }
X";" { yylval.ival = SEMI; return SEMI; }
X"," { yylval.ival = COMMA; return COMMA; }
X"." { yylval.dval = dotval; return NUMBER; }
X\n { if (!ignorenl) { yylval.ival = NL; return NL; } }
X"(" { yylval.ival = OP; return OP; }
X")" { yylval.ival = CP; return CP; }
X"[" { yylval.ival = OS; return OS; }
X"]" { yylval.ival = CS; return CS; }
X"{" { yylval.ival = OC; return OC; }
X"}" { yylval.ival = CC; return CC; }
X"+" { yylval.ival = PLUS; return PLUS; }
X"-" { yylval.ival = MINUS; return MINUS; }
X"*" { yylval.ival = TIMES; return TIMES; }
X"/" { yylval.ival = DIVIDE; return DIVIDE; }
X"%" { yylval.ival = MOD; return MOD; }
X"!" { yylval.ival = BANG; return BANG; }
X"#" { yylval.ival = POUND; return POUND; }
X"^" { yylval.ival = POW; return POW; }
X"=" { yylval.ival = ASSIGN; return ASSIGN; }
X"++" { yylval.ival = INC; return INC; }
X"--" { yylval.ival = DEC; return DEC; }
X"==" { yylval.ival = EQ; return EQ; }
X"!=" { yylval.ival = NE; return NE; }
X"<" { yylval.ival = LT; return LT; }
X">" { yylval.ival = GT; return GT; }
X"<=" { yylval.ival = LE; return LE; }
X">=" { yylval.ival = GE; return GE; }
X"&&" { yylval.ival = AND; return AND; }
X"||" { yylval.ival = OR; return OR; }
X"?" { yylval.ival = QUEST; return QUEST; }
X":" { yylval.ival = COLON; return COLON; }
X" " ;
X"\t" ;
X\"([^\n\"]|\\\")*\" {
X register char *c, *s;
X yytext[yyleng - 1] = '\0';
X yylval.cval = malloc (yyleng - 1);
X c = yylval.cval;
X s = yytext + 1;
X while (*s) {
X if (*s == '\\') {
X switch (*++s) {
X case '0':
X *c++ = '\0';
X break;
X case 'b':
X *c++ = '\b';
X break;
X case 'n':
X *c++ = '\n';
X break;
X case 't':
X *c++ = '\t';
X break;
X case 'f':
X *c++ = '\f';
X break;
X default:
X *c++ = *s;
X }
X } else
X *c++ = *s;
X ++s;
X }
X *c = '\0';
X return STRING;
X }
X0[0-7]* {
X yylval.dval = (double) atoo (yytext);
X return NUMBER;
X }
X0x[0-9a-fA-F]+ {
X yylval.dval = (double) atox (yytext+2);
X return NUMBER;
X }
X(([0-9]+((\.[0-9]*)?))|(\.[0-9]+))(([Ee][-+]?[0-9]+)?) {
X yylval.dval = atof (yytext);
X return NUMBER;
X }
X[a-zA-Z][0-9a-zA-Z_]* {
X yylval.nval = lookUp (yytext);
X return NAME;
X }
X. fprintf (stderr, "character \\%o ignored\n", *yytext);
X%%
X
Xskipcomment ()
X{
X int c;
X
X c = input();
X for (;;) {
X while (c != '*')
X c = input();
X c = input();
X if (c == '/')
X return;
X }
X}
X
Xatox (s)
Xregister char *s;
X{
X register int result;
X register int digit;
X
X result = 0;
X for (;;) {
X switch (*s) {
X case '0': case '1': case '2': case '3': case '4':
X case '5': case '6': case '7': case '8': case '9':
X digit = *s - '0';
X break;
X case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
X digit = *s - 'a' + 10;
X break;
X case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
X digit = *s - 'A' + 10;
X break;
X default:
X return result;
X }
X result = (result << 4) + digit;
X ++s;
X }
X}
X
Xatoo (s)
Xregister char *s;
X{
X register int result;
X
X result = 0;
X while ('0' <= *s && *s <= '7')
X result = (result << 3) + *s++ - '0';
X return result;
X}
X
Xlexfile(s)
Xchar *s;
X{
X FILE *f;
X f = fopen (s, "r");
X if (f == NULL) {
X fprintf (stderr, "cannot open file %s\n", s);
X return 0;
X }
X ++yyfiledeep;
X yyin = f;
X yyfile = s;
X return 1;
X}
X
Xlexstdin()
X{
X --yyfiledeep;
X fclose (yyin);
X yyin = stdin;
X}
X
Xpushinput (s)
Xchar *s;
X{
X FILE *f;
X
X if (yyinpt == yyinstack) {
X fprintf (stderr, "files nested too deeply\n");
X return;
X }
X f = fopen (s, "r");
X if (f == NULL) {
X fprintf (stderr, "cannot open file %s\n", s);
X return;
X }
X ++yyfiledeep;
X *--yyinpt = yyin;
X *--linenopt = yylineno;
X *--fnamept = yyfile;
X yyin = f;
X yylineno = 1;
X yyfile = s;
X return;
X}
X
Xpopinput ()
X{
X int c;
X
X do {
X fclose (yyin);
X if (yyinpt == yyinstack + YYINDEEP)
X return 0;
X yyin = *yyinpt++;
X yylineno = *linenopt++;
X yyfile = *fnamept++;
X --yyfiledeep;
X } while ((c = getc(yyin)) == EOF);
X return c;
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > main.c
X/*
X * main.c
X *
X * main routine for ic
X */
X
X# include <setjmp.h>
X# include <signal.h>
X# include <stdio.h>
X# include "ic.h"
X
Xjmp_buf jmpint;
X
Xchar femess[] = "Floating Exception\n";
X
Xmain (argc, argv)
Xchar **argv;
X{
X int intr(), ferr();
X
X initbuiltin ();
X switch (setjmp (jmpint)) {
X case 2:
X fprintf (stderr, femess);
X case 0:
X signal (SIGINT, intr);
X signal (SIGFPE, ferr);
X while (*++argv)
X parsefile (*argv);
X break;
X case 1:
X putchar ('\n');
X break;
X }
X switch (setjmp (jmpint)) {
X case 0:
X break;
X case 1:
X putchar ('\n');
X break;
X case 2:
X fprintf (stderr, femess);
X break;
X }
X signal (SIGINT, intr);
X signal (SIGFPE, ferr);
X yyparse ();
X}
X
Xintr ()
X{
X int intr();
X signal (SIGINT, intr);
X longjmp (jmpint, 1);
X}
X
Xferr()
X{
X int ferr();
X signal (SIGFPE, ferr);
X longjmp (jmpint, 2);
X}
X
Xparsefile (s)
Xchar *s;
X{
X if (lexfile (s)) {
X yyparse ();
X lexstdin ();
X }
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > symbol.c
X/*
X * symbol.c
X *
X * deal with the symbol table
X */
X
X# include "ic.h"
X
X# define HASHSIZE 63
X# define SYMALLOC 64
X
Xstatic struct symbol *htable[HASHSIZE];
X
Xstatic int level;
X
Xhash (name)
Xregister char *name;
X{
X register value;
X
X value = 0;
X while (*name)
X value += *name++;
X return value % HASHSIZE;
X}
X
Xsymbol *
XlookUp (name)
Xchar *name;
X{
X register symbol *sym;
X symbol **queue;
X symbol *insertSym();
X char *malloc(), *strcpy();
X
X queue = & htable[hash(name)];
X for (sym = *queue; sym; sym = sym->s_next)
X if (!strcmp (sym->s_name, name))
X return sym;
X return insertSym (strcpy (malloc (strlen(name)+1), name));
X}
X
Xsymbol *
XinsertSym (name)
Xchar *name;
X{
X symbol **queue, *symAlloc(), *sym;
X
X queue = & htable[hash(name)];
X sym = symAlloc ();
X sym->s_name = name;
X sym->s_back = 0;
X sym->s_level = level;
X if (sym->s_next = *queue)
X (*queue)->s_back = sym;
X sym->s_value = 0;
X sym->s_type = UNDEF;
X *queue = sym;
X return sym;
X}
X
XextractSym (s)
Xsymbol *s;
X{
X if (s->s_back)
X s->s_back->s_next = s->s_next;
X else
X htable[hash(s->s_name)] = s->s_next;
X if (s->s_next)
X s->s_next->s_back = s->s_back;
X}
X
Xpushlevel()
X{
X ++level;
X}
X
Xpoplevel()
X{
X --level;
X}
X
Xstatic struct symbol initblock[SYMALLOC];
Xstatic int initused = 0;
Xstatic struct symbol *freelist;
X
Xsymbol *
XsymAlloc ()
X{
X char *malloc ();
X register symbol *s;
X
X if (!freelist) {
X if (!initused)
X s = initblock;
X else
X s = (symbol *) malloc (sizeof (symbol) * SYMALLOC);
X freelist = s;
X while (s != freelist + SYMALLOC - 1) {
X s->s_next = s+1;
X ++s;
X }
X s->s_next = (symbol *) 0;
X }
X s = freelist;
X freelist = s->s_next;
X s->s_next = 0;
X return s;
X}
X
XsymFree (s)
Xsymbol *s;
X{
X s->s_next = freelist;
X freelist = s;
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > util.c
X/*
X * util.c
X *
X * general purpose utilities
X */
X
X# include <math.h>
X
Xdouble
Xdist (x0, y0, x1, y1)
Xdouble x0, y0, x1, y1;
X{
X register double tx, ty;
X
X tx = x0 - x1;
X ty = y0 - y1;
X return sqrt (tx*tx + ty*ty);
X}
X
Xprintinbase (base, value)
Xdouble base, value;
X{
X register int ibase;
X register int ivalue;
X char buf[256];
X register char *c;
X int sign;
X register int digit;
X
X ivalue = value;
X if ((ibase = base) <= 0) {
X printf ("Illegal base: %d\n", ibase);
X }
X c = buf + sizeof (buf);
X *--c = '\0';
X sign = 1;
X if (ivalue < 0) {
X sign = -1;
X ivalue = -ivalue;
X }
X while (ivalue) {
X digit = ivalue % ibase;
X if (digit >= 10)
X *--c = digit + 'a';
X else
X *--c = digit + '0';
X ivalue /= ibase;
X }
X if (sign == -1)
X *--c = '-';
X puts (c);
X}
SHAR_EOF
exit
More information about the Comp.sources.unix
mailing list