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 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 }