added olegfink's patches for adding cal and hoc - 9base - revived minimalist po… | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit f61f650899ccc18561eb21fce96e4770508adb71 | |
parent a34055fb4b74b06c01e0e0f9623eba002bda6ef1 | |
Author: Anselm R Garbe <[email protected]> | |
Date: Mon, 10 Aug 2009 15:04:03 +0100 | |
added olegfink's patches for adding cal and hoc | |
Diffstat: | |
M Makefile | 8 ++++---- | |
A cal/Makefile | 6 ++++++ | |
A cal/cal.1 | 46 +++++++++++++++++++++++++++++… | |
A cal/cal.c | 313 +++++++++++++++++++++++++++++… | |
A hoc/Makefile | 9 +++++++++ | |
A hoc/code.c | 666 +++++++++++++++++++++++++++++… | |
A hoc/hoc.1 | 144 +++++++++++++++++++++++++++++… | |
A hoc/hoc.h | 83 +++++++++++++++++++++++++++++… | |
A hoc/hoc.y | 398 +++++++++++++++++++++++++++++… | |
A hoc/init.c | 69 ++++++++++++++++++++++++++++++ | |
A hoc/math.c | 75 +++++++++++++++++++++++++++++… | |
A hoc/symbol.c | 55 +++++++++++++++++++++++++++++… | |
12 files changed, 1868 insertions(+), 4 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -1,10 +1,10 @@ | |
-# 9base - awk basename cat cleanname echo grep rc sed seq sleep sort tee | |
-# test touch tr uniq from Plan 9 | |
+# 9base - awk basename cal cat cleanname echo grep rc sed seq sleep | |
+# hoc sort tee test touch tr uniq from Plan 9 | |
include config.mk | |
-SUBDIRS = lib9 yacc awk basename bc dc cat cleanname date echo grep ls \ | |
- rc read sed seq sleep sort tee test touch tr uniq | |
+SUBDIRS = lib9 yacc awk basename bc dc cal cat cleanname date echo grep ls \ | |
+ hoc rc read sed seq sleep sort tee test touch tr uniq | |
all: | |
@echo 9base build options: | |
diff --git a/cal/Makefile b/cal/Makefile | |
@@ -0,0 +1,6 @@ | |
+# cal - cal unix port from plan9 | |
+# Depends on ../lib9 | |
+ | |
+TARG = cal | |
+ | |
+include ../std.mk | |
diff --git a/cal/cal.1 b/cal/cal.1 | |
@@ -0,0 +1,46 @@ | |
+.TH CAL 1 | |
+.SH NAME | |
+cal \- print calendar | |
+.SH SYNOPSIS | |
+.B cal | |
+[ | |
+.I month | |
+] | |
+[ | |
+.I year | |
+] | |
+.SH DESCRIPTION | |
+.I Cal | |
+prints a calendar. | |
+.I Month | |
+is either a number from 1 to 12, | |
+a lower case month name, | |
+or a lower case three-letter prefix of a month name. | |
+.I Year | |
+can be between 1 | |
+and 9999. | |
+If either | |
+.I month | |
+or | |
+.I year | |
+is omitted, the current month or year is used. | |
+If only one argument is given, and it is a number larger than 12, | |
+a calendar for all twelve months of the given year is produced; | |
+otherwise a calendar for just one month is printed. | |
+The calendar | |
+produced is that for England and her colonies. | |
+.PP | |
+Try | |
+.EX | |
+ cal sep 1752 | |
+.EE | |
+.SH SOURCE | |
+.B \*9/src/cmd/cal.c | |
+.SH BUGS | |
+The year is always considered to start in January even though this | |
+is historically naive. | |
+.PP | |
+Beware that | |
+.L "cal 90" | |
+refers to the early Christian era, | |
+not the 20th century. | |
diff --git a/cal/cal.c b/cal/cal.c | |
@@ -0,0 +1,313 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <bio.h> | |
+ | |
+char dayw[] = | |
+{ | |
+ " S M Tu W Th F S" | |
+}; | |
+char *smon[] = | |
+{ | |
+ "January", "February", "March", "April", | |
+ "May", "June", "July", "August", | |
+ "September", "October", "November", "December", | |
+}; | |
+char mon[] = | |
+{ | |
+ 0, | |
+ 31, 29, 31, 30, | |
+ 31, 30, 31, 31, | |
+ 30, 31, 30, 31, | |
+}; | |
+char string[432]; | |
+Biobuf bout; | |
+ | |
+void main(int argc, char *argv[]); | |
+int number(char *str); | |
+void pstr(char *str, int n); | |
+void cal(int m, int y, char *p, int w); | |
+int jan1(int yr); | |
+int curmo(void); | |
+int curyr(void); | |
+ | |
+void | |
+main(int argc, char *argv[]) | |
+{ | |
+ int y, i, j, m; | |
+ | |
+ if(argc > 3) { | |
+ fprint(2, "usage: cal [month] [year]\n"); | |
+ exits("usage"); | |
+ } | |
+ Binit(&bout, 1, OWRITE); | |
+ | |
+/* | |
+ * no arg, print current month | |
+ */ | |
+ if(argc == 1) { | |
+ m = curmo(); | |
+ y = curyr(); | |
+ goto xshort; | |
+ } | |
+ | |
+/* | |
+ * one arg | |
+ * if looks like a month, print month | |
+ * else print year | |
+ */ | |
+ if(argc == 2) { | |
+ y = number(argv[1]); | |
+ if(y < 0) | |
+ y = -y; | |
+ if(y >= 1 && y <= 12) { | |
+ m = y; | |
+ y = curyr(); | |
+ goto xshort; | |
+ } | |
+ goto xlong; | |
+ } | |
+ | |
+/* | |
+ * two arg, month and year | |
+ */ | |
+ m = number(argv[1]); | |
+ if(m < 0) | |
+ m = -m; | |
+ y = number(argv[2]); | |
+ goto xshort; | |
+ | |
+/* | |
+ * print out just month | |
+ */ | |
+xshort: | |
+ if(m < 1 || m > 12) | |
+ goto badarg; | |
+ if(y < 1 || y > 9999) | |
+ goto badarg; | |
+ Bprint(&bout, " %s %u\n", smon[m-1], y); | |
+ Bprint(&bout, "%s\n", dayw); | |
+ cal(m, y, string, 24); | |
+ for(i=0; i<6*24; i+=24) | |
+ pstr(string+i, 24); | |
+ exits(0); | |
+ | |
+/* | |
+ * print out complete year | |
+ */ | |
+xlong: | |
+ y = number(argv[1]); | |
+ if(y<1 || y>9999) | |
+ goto badarg; | |
+ Bprint(&bout, "\n\n\n"); | |
+ Bprint(&bout, " %u\n", y); | |
+ Bprint(&bout, "\n"); | |
+ for(i=0; i<12; i+=3) { | |
+ for(j=0; j<6*72; j++) | |
+ string[j] = '\0'; | |
+ Bprint(&bout, " %.3s", smon[i]); | |
+ Bprint(&bout, " %.3s", smon[i+1]); | |
+ Bprint(&bout, " %.3s\n", smon[i+2]); | |
+ Bprint(&bout, "%s %s %s\n", dayw, dayw, dayw); | |
+ cal(i+1, y, string, 72); | |
+ cal(i+2, y, string+23, 72); | |
+ cal(i+3, y, string+46, 72); | |
+ for(j=0; j<6*72; j+=72) | |
+ pstr(string+j, 72); | |
+ } | |
+ Bprint(&bout, "\n\n\n"); | |
+ exits(0); | |
+ | |
+badarg: | |
+ Bprint(&bout, "cal: bad argument\n"); | |
+} | |
+ | |
+struct | |
+{ | |
+ char* word; | |
+ int val; | |
+} dict[] = | |
+{ | |
+ "jan", 1, | |
+ "january", 1, | |
+ "feb", 2, | |
+ "february", 2, | |
+ "mar", 3, | |
+ "march", 3, | |
+ "apr", 4, | |
+ "april", 4, | |
+ "may", 5, | |
+ "jun", 6, | |
+ "june", 6, | |
+ "jul", 7, | |
+ "july", 7, | |
+ "aug", 8, | |
+ "august", 8, | |
+ "sep", 9, | |
+ "sept", 9, | |
+ "september", 9, | |
+ "oct", 10, | |
+ "october", 10, | |
+ "nov", 11, | |
+ "november", 11, | |
+ "dec", 12, | |
+ "december", 12, | |
+ 0 | |
+}; | |
+ | |
+/* | |
+ * convert to a number. | |
+ * if its a dictionary word, | |
+ * return negative number | |
+ */ | |
+int | |
+number(char *str) | |
+{ | |
+ int n, c; | |
+ char *s; | |
+ | |
+ for(n=0; s=dict[n].word; n++) | |
+ if(strcmp(s, str) == 0) | |
+ return -dict[n].val; | |
+ n = 0; | |
+ s = str; | |
+ while(c = *s++) { | |
+ if(c<'0' || c>'9') | |
+ return 0; | |
+ n = n*10 + c-'0'; | |
+ } | |
+ return n; | |
+} | |
+ | |
+void | |
+pstr(char *str, int n) | |
+{ | |
+ int i; | |
+ char *s; | |
+ | |
+ s = str; | |
+ i = n; | |
+ while(i--) | |
+ if(*s++ == '\0') | |
+ s[-1] = ' '; | |
+ i = n+1; | |
+ while(i--) | |
+ if(*--s != ' ') | |
+ break; | |
+ s[1] = '\0'; | |
+ Bprint(&bout, "%s\n", str); | |
+} | |
+ | |
+void | |
+cal(int m, int y, char *p, int w) | |
+{ | |
+ int d, i; | |
+ char *s; | |
+ | |
+ s = p; | |
+ d = jan1(y); | |
+ mon[2] = 29; | |
+ mon[9] = 30; | |
+ | |
+ switch((jan1(y+1)+7-d)%7) { | |
+ | |
+ /* | |
+ * non-leap year | |
+ */ | |
+ case 1: | |
+ mon[2] = 28; | |
+ break; | |
+ | |
+ /* | |
+ * 1752 | |
+ */ | |
+ default: | |
+ mon[9] = 19; | |
+ break; | |
+ | |
+ /* | |
+ * leap year | |
+ */ | |
+ case 2: | |
+ ; | |
+ } | |
+ for(i=1; i<m; i++) | |
+ d += mon[i]; | |
+ d %= 7; | |
+ s += 3*d; | |
+ for(i=1; i<=mon[m]; i++) { | |
+ if(i==3 && mon[m]==19) { | |
+ i += 11; | |
+ mon[m] += 11; | |
+ } | |
+ if(i > 9) | |
+ *s = i/10+'0'; | |
+ s++; | |
+ *s++ = i%10+'0'; | |
+ s++; | |
+ if(++d == 7) { | |
+ d = 0; | |
+ s = p+w; | |
+ p = s; | |
+ } | |
+ } | |
+} | |
+ | |
+/* | |
+ * return day of the week | |
+ * of jan 1 of given year | |
+ */ | |
+int | |
+jan1(int yr) | |
+{ | |
+ int y, d; | |
+ | |
+/* | |
+ * normal gregorian calendar | |
+ * one extra day per four years | |
+ */ | |
+ | |
+ y = yr; | |
+ d = 4+y+(y+3)/4; | |
+ | |
+/* | |
+ * julian calendar | |
+ * regular gregorian | |
+ * less three days per 400 | |
+ */ | |
+ | |
+ if(y > 1800) { | |
+ d -= (y-1701)/100; | |
+ d += (y-1601)/400; | |
+ } | |
+ | |
+/* | |
+ * great calendar changeover instant | |
+ */ | |
+ | |
+ if(y > 1752) | |
+ d += 3; | |
+ | |
+ return d%7; | |
+} | |
+ | |
+/* | |
+ * system dependent | |
+ * get current month and year | |
+ */ | |
+int | |
+curmo(void) | |
+{ | |
+ Tm *tm; | |
+ | |
+ tm = localtime(time(0)); | |
+ return tm->mon+1; | |
+} | |
+ | |
+int | |
+curyr(void) | |
+{ | |
+ Tm *tm; | |
+ | |
+ tm = localtime(time(0)); | |
+ return tm->year+1900; | |
+} | |
diff --git a/hoc/Makefile b/hoc/Makefile | |
@@ -0,0 +1,9 @@ | |
+# hoc - hoc unix port from plan9 | |
+# Depends on ../lib9 | |
+ | |
+TARG = hoc | |
+OFILES = y.tab.o init.o code.o math.o symbol.o | |
+YFILES = hoc.y | |
+MANFILES = hoc.1 | |
+ | |
+include ../yacc.mk | |
diff --git a/hoc/code.c b/hoc/code.c | |
@@ -0,0 +1,666 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <bio.h> | |
+#include "hoc.h" | |
+#include "y.tab.h" | |
+ | |
+#define NSTACK 256 | |
+ | |
+static Datum stack[NSTACK]; /* the stack */ | |
+static Datum *stackp; /* next free spot on stack */ | |
+ | |
+#define NPROG 2000 | |
+Inst prog[NPROG]; /* the machine */ | |
+Inst *progp; /* next free spot for code generation */ | |
+Inst *pc; /* program counter during execution */ | |
+Inst *progbase = prog; /* start of current subprogram */ | |
+int returning; /* 1 if return stmt seen */ | |
+int indef; /* 1 if parsing a func or proc */ | |
+ | |
+typedef struct Frame { /* proc/func call stack frame */ | |
+ Symbol *sp; /* symbol table entry */ | |
+ Inst *retpc; /* where to resume after return */ | |
+ Datum *argn; /* n-th argument on stack */ | |
+ int nargs; /* number of arguments */ | |
+} Frame; | |
+#define NFRAME 100 | |
+Frame frame[NFRAME]; | |
+Frame *fp; /* frame pointer */ | |
+ | |
+void | |
+initcode(void) | |
+{ | |
+ progp = progbase; | |
+ stackp = stack; | |
+ fp = frame; | |
+ returning = 0; | |
+ indef = 0; | |
+} | |
+ | |
+void | |
+nop(void) | |
+{ | |
+} | |
+ | |
+void | |
+push(Datum d) | |
+{ | |
+ if (stackp >= &stack[NSTACK]) | |
+ execerror("stack too deep", 0); | |
+ *stackp++ = d; | |
+} | |
+ | |
+Datum | |
+pop(void) | |
+{ | |
+ if (stackp == stack) | |
+ execerror("stack underflow", 0); | |
+ return *--stackp; | |
+} | |
+ | |
+void | |
+xpop(void) /* for when no value is wanted */ | |
+{ | |
+ if (stackp == stack) | |
+ execerror("stack underflow", (char *)0); | |
+ --stackp; | |
+} | |
+ | |
+void | |
+constpush(void) | |
+{ | |
+ Datum d; | |
+ d.val = ((Symbol *)*pc++)->u.val; | |
+ push(d); | |
+} | |
+ | |
+void | |
+varpush(void) | |
+{ | |
+ Datum d; | |
+ d.sym = (Symbol *)(*pc++); | |
+ push(d); | |
+} | |
+ | |
+void | |
+whilecode(void) | |
+{ | |
+ Datum d; | |
+ Inst *savepc = pc; | |
+ | |
+ execute(savepc+2); /* condition */ | |
+ d = pop(); | |
+ while (d.val) { | |
+ execute(*((Inst **)(savepc))); /* body */ | |
+ if (returning) | |
+ break; | |
+ execute(savepc+2); /* condition */ | |
+ d = pop(); | |
+ } | |
+ if (!returning) | |
+ pc = *((Inst **)(savepc+1)); /* next stmt */ | |
+} | |
+ | |
+void | |
+forcode(void) | |
+{ | |
+ Datum d; | |
+ Inst *savepc = pc; | |
+ | |
+ execute(savepc+4); /* precharge */ | |
+ pop(); | |
+ execute(*((Inst **)(savepc))); /* condition */ | |
+ d = pop(); | |
+ while (d.val) { | |
+ execute(*((Inst **)(savepc+2))); /* body */ | |
+ if (returning) | |
+ break; | |
+ execute(*((Inst **)(savepc+1))); /* post loop */ | |
+ pop(); | |
+ execute(*((Inst **)(savepc))); /* condition */ | |
+ d = pop(); | |
+ } | |
+ if (!returning) | |
+ pc = *((Inst **)(savepc+3)); /* next stmt */ | |
+} | |
+ | |
+void | |
+ifcode(void) | |
+{ | |
+ Datum d; | |
+ Inst *savepc = pc; /* then part */ | |
+ | |
+ execute(savepc+3); /* condition */ | |
+ d = pop(); | |
+ if (d.val) | |
+ execute(*((Inst **)(savepc))); | |
+ else if (*((Inst **)(savepc+1))) /* else part? */ | |
+ execute(*((Inst **)(savepc+1))); | |
+ if (!returning) | |
+ pc = *((Inst **)(savepc+2)); /* next stmt */ | |
+} | |
+ | |
+void | |
+define(Symbol* sp, Formal *f) /* put func/proc in symbol table */ | |
+{ | |
+ Fndefn *fd; | |
+ int n; | |
+ | |
+ fd = emalloc(sizeof(Fndefn)); | |
+ fd->code = progbase; /* start of code */ | |
+ progbase = progp; /* next code starts here */ | |
+ fd->formals = f; | |
+ for(n=0; f; f=f->next) | |
+ n++; | |
+ fd->nargs = n; | |
+ sp->u.defn = fd; | |
+} | |
+ | |
+void | |
+call(void) /* call a function */ | |
+{ | |
+ Formal *f; | |
+ Datum *arg; | |
+ Saveval *s; | |
+ int i; | |
+ | |
+ Symbol *sp = (Symbol *)pc[0]; /* symbol table entry */ | |
+ /* for function */ | |
+ if (fp >= &frame[NFRAME]) | |
+ execerror(sp->name, "call nested too deeply"); | |
+ fp++; | |
+ fp->sp = sp; | |
+ fp->nargs = (int)(uintptr)pc[1]; | |
+ fp->retpc = pc + 2; | |
+ fp->argn = stackp - 1; /* last argument */ | |
+ if(fp->nargs != sp->u.defn->nargs) | |
+ execerror(sp->name, "called with wrong number of arguments"); | |
+ /* bind formals */ | |
+ f = sp->u.defn->formals; | |
+ arg = stackp - fp->nargs; | |
+ while(f){ | |
+ s = emalloc(sizeof(Saveval)); | |
+ s->val = f->sym->u; | |
+ s->type = f->sym->type; | |
+ s->next = f->save; | |
+ f->save = s; | |
+ f->sym->u.val = arg->val; | |
+ f->sym->type = VAR; | |
+ f = f->next; | |
+ arg++; | |
+ } | |
+ for (i = 0; i < fp->nargs; i++) | |
+ pop(); /* pop arguments; no longer needed */ | |
+ execute(sp->u.defn->code); | |
+ returning = 0; | |
+} | |
+ | |
+void | |
+restore(Symbol *sp) /* restore formals associated with symbol */ | |
+{ | |
+ Formal *f; | |
+ Saveval *s; | |
+ | |
+ f = sp->u.defn->formals; | |
+ while(f){ | |
+ s = f->save; | |
+ if(s == 0) /* more actuals than formals */ | |
+ break; | |
+ f->sym->u = s->val; | |
+ f->sym->type = s->type; | |
+ f->save = s->next; | |
+ free(s); | |
+ f = f->next; | |
+ } | |
+} | |
+ | |
+void | |
+restoreall(void) /* restore all variables in case of error */ | |
+{ | |
+ while(fp>=frame && fp->sp){ | |
+ restore(fp->sp); | |
+ --fp; | |
+ } | |
+ fp = frame; | |
+} | |
+ | |
+static void | |
+ret(void) /* common return from func or proc */ | |
+{ | |
+ /* restore formals */ | |
+ restore(fp->sp); | |
+ pc = (Inst *)fp->retpc; | |
+ --fp; | |
+ returning = 1; | |
+} | |
+ | |
+void | |
+funcret(void) /* return from a function */ | |
+{ | |
+ Datum d; | |
+ if (fp->sp->type == PROCEDURE) | |
+ execerror(fp->sp->name, "(proc) returns value"); | |
+ d = pop(); /* preserve function return value */ | |
+ ret(); | |
+ push(d); | |
+} | |
+ | |
+void | |
+procret(void) /* return from a procedure */ | |
+{ | |
+ if (fp->sp->type == FUNCTION) | |
+ execerror(fp->sp->name, | |
+ "(func) returns no value"); | |
+ ret(); | |
+} | |
+ | |
+void | |
+bltin(void) | |
+{ | |
+ | |
+ Datum d; | |
+ d = pop(); | |
+ d.val = (*(double (*)(double))*pc++)(d.val); | |
+ push(d); | |
+} | |
+ | |
+void | |
+add(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val += d2.val; | |
+ push(d1); | |
+} | |
+ | |
+void | |
+sub(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val -= d2.val; | |
+ push(d1); | |
+} | |
+ | |
+void | |
+mul(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val *= d2.val; | |
+ push(d1); | |
+} | |
+ | |
+void | |
+div(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ if (d2.val == 0.0) | |
+ execerror("division by zero", (char *)0); | |
+ d1 = pop(); | |
+ d1.val /= d2.val; | |
+ push(d1); | |
+} | |
+ | |
+void | |
+mod(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ if (d2.val == 0.0) | |
+ execerror("division by zero", (char *)0); | |
+ d1 = pop(); | |
+ /* d1.val %= d2.val; */ | |
+ d1.val = fmod(d1.val, d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+negate(void) | |
+{ | |
+ Datum d; | |
+ d = pop(); | |
+ d.val = -d.val; | |
+ push(d); | |
+} | |
+ | |
+void | |
+verify(Symbol* s) | |
+{ | |
+ if (s->type != VAR && s->type != UNDEF) | |
+ execerror("attempt to evaluate non-variable", s->name); | |
+ if (s->type == UNDEF) | |
+ execerror("undefined variable", s->name); | |
+} | |
+ | |
+void | |
+eval(void) /* evaluate variable on stack */ | |
+{ | |
+ Datum d; | |
+ d = pop(); | |
+ verify(d.sym); | |
+ d.val = d.sym->u.val; | |
+ push(d); | |
+} | |
+ | |
+void | |
+preinc(void) | |
+{ | |
+ Datum d; | |
+ d.sym = (Symbol *)(*pc++); | |
+ verify(d.sym); | |
+ d.val = d.sym->u.val += 1.0; | |
+ push(d); | |
+} | |
+ | |
+void | |
+predec(void) | |
+{ | |
+ Datum d; | |
+ d.sym = (Symbol *)(*pc++); | |
+ verify(d.sym); | |
+ d.val = d.sym->u.val -= 1.0; | |
+ push(d); | |
+} | |
+ | |
+void | |
+postinc(void) | |
+{ | |
+ Datum d; | |
+ double v; | |
+ d.sym = (Symbol *)(*pc++); | |
+ verify(d.sym); | |
+ v = d.sym->u.val; | |
+ d.sym->u.val += 1.0; | |
+ d.val = v; | |
+ push(d); | |
+} | |
+ | |
+void | |
+postdec(void) | |
+{ | |
+ Datum d; | |
+ double v; | |
+ d.sym = (Symbol *)(*pc++); | |
+ verify(d.sym); | |
+ v = d.sym->u.val; | |
+ d.sym->u.val -= 1.0; | |
+ d.val = v; | |
+ push(d); | |
+} | |
+ | |
+void | |
+gt(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val > d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+lt(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val < d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+ge(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val >= d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+le(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val <= d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+eq(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val == d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+ne(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val != d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+and(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val != 0.0 && d2.val != 0.0); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+or(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = (double)(d1.val != 0.0 || d2.val != 0.0); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+not(void) | |
+{ | |
+ Datum d; | |
+ d = pop(); | |
+ d.val = (double)(d.val == 0.0); | |
+ push(d); | |
+} | |
+ | |
+void | |
+power(void) | |
+{ | |
+ Datum d1, d2; | |
+ d2 = pop(); | |
+ d1 = pop(); | |
+ d1.val = Pow(d1.val, d2.val); | |
+ push(d1); | |
+} | |
+ | |
+void | |
+assign(void) | |
+{ | |
+ Datum d1, d2; | |
+ d1 = pop(); | |
+ d2 = pop(); | |
+ if (d1.sym->type != VAR && d1.sym->type != UNDEF) | |
+ execerror("assignment to non-variable", | |
+ d1.sym->name); | |
+ d1.sym->u.val = d2.val; | |
+ d1.sym->type = VAR; | |
+ push(d2); | |
+} | |
+ | |
+void | |
+addeq(void) | |
+{ | |
+ Datum d1, d2; | |
+ d1 = pop(); | |
+ d2 = pop(); | |
+ if (d1.sym->type != VAR && d1.sym->type != UNDEF) | |
+ execerror("assignment to non-variable", | |
+ d1.sym->name); | |
+ d2.val = d1.sym->u.val += d2.val; | |
+ d1.sym->type = VAR; | |
+ push(d2); | |
+} | |
+ | |
+void | |
+subeq(void) | |
+{ | |
+ Datum d1, d2; | |
+ d1 = pop(); | |
+ d2 = pop(); | |
+ if (d1.sym->type != VAR && d1.sym->type != UNDEF) | |
+ execerror("assignment to non-variable", | |
+ d1.sym->name); | |
+ d2.val = d1.sym->u.val -= d2.val; | |
+ d1.sym->type = VAR; | |
+ push(d2); | |
+} | |
+ | |
+void | |
+muleq(void) | |
+{ | |
+ Datum d1, d2; | |
+ d1 = pop(); | |
+ d2 = pop(); | |
+ if (d1.sym->type != VAR && d1.sym->type != UNDEF) | |
+ execerror("assignment to non-variable", | |
+ d1.sym->name); | |
+ d2.val = d1.sym->u.val *= d2.val; | |
+ d1.sym->type = VAR; | |
+ push(d2); | |
+} | |
+ | |
+void | |
+diveq(void) | |
+{ | |
+ Datum d1, d2; | |
+ d1 = pop(); | |
+ d2 = pop(); | |
+ if (d1.sym->type != VAR && d1.sym->type != UNDEF) | |
+ execerror("assignment to non-variable", | |
+ d1.sym->name); | |
+ d2.val = d1.sym->u.val /= d2.val; | |
+ d1.sym->type = VAR; | |
+ push(d2); | |
+} | |
+ | |
+void | |
+ppush(Datum *d) | |
+{ | |
+ push(*d); | |
+} | |
+ | |
+void | |
+modeq(void) | |
+{ | |
+ Datum d1, d2; | |
+ long x; | |
+ | |
+ d1 = pop(); | |
+ d2 = pop(); | |
+ if (d1.sym->type != VAR && d1.sym->type != UNDEF) | |
+ execerror("assignment to non-variable", | |
+ d1.sym->name); | |
+ /* d2.val = d1.sym->u.val %= d2.val; */ | |
+ x = d1.sym->u.val; | |
+ x %= (long) d2.val; | |
+ d2.val = x; | |
+ d1.sym->u.val = x; | |
+ d1.sym->type = VAR; | |
+ | |
+ /* push(d2) generates a compiler error on Linux w. gcc 2.95.4 */ | |
+ ppush(&d2); | |
+} | |
+ | |
+void | |
+printtop(void) /* pop top value from stack, print it */ | |
+{ | |
+ Datum d; | |
+ static Symbol *s; /* last value computed */ | |
+ if (s == 0) | |
+ s = install("_", VAR, 0.0); | |
+ d = pop(); | |
+ print("%.17g\n", d.val); | |
+ s->u.val = d.val; | |
+} | |
+ | |
+void | |
+prexpr(void) /* print numeric value */ | |
+{ | |
+ Datum d; | |
+ d = pop(); | |
+ print("%.17g ", d.val); | |
+} | |
+ | |
+void | |
+prstr(void) /* print string value */ | |
+{ | |
+ print("%s", (char *) *pc++); | |
+} | |
+ | |
+void | |
+varread(void) /* read into variable */ | |
+{ | |
+ Datum d; | |
+ extern Biobuf *bin; | |
+ Symbol *var = (Symbol *) *pc++; | |
+ int c; | |
+ | |
+ Again: | |
+ do | |
+ c = Bgetc(bin); | |
+ while(c==' ' || c=='\t'); | |
+ if(c == Beof){ | |
+ Iseof: | |
+ if(moreinput()) | |
+ goto Again; | |
+ d.val = var->u.val = 0.0; | |
+ goto Return; | |
+ } | |
+ | |
+ if(strchr("+-.0123456789", c) == 0) | |
+ execerror("non-number read into", var->name); | |
+ Bungetc(bin); | |
+ if(Bgetd(bin, &var->u.val) == Beof) | |
+ goto Iseof; | |
+ else | |
+ d.val = 1.0; | |
+ Return: | |
+ var->type = VAR; | |
+ push(d); | |
+} | |
+ | |
+Inst* | |
+code(Inst f) /* install one instruction or operand */ | |
+{ | |
+ Inst *oprogp = progp; | |
+ if (progp >= &prog[NPROG]) | |
+ execerror("program too big", (char *)0); | |
+ *progp++ = f; | |
+ return oprogp; | |
+} | |
+ | |
+void | |
+execute(Inst* p) | |
+{ | |
+ for (pc = p; *pc != STOP && !returning; ) | |
+ (*((++pc)[-1]))(); | |
+} | |
diff --git a/hoc/hoc.1 b/hoc/hoc.1 | |
@@ -0,0 +1,144 @@ | |
+.TH HOC 1 | |
+.SH NAME | |
+hoc \- interactive floating point language | |
+.SH SYNOPSIS | |
+.B hoc | |
+[ | |
+.I file ... | |
+] | |
+[ | |
+.B -e | |
+.I expression | |
+] | |
+.SH DESCRIPTION | |
+.I Hoc | |
+interprets a simple language for floating point arithmetic, | |
+at about the level of BASIC, with C-like syntax and | |
+functions. | |
+.PP | |
+The named | |
+.I files | |
+are read and interpreted in order. | |
+If no | |
+.I file | |
+is given or if | |
+.I file | |
+is | |
+.L - | |
+.I hoc | |
+interprets the standard input. | |
+The | |
+.B -e | |
+option allows input to | |
+.I hoc | |
+to be specified on the command line, to be treated as if it appeared in a file. | |
+.PP | |
+.I Hoc | |
+input consists of | |
+.I expressions | |
+and | |
+.IR statements . | |
+Expressions are evaluated and their results printed. | |
+Statements, typically assignments and function or procedure | |
+definitions, produce no output unless they explicitly call | |
+.IR print . | |
+.PP | |
+Variable names have the usual syntax, including | |
+.LR _ ; | |
+the name | |
+.L _ | |
+by itself contains the value of the last expression evaluated. | |
+The variables | |
+.BR E , | |
+.BR PI , | |
+.BR PHI , | |
+.BR GAMMA | |
+and | |
+.B DEG | |
+are predefined; the last is 59.25..., degrees per radian. | |
+.PP | |
+Expressions are formed with these C-like operators, listed by | |
+decreasing precedence. | |
+.TP | |
+.B ^ | |
+exponentiation | |
+.TP | |
+.B ! - ++ -- | |
+.TP | |
+.B * / % | |
+.TP | |
+.B + - | |
+.TP | |
+.B > >= < <= == != | |
+.TP | |
+.B && | |
+.TP | |
+.B || | |
+.TP | |
+.B = += -= *= /= %= | |
+.PP | |
+Built in functions are | |
+.BR abs , | |
+.BR acos , | |
+.BR asin , | |
+.B atan | |
+(one argument), | |
+.BR cos , | |
+.BR cosh , | |
+.BR exp , | |
+.BR int , | |
+.BR log , | |
+.BR log10 , | |
+.BR sin , | |
+.BR sinh , | |
+.BR sqrt , | |
+.BR tan , | |
+and | |
+.BR tanh . | |
+The function | |
+.B read(x) | |
+reads a value into the variable | |
+.B x | |
+and returns 0 at EOF; | |
+the statement | |
+.B print | |
+prints a list of expressions that may include | |
+string constants such as | |
+\fL"hello\en"\f1.\fP | |
+.PP | |
+Control flow statements are | |
+.BR if - else , | |
+.BR while , | |
+and | |
+.BR for , | |
+with braces for grouping. | |
+Newline ends a statement. | |
+Backslash-newline is equivalent to a space. | |
+.PP | |
+Functions and procedures are introduced by the words | |
+.B func | |
+and | |
+.BR proc ; | |
+.B return | |
+is used to return with a value from a function. | |
+.SH EXAMPLES | |
+.EX | |
+func gcd(a, b) { | |
+ temp = abs(a) % abs(b) | |
+ if(temp == 0) return abs(b) | |
+ return gcd(b, temp) | |
+} | |
+for(i=1; i<12; i++) print gcd(i,12) | |
+.EE | |
+.SH SOURCE | |
+.B \*9/src/cmd/hoc | |
+.SH "SEE ALSO" | |
+.IR bc (1), | |
+.IR dc (1) | |
+.br | |
+B. W. Kernighan and R. Pike, | |
+.I | |
+The Unix Programming Environment, | |
+Prentice-Hall, 1984 | |
+.SH BUGS | |
+Error recovery is imperfect within function and procedure definitions. | |
diff --git a/hoc/hoc.h b/hoc/hoc.h | |
@@ -0,0 +1,83 @@ | |
+typedef void (*Inst)(void); | |
+#define STOP (Inst) 0 | |
+ | |
+typedef struct Symbol Symbol; | |
+typedef union Datum Datum; | |
+typedef struct Formal Formal; | |
+typedef struct Saveval Saveval; | |
+typedef struct Fndefn Fndefn; | |
+typedef union Symval Symval; | |
+ | |
+union Symval { /* value of a symbol */ | |
+ double val; /* VAR */ | |
+ double (*ptr)(double); /* BLTIN */ | |
+ Fndefn *defn; /* FUNCTION, PROCEDURE */ | |
+ char *str; /* STRING */ | |
+}; | |
+ | |
+struct Symbol { /* symbol table entry */ | |
+ char *name; | |
+ long type; | |
+ Symval u; | |
+ struct Symbol *next; /* to link to another */ | |
+}; | |
+Symbol *install(char*, int, double), *lookup(char*); | |
+ | |
+union Datum { /* interpreter stack type */ | |
+ double val; | |
+ Symbol *sym; | |
+}; | |
+ | |
+struct Saveval { /* saved value of variable */ | |
+ Symval val; | |
+ long type; | |
+ Saveval *next; | |
+}; | |
+ | |
+struct Formal { /* formal parameter */ | |
+ Symbol *sym; | |
+ Saveval *save; | |
+ Formal *next; | |
+}; | |
+ | |
+struct Fndefn { /* formal parameter */ | |
+ Inst *code; | |
+ Formal *formals; | |
+ int nargs; | |
+}; | |
+ | |
+extern Formal *formallist(Symbol*, Formal*); | |
+extern double Fgetd(int); | |
+extern int moreinput(void); | |
+extern void restore(Symbol*); | |
+extern void restoreall(void); | |
+extern void execerror(char*, char*); | |
+extern void define(Symbol*, Formal*), verify(Symbol*); | |
+extern Datum pop(void); | |
+extern void initcode(void), push(Datum), xpop(void), constpush(void); | |
+extern void varpush(void); | |
+#define div hocdiv | |
+extern void eval(void), add(void), sub(void), mul(void), div(void), mod… | |
+extern void negate(void), power(void); | |
+extern void addeq(void), subeq(void), muleq(void), diveq(void), modeq(v… | |
+ | |
+extern Inst *progp, *progbase, prog[], *code(Inst); | |
+extern void assign(void), bltin(void), varread(void); | |
+extern void prexpr(void), prstr(void); | |
+extern void gt(void), lt(void), eq(void), ge(void), le(void), ne(void); | |
+extern void and(void), or(void), not(void); | |
+extern void ifcode(void), whilecode(void), forcode(void); | |
+extern void call(void), arg(void), argassign(void); | |
+extern void funcret(void), procret(void); | |
+extern void preinc(void), predec(void), postinc(void), postdec(void); | |
+extern void execute(Inst*); | |
+extern void printtop(void); | |
+ | |
+extern double Log(double), Log10(double), Gamma(double), Sqrt(double), … | |
+extern double Asin(double), Acos(double), Sinh(double), Cosh(double), i… | |
+extern double Pow(double, double); | |
+ | |
+extern void init(void); | |
+extern int yyparse(void); | |
+extern void execerror(char*, char*); | |
+extern void *emalloc(unsigned); | |
diff --git a/hoc/hoc.y b/hoc/hoc.y | |
@@ -0,0 +1,398 @@ | |
+%{ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <bio.h> | |
+#include <ctype.h> | |
+#include "hoc.h" | |
+#define code2(c1,c2) code(c1); code(c2) | |
+#define code3(c1,c2,c3) code(c1); code(c2); code(c3) | |
+%} | |
+%union { | |
+ Symbol *sym; /* symbol table pointer */ | |
+ Inst *inst; /* machine instruction */ | |
+ int narg; /* number of arguments */ | |
+ Formal *formals; /* list of formal parameters */ | |
+} | |
+%token <sym> NUMBER STRING PRINT VAR BLTIN UNDEF WHILE FOR IF EL… | |
+%token <sym> FUNCTION PROCEDURE RETURN FUNC PROC READ | |
+%type <formals> formals | |
+%type <inst> expr stmt asgn prlist stmtlist | |
+%type <inst> cond while for if begin end | |
+%type <sym> procname | |
+%type <narg> arglist | |
+%right '=' ADDEQ SUBEQ MULEQ DIVEQ MODEQ | |
+%left OR | |
+%left AND | |
+%left GT GE LT LE EQ NE | |
+%left '+' '-' | |
+%left '*' '/' '%' | |
+%left UNARYMINUS NOT INC DEC | |
+%right '^' | |
+%% | |
+list: /* nothing */ | |
+ | list '\n' | |
+ | list defn '\n' | |
+ | list asgn '\n' { code2(xpop, STOP); return 1; } | |
+ | list stmt '\n' { code(STOP); return 1; } | |
+ | list expr '\n' { code2(printtop, STOP); return 1; } | |
+ | list error '\n' { yyerrok; } | |
+ ; | |
+asgn: VAR '=' expr { code3(varpush,(Inst)$1,assign); $$=$3; } | |
+ | VAR ADDEQ expr { code3(varpush,(Inst)$1,addeq); $$=$3; } | |
+ | VAR SUBEQ expr { code3(varpush,(Inst)$1,subeq); $$=$3; } | |
+ | VAR MULEQ expr { code3(varpush,(Inst)$1,muleq); $$=$3; } | |
+ | VAR DIVEQ expr { code3(varpush,(Inst)$1,diveq); $$=$3; } | |
+ | VAR MODEQ expr { code3(varpush,(Inst)$1,modeq); $$=$3; } | |
+ ; | |
+stmt: expr { code(xpop); } | |
+ | RETURN { defnonly("return"); code(procret); } | |
+ | RETURN expr | |
+ { defnonly("return"); $$=$2; code(funcret); } | |
+ | PROCEDURE begin '(' arglist ')' | |
+ { $$ = $2; code3(call, (Inst)$1, (Inst)(uintptr)$4); } | |
+ | PRINT prlist { $$ = $2; } | |
+ | while '(' cond ')' stmt end { | |
+ ($1)[1] = (Inst)$5; /* body of loop */ | |
+ ($1)[2] = (Inst)$6; } /* end, if cond fails */ | |
+ | for '(' cond ';' cond ';' cond ')' stmt end { | |
+ ($1)[1] = (Inst)$5; /* condition */ | |
+ ($1)[2] = (Inst)$7; /* post loop */ | |
+ ($1)[3] = (Inst)$9; /* body of loop */ | |
+ ($1)[4] = (Inst)$10; } /* end, if cond fails */ | |
+ | if '(' cond ')' stmt end { /* else-less if */ | |
+ ($1)[1] = (Inst)$5; /* thenpart */ | |
+ ($1)[3] = (Inst)$6; } /* end, if cond fails */ | |
+ | if '(' cond ')' stmt end ELSE stmt end { /* if with else */ | |
+ ($1)[1] = (Inst)$5; /* thenpart */ | |
+ ($1)[2] = (Inst)$8; /* elsepart */ | |
+ ($1)[3] = (Inst)$9; } /* end, if cond fails */ | |
+ | '{' stmtlist '}' { $$ = $2; } | |
+ ; | |
+cond: expr { code(STOP); } | |
+ ; | |
+while: WHILE { $$ = code3(whilecode,STOP,STOP); } | |
+ ; | |
+for: FOR { $$ = code(forcode); code3(STOP,STOP,STOP); code(STO… | |
+ ; | |
+if: IF { $$ = code(ifcode); code3(STOP,STOP,STOP); } | |
+ ; | |
+begin: /* nothing */ { $$ = progp; } | |
+ ; | |
+end: /* nothing */ { code(STOP); $$ = progp; } | |
+ ; | |
+stmtlist: /* nothing */ { $$ = progp; } | |
+ | stmtlist '\n' | |
+ | stmtlist stmt | |
+ ; | |
+expr: NUMBER { $$ = code2(constpush, (Inst)$1); } | |
+ | VAR { $$ = code3(varpush, (Inst)$1, eval); } | |
+ | asgn | |
+ | FUNCTION begin '(' arglist ')' | |
+ { $$ = $2; code3(call,(Inst)$1,(Inst)(uintptr)$4); } | |
+ | READ '(' VAR ')' { $$ = code2(varread, (Inst)$3); } | |
+ | BLTIN '(' expr ')' { $$=$3; code2(bltin, (Inst)$1->u.ptr); } | |
+ | '(' expr ')' { $$ = $2; } | |
+ | expr '+' expr { code(add); } | |
+ | expr '-' expr { code(sub); } | |
+ | expr '*' expr { code(mul); } | |
+ | expr '/' expr { code(div); } | |
+ | expr '%' expr { code(mod); } | |
+ | expr '^' expr { code (power); } | |
+ | '-' expr %prec UNARYMINUS { $$=$2; code(negate); } | |
+ | expr GT expr { code(gt); } | |
+ | expr GE expr { code(ge); } | |
+ | expr LT expr { code(lt); } | |
+ | expr LE expr { code(le); } | |
+ | expr EQ expr { code(eq); } | |
+ | expr NE expr { code(ne); } | |
+ | expr AND expr { code(and); } | |
+ | expr OR expr { code(or); } | |
+ | NOT expr { $$ = $2; code(not); } | |
+ | INC VAR { $$ = code2(preinc,(Inst)$2); } | |
+ | DEC VAR { $$ = code2(predec,(Inst)$2); } | |
+ | VAR INC { $$ = code2(postinc,(Inst)$1); } | |
+ | VAR DEC { $$ = code2(postdec,(Inst)$1); } | |
+ ; | |
+prlist: expr { code(prexpr); } | |
+ | STRING { $$ = code2(prstr, (Inst)$1); } | |
+ | prlist ',' expr { code(prexpr); } | |
+ | prlist ',' STRING { code2(prstr, (Inst)$3); } | |
+ ; | |
+defn: FUNC procname { $2->type=FUNCTION; indef=1; } | |
+ '(' formals ')' stmt { code(procret); define($2, $5); indef=0; } | |
+ | PROC procname { $2->type=PROCEDURE; indef=1; } | |
+ '(' formals ')' stmt { code(procret); define($2, $5); indef=0; } | |
+ ; | |
+formals: { $$ = 0; } | |
+ | VAR { $$ = formallist($1, 0); } | |
+ | VAR ',' formals { $$ = formallist($1, $3); } | |
+ ; | |
+procname: VAR | |
+ | FUNCTION | |
+ | PROCEDURE | |
+ ; | |
+arglist: /* nothing */ { $$ = 0; } | |
+ | expr { $$ = 1; } | |
+ | arglist ',' expr { $$ = $1 + 1; } | |
+ ; | |
+%% | |
+ /* end of grammar */ | |
+char *progname; | |
+int lineno = 1; | |
+jmp_buf begin; | |
+int indef; | |
+char *infile; /* input file name */ | |
+Biobuf *bin; /* input file descriptor */ | |
+Biobuf binbuf; | |
+char **gargv; /* global argument list */ | |
+int gargc; | |
+ | |
+int c = '\n'; /* global for use by warning() */ | |
+ | |
+int backslash(int), follow(int, int, int); | |
+void defnonly(char*), run(void); | |
+void warning(char*, char*); | |
+ | |
+int | |
+yylex(void) /* hoc6 */ | |
+{ | |
+ while ((c=Bgetc(bin)) == ' ' || c == '\t') | |
+ ; | |
+ if (c < 0) | |
+ return 0; | |
+ if (c == '\\') { | |
+ c = Bgetc(bin); | |
+ if (c == '\n') { | |
+ lineno++; | |
+ return yylex(); | |
+ } | |
+ } | |
+ if (c == '#') { /* comment */ | |
+ while ((c=Bgetc(bin)) != '\n' && c >= 0) | |
+ ; | |
+ if (c == '\n') | |
+ lineno++; | |
+ return c; | |
+ } | |
+ if (c == '.' || isdigit(c)) { /* number */ | |
+ double d; | |
+ Bungetc(bin); | |
+ Bgetd(bin, &d); | |
+ yylval.sym = install("", NUMBER, d); | |
+ return NUMBER; | |
+ } | |
+ if (isalpha(c) || c == '_') { | |
+ Symbol *s; | |
+ char sbuf[100], *p = sbuf; | |
+ do { | |
+ if (p >= sbuf + sizeof(sbuf) - 1) { | |
+ *p = '\0'; | |
+ execerror("name too long", sbuf); | |
+ } | |
+ *p++ = c; | |
+ } while ((c=Bgetc(bin)) >= 0 && (isalnum(c) || c == '_')); | |
+ Bungetc(bin); | |
+ *p = '\0'; | |
+ if ((s=lookup(sbuf)) == 0) | |
+ s = install(sbuf, UNDEF, 0.0); | |
+ yylval.sym = s; | |
+ return s->type == UNDEF ? VAR : s->type; | |
+ } | |
+ if (c == '"') { /* quoted string */ | |
+ char sbuf[100], *p; | |
+ for (p = sbuf; (c=Bgetc(bin)) != '"'; p++) { | |
+ if (c == '\n' || c == Beof) | |
+ execerror("missing quote", ""); | |
+ if (p >= sbuf + sizeof(sbuf) - 1) { | |
+ *p = '\0'; | |
+ execerror("string too long", sbuf); | |
+ } | |
+ *p = backslash(c); | |
+ } | |
+ *p = 0; | |
+ yylval.sym = (Symbol *)emalloc(strlen(sbuf)+1); | |
+ strcpy((char*)yylval.sym, sbuf); | |
+ return STRING; | |
+ } | |
+ switch (c) { | |
+ case '+': return follow('+', INC, follow('=', ADDEQ, '+')); | |
+ case '-': return follow('-', DEC, follow('=', SUBEQ, '-')); | |
+ case '*': return follow('=', MULEQ, '*'); | |
+ case '/': return follow('=', DIVEQ, '/'); | |
+ case '%': return follow('=', MODEQ, '%'); | |
+ case '>': return follow('=', GE, GT); | |
+ case '<': return follow('=', LE, LT); | |
+ case '=': return follow('=', EQ, '='); | |
+ case '!': return follow('=', NE, NOT); | |
+ case '|': return follow('|', OR, '|'); | |
+ case '&': return follow('&', AND, '&'); | |
+ case '\n': lineno++; return '\n'; | |
+ default: return c; | |
+ } | |
+} | |
+ | |
+int | |
+backslash(int c) /* get next char with \'s interpreted */ | |
+{ | |
+ static char transtab[] = "b\bf\fn\nr\rt\t"; | |
+ if (c != '\\') | |
+ return c; | |
+ c = Bgetc(bin); | |
+ if (islower(c) && strchr(transtab, c)) | |
+ return strchr(transtab, c)[1]; | |
+ return c; | |
+} | |
+ | |
+int | |
+follow(int expect, int ifyes, int ifno) /* look ahead for >=, etc. */ | |
+{ | |
+ int c = Bgetc(bin); | |
+ | |
+ if (c == expect) | |
+ return ifyes; | |
+ Bungetc(bin); | |
+ return ifno; | |
+} | |
+ | |
+void | |
+yyerror(char* s) /* report compile-time error */ | |
+{ | |
+/*rob | |
+ warning(s, (char *)0); | |
+ longjmp(begin, 0); | |
+rob*/ | |
+ execerror(s, (char *)0); | |
+} | |
+ | |
+void | |
+execerror(char* s, char* t) /* recover from run-time error */ | |
+{ | |
+ warning(s, t); | |
+ Bseek(bin, 0L, 2); /* flush rest of file */ | |
+ restoreall(); | |
+ longjmp(begin, 0); | |
+} | |
+ | |
+void | |
+fpecatch(void) /* catch floating point exceptions */ | |
+{ | |
+ execerror("floating point exception", (char *) 0); | |
+} | |
+ | |
+void | |
+intcatch(void) /* catch interrupts */ | |
+{ | |
+ execerror("interrupt", 0); | |
+} | |
+ | |
+void | |
+run(void) /* execute until EOF */ | |
+{ | |
+ setjmp(begin); | |
+ for (initcode(); yyparse(); initcode()) | |
+ execute(progbase); | |
+} | |
+ | |
+void | |
+main(int argc, char* argv[]) /* hoc6 */ | |
+{ | |
+ static int first = 1; | |
+#ifdef YYDEBUG | |
+ extern int yydebug; | |
+ yydebug=3; | |
+#endif | |
+ progname = argv[0]; | |
+ init(); | |
+ if (argc == 1) { /* fake an argument list */ | |
+ static char *stdinonly[] = { "-" }; | |
+ | |
+ gargv = stdinonly; | |
+ gargc = 1; | |
+ } else if (first) { /* for interrupts */ | |
+ first = 0; | |
+ gargv = argv+1; | |
+ gargc = argc-1; | |
+ } | |
+ Binit(&binbuf, 0, OREAD); | |
+ bin = &binbuf; | |
+ while (moreinput()) | |
+ run(); | |
+ exits(0); | |
+} | |
+ | |
+int | |
+moreinput(void) | |
+{ | |
+ char *expr; | |
+ static char buf[64]; | |
+ int fd; | |
+ static Biobuf b; | |
+ | |
+ if (gargc-- <= 0) | |
+ return 0; | |
+ if (bin && bin != &binbuf) | |
+ Bterm(bin); | |
+ infile = *gargv++; | |
+ lineno = 1; | |
+ if (strcmp(infile, "-") == 0) { | |
+ bin = &binbuf; | |
+ infile = 0; | |
+ return 1; | |
+ } | |
+ if(strncmp(infile, "-e", 2) == 0) { | |
+ if(infile[2]==0){ | |
+ if(gargc == 0){ | |
+ fprint(2, "%s: no argument for -e\n", progname… | |
+ return 0; | |
+ } | |
+ gargc--; | |
+ expr = *gargv++; | |
+ }else | |
+ expr = infile+2; | |
+ sprint(buf, "/tmp/hocXXXXXXX"); | |
+ fd = mkstemp(buf); | |
+ remove(buf); | |
+/* | |
+ infile = mktemp(buf); | |
+ fd = create(infile, ORDWR|ORCLOSE, 0600); | |
+ if(fd < 0){ | |
+ fprint(2, "%s: can't create temp. file: %r\n", prognam… | |
+ return 0; | |
+ } | |
+*/ | |
+ fprint(fd, "%s\n", expr); | |
+ /* leave fd around; file will be removed on exit */ | |
+ /* the following looks weird but is required for unix version … | |
+ bin = &b; | |
+ seek(fd, 0, 0); | |
+ Binit(bin, fd, OREAD); | |
+ } else { | |
+ bin=Bopen(infile, OREAD); | |
+ if (bin == 0) { | |
+ fprint(2, "%s: can't open %s\n", progname, infile); | |
+ return moreinput(); | |
+ } | |
+ } | |
+ return 1; | |
+} | |
+ | |
+void | |
+warning(char* s, char* t) /* print warning message */ | |
+{ | |
+ fprint(2, "%s: %s", progname, s); | |
+ if (t) | |
+ fprint(2, " %s", t); | |
+ if (infile) | |
+ fprint(2, " in %s", infile); | |
+ fprint(2, " near line %d\n", lineno); | |
+ while (c != '\n' && c != Beof) | |
+ if((c = Bgetc(bin)) == '\n') /* flush rest of input lin… | |
+ lineno++; | |
+} | |
+ | |
+void | |
+defnonly(char *s) /* warn if illegal definition */ | |
+{ | |
+ if (!indef) | |
+ execerror(s, "used outside definition"); | |
+} | |
diff --git a/hoc/init.c b/hoc/init.c | |
@@ -0,0 +1,69 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include "hoc.h" | |
+#include "y.tab.h" | |
+ | |
+static struct { /* Keywords */ | |
+ char *name; | |
+ int kval; | |
+} keywords[] = { | |
+ "proc", PROC, | |
+ "func", FUNC, | |
+ "return", RETURN, | |
+ "if", IF, | |
+ "else", ELSE, | |
+ "while", WHILE, | |
+ "for", FOR, | |
+ "print", PRINT, | |
+ "read", READ, | |
+ 0, 0 | |
+}; | |
+ | |
+static struct { /* Constants */ | |
+ char *name; | |
+ double cval; | |
+} consts[] = { | |
+ "PI", 3.14159265358979323846, | |
+ "E", 2.71828182845904523536, | |
+ "GAMMA", 0.57721566490153286060, /* Euler */ | |
+ "DEG", 57.29577951308232087680, /* deg/radian */ | |
+ "PHI", 1.61803398874989484820, /* golden ratio */ | |
+ 0, 0 | |
+}; | |
+ | |
+static struct { /* Built-ins */ | |
+ char *name; | |
+ double (*func)(double); | |
+} builtins[] = { | |
+ "sin", sin, | |
+ "cos", cos, | |
+ "tan", tan, | |
+ "atan", atan, | |
+ "asin", Asin, /* checks range */ | |
+ "acos", Acos, /* checks range */ | |
+ "sinh", Sinh, /* checks range */ | |
+ "cosh", Cosh, /* checks range */ | |
+ "tanh", tanh, | |
+ "log", Log, /* checks range */ | |
+ "log10", Log10, /* checks range */ | |
+ "exp", Exp, /* checks range */ | |
+ "sqrt", Sqrt, /* checks range */ | |
+ "int", integer, | |
+ "abs", fabs, | |
+ 0, 0 | |
+}; | |
+ | |
+void | |
+init(void) /* install constants and built-ins in table */ | |
+{ | |
+ int i; | |
+ Symbol *s; | |
+ for (i = 0; keywords[i].name; i++) | |
+ install(keywords[i].name, keywords[i].kval, 0.0); | |
+ for (i = 0; consts[i].name; i++) | |
+ install(consts[i].name, VAR, consts[i].cval); | |
+ for (i = 0; builtins[i].name; i++) { | |
+ s = install(builtins[i].name, BLTIN, 0.0); | |
+ s->u.ptr = builtins[i].func; | |
+ } | |
+} | |
diff --git a/hoc/math.c b/hoc/math.c | |
@@ -0,0 +1,75 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+ | |
+#include "hoc.h" | |
+ | |
+double errcheck(double, char*); | |
+ | |
+double | |
+Log(double x) | |
+{ | |
+ return errcheck(log(x), "log"); | |
+} | |
+double | |
+Log10(double x) | |
+{ | |
+ return errcheck(log10(x), "log10"); | |
+} | |
+ | |
+double | |
+Sqrt(double x) | |
+{ | |
+ return errcheck(sqrt(x), "sqrt"); | |
+} | |
+ | |
+double | |
+Exp(double x) | |
+{ | |
+ return errcheck(exp(x), "exp"); | |
+} | |
+ | |
+double | |
+Asin(double x) | |
+{ | |
+ return errcheck(asin(x), "asin"); | |
+} | |
+ | |
+double | |
+Acos(double x) | |
+{ | |
+ return errcheck(acos(x), "acos"); | |
+} | |
+ | |
+double | |
+Sinh(double x) | |
+{ | |
+ return errcheck(sinh(x), "sinh"); | |
+} | |
+double | |
+Cosh(double x) | |
+{ | |
+ return errcheck(cosh(x), "cosh"); | |
+} | |
+double | |
+Pow(double x, double y) | |
+{ | |
+ return errcheck(pow(x,y), "exponentiation"); | |
+} | |
+ | |
+double | |
+integer(double x) | |
+{ | |
+ if(x<-2147483648.0 || x>2147483647.0) | |
+ execerror("argument out of domain", 0); | |
+ return (double)(long)x; | |
+} | |
+ | |
+double | |
+errcheck(double d, char* s) /* check result of library call */ | |
+{ | |
+ if(isNaN(d)) | |
+ execerror(s, "argument out of domain"); | |
+ if(isInf(d, 0)) | |
+ execerror(s, "result out of range"); | |
+ return d; | |
+} | |
diff --git a/hoc/symbol.c b/hoc/symbol.c | |
@@ -0,0 +1,55 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include "hoc.h" | |
+#include "y.tab.h" | |
+ | |
+static Symbol *symlist = 0; /* symbol table: linked list */ | |
+ | |
+Symbol* | |
+lookup(char* s) /* find s in symbol table */ | |
+{ | |
+ Symbol *sp; | |
+ | |
+ for (sp = symlist; sp != (Symbol *) 0; sp = sp->next) | |
+ if (strcmp(sp->name, s) == 0) | |
+ return sp; | |
+ return 0; /* 0 ==> not found */ | |
+} | |
+ | |
+Symbol* | |
+install(char* s, int t, double d) /* install s in symbol table */ | |
+{ | |
+ Symbol *sp; | |
+ | |
+ sp = emalloc(sizeof(Symbol)); | |
+ sp->name = emalloc(strlen(s)+1); /* +1 for '\0' */ | |
+ strcpy(sp->name, s); | |
+ sp->type = t; | |
+ sp->u.val = d; | |
+ sp->next = symlist; /* put at front of list */ | |
+ symlist = sp; | |
+ return sp; | |
+} | |
+ | |
+void* | |
+emalloc(unsigned n) /* check return from malloc */ | |
+{ | |
+ char *p; | |
+ | |
+ p = malloc(n); | |
+ if (p == 0) | |
+ execerror("out of memory", (char *) 0); | |
+ return p; | |
+} | |
+ | |
+Formal* | |
+formallist(Symbol *formal, Formal *list) /* add formal to list */ | |
+{ | |
+ Formal *f; | |
+ | |
+ f = emalloc(sizeof(Formal)); | |
+ f->sym = formal; | |
+ f->save = 0; | |
+ f->next = list; | |
+ return f; | |
+} |