diff options
Diffstat (limited to 'src/cmd/hoc/hoc.y')
| -rw-r--r-- | src/cmd/hoc/hoc.y | 398 |
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"); +} |
