xref: /plan9/sys/src/cmd/hoc/hoc.y (revision 1066d6debf4f3ce80fbab98c906650d920c13a7a)
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