summaryrefslogtreecommitdiffstats
path: root/src/cmd/hoc/hoc.y
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/hoc/hoc.y')
-rw-r--r--src/cmd/hoc/hoc.y398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/cmd/hoc/hoc.y b/src/cmd/hoc/hoc.y
new file mode 100644
index 00000000..4d353737
--- /dev/null
+++ b/src/cmd/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 ELSE
+%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)$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(STOP); }
+ ;
+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)$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", progname);
+ 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 line */
+ lineno++;
+}
+
+void
+defnonly(char *s) /* warn if illegal definition */
+{
+ if (!indef)
+ execerror(s, "used outside definition");
+}