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