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