-
Notifications
You must be signed in to change notification settings - Fork 24
/
parse.y
186 lines (146 loc) · 5.53 KB
/
parse.y
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
175
176
177
178
179
180
181
182
183
184
185
/* 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 '^' '='
%right ELSE TWIDDLE
%left WHILE ')'
%left ANDAND OROR '\n'
%left BANG SUBSHELL
%left PIPE
%left PREDIR /* fictitious */
%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
arg args else
%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 optcaret '=' optcaret word { $$ = mk(nAssign,$1,$5); }
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 else { $$ = $2 != NULL ? mk(nElse, $1, $2) : $1; }
else : /* empty */ %prec ELSE { $$ = NULL; }
| ELSE optnl cmd { $$ = $3; }
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 PREDIR { $$ = ($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 %prec ELSE { $$ = mk(nRmfn,$2); }
optcaret : /* empty */ %prec '^'
| '^'
simple : first %prec ELSE
| first args %prec ELSE { $$ = ($2 != NULL ? mk(nArgs,$1,$2) : $1); }
args : arg
| args arg { $$ = ($2 != NULL ? mk(nArgs,$1,$2) : $1); }
arg : word
| redir
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);
}