1
- script "one or more statements separated by control operators"
1
+ script
2
+ = sections :(spaceNL * statementList spaceNL * )+
3
+
4
+ statementList "a list of statements"
2
5
= first :statement
3
- rest :(controlOperator statement )*
4
- last :controlOperator ?
5
- spaceNL *
6
+ rest :(space * controlOperator spaceNL * statement )*
7
+ last :(space * controlOperator )?
6
8
7
9
statement
8
10
= statement :( command
9
11
/ conditionalLoop
10
12
/ ifBlock
11
13
)
12
- space * next : chainedStatement ?
14
+ next :( space * chainedStatement ) ?
13
15
14
16
chainedStatement
15
17
= operator :('&&' / '||' ) spaceNL * statement :statement
@@ -18,36 +20,39 @@ controlOperator
18
20
= op :('&' / ';' / '\n ' )
19
21
20
22
command "a single command"
21
- = spaceNL *
22
- pre :((variableAssignment / redirect ) space + )*
23
- name :commandName
23
+ = pre :((variableAssignment / redirect ) space + )*
24
+ name :(commandName / builtinCommandName )
24
25
post :(space + (redirect / argument ))*
25
26
26
27
conditionalLoop
27
- = kind :("while" / "until" ) spaceNL + test :script spaceNL *
28
- "do" spaceNL *
29
- body :script spaceNL *
30
- "done" spaceNL *
28
+ = kind :("while" / "until" ) spaceNL + test :condition spaceNL *
29
+ "do" spaceNL
30
+ body :script
31
+ "done"
31
32
32
33
ifBlock
33
- = "if" spaceNL + test :script spaceNL *
34
- "then" spaceNL * body :script spaceNL *
34
+ = "if" spaceNL + test :script
35
+ "then" spaceNL + body :script
35
36
elifBlocks :elifBlock *
36
37
elseBody :("else" script )?
37
- "fi" spaceNL *
38
+ "fi"
38
39
39
40
elifBlock
40
- = "elif" spaceNL + test :script "then" spaceNL + body :script
41
+ = "elif" spaceNL + test :condition "then" spaceNL + body :script
41
42
42
43
condition
43
- = '[' test :script ']'
44
+ = test :script
44
45
45
46
variableAssignment
46
47
= writableVariableName '=' argument
47
48
48
49
commandName "command name"
49
50
= ! redirect ! keyword name :(concatenation / '[' )
50
51
52
+ builtinCommandName
53
+ = '['
54
+ / '[['
55
+
51
56
argument "command argument"
52
57
= commandName
53
58
/ commandSubstitution
@@ -63,32 +68,37 @@ concatenation
63
68
/ doubleQuote
64
69
)+
65
70
66
- bareword = cs :barewordChar +
71
+ bareword
72
+ = cs :barewordChar +
67
73
68
74
barewordChar
69
75
= '\\ ' chr :barewordMeta { return chr }
70
76
/ ! barewordMeta chr :. { return chr }
71
77
72
- barewordMeta = [$"';&<>\n ()\[\] *?|` ]
78
+ barewordMeta = [$"';&<>\n ()\[ *?|` ]
73
79
74
- glob = (barewordChar * ('*' / '?' / characterRange / braceExpansion )+ barewordChar * )+
80
+ glob
81
+ = barewordChar * ('*' / '?' / characterRange / braceExpansion )+ barewordChar *
75
82
76
- characterRange =
77
- $('[' ! '-' . '-' ! '-' . ']' )
83
+ characterRange
84
+ = $('[' ! '-' . '-' ! '-' . ']' )
78
85
79
- braceExpansion =
80
- (.? ! '$' ) '{' barewordChar + '}'
86
+ braceExpansion
87
+ = (.? ! '$' ) '{' barewordChar + '}'
81
88
82
- singleQuote = "'" inner :$([^']* ) "'"
89
+ singleQuote
90
+ = "'" inner :$([^']* ) "'"
83
91
84
- doubleQuote = '"' contents :(expandsInQuotes / doubleQuoteChar + )* '"'
92
+ doubleQuote
93
+ = '"' contents :(expandsInQuotes / doubleQuoteChar + )* '"'
85
94
86
95
doubleQuoteChar
87
96
= '\\ ' chr :doubleQuoteMeta { return chr }
88
97
/ '\\\\ ' { return ' \\ ' }
89
98
/ ! doubleQuoteMeta chr :. { return chr }
90
99
91
- doubleQuoteMeta = '"' / '$' / '`'
100
+ doubleQuoteMeta
101
+ = '"' / '$' / '`'
92
102
93
103
expandsInQuotes
94
104
= backticks
@@ -107,10 +117,10 @@ backticks
107
117
= '`' commands :(! backticks command )+ '`'
108
118
109
119
subshell
110
- = '$(' commands :script ')'
120
+ = '$(' commands :statementList ')'
111
121
112
122
commandSubstitution
113
- = rw :[<>] '(' commands :script ')'
123
+ = rw :[<>] '(' commands :statementList ')'
114
124
115
125
redirect
116
126
= moveFd / duplicateFd / redirectFd / pipe
@@ -151,7 +161,9 @@ keyword
151
161
/ "then"
152
162
/ "else"
153
163
/ "elif"
154
- / "fi" )
164
+ / "fi"
165
+ / "[["
166
+ )
155
167
( spaceNL + / EOF )
156
168
157
169
continuationStart
0 commit comments