coffee-script 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.1.6' # Keep in sync with the gemspec.
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: =&gt; … </string>
41
41
  <key>match</key>
42
- <string>([a-zA-Z_?.$]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string>
42
+ <string>([a-zA-Z0-9_?.$*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</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 =&gt; … </string>
62
62
  <key>match</key>
63
- <string>([a-zA-Z_?., $]*)\s*(=&gt;)</string>
63
+ <string>([a-zA-Z0-9_?., $*]*)\s*(=&gt;)</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|when|catch|continue|else|finally|for|if|return|switch|then|throw|try|unless|while)\b</string>
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>!|\$|%|&amp;|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
262
+ <string>!|\$|%|&amp;|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\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
- # Path to the Narwhal Launcher:
23
- LAUNCHER = File.expand_path(File.dirname(__FILE__)) + '/narwhal/js/launcher.js'
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 "narwhal #{LAUNCHER}"
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 "narwhal #{LAUNCHER} #{sources}"
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)
@@ -1,24 +1,26 @@
1
1
  class Parser
2
2
 
3
3
  # Declare tokens produced by the lexer
4
- token IF ELSE THEN UNLESS
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 WHEN
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
- nonassoc UMINUS NOT '!' '!!' '~' '++' '--'
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 THROW FOR IN WHILE NEW SUPER
33
- left UNLESS IF ELSE EXTENDS
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.new(val) }
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 { result = AssignNode.new(val[0], val[2]) }
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 { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
124
- | STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
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
- # Function definition.
196
- Code:
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
- # The body of a function.
202
- CodeBody:
203
- /* nothing */ { result = Expressions.new([]) }
204
- | Expressions { result = val[0] }
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
- PARAM { result = val }
210
- | ParamList "," PARAM { result = val[0] << val[2] }
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 "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
264
- | Invocation "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
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
- "[" Value "." "." Value "]" { result = RangeNode.new(val[1], val[4]) }
275
- | "[" Value "." "." "." Value "]" { result = RangeNode.new(val[1], val[5], true) }
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 Expressions Catch "." { result = TryNode.new(val[1], val[2][0], val[2][1]) }
294
- | TRY Expressions Catch
295
- FINALLY Expressions "." { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
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
- /* nothing */ { result = [nil, nil] }
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
- "(" Expressions ")" { result = ParentheticalNode.new(val[1]) }
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 Then
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][0], val[2][0], val[3][1], val[2][1]) }
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 PureExpression "." { result = [val[1]] }
336
- | IN PureExpression
337
- IF Expression "." { result = [val[1], val[3]] }
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 Then
343
- Whens "." { result = val[3].rewrite_condition(val[1]) }
344
- | SWITCH Expression Then
345
- Whens ELSE Expressions "." { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
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
- WHEN Expression Then Expressions { result = IfNode.new(val[1], val[3]) }
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 IF Expression
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
- "." { result = nil }
377
- | ELSE Expressions "." { result = val[1] }
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
- IF Expression
389
- Then Expressions IfEnd { result = IfNode.new(val[1], val[3], val[4]) }
390
- | Expression IF Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, {:statement => true}) }
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