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