rc

[fork] interactive rc shell
git clone https://hhvn.uk/rc
git clone git://hhvn.uk/rc
Log | Files | Refs | README | LICENSE

parse.y (5837B)


      1 /* parse.y */
      2 
      3 /*
      4  * Adapted from rc grammar, v10 manuals, volume 2.
      5  */
      6 
      7 %{
      8 /* note that this actually needs to appear before any system header
      9    files are included; byacc likes to throw in <stdlib.h> first. */
     10 #include "rc.h"
     11 
     12 static Node *star, *nolist;
     13 Node *parsetree;	/* not using yylval because bison declares it as an auto */
     14 %}
     15 
     16 %token ANDAND BACKBACK BANG CASE COUNT DUP ELSE END FLAT FN FOR IF IN NOT
     17 %token OROR PIPE REDIR SREDIR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD HUH
     18 
     19 %left NOT
     20 %left '^' '='
     21 %right ELSE TWIDDLE
     22 %left WHILE ')'
     23 %left ANDAND OROR '\n'
     24 %left BANG SUBSHELL
     25 %left PIPE
     26 %left PREDIR /* fictitious */
     27 %right '$' 
     28 %left SUB
     29 /*
     30 */
     31 
     32 %union {
     33 	struct Node *node;
     34 	struct Redir redir;
     35 	struct Pipe pipe;
     36 	struct Dup dup;
     37 	struct Word word;
     38 	char *keyword;
     39 }
     40 
     41 %type <redir> REDIR SREDIR
     42 %type <pipe> PIPE
     43 %type <dup> DUP
     44 %type <word> WORD
     45 %type <keyword> keyword
     46 %type <node> assign body brace case cbody cmd cmdsa cmdsan comword epilog
     47 	     first line nlwords paren redir sword simple iftail word words
     48 	     arg args else
     49 
     50 %start rc
     51 
     52 %%
     53 
     54 rc	: line end		{ parsetree = $1; YYACCEPT; }
     55 	| error end		{ yyerrok; parsetree = NULL; YYABORT; }
     56 
     57 /* an rc line may end in end-of-file as well as newline, e.g., rc -c 'ls' */
     58 end	: END	/* EOF */	{ if (!heredoc(1)) YYABORT; } /* flag error if there is a heredoc in the queue */
     59 	| '\n'			{ if (!heredoc(0)) YYABORT; } /* get heredoc on \n */
     60 
     61 /* a cmdsa is a command followed by ampersand or newline (used in "line" and "body") */
     62 cmdsa	: cmd ';'
     63 	| cmd '&'		{ $$ = ($1 != NULL ? mk(nNowait,$1) : $1); }
     64 
     65 /* a line is a single command, or a command terminated by ; or & followed by a line (recursive) */
     66 line	: cmd
     67 	| cmdsa line		{ $$ = ($1 != NULL ? mk(nBody,$1,$2) : $2); }
     68 
     69 /* a body is like a line, only commands may also be terminated by newline */
     70 body	: cmd
     71 	| cmdsan body		{ $$ = ($1 == NULL ? $2 : $2 == NULL ? $1 : mk(nBody,$1,$2)); }
     72 
     73 cmdsan	: cmdsa
     74 	| cmd '\n'		{ $$ = $1; if (!heredoc(0)) YYABORT; } /* get h.d. on \n */
     75 
     76 brace	: '{' body '}'		{ $$ = $2; }
     77 
     78 paren	: '(' body ')'		{ $$ = $2; }
     79 
     80 assign	: first optcaret '=' optcaret word	{ $$ = mk(nAssign,$1,$5); }
     81 
     82 epilog	:			{ $$ = NULL; }
     83 	| redir epilog		{ $$ = mk(nEpilog,$1,$2); }
     84 
     85 /* a redirection is a dup (e.g., >[1=2]) or a file redirection. (e.g., > /dev/null) */
     86 redir	: DUP			{ $$ = mk(nDup,$1.type,$1.left,$1.right); }
     87 	| REDIR word		{ $$ = mk(nRedir,$1.type,$1.fd,$2);
     88 				  if ($1.type == rHeredoc && !qdoc($2, $$)) YYABORT; /* queue heredocs up */
     89 				}
     90 	| SREDIR word		{ $$ = mk(nRedir,$1.type,$1.fd,$2);
     91 				  if ($1.type == rHeredoc && !qdoc($2, $$)) YYABORT; /* queue heredocs up */
     92 				}
     93 
     94 case	: CASE words ';'	 		{ $$ = mk(nCase, $2); }
     95 	| CASE words '\n'	 		{ $$ = mk(nCase, $2); }
     96 
     97 cbody	: cmd					{ $$ = mk(nCbody, $1, NULL); }
     98 	| case cbody				{ $$ = mk(nCbody, $1, $2); }
     99 	| cmdsan cbody				{ $$ = mk(nCbody, $1, $2); }
    100 
    101 iftail	: cmd else				{ $$ = $2 != NULL ? mk(nElse, $1, $2) : $1; }
    102 
    103 else	: /* empty */	%prec ELSE		{ $$ = NULL; }
    104 	| ELSE optnl cmd			{ $$ = $3; }
    105 
    106 cmd	: /* empty */	%prec WHILE		{ $$ = NULL; }
    107 	| simple
    108 	| brace epilog				{ $$ = mk(nBrace,$1,$2); }
    109 	| IF paren optnl iftail			{ $$ = mk(nIf,$2,$4); }
    110 	| IF NOT optnl cmd			{ $$ = mk(nIfnot,$4); }
    111 	| FOR '(' word IN words ')' optnl cmd	{ $$ = mk(nForin,$3,$5,$8); }
    112 	| FOR '(' word ')' optnl cmd		{ $$ = mk(nForin,$3,star,$6); }
    113 	| WHILE paren optnl cmd			{ $$ = mk(nWhile,$2,$4); }
    114 	| SWITCH '(' word ')' optnl '{' cbody '}' { $$ = mk(nSwitch,$3,$7); }
    115 	| TWIDDLE optcaret word words		{ $$ = mk(nMatch,$3,$4); }
    116 	| cmd ANDAND optnl cmd			{ $$ = mk(nAndalso,$1,$4); }
    117 	| cmd OROR optnl cmd			{ $$ = mk(nOrelse,$1,$4); }
    118 	| cmd PIPE optnl cmd			{ $$ = mk(nPipe,$2.left,$2.right,$1,$4); }
    119 	| redir cmd	%prec PREDIR		{ $$ = ($2 != NULL ? mk(nPre,$1,$2) : $1); }
    120 	| assign cmd	%prec BANG		{ $$ = ($2 != NULL ? mk(nPre,$1,$2) : $1); }
    121 	| BANG optcaret cmd			{ $$ = mk(nBang,$3); }
    122 	| SUBSHELL optcaret cmd			{ $$ = mk(nSubshell,$3); }
    123 	| FN words brace			{ $$ = mk(nNewfn,$2,$3); }
    124 	| FN words	%prec ELSE		{ $$ = mk(nRmfn,$2); }
    125 
    126 optcaret : /* empty */	%prec '^'
    127 	| '^'
    128 
    129 simple	: first		%prec ELSE
    130 	| first args	%prec ELSE	{ $$ = ($2 != NULL ? mk(nArgs,$1,$2) : $1); }
    131 
    132 args	: arg
    133 	| args arg			{ $$ = ($2 != NULL ? mk(nArgs,$1,$2) : $1); }
    134 
    135 arg	: word
    136 	| redir
    137 
    138 first	: comword
    139 	| first '^' sword		{ $$ = mk(nConcat,$1,$3); }
    140 
    141 sword	: comword
    142 	| keyword			{ $$ = mk(nWord, $1, NULL, FALSE); }
    143 
    144 word	: sword
    145 	| word '^' sword		{ $$ = mk(nConcat,$1,$3); }
    146 
    147 comword	: '$' sword			{ $$ = mk(nVar,$2); }
    148 	| '$' sword SUB words ')'	{ $$ = mk(nVarsub,$2,$4); }
    149 	| COUNT sword			{ $$ = mk(nCount,$2); }
    150 	| FLAT sword			{ $$ = mk(nFlat, $2); }
    151 	| '`' sword			{ $$ = mk(nBackq,nolist,$2); }
    152 	| '`' brace			{ $$ = mk(nBackq,nolist,$2); }
    153 	| '`' word brace		{ $$ = mk(nBackq,$2,$3); }
    154 	| '`' word sword		{ $$ = mk(nBackq,$2,$3); }
    155 	| BACKBACK word	brace		{ $$ = mk(nBackq,$2,$3); }
    156 	| BACKBACK word	sword		{ $$ = mk(nBackq,$2,$3); }
    157 	| '(' nlwords ')'		{ $$ = $2; }
    158 	| REDIR brace			{ $$ = mk(nNmpipe,$1.type,$1.fd,$2); }
    159 	| WORD				{ $$ = mk(nWord, $1.w, $1.m, $1.q); }
    160 
    161 keyword	: FOR		{ $$ = "for"; }
    162 	| IN		{ $$ = "in"; }
    163 	| WHILE		{ $$ = "while"; }
    164 	| IF		{ $$ = "if"; }
    165 	| NOT		{ $$ = "not"; }
    166 	| SWITCH	{ $$ = "switch"; }
    167 	| FN		{ $$ = "fn"; }
    168 	| ELSE		{ $$ = "else"; }
    169 	| CASE		{ $$ = "case"; }
    170 	| TWIDDLE	{ $$ = "~"; }
    171 	| BANG		{ $$ = "!"; }
    172 	| SUBSHELL	{ $$ = "@"; }
    173 	| '='		{ $$ = "="; }
    174 
    175 words	:		{ $$ = NULL; }
    176 	| words word	{ $$ = ($1 != NULL ? ($2 != NULL ? mk(nLappend,$1,$2) : $1) : $2); }
    177 
    178 nlwords :		{ $$ = NULL; }
    179 	| nlwords '\n'
    180 	| nlwords word	{ $$ = ($1 != NULL ? ($2 != NULL ? mk(nLappend,$1,$2) : $1) : $2); }
    181 
    182 optnl	: /* empty */
    183 	| optnl '\n'
    184 
    185 %%
    186 
    187 void initparse() {
    188 	star = treecpy(mk(nVar, mk(nWord,"*", NULL, FALSE)), ealloc);
    189 	nolist = treecpy(mk(nVar, mk(nWord,"ifs", NULL, FALSE)), ealloc);
    190 }
    191