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