parse.c (10522B)
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 23 dropnl(int tok) 24 { 25 while(tok == ' ' || tok == '\n') 26 tok = yylex(); 27 return tok; 28 } 29 30 static int 31 dropsp(int tok) 32 { 33 while(tok == ' ') 34 tok = yylex(); 35 return tok; 36 } 37 38 static void 39 syntax(int tok) 40 { 41 USED(tok); 42 yyerror("syntax error"); 43 longjmp(yyjmp, 1); 44 } 45 46 int 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* 69 line(int tok, int *ptok) 70 { 71 return cmds(tok, ptok, 0); 72 } 73 74 static tree* 75 body(int tok, int *ptok) 76 { 77 return cmds(tok, ptok, 1); 78 } 79 80 static tree* 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* 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* 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* 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* 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* 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* 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* 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 302 case FN: 303 // | FN words brace {$$=tree2(FN, $2, $3);} 304 // | FN words {$$=tree1(FN, $2);} 305 t1 = words(yylex(), &tok); 306 if(tok != '{') { 307 *ptok = tok; 308 return tree1(FN, t1); 309 } 310 t2 = brace(tok); 311 *ptok = dropsp(yylex()); 312 return tree2(FN, t1, t2); 313 314 case TWIDDLE: 315 // | TWIDDLE word words {$$=mung2($1, $2, $3);} 316 t1 = yylval.tree; 317 t2 = yyword(yylex(), &tok, 1); 318 t3 = words(tok, ptok); 319 return mung2(t1, t2, t3); 320 321 case BANG: 322 case SUBSHELL: 323 // | BANG cmd {$$=mung1($1, $2);} 324 // | SUBSHELL cmd {$$=mung1($1, $2);} 325 // Note: cmd2: ! x | y is !{x | y} not {!x} | y. 326 t1 = yylval.tree; 327 return mung1(t1, cmd2(yylex(), ptok)); 328 329 case REDIR: 330 case DUP: 331 // | redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} 332 // Note: cmd2: {>x echo a | tr a-z A-Z} writes A to x. 333 t1 = yyredir(tok, &tok); 334 t2 = cmd2(tok, ptok); 335 return mung2(t1, t1->child[0], t2); 336 337 case '{': 338 // | brace epilog {$$=epimung($1, $2);} 339 t1 = brace(tok); 340 tok = dropsp(yylex()); 341 t2 = epilog(tok, ptok); 342 return epimung(t1, t2); 343 } 344 345 if(!iswordtok(tok)) { 346 *ptok = tok; 347 return nil; 348 } 349 350 // cmd: ... 351 // | simple {$$=simplemung($1);} 352 // | assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} 353 // assign: first '=' word {$$=tree2('=', $1, $3);} 354 // Note: first is same as word except for disallowing all the leading keywords, 355 // but all those keywords have been picked off in the switch above. 356 // Except NOT, but disallowing that in yacc was likely a mistake anyway: 357 // there's no ambiguity in not=1 or not x y z. 358 t1 = yyword(tok, &tok, 0); 359 if(tok == '=') { 360 // assignment 361 // Note: cmd2: {x=1 true | echo $x} echoes 1. 362 t1 = tree2('=', t1, yyword(yylex(), &tok, 1)); 363 t2 = cmd2(tok, ptok); 364 return mung3(t1, t1->child[0], t1->child[1], t2); 365 } 366 367 // simple: first 368 // | simple word {$$=tree2(ARGLIST, $1, $2);} 369 // | simple redir {$$=tree2(ARGLIST, $1, $2);} 370 for(;;) { 371 if(tok == REDIR || tok == DUP) { 372 t1 = tree2(ARGLIST, t1, yyredir(tok, &tok)); 373 } else if(iswordtok(tok)) { 374 t1 = tree2(ARGLIST, t1, yyword(tok, &tok, 1)); 375 } else { 376 break; 377 } 378 } 379 *ptok = tok; 380 return simplemung(t1); 381 } 382 383 static tree* 384 words(int tok, int *ptok) 385 { 386 tree *t; 387 388 // words: {$$=(tree*)0;} 389 // | words word {$$=tree2(WORDS, $1, $2);} 390 391 t = nil; 392 tok = dropsp(tok); 393 while(iswordtok(tok)) 394 t = tree2(WORDS, t, yyword(tok, &tok, 1)); 395 *ptok = tok; 396 return t; 397 } 398 399 static tree* 400 yyword(int tok, int *ptok, int eqok) 401 { 402 tree *t; 403 404 // word: keyword {lastword=1; $1->type=WORD;} 405 // | comword 406 // | word '^' word {$$=tree2('^', $1, $3);} 407 // comword: '$' word {$$=tree1('$', $2);} 408 // | '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} 409 // | '"' word {$$=tree1('"', $2);} 410 // | COUNT word {$$=tree1(COUNT, $2);} 411 // | WORD 412 // | '`' brace {$$=tree1('`', $2);} 413 // | '(' words ')' {$$=tree1(PAREN, $2);} 414 // | REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} 415 // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN 416 // 417 // factored into: 418 // 419 // word: word1 420 // | word '^' word1 421 // 422 // word1: keyword | comword 423 424 t = word1(tok, &tok); 425 if(tok == '=' && !eqok) 426 goto out; 427 for(;;) { 428 if(iswordtok(tok)) { 429 // No free carats around parens. 430 if(t->type == PAREN || tok == '(') 431 syntax(tok); 432 t = tree2('^', t, word1(tok, &tok)); 433 continue; 434 } 435 tok = dropsp(tok); 436 if(tok == '^') { 437 t = tree2('^', t, word1(yylex(), &tok)); 438 continue; 439 } 440 break; 441 } 442 out: 443 *ptok = dropsp(tok); 444 return t; 445 } 446 447 static tree* 448 word1(int tok, int *ptok) 449 { 450 tree *w, *sub, *t; 451 452 tok = dropsp(tok); 453 switch(tok) { 454 default: 455 syntax(tok); 456 457 case WORD: 458 case FOR: 459 case IN: 460 case WHILE: 461 case IF: 462 case NOT: 463 case TWIDDLE: 464 case BANG: 465 case SUBSHELL: 466 case SWITCH: 467 case FN: 468 // | WORD 469 // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN 470 t = yylval.tree; 471 t->type = WORD; 472 *ptok = yylex(); 473 return t; 474 475 case '=': 476 *ptok = yylex(); 477 return token("=", WORD); 478 479 case '$': 480 // comword: '$' word1 {$$=tree1('$', $2);} 481 // | '$' word1 SUB words ')' {$$=tree2(SUB, $2, $4);} 482 w = word1(yylex(), &tok); 483 if(tok == '(') { 484 sub = words(yylex(), &tok); 485 if(tok != ')') 486 syntax(tok); 487 *ptok = yylex(); 488 return tree2(SUB, w, sub); 489 } 490 *ptok = tok; 491 return tree1('$', w); 492 493 case '"': 494 // | '"' word1 {$$=tree1('"', $2);} 495 return tree1('"', word1(yylex(), ptok)); 496 497 case COUNT: 498 // | COUNT word1 {$$=tree1(COUNT, $2);} 499 return tree1(COUNT, word1(yylex(), ptok)); 500 501 case '`': 502 // | '`' brace {$$=tree1('`', $2);} 503 t = tree1('`', brace(yylex())); 504 *ptok = yylex(); 505 return t; 506 507 case '(': 508 // | '(' words ')' {$$=tree1(PAREN, $2);} 509 t = tree1(PAREN, words(yylex(), &tok)); 510 if(tok != ')') 511 syntax(tok); 512 *ptok = yylex(); 513 return t; 514 515 case REDIRW: 516 // | REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;} 517 t = yylval.tree; 518 t = mung1(t, brace(yylex())); 519 t->type = PIPEFD; 520 *ptok = yylex(); 521 return t; 522 } 523 } 524 525 static int 526 iswordtok(int tok) 527 { 528 switch(tok) { 529 case FOR: 530 case IN: 531 case WHILE: 532 case IF: 533 case NOT: 534 case TWIDDLE: 535 case BANG: 536 case SUBSHELL: 537 case SWITCH: 538 case FN: 539 case '$': 540 case '"': 541 case COUNT: 542 case WORD: 543 case '`': 544 case '(': 545 case REDIRW: 546 case '=': 547 return 1; 548 } 549 return 0; 550 }