keith@reed.UUCP (Keith Packard) (03/15/86)
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