coffee-script 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/coffee-script.gemspec +3 -3
- data/examples/code.coffee +43 -43
- data/examples/documents.coffee +17 -17
- data/examples/poignant.coffee +22 -22
- data/examples/underscore.coffee +457 -416
- data/lib/coffee-script.rb +2 -1
- data/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +23 -4
- data/lib/coffee_script/command_line.rb +5 -4
- data/lib/coffee_script/grammar.y +99 -78
- data/lib/coffee_script/lexer.rb +91 -47
- data/lib/coffee_script/narwhal/coffee-script.coffee +16 -15
- data/lib/coffee_script/narwhal/{js → lib}/coffee-script.js +22 -17
- data/lib/coffee_script/narwhal/{js → lib/coffee-script}/loader.js +6 -4
- data/lib/coffee_script/narwhal/loader.coffee +3 -3
- data/lib/coffee_script/nodes.rb +307 -255
- data/lib/coffee_script/parse_error.rb +3 -2
- data/lib/coffee_script/parser.output +10284 -9773
- data/lib/coffee_script/parser.rb +1286 -1141
- data/lib/coffee_script/rewriter.rb +208 -0
- data/lib/coffee_script/scope.rb +15 -10
- data/package.json +9 -0
- metadata +6 -6
- data/lib/coffee_script/narwhal/js/launcher.js +0 -3
- data/lib/coffee_script/narwhal/launcher.coffee +0 -1
data/lib/coffee-script.rb
CHANGED
@@ -4,12 +4,13 @@ require "coffee_script/parser"
|
|
4
4
|
require "coffee_script/nodes"
|
5
5
|
require "coffee_script/value"
|
6
6
|
require "coffee_script/scope"
|
7
|
+
require "coffee_script/rewriter"
|
7
8
|
require "coffee_script/parse_error"
|
8
9
|
|
9
10
|
# Namespace for all CoffeeScript internal classes.
|
10
11
|
module CoffeeScript
|
11
12
|
|
12
|
-
VERSION = '0.
|
13
|
+
VERSION = '0.2.0' # Keep in sync with the gemspec.
|
13
14
|
|
14
15
|
# Compile a script (String or IO) to JavaScript.
|
15
16
|
def self.compile(script, options={})
|
@@ -39,7 +39,7 @@
|
|
39
39
|
<key>comment</key>
|
40
40
|
<string>match stuff like: funcName: => … </string>
|
41
41
|
<key>match</key>
|
42
|
-
<string>([a-zA-
|
42
|
+
<string>([a-zA-Z0-9_?.$*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=>)</string>
|
43
43
|
<key>name</key>
|
44
44
|
<string>meta.function.coffee</string>
|
45
45
|
</dict>
|
@@ -60,7 +60,7 @@
|
|
60
60
|
<key>comment</key>
|
61
61
|
<string>match stuff like: a => … </string>
|
62
62
|
<key>match</key>
|
63
|
-
<string>([a-zA-
|
63
|
+
<string>([a-zA-Z0-9_?., $*]*)\s*(=>)</string>
|
64
64
|
<key>name</key>
|
65
65
|
<string>meta.inline.function.coffee</string>
|
66
66
|
</dict>
|
@@ -204,10 +204,29 @@
|
|
204
204
|
</dict>
|
205
205
|
<dict>
|
206
206
|
<key>match</key>
|
207
|
-
<string>\b(break|
|
207
|
+
<string>\b(break|by|catch|continue|else|finally|for|if|return|switch|then|throw|try|unless|when|while)\b</string>
|
208
208
|
<key>name</key>
|
209
209
|
<string>keyword.control.coffee</string>
|
210
210
|
</dict>
|
211
|
+
<dict>
|
212
|
+
<key>match</key>
|
213
|
+
<string>\b([a-zA-Z$_]\w*)(\:)\s</string>
|
214
|
+
<key>name</key>
|
215
|
+
<string>variable.assignment.coffee</string>
|
216
|
+
<key>captures</key>
|
217
|
+
<dict>
|
218
|
+
<key>1</key>
|
219
|
+
<dict>
|
220
|
+
<key>name</key>
|
221
|
+
<string>entity.name.function.coffee</string>
|
222
|
+
</dict>
|
223
|
+
<key>2</key>
|
224
|
+
<dict>
|
225
|
+
<key>name</key>
|
226
|
+
<string>keyword.operator.coffee</string>
|
227
|
+
</dict>
|
228
|
+
</dict>
|
229
|
+
</dict>
|
211
230
|
<dict>
|
212
231
|
<key>match</key>
|
213
232
|
<string>\b(true|on|yes)\b</string>
|
@@ -240,7 +259,7 @@
|
|
240
259
|
</dict>
|
241
260
|
<dict>
|
242
261
|
<key>match</key>
|
243
|
-
<string>!|\$|%|&
|
262
|
+
<string>!|\$|%|&|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
|
244
263
|
<key>name</key>
|
245
264
|
<string>keyword.operator.coffee</string>
|
246
265
|
</dict>
|
@@ -19,8 +19,9 @@ Usage:
|
|
19
19
|
# Seconds to pause between checks for changed source files.
|
20
20
|
WATCH_INTERVAL = 0.5
|
21
21
|
|
22
|
-
#
|
23
|
-
|
22
|
+
# Command to execute in Narwhal
|
23
|
+
PACKAGE = File.expand_path(File.dirname(__FILE__) + '/../..')
|
24
|
+
LAUNCHER = "narwhal -p #{PACKAGE} -e 'require(\"coffee-script\").run(system.args);'"
|
24
25
|
|
25
26
|
# Run the CommandLine off the contents of ARGV.
|
26
27
|
def initialize
|
@@ -107,7 +108,7 @@ Usage:
|
|
107
108
|
|
108
109
|
# Use Narwhal to run an interactive CoffeeScript session.
|
109
110
|
def launch_repl
|
110
|
-
exec "
|
111
|
+
exec "#{LAUNCHER}"
|
111
112
|
rescue Errno::ENOENT
|
112
113
|
puts "Error: Narwhal must be installed to use the interactive REPL."
|
113
114
|
exit(1)
|
@@ -116,7 +117,7 @@ Usage:
|
|
116
117
|
# Use Narwhal to compile and execute CoffeeScripts.
|
117
118
|
def run_scripts
|
118
119
|
sources = @sources.join(' ')
|
119
|
-
exec "
|
120
|
+
exec "#{LAUNCHER} #{sources}"
|
120
121
|
rescue Errno::ENOENT
|
121
122
|
puts "Error: Narwhal must be installed in order to execute CoffeeScripts."
|
122
123
|
exit(1)
|
data/lib/coffee_script/grammar.y
CHANGED
@@ -1,24 +1,26 @@
|
|
1
1
|
class Parser
|
2
2
|
|
3
3
|
# Declare tokens produced by the lexer
|
4
|
-
token IF ELSE
|
4
|
+
token IF ELSE UNLESS
|
5
5
|
token NUMBER STRING REGEX
|
6
6
|
token TRUE FALSE YES NO ON OFF
|
7
7
|
token IDENTIFIER PROPERTY_ACCESS
|
8
|
-
token CODE PARAM NEW RETURN
|
8
|
+
token CODE PARAM PARAM_SPLAT NEW RETURN
|
9
9
|
token TRY CATCH FINALLY THROW
|
10
10
|
token BREAK CONTINUE
|
11
|
-
token FOR IN WHILE
|
12
|
-
token SWITCH
|
11
|
+
token FOR IN BY WHEN WHILE
|
12
|
+
token SWITCH LEADING_WHEN
|
13
13
|
token DELETE INSTANCEOF TYPEOF
|
14
14
|
token SUPER EXTENDS
|
15
15
|
token NEWLINE
|
16
16
|
token COMMENT
|
17
17
|
token JS
|
18
|
+
token INDENT OUTDENT
|
18
19
|
|
19
20
|
# Declare order of operations.
|
20
21
|
prechigh
|
21
|
-
|
22
|
+
left '?'
|
23
|
+
nonassoc UMINUS PARAM_SPLAT SPLAT NOT '!' '!!' '~' '++' '--'
|
22
24
|
left '*' '/' '%'
|
23
25
|
left '+' '-'
|
24
26
|
left '<<' '>>' '>>>'
|
@@ -29,51 +31,40 @@ prechigh
|
|
29
31
|
right '-=' '+=' '/=' '*=' '%='
|
30
32
|
right DELETE INSTANCEOF TYPEOF
|
31
33
|
left '.'
|
32
|
-
right
|
33
|
-
left
|
34
|
+
right INDENT
|
35
|
+
left OUTDENT
|
36
|
+
right WHEN LEADING_WHEN IN BY
|
37
|
+
right THROW FOR NEW SUPER
|
38
|
+
left EXTENDS
|
34
39
|
left ASSIGN '||=' '&&='
|
35
|
-
right RETURN
|
40
|
+
right RETURN '=>' UNLESS IF ELSE WHILE
|
36
41
|
preclow
|
37
42
|
|
38
|
-
# We expect 3 shift/reduce errors for optional syntax.
|
39
|
-
# There used to be 252 -- greatly improved.
|
40
|
-
expect 3
|
41
|
-
|
42
43
|
rule
|
43
44
|
|
44
45
|
# All parsing will end in this rule, being the trunk of the AST.
|
45
46
|
Root:
|
46
|
-
/* nothing */ { result = Expressions.new
|
47
|
-
| Terminator { result = Expressions.new
|
47
|
+
/* nothing */ { result = Expressions.new }
|
48
|
+
| Terminator { result = Expressions.new }
|
48
49
|
| Expressions { result = val[0] }
|
50
|
+
| Block Terminator { result = val[0] }
|
49
51
|
;
|
50
52
|
|
51
53
|
# Any list of expressions or method body, seperated by line breaks or semis.
|
52
54
|
Expressions:
|
53
|
-
Expression { result = Expressions.
|
55
|
+
Expression { result = Expressions.wrap(val) }
|
54
56
|
| Expressions Terminator Expression { result = val[0] << val[2] }
|
55
57
|
| Expressions Terminator { result = val[0] }
|
56
|
-
| Terminator Expressions { result = val[1] }
|
57
58
|
;
|
58
59
|
|
59
60
|
# All types of expressions in our language.
|
60
61
|
Expression:
|
61
|
-
PureExpression
|
62
|
-
| Statement
|
63
|
-
;
|
64
|
-
|
65
|
-
# The parts that are natural JavaScript expressions.
|
66
|
-
PureExpression:
|
67
62
|
Value
|
68
63
|
| Call
|
69
64
|
| Code
|
70
65
|
| Operation
|
71
66
|
| Range
|
72
|
-
|
73
|
-
|
74
|
-
# We have to take extra care to convert these statements into expressions.
|
75
|
-
Statement:
|
76
|
-
Assign
|
67
|
+
| Assign
|
77
68
|
| If
|
78
69
|
| Try
|
79
70
|
| Throw
|
@@ -82,21 +73,22 @@ rule
|
|
82
73
|
| For
|
83
74
|
| Switch
|
84
75
|
| Extends
|
76
|
+
| Splat
|
77
|
+
| Existence
|
85
78
|
| Comment
|
86
79
|
;
|
87
80
|
|
81
|
+
Block:
|
82
|
+
INDENT Expressions OUTDENT { result = val[1] }
|
83
|
+
| INDENT OUTDENT { result = Expressions.new }
|
84
|
+
;
|
85
|
+
|
88
86
|
# All tokens that can terminate an expression.
|
89
87
|
Terminator:
|
90
88
|
"\n"
|
91
89
|
| ";"
|
92
90
|
;
|
93
91
|
|
94
|
-
# All tokens that can serve to begin the second block of a multi-part expression.
|
95
|
-
Then:
|
96
|
-
THEN
|
97
|
-
| Terminator
|
98
|
-
;
|
99
|
-
|
100
92
|
# All hard-coded values.
|
101
93
|
Literal:
|
102
94
|
NUMBER { result = LiteralNode.new(val[0]) }
|
@@ -115,13 +107,13 @@ rule
|
|
115
107
|
|
116
108
|
# Assignment to a variable.
|
117
109
|
Assign:
|
118
|
-
Value ASSIGN Expression
|
110
|
+
Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
|
119
111
|
;
|
120
112
|
|
121
113
|
# Assignment within an object literal.
|
122
114
|
AssignObj:
|
123
|
-
IDENTIFIER ASSIGN Expression
|
124
|
-
| STRING ASSIGN Expression
|
115
|
+
IDENTIFIER ASSIGN Expression { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
|
116
|
+
| STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
|
125
117
|
| Comment { result = val[0] }
|
126
118
|
;
|
127
119
|
|
@@ -146,6 +138,8 @@ rule
|
|
146
138
|
| '~' Expression { result = OpNode.new(val[0], val[1]) }
|
147
139
|
| '--' Expression { result = OpNode.new(val[0], val[1]) }
|
148
140
|
| '++' Expression { result = OpNode.new(val[0], val[1]) }
|
141
|
+
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
|
142
|
+
| TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
|
149
143
|
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
|
150
144
|
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
|
151
145
|
|
@@ -187,27 +181,32 @@ rule
|
|
187
181
|
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
188
182
|
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
189
183
|
|
190
|
-
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
|
191
|
-
| TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
|
192
184
|
| Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
193
185
|
;
|
194
186
|
|
195
|
-
|
196
|
-
|
197
|
-
ParamList "=>" CodeBody "." { result = CodeNode.new(val[0], val[2]) }
|
198
|
-
| "=>" CodeBody "." { result = CodeNode.new([], val[1]) }
|
187
|
+
Existence:
|
188
|
+
Expression '?' { result = ExistenceNode.new(val[0]) }
|
199
189
|
;
|
200
190
|
|
201
|
-
#
|
202
|
-
|
203
|
-
|
204
|
-
|
|
191
|
+
# Function definition.
|
192
|
+
Code:
|
193
|
+
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) }
|
194
|
+
| "=>" Block { result = CodeNode.new([], val[1]) }
|
205
195
|
;
|
206
196
|
|
207
197
|
# The parameters to a function definition.
|
208
198
|
ParamList:
|
209
|
-
|
210
|
-
| ParamList ","
|
199
|
+
Param { result = val }
|
200
|
+
| ParamList "," Param { result = val[0] << val[2] }
|
201
|
+
;
|
202
|
+
|
203
|
+
Param:
|
204
|
+
PARAM
|
205
|
+
| PARAM_SPLAT PARAM { result = ParamSplatNode.new(val[1]) }
|
206
|
+
;
|
207
|
+
|
208
|
+
Splat:
|
209
|
+
'*' Value = SPLAT { result = ArgSplatNode.new(val[1]) }
|
211
210
|
;
|
212
211
|
|
213
212
|
# Expressions that can be treated as values.
|
@@ -240,10 +239,13 @@ rule
|
|
240
239
|
|
241
240
|
# Assignment within an object literal (comma or newline separated).
|
242
241
|
AssignList:
|
243
|
-
/* nothing */ { result = []}
|
242
|
+
/* nothing */ { result = [] }
|
244
243
|
| AssignObj { result = val }
|
245
244
|
| AssignList "," AssignObj { result = val[0] << val[2] }
|
246
245
|
| AssignList Terminator AssignObj { result = val[0] << val[2] }
|
246
|
+
| AssignList ","
|
247
|
+
Terminator AssignObj { result = val[0] << val[3] }
|
248
|
+
| INDENT AssignList OUTDENT { result = val[1] }
|
247
249
|
;
|
248
250
|
|
249
251
|
# All flavors of function call (instantiation, super, and regular).
|
@@ -260,8 +262,14 @@ rule
|
|
260
262
|
|
261
263
|
# A generic function invocation.
|
262
264
|
Invocation:
|
263
|
-
Value
|
264
|
-
| Invocation
|
265
|
+
Value Arguments { result = CallNode.new(val[0], val[1]) }
|
266
|
+
| Invocation Arguments { result = CallNode.new(val[0], val[1]) }
|
267
|
+
# | Invocation Code { result = val[0] << val[1] }
|
268
|
+
;
|
269
|
+
|
270
|
+
Arguments:
|
271
|
+
"(" ArgList ")" { result = val[1] }
|
272
|
+
| "(" ArgList ")" Code { result = val[1] << val[3] }
|
265
273
|
;
|
266
274
|
|
267
275
|
# Calling super.
|
@@ -271,8 +279,10 @@ rule
|
|
271
279
|
|
272
280
|
# The range literal.
|
273
281
|
Range:
|
274
|
-
"["
|
275
|
-
|
282
|
+
"[" Expression
|
283
|
+
"." "." Expression "]" { result = RangeNode.new(val[1], val[4]) }
|
284
|
+
| "[" Expression
|
285
|
+
"." "." "." Expression "]" { result = RangeNode.new(val[1], val[5], true) }
|
276
286
|
;
|
277
287
|
|
278
288
|
# The array literal.
|
@@ -284,21 +294,25 @@ rule
|
|
284
294
|
ArgList:
|
285
295
|
/* nothing */ { result = [] }
|
286
296
|
| Expression { result = val }
|
297
|
+
| INDENT Expression { result = [val[1]] }
|
287
298
|
| ArgList "," Expression { result = val[0] << val[2] }
|
288
299
|
| ArgList Terminator Expression { result = val[0] << val[2] }
|
300
|
+
| ArgList "," Terminator Expression { result = val[0] << val[3] }
|
301
|
+
| ArgList "," INDENT Expression { result = val[0] << val[3] }
|
302
|
+
| ArgList OUTDENT { result = val[0] }
|
289
303
|
;
|
290
304
|
|
291
305
|
# Try/catch/finally exception handling blocks.
|
292
306
|
Try:
|
293
|
-
TRY
|
294
|
-
| TRY
|
295
|
-
|
307
|
+
TRY Block Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
|
308
|
+
| TRY Block FINALLY Block { result = TryNode.new(val[1], nil, nil, val[3]) }
|
309
|
+
| TRY Block Catch
|
310
|
+
FINALLY Block { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
|
296
311
|
;
|
297
312
|
|
298
313
|
# A catch clause.
|
299
314
|
Catch:
|
300
|
-
|
301
|
-
| CATCH IDENTIFIER Expressions { result = [val[1], val[2]] }
|
315
|
+
CATCH IDENTIFIER Block { result = [val[1], val[2]] }
|
302
316
|
;
|
303
317
|
|
304
318
|
# Throw an exception.
|
@@ -308,20 +322,20 @@ rule
|
|
308
322
|
|
309
323
|
# Parenthetical expressions.
|
310
324
|
Parenthetical:
|
311
|
-
"("
|
325
|
+
"(" Expression ")" { result = ParentheticalNode.new(val[1], val[0].line) }
|
312
326
|
;
|
313
327
|
|
314
328
|
# The while loop. (there is no do..while).
|
315
329
|
While:
|
316
|
-
WHILE Expression
|
317
|
-
Expressions "." { result = WhileNode.new(val[1], val[3]) }
|
330
|
+
WHILE Expression Block { result = WhileNode.new(val[1], val[2]) }
|
318
331
|
;
|
319
332
|
|
320
333
|
# Array comprehensions, including guard and current index.
|
321
334
|
# Looks a little confusing, check nodes.rb for the arguments to ForNode.
|
322
335
|
For:
|
323
336
|
Expression FOR
|
324
|
-
ForVariables ForSource { result = ForNode.new(val[0], val[3]
|
337
|
+
ForVariables ForSource { result = ForNode.new(val[0], val[3], val[2][0], val[2][1]) }
|
338
|
+
| FOR ForVariables ForSource Block { result = ForNode.new(val[3], val[2], val[1][0], val[1][1]) }
|
325
339
|
;
|
326
340
|
|
327
341
|
# An array comprehension has variables for the current element and index.
|
@@ -332,17 +346,19 @@ rule
|
|
332
346
|
|
333
347
|
# The source of the array comprehension can optionally be filtered.
|
334
348
|
ForSource:
|
335
|
-
IN
|
336
|
-
|
|
337
|
-
|
349
|
+
IN Expression { result = {:source => val[1]} }
|
350
|
+
| ForSource
|
351
|
+
WHEN Expression { result = val[0].merge(:filter => val[2]) }
|
352
|
+
| ForSource
|
353
|
+
BY Expression { result = val[0].merge(:step => val[2]) }
|
338
354
|
;
|
339
355
|
|
340
356
|
# Switch/When blocks.
|
341
357
|
Switch:
|
342
|
-
SWITCH Expression
|
343
|
-
Whens
|
344
|
-
| SWITCH Expression
|
345
|
-
Whens ELSE
|
358
|
+
SWITCH Expression INDENT
|
359
|
+
Whens OUTDENT { result = val[3].rewrite_condition(val[1]) }
|
360
|
+
| SWITCH Expression INDENT
|
361
|
+
Whens ELSE Block OUTDENT { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
|
346
362
|
;
|
347
363
|
|
348
364
|
# The inner list of whens.
|
@@ -353,16 +369,22 @@ rule
|
|
353
369
|
|
354
370
|
# An individual when.
|
355
371
|
When:
|
356
|
-
|
372
|
+
LEADING_WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
|
373
|
+
| LEADING_WHEN Expression Block
|
374
|
+
Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
|
375
|
+
| Comment
|
357
376
|
;
|
358
377
|
|
359
378
|
# All of the following nutso if-else destructuring is to make the
|
360
379
|
# grammar expand unambiguously.
|
361
380
|
|
381
|
+
IfBlock:
|
382
|
+
IF Expression Block { result = IfNode.new(val[1], val[2]) }
|
383
|
+
;
|
384
|
+
|
362
385
|
# An elsif portion of an if-else block.
|
363
386
|
ElsIf:
|
364
|
-
ELSE
|
365
|
-
Then Expressions { result = IfNode.new(val[2], val[4]) }
|
387
|
+
ELSE IfBlock { result = val[1].force_statement }
|
366
388
|
;
|
367
389
|
|
368
390
|
# Multiple elsifs can be chained together.
|
@@ -373,8 +395,8 @@ rule
|
|
373
395
|
|
374
396
|
# Terminating else bodies are strictly optional.
|
375
397
|
ElseBody
|
376
|
-
|
377
|
-
| ELSE
|
398
|
+
/* nothing */ { result = nil }
|
399
|
+
| ELSE Block { result = val[1] }
|
378
400
|
;
|
379
401
|
|
380
402
|
# All the alternatives for ending an if-else block.
|
@@ -385,10 +407,9 @@ rule
|
|
385
407
|
|
386
408
|
# The full complement of if blocks, including postfix one-liner ifs and unlesses.
|
387
409
|
If:
|
388
|
-
|
389
|
-
|
390
|
-
| Expression
|
391
|
-
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, {:statement => true, :invert => true}) }
|
410
|
+
IfBlock IfEnd { result = val[0].add_else(val[1]) }
|
411
|
+
| Expression IF Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true}) }
|
412
|
+
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true, :invert => true}) }
|
392
413
|
;
|
393
414
|
|
394
415
|
end
|