plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }