1 %{ 2 #include "hoc.h" 3 #define code2(c1,c2) code(c1); code(c2) 4 #define code3(c1,c2,c3) code(c1); code(c2); code(c3) 5 %} 6 %union { 7 Symbol *sym; /* symbol table pointer */ 8 Inst *inst; /* machine instruction */ 9 int narg; /* number of arguments */ 10 Formal *formals; /* list of formal parameters */ 11 } 12 %token <sym> NUMBER STRING PRINT VAR BLTIN UNDEF WHILE FOR IF ELSE 13 %token <sym> FUNCTION PROCEDURE RETURN FUNC PROC READ 14 %type <formals> formals 15 %type <inst> expr stmt asgn prlist stmtlist 16 %type <inst> cond while for if begin end 17 %type <sym> procname 18 %type <narg> arglist 19 %right '=' ADDEQ SUBEQ MULEQ DIVEQ MODEQ 20 %left OR 21 %left AND 22 %left GT GE LT LE EQ NE 23 %left '+' '-' 24 %left '*' '/' '%' 25 %left UNARYMINUS NOT INC DEC 26 %right '^' 27 %% 28 list: /* nothing */ 29 | list '\n' 30 | list defn '\n' 31 | list asgn '\n' { code2(xpop, STOP); return 1; } 32 | list stmt '\n' { code(STOP); return 1; } 33 | list expr '\n' { code2(printtop, STOP); return 1; } 34 | list error '\n' { yyerrok; } 35 ; 36 asgn: VAR '=' expr { code3(varpush,(Inst)$1,assign); $$=$3; } 37 | VAR ADDEQ expr { code3(varpush,(Inst)$1,addeq); $$=$3; } 38 | VAR SUBEQ expr { code3(varpush,(Inst)$1,subeq); $$=$3; } 39 | VAR MULEQ expr { code3(varpush,(Inst)$1,muleq); $$=$3; } 40 | VAR DIVEQ expr { code3(varpush,(Inst)$1,diveq); $$=$3; } 41 | VAR MODEQ expr { code3(varpush,(Inst)$1,modeq); $$=$3; } 42 ; 43 stmt: expr { code(xpop); } 44 | RETURN { defnonly("return"); code(procret); } 45 | RETURN expr 46 { defnonly("return"); $$=$2; code(funcret); } 47 | PROCEDURE begin '(' arglist ')' 48 { $$ = $2; code3(call, (Inst)$1, (Inst)$4); } 49 | PRINT prlist { $$ = $2; } 50 | while '(' cond ')' stmt end { 51 ($1)[1] = (Inst)$5; /* body of loop */ 52 ($1)[2] = (Inst)$6; } /* end, if cond fails */ 53 | for '(' cond ';' cond ';' cond ')' stmt end { 54 ($1)[1] = (Inst)$5; /* condition */ 55 ($1)[2] = (Inst)$7; /* post loop */ 56 ($1)[3] = (Inst)$9; /* body of loop */ 57 ($1)[4] = (Inst)$10; } /* end, if cond fails */ 58 | if '(' cond ')' stmt end { /* else-less if */ 59 ($1)[1] = (Inst)$5; /* thenpart */ 60 ($1)[3] = (Inst)$6; } /* end, if cond fails */ 61 | if '(' cond ')' stmt end ELSE stmt end { /* if with else */ 62 ($1)[1] = (Inst)$5; /* thenpart */ 63 ($1)[2] = (Inst)$8; /* elsepart */ 64 ($1)[3] = (Inst)$9; } /* end, if cond fails */ 65 | '{' stmtlist '}' { $$ = $2; } 66 ; 67 cond: expr { code(STOP); } 68 ; 69 while: WHILE { $$ = code3(whilecode,STOP,STOP); } 70 ; 71 for: FOR { $$ = code(forcode); code3(STOP,STOP,STOP); code(STOP); } 72 ; 73 if: IF { $$ = code(ifcode); code3(STOP,STOP,STOP); } 74 ; 75 begin: /* nothing */ { $$ = progp; } 76 ; 77 end: /* nothing */ { code(STOP); $$ = progp; } 78 ; 79 stmtlist: /* nothing */ { $$ = progp; } 80 | stmtlist '\n' 81 | stmtlist stmt 82 ; 83 expr: NUMBER { $$ = code2(constpush, (Inst)$1); } 84 | VAR { $$ = code3(varpush, (Inst)$1, eval); } 85 | asgn 86 | FUNCTION begin '(' arglist ')' 87 { $$ = $2; code3(call,(Inst)$1,(Inst)$4); } 88 | READ '(' VAR ')' { $$ = code2(varread, (Inst)$3); } 89 | BLTIN '(' expr ')' { $$=$3; code2(bltin, (Inst)$1->u.ptr); } 90 | '(' expr ')' { $$ = $2; } 91 | expr '+' expr { code(add); } 92 | expr '-' expr { code(sub); } 93 | expr '*' expr { code(mul); } 94 | expr '/' expr { code(div); } 95 | expr '%' expr { code(mod); } 96 | expr '^' expr { code (power); } 97 | '-' expr %prec UNARYMINUS { $$=$2; code(negate); } 98 | expr GT expr { code(gt); } 99 | expr GE expr { code(ge); } 100 | expr LT expr { code(lt); } 101 | expr LE expr { code(le); } 102 | expr EQ expr { code(eq); } 103 | expr NE expr { code(ne); } 104 | expr AND expr { code(and); } 105 | expr OR expr { code(or); } 106 | NOT expr { $$ = $2; code(not); } 107 | INC VAR { $$ = code2(preinc,(Inst)$2); } 108 | DEC VAR { $$ = code2(predec,(Inst)$2); } 109 | VAR INC { $$ = code2(postinc,(Inst)$1); } 110 | VAR DEC { $$ = code2(postdec,(Inst)$1); } 111 ; 112 prlist: expr { code(prexpr); } 113 | STRING { $$ = code2(prstr, (Inst)$1); } 114 | prlist ',' expr { code(prexpr); } 115 | prlist ',' STRING { code2(prstr, (Inst)$3); } 116 ; 117 defn: FUNC procname { $2->type=FUNCTION; indef=1; } 118 '(' formals ')' stmt { code(procret); define($2, $5); indef=0; } 119 | PROC procname { $2->type=PROCEDURE; indef=1; } 120 '(' formals ')' stmt { code(procret); define($2, $5); indef=0; } 121 ; 122 formals: { $$ = 0; } 123 | VAR { $$ = formallist($1, 0); } 124 | VAR ',' formals { $$ = formallist($1, $3); } 125 ; 126 procname: VAR 127 | FUNCTION 128 | PROCEDURE 129 ; 130 arglist: /* nothing */ { $$ = 0; } 131 | expr { $$ = 1; } 132 | arglist ',' expr { $$ = $1 + 1; } 133 ; 134 %% 135 /* end of grammar */ 136 #include <u.h> 137 #include <libc.h> 138 #include <bio.h> 139 #include <ctype.h> 140 char *progname; 141 int lineno = 1; 142 jmp_buf begin; 143 int indef; 144 char *infile; /* input file name */ 145 Biobuf *bin; /* input file descriptor */ 146 Biobuf binbuf; 147 char **gargv; /* global argument list */ 148 int gargc; 149 150 int c = '\n'; /* global for use by warning() */ 151 152 int backslash(int), follow(int, int, int); 153 void defnonly(char*), run(void); 154 void warning(char*, char*); 155 156 yylex(void) /* hoc6 */ 157 { 158 while ((c=Bgetc(bin)) == ' ' || c == '\t') 159 ; 160 if (c < 0) 161 return 0; 162 if (c == '\\') { 163 c = Bgetc(bin); 164 if (c == '\n') { 165 lineno++; 166 return yylex(); 167 } 168 } 169 if (c == '#') { /* comment */ 170 while ((c=Bgetc(bin)) != '\n' && c >= 0) 171 ; 172 if (c == '\n') 173 lineno++; 174 return c; 175 } 176 if (c == '.' || isdigit(c)) { /* number */ 177 double d; 178 Bungetc(bin); 179 Bgetd(bin, &d); 180 yylval.sym = install("", NUMBER, d); 181 return NUMBER; 182 } 183 if (isalpha(c) || c == '_' || c >= 0x80) { 184 Symbol *s; 185 char sbuf[100], *p = sbuf; 186 do { 187 if (p >= sbuf + sizeof(sbuf) - 1) { 188 *p = '\0'; 189 execerror("name too long", sbuf); 190 } 191 *p++ = c; 192 } while ((c=Bgetc(bin)) >= 0 && (isalnum(c) || c == '_' || c >= 0x80)); 193 Bungetc(bin); 194 *p = '\0'; 195 if ((s=lookup(sbuf)) == 0) 196 s = install(sbuf, UNDEF, 0.0); 197 yylval.sym = s; 198 return s->type == UNDEF ? VAR : s->type; 199 } 200 if (c == '"') { /* quoted string */ 201 char sbuf[100], *p; 202 for (p = sbuf; (c=Bgetc(bin)) != '"'; p++) { 203 if (c == '\n' || c == Beof) 204 execerror("missing quote", ""); 205 if (p >= sbuf + sizeof(sbuf) - 1) { 206 *p = '\0'; 207 execerror("string too long", sbuf); 208 } 209 *p = backslash(c); 210 } 211 *p = 0; 212 yylval.sym = (Symbol *)emalloc(strlen(sbuf)+1); 213 strcpy((char*)yylval.sym, sbuf); 214 return STRING; 215 } 216 switch (c) { 217 case '+': return follow('+', INC, follow('=', ADDEQ, '+')); 218 case '-': return follow('-', DEC, follow('=', SUBEQ, '-')); 219 case '*': return follow('=', MULEQ, '*'); 220 case '/': return follow('=', DIVEQ, '/'); 221 case '%': return follow('=', MODEQ, '%'); 222 case '>': return follow('=', GE, GT); 223 case '<': return follow('=', LE, LT); 224 case '=': return follow('=', EQ, '='); 225 case '!': return follow('=', NE, NOT); 226 case '|': return follow('|', OR, '|'); 227 case '&': return follow('&', AND, '&'); 228 case '\n': lineno++; return '\n'; 229 default: return c; 230 } 231 } 232 233 backslash(int c) /* get next char with \'s interpreted */ 234 { 235 static char transtab[] = "b\bf\fn\nr\rt\t"; 236 if (c != '\\') 237 return c; 238 c = Bgetc(bin); 239 if (islower(c) && strchr(transtab, c)) 240 return strchr(transtab, c)[1]; 241 return c; 242 } 243 244 follow(int expect, int ifyes, int ifno) /* look ahead for >=, etc. */ 245 { 246 int c = Bgetc(bin); 247 248 if (c == expect) 249 return ifyes; 250 Bungetc(bin); 251 return ifno; 252 } 253 254 void 255 yyerror(char* s) /* report compile-time error */ 256 { 257 /*rob 258 warning(s, (char *)0); 259 longjmp(begin, 0); 260 rob*/ 261 execerror(s, (char *)0); 262 } 263 264 void 265 execerror(char* s, char* t) /* recover from run-time error */ 266 { 267 warning(s, t); 268 Bseek(bin, 0L, 2); /* flush rest of file */ 269 restoreall(); 270 longjmp(begin, 0); 271 } 272 273 void 274 fpecatch(void) /* catch floating point exceptions */ 275 { 276 execerror("floating point exception", (char *) 0); 277 } 278 279 void 280 intcatch(void) /* catch interrupts */ 281 { 282 execerror("interrupt", 0); 283 } 284 285 void 286 run(void) /* execute until EOF */ 287 { 288 setjmp(begin); 289 for (initcode(); yyparse(); initcode()) 290 execute(progbase); 291 } 292 293 void 294 main(int argc, char* argv[]) /* hoc6 */ 295 { 296 static int first = 1; 297 #ifdef YYDEBUG 298 extern int yydebug; 299 yydebug=3; 300 #endif 301 progname = argv[0]; 302 init(); 303 if (argc == 1) { /* fake an argument list */ 304 static char *stdinonly[] = { "-" }; 305 306 gargv = stdinonly; 307 gargc = 1; 308 } else if (first) { /* for interrupts */ 309 first = 0; 310 gargv = argv+1; 311 gargc = argc-1; 312 } 313 Binit(&binbuf, 0, OREAD); 314 bin = &binbuf; 315 while (moreinput()) 316 run(); 317 exits(0); 318 } 319 320 moreinput(void) 321 { 322 char *expr; 323 static char buf[64]; 324 int fd; 325 static Biobuf b; 326 327 if (gargc-- <= 0) 328 return 0; 329 if (bin && bin != &binbuf) 330 Bterm(bin); 331 infile = *gargv++; 332 lineno = 1; 333 if (strcmp(infile, "-") == 0) { 334 bin = &binbuf; 335 infile = 0; 336 return 1; 337 } 338 if(strncmp(infile, "-e", 2) == 0) { 339 if(infile[2]==0){ 340 if(gargc == 0){ 341 fprint(2, "%s: no argument for -e\n", progname); 342 return 0; 343 } 344 gargc--; 345 expr = *gargv++; 346 }else 347 expr = infile+2; 348 sprint(buf, "/tmp/hocXXXXXXX"); 349 infile = mktemp(buf); 350 fd = create(infile, ORDWR|ORCLOSE, 0600); 351 if(fd < 0){ 352 fprint(2, "%s: can't create temp. file: %r\n", progname); 353 return 0; 354 } 355 fprint(fd, "%s\n", expr); 356 /* leave fd around; file will be removed on exit */ 357 /* the following looks weird but is required for unix version */ 358 bin = &b; 359 seek(fd, 0, 0); 360 Binit(bin, fd, OREAD); 361 } else { 362 bin=Bopen(infile, OREAD); 363 if (bin == 0) { 364 fprint(2, "%s: can't open %s\n", progname, infile); 365 return moreinput(); 366 } 367 } 368 return 1; 369 } 370 371 void 372 warning(char* s, char* t) /* print warning message */ 373 { 374 fprint(2, "%s: %s", progname, s); 375 if (t) 376 fprint(2, " %s", t); 377 if (infile) 378 fprint(2, " in %s", infile); 379 fprint(2, " near line %d\n", lineno); 380 while (c != '\n' && c != Beof) 381 if((c = Bgetc(bin)) == '\n') /* flush rest of input line */ 382 lineno++; 383 } 384 385 void 386 defnonly(char *s) /* warn if illegal definition */ 387 { 388 if (!indef) 389 execerror(s, "used outside definition"); 390 } 391