-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparse.y
174 lines (137 loc) · 5.26 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
/* 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);
}