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/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