1 #include "rc.h"
2 #include "io.h"
3 #include "fns.h"
4 
5 static tree*	body(int tok, int *ptok);
6 static tree*	brace(int tok);
7 static tree*	cmd(int tok, int *ptok);
8 static tree*	cmd2(int tok, int *ptok);
9 static tree*	cmd3(int tok, int *ptok);
10 static tree*	cmds(int tok, int *ptok, int nlok);
11 static tree*	epilog(int tok, int *ptok);
12 static int	iswordtok(int tok);
13 static tree*	line(int tok, int *ptok);
14 static tree*	paren(int tok);
15 static tree*	yyredir(int tok, int *ptok);
16 static tree*	yyword(int tok, int *ptok, int eqok);
17 static tree*	word1(int tok, int *ptok);
18 static tree*	words(int tok, int *ptok);
19 
20 static jmp_buf yyjmp;
21 
22 static int
dropnl(int tok)23 dropnl(int tok)
24 {
25 	while(tok == ' ' || tok == '\n')
26 		tok = yylex();
27 	return tok;
28 }
29 
30 static int
dropsp(int tok)31 dropsp(int tok)
32 {
33 	while(tok == ' ')
34 		tok = yylex();
35 	return tok;
36 }
37 
38 static void
syntax(int tok)39 syntax(int tok)
40 {
41 	USED(tok);
42 	yyerror("syntax error");
43 	longjmp(yyjmp, 1);
44 }
45 
46 int
parse(void)47 parse(void)
48 {
49 	tree *t;
50 	int tok;
51 
52 	if(setjmp(yyjmp))
53 		return 1;
54 
55 	// rc:				{ return 1;}
56 	// |	line '\n'		{return !compile($1);}
57 
58 	tok = dropsp(yylex());
59 	if(tok == EOF)
60 		return 1;
61 	t = line(tok, &tok);
62 	if(tok != '\n')
63 		yyerror("missing newline at end of line");
64 	yylval.tree = t;
65 	return !compile(t);
66 }
67 
68 static tree*
line(int tok,int * ptok)69 line(int tok, int *ptok)
70 {
71 	return cmds(tok, ptok, 0);
72 }
73 
74 static tree*
body(int tok,int * ptok)75 body(int tok, int *ptok)
76 {
77 	return cmds(tok, ptok, 1);
78 }
79 
80 static tree*
cmds(int tok,int * ptok,int nlok)81 cmds(int tok, int *ptok, int nlok)
82 {
83 	tree *t, **last, *t2;
84 
85 	// line:	cmd
86 	// |	cmdsa line		{$$=tree2(';', $1, $2);}
87 	// cmdsa:	cmd ';'
88 	// |	cmd '&'			{$$=tree1('&', $1);}
89 
90 	// body:	cmd
91 	// |	cmdsan body		{$$=tree2(';', $1, $2);}
92 	// cmdsan:	cmdsa
93 	// |	cmd '\n'
94 
95 	t = nil;
96 	last = nil;
97 	for(;;) {
98 		t2 = cmd(tok, &tok);
99 		if(tok == '&')
100 			t2 = tree1('&', t2);
101 		if(t2 != nil) {
102 			// slot into list t
103 			if(last == nil) {
104 				t = t2;
105 				last = &t;
106 			} else {
107 				*last = tree2(';', *last, t2);
108 				last = &(*last)->child[1];
109 			}
110 		}
111 		if(tok != ';' && tok != '&' && (!nlok || tok != '\n'))
112 			break;
113 		tok = yylex();
114 	}
115 	*ptok = tok;
116 	return t;
117 }
118 
119 static tree*
brace(int tok)120 brace(int tok)
121 {
122 	tree *t;
123 
124 	// brace:	'{' body '}'		{$$=tree1(BRACE, $2);}
125 
126 	tok = dropsp(tok);
127 	if(tok != '{')
128 		syntax(tok);
129 	t = body(yylex(), &tok);
130 	if(tok != '}')
131 		syntax(tok);
132 	return tree1(BRACE, t);
133 }
134 
135 static tree*
paren(int tok)136 paren(int tok)
137 {
138 	tree *t;
139 
140 	// paren:	'(' body ')'		{$$=tree1(PCMD, $2);}
141 
142 	tok = dropsp(tok);
143 	if(tok != '(')
144 		syntax(tok);
145 	t = body(yylex(), &tok);
146 	if(tok != ')')
147 		syntax(tok);
148 	return tree1(PCMD, t);
149 }
150 
151 static tree*
epilog(int tok,int * ptok)152 epilog(int tok, int *ptok)
153 {
154 	tree *t, *r;
155 
156 	// epilog:				{$$=0;}
157 	// |	redir epilog		{$$=mung2($1, $1->child[0], $2);}
158 
159 	if(tok != REDIR && tok != DUP) {
160 		*ptok = tok;
161 		return nil;
162 	}
163 
164 	r = yyredir(tok, &tok);
165 	t = epilog(tok, &tok);
166 	*ptok = tok;
167 	return mung2(r, r->child[0], t);
168 }
169 
170 static tree*
yyredir(int tok,int * ptok)171 yyredir(int tok, int *ptok)
172 {
173 	tree *r, *w;
174 
175 	// redir:	REDIR word		{$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);}
176 	// |	DUP
177 
178 	switch(tok) {
179 	default:
180 		syntax(tok);
181 	case DUP:
182 		r = yylval.tree;
183 		*ptok = dropsp(yylex());
184 		break;
185 	case REDIR:
186 		r = yylval.tree;
187 		w = yyword(yylex(), &tok, 1);
188 		*ptok = dropsp(tok);
189 		r = mung1(r, r->rtype==HERE?heredoc(w):w);
190 		break;
191 	}
192 	return r;
193 }
194 
195 static tree*
cmd(int tok,int * ptok)196 cmd(int tok, int *ptok)
197 {
198 	int op;
199 	tree *t1, *t2;
200 
201 	// |	cmd ANDAND cmd		{$$=tree2(ANDAND, $1, $3);}
202 	// |	cmd OROR cmd		{$$=tree2(OROR, $1, $3);}
203 
204 	tok = dropsp(tok);
205 	t1 = cmd2(tok, &tok);
206 	while(tok == ANDAND || tok == OROR) {
207 		op = tok;
208 		t2 = cmd2(dropnl(yylex()), &tok);
209 		t1 = tree2(op, t1, t2);
210 	}
211 	*ptok = tok;
212 	return t1;
213 }
214 
215 static tree*
cmd2(int tok,int * ptok)216 cmd2(int tok, int *ptok)
217 {
218 	tree *t1, *t2, *t3;
219 
220 	// |	cmd PIPE cmd		{$$=mung2($2, $1, $3);}
221 	t1 = cmd3(tok, &tok);
222 	while(tok == PIPE) {
223 		t2 = yylval.tree;
224 		t3 = cmd3(dropnl(yylex()), &tok);
225 		t1 = mung2(t2, t1, t3);
226 	}
227 	*ptok = tok;
228 	return t1;
229 }
230 
231 static tree*
cmd3(int tok,int * ptok)232 cmd3(int tok, int *ptok)
233 {
234 	tree *t1, *t2, *t3, *t4;
235 
236 	tok = dropsp(tok);
237 	switch(tok) {
238 	case ';':
239 	case '&':
240 	case '\n':
241 		*ptok = tok;
242 		return nil;
243 
244 	case IF:
245 		// |	IF paren {skipnl();} cmd	{$$=mung2($1, $2, $4);}
246 		// |	IF NOT {skipnl();} cmd	{$$=mung1($2, $4);}
247 		t1 = yylval.tree;
248 		tok = dropsp(yylex());
249 		if(tok == NOT) {
250 			t1 = yylval.tree;
251 			t2 = cmd(dropnl(yylex()), ptok);
252 			return mung1(t1, t2);
253 		}
254 		t2 = paren(tok);
255 		t3 = cmd(dropnl(yylex()), ptok);
256 		return mung2(t1, t2, t3);
257 
258 	case FOR:
259 		// |	FOR '(' word IN words ')' {skipnl();} cmd
260 		//		{$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);}
261 		// |	FOR '(' word ')' {skipnl();} cmd
262 		//		{$$=mung3($1, $3, (tree *)0, $6);}
263 		t1 = yylval.tree;
264 		tok = dropsp(yylex());
265 		if(tok != '(')
266 			syntax(tok);
267 		t2 = yyword(yylex(), &tok, 1);
268 		switch(tok) {
269 		default:
270 			syntax(tok);
271 		case ')':
272 			t3 = nil;
273 			break;
274 		case IN:
275 			t3 = words(yylex(), &tok);
276 			if(t3 == nil)
277 				t3 = tree1(PAREN, nil);
278 			if(tok != ')')
279 				syntax(tok);
280 			break;
281 		}
282 		t4 = cmd(dropnl(yylex()), ptok);
283 		return mung3(t1, t2, t3, t4);
284 
285 	case WHILE:
286 		// |	WHILE paren {skipnl();} cmd
287 		//		{$$=mung2($1, $2, $4);}
288 		t1 = yylval.tree;
289 		t2 = paren(yylex());
290 		t3 = cmd(dropnl(yylex()), ptok);
291 		return mung2(t1, t2, t3);
292 
293 	case SWITCH:
294 		// |	SWITCH word {skipnl();} brace
295 		//		{$$=tree2(SWITCH, $2, $4);}
296 		t1 = yyword(yylex(), &tok, 1);
297 		tok = dropnl(tok); // doesn't work in yacc grammar but works here!
298 		t2 = brace(tok);
299 		*ptok = dropsp(yylex());
300 		return tree2(SWITCH, t1, t2);
301 		// Note: cmd: a && for(x) y && b is a && {for (x) {y && b}}.
302 		return cmd(tok, ptok);
303 
304 	case FN:
305 		// |	FN words brace		{$$=tree2(FN, $2, $3);}
306 		// |	FN words		{$$=tree1(FN, $2);}
307 		t1 = words(yylex(), &tok);
308 		if(tok != '{') {
309 			*ptok = tok;
310 			return tree1(FN, t1);
311 		}
312 		t2 = brace(tok);
313 		*ptok = dropsp(yylex());
314 		return tree2(FN, t1, t2);
315 
316 	case TWIDDLE:
317 		// |	TWIDDLE word words	{$$=mung2($1, $2, $3);}
318 		t1 = yylval.tree;
319 		t2 = yyword(yylex(), &tok, 1);
320 		t3 = words(tok, ptok);
321 		return mung2(t1, t2, t3);
322 
323 	case BANG:
324 	case SUBSHELL:
325 		// |	BANG cmd		{$$=mung1($1, $2);}
326 		// |	SUBSHELL cmd		{$$=mung1($1, $2);}
327 		// Note: cmd2: ! x | y is !{x | y} not {!x} | y.
328 		t1 = yylval.tree;
329 		return mung1(t1, cmd2(yylex(), ptok));
330 
331 	case REDIR:
332 	case DUP:
333 		// |	redir cmd  %prec BANG	{$$=mung2($1, $1->child[0], $2);}
334 		// Note: cmd2: {>x echo a | tr a-z A-Z} writes A to x.
335 		t1 = yyredir(tok, &tok);
336 		t2 = cmd2(tok, ptok);
337 		return mung2(t1, t1->child[0], t2);
338 
339 	case '{':
340 		// |	brace epilog		{$$=epimung($1, $2);}
341 		t1 = brace(tok);
342 		tok = dropsp(yylex());
343 		t2 = epilog(tok, ptok);
344 		return epimung(t1, t2);
345 	}
346 
347 	if(!iswordtok(tok)) {
348 		*ptok = tok;
349 		return nil;
350 	}
351 
352 	// cmd: ...
353 	// |	simple			{$$=simplemung($1);}
354 	// |	assign cmd %prec BANG	{$$=mung3($1, $1->child[0], $1->child[1], $2);}
355 	// assign:	first '=' word		{$$=tree2('=', $1, $3);}
356 	// Note: first is same as word except for disallowing all the leading keywords,
357 	// but all those keywords have been picked off in the switch above.
358 	// Except NOT, but disallowing that in yacc was likely a mistake anyway:
359 	// there's no ambiguity in not=1 or not x y z.
360 	t1 = yyword(tok, &tok, 0);
361 	if(tok == '=') {
362 		// assignment
363 		// Note: cmd2: {x=1 true | echo $x} echoes 1.
364 		t1 = tree2('=', t1, yyword(yylex(), &tok, 1));
365 		t2 = cmd2(tok, ptok);
366 		return mung3(t1, t1->child[0], t1->child[1], t2);
367 	}
368 
369 	// simple:	first
370 	// |	simple word		{$$=tree2(ARGLIST, $1, $2);}
371 	// |	simple redir		{$$=tree2(ARGLIST, $1, $2);}
372 	for(;;) {
373 		if(tok == REDIR || tok == DUP) {
374 			t1 = tree2(ARGLIST, t1, yyredir(tok, &tok));
375 		} else if(iswordtok(tok)) {
376 			t1 = tree2(ARGLIST, t1, yyword(tok, &tok, 1));
377 		} else {
378 			break;
379 		}
380 	}
381 	*ptok = tok;
382 	return simplemung(t1);
383 }
384 
385 static tree*
words(int tok,int * ptok)386 words(int tok, int *ptok)
387 {
388 	tree *t;
389 
390 	// words:				{$$=(tree*)0;}
391 	// |	words word		{$$=tree2(WORDS, $1, $2);}
392 
393 	t = nil;
394 	tok = dropsp(tok);
395 	while(iswordtok(tok))
396 		t = tree2(WORDS, t, yyword(tok, &tok, 1));
397 	*ptok = tok;
398 	return t;
399 }
400 
401 static tree*
yyword(int tok,int * ptok,int eqok)402 yyword(int tok, int *ptok, int eqok)
403 {
404 	tree *t;
405 
406 	// word:	keyword			{lastword=1; $1->type=WORD;}
407 	// |	comword
408 	// |	word '^' word		{$$=tree2('^', $1, $3);}
409 	// comword: '$' word		{$$=tree1('$', $2);}
410 	// |	'$' word SUB words ')'	{$$=tree2(SUB, $2, $4);}
411 	// |	'"' word		{$$=tree1('"', $2);}
412 	// |	COUNT word		{$$=tree1(COUNT, $2);}
413 	// |	WORD
414 	// |	'`' brace		{$$=tree1('`', $2);}
415 	// |	'(' words ')'		{$$=tree1(PAREN, $2);}
416 	// |	REDIR brace		{$$=mung1($1, $2); $$->type=PIPEFD;}
417 	// keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
418 	//
419 	// factored into:
420 	//
421 	// word: word1
422 	// | word '^' word1
423 	//
424 	// word1: keyword | comword
425 
426 	t = word1(tok, &tok);
427 	if(tok == '=' && !eqok)
428 		goto out;
429 	for(;;) {
430 		if(iswordtok(tok)) {
431 			// No free carats around parens.
432 			if(t->type == PAREN || tok == '(')
433 				syntax(tok);
434 			t = tree2('^', t, word1(tok, &tok));
435 			continue;
436 		}
437 		tok = dropsp(tok);
438 		if(tok == '^') {
439 			t = tree2('^', t, word1(yylex(), &tok));
440 			continue;
441 		}
442 		break;
443 	}
444 out:
445 	*ptok = dropsp(tok);
446 	return t;
447 }
448 
449 static tree*
word1(int tok,int * ptok)450 word1(int tok, int *ptok)
451 {
452 	tree *w, *sub, *t;
453 
454 	tok = dropsp(tok);
455 	switch(tok) {
456 	default:
457 		syntax(tok);
458 
459 	case WORD:
460 	case FOR:
461 	case IN:
462 	case WHILE:
463 	case IF:
464 	case NOT:
465 	case TWIDDLE:
466 	case BANG:
467 	case SUBSHELL:
468 	case SWITCH:
469 	case FN:
470 		// |	WORD
471 		// keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
472 		t = yylval.tree;
473 		t->type = WORD;
474 		*ptok = yylex();
475 		return t;
476 
477 	case '=':
478 		*ptok = yylex();
479 		return token("=", WORD);
480 
481 	case '$':
482 		// comword: '$' word1		{$$=tree1('$', $2);}
483 		// |	'$' word1 SUB words ')'	{$$=tree2(SUB, $2, $4);}
484 		w = word1(yylex(), &tok);
485 		if(tok == '(') {
486 			sub = words(yylex(), &tok);
487 			if(tok != ')')
488 				syntax(tok);
489 			*ptok = yylex();
490 			return tree2(SUB, w, sub);
491 		}
492 		*ptok = tok;
493 		return tree1('$', w);
494 
495 	case '"':
496 		// |	'"' word1		{$$=tree1('"', $2);}
497 		return tree1('"', word1(yylex(), ptok));
498 
499 	case COUNT:
500 		// |	COUNT word1		{$$=tree1(COUNT, $2);}
501 		return tree1(COUNT, word1(yylex(), ptok));
502 
503 	case '`':
504 		// |	'`' brace		{$$=tree1('`', $2);}
505 		t = tree1('`', brace(yylex()));
506 		*ptok = yylex();
507 		return t;
508 
509 	case '(':
510 		// |	'(' words ')'		{$$=tree1(PAREN, $2);}
511 		t = tree1(PAREN, words(yylex(), &tok));
512 		if(tok != ')')
513 			syntax(tok);
514 		*ptok = yylex();
515 		return t;
516 
517 	case REDIRW:
518 		// |	REDIRW brace		{$$=mung1($1, $2); $$->type=PIPEFD;}
519 		t = yylval.tree;
520 		t = mung1(t, brace(yylex()));
521 		t->type = PIPEFD;
522 		*ptok = yylex();
523 		return t;
524 	}
525 }
526 
527 static int
iswordtok(int tok)528 iswordtok(int tok)
529 {
530 	switch(tok) {
531 	case FOR:
532 	case IN:
533 	case WHILE:
534 	case IF:
535 	case NOT:
536 	case TWIDDLE:
537 	case BANG:
538 	case SUBSHELL:
539 	case SWITCH:
540 	case FN:
541 	case '$':
542 	case '"':
543 	case COUNT:
544 	case WORD:
545 	case '`':
546 	case '(':
547 	case REDIRW:
548 	case '=':
549 		return 1;
550 	}
551 	return 0;
552 }
553