hoc.y - 9base - revived minimalist port of Plan 9 userland to Unix | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
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… | |
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 els… | |
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); co… | |
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=… | |
123 | PROC procname { $2->type=PROCEDURE; indef=1; } | |
124 '(' formals ')' stmt { code(procret); define($2, $5); indef=… | |
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 : fol… | |
219 case '-': return follow('-', DEC, '-') == DEC ? DEC : fol… | |
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", pr… | |
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", p… | |
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 ve… | |
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, infil… | |
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 inp… | |
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 } |