coffee-script 0.2.5 → 0.2.6

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.
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'coffee-script'
3
- s.version = '0.2.5' # Keep version in sync with coffee-script.rb
4
- s.date = '2010-1-13'
3
+ s.version = '0.2.6' # Keep version in sync with coffee-script.rb
4
+ s.date = '2010-1-17'
5
5
 
6
6
  s.homepage = "http://jashkenas.github.com/coffee-script/"
7
7
  s.summary = "The CoffeeScript Compiler"
data/lib/coffee-script.rb CHANGED
@@ -10,7 +10,7 @@ require "coffee_script/parse_error"
10
10
  # Namespace for all CoffeeScript internal classes.
11
11
  module CoffeeScript
12
12
 
13
- VERSION = '0.2.5' # Keep in sync with the gemspec.
13
+ VERSION = '0.2.6' # Keep in sync with the gemspec.
14
14
 
15
15
  # Compile a script (String or IO) to JavaScript.
16
16
  def self.compile(script, options={})
@@ -230,6 +230,39 @@
230
230
  <key>name</key>
231
231
  <string>comment.line.coffee</string>
232
232
  </dict>
233
+ <dict>
234
+ <key>begin</key>
235
+ <string>(?&lt;=[=(:]|^|return)\s*(/)(?![/*+{}?])</string>
236
+ <key>beginCaptures</key>
237
+ <dict>
238
+ <key>1</key>
239
+ <dict>
240
+ <key>name</key>
241
+ <string>punctuation.definition.string.begin.coffee</string>
242
+ </dict>
243
+ </dict>
244
+ <key>end</key>
245
+ <string>(/)[igm]*</string>
246
+ <key>endCaptures</key>
247
+ <dict>
248
+ <key>1</key>
249
+ <dict>
250
+ <key>name</key>
251
+ <string>punctuation.definition.string.end.coffee</string>
252
+ </dict>
253
+ </dict>
254
+ <key>name</key>
255
+ <string>string.regexp.coffee</string>
256
+ <key>patterns</key>
257
+ <array>
258
+ <dict>
259
+ <key>match</key>
260
+ <string>\\.</string>
261
+ <key>name</key>
262
+ <string>constant.character.escape.coffee</string>
263
+ </dict>
264
+ </array>
265
+ </dict>
233
266
  <dict>
234
267
  <key>match</key>
235
268
  <string>\b(break|by|catch|continue|else|finally|for|in|of|if|return|switch|then|throw|try|unless|when|while)\b</string>
@@ -238,7 +271,7 @@
238
271
  </dict>
239
272
  <dict>
240
273
  <key>match</key>
241
- <string>\b([a-zA-Z$_](\w|\$|:|\.)*)(\:)\s</string>
274
+ <string>\b([a-zA-Z$_](\w|\$|:|\.)*\s*(?=\:))</string>
242
275
  <key>name</key>
243
276
  <string>variable.assignment.coffee</string>
244
277
  <key>captures</key>
@@ -248,11 +281,6 @@
248
281
  <key>name</key>
249
282
  <string>entity.name.function.coffee</string>
250
283
  </dict>
251
- <key>3</key>
252
- <dict>
253
- <key>name</key>
254
- <string>keyword.operator.coffee</string>
255
- </dict>
256
284
  </dict>
257
285
  </dict>
258
286
  <dict>
@@ -297,39 +325,6 @@
297
325
  <key>name</key>
298
326
  <string>constant.language.coffee</string>
299
327
  </dict>
300
- <dict>
301
- <key>begin</key>
302
- <string>(?&lt;=[=(:]|^|return)\s*(/)(?![/*+{}?])</string>
303
- <key>beginCaptures</key>
304
- <dict>
305
- <key>1</key>
306
- <dict>
307
- <key>name</key>
308
- <string>punctuation.definition.string.begin.coffee</string>
309
- </dict>
310
- </dict>
311
- <key>end</key>
312
- <string>(/)[igm]*</string>
313
- <key>endCaptures</key>
314
- <dict>
315
- <key>1</key>
316
- <dict>
317
- <key>name</key>
318
- <string>punctuation.definition.string.end.coffee</string>
319
- </dict>
320
- </dict>
321
- <key>name</key>
322
- <string>string.regexp.coffee</string>
323
- <key>patterns</key>
324
- <array>
325
- <dict>
326
- <key>match</key>
327
- <string>\\.</string>
328
- <key>name</key>
329
- <string>constant.character.escape.coffee</string>
330
- </dict>
331
- </array>
332
- </dict>
333
328
  <dict>
334
329
  <key>match</key>
335
330
  <string>\;</string>
@@ -16,6 +16,7 @@ token ARGUMENTS
16
16
  token NEWLINE
17
17
  token COMMENT
18
18
  token JS
19
+ token THIS
19
20
  token INDENT OUTDENT
20
21
 
21
22
  # Declare order of operations.
@@ -37,7 +38,7 @@ prechigh
37
38
  right WHEN LEADING_WHEN IN OF BY
38
39
  right THROW FOR NEW SUPER
39
40
  left EXTENDS
40
- left ASSIGN '||=' '&&='
41
+ left ASSIGN '||=' '&&=' '?='
41
42
  right RETURN
42
43
  right '=>' '==>' UNLESS IF ELSE WHILE
43
44
  preclow
@@ -102,12 +103,12 @@ rule
102
103
  | BREAK { result = LiteralNode.new(val[0]) }
103
104
  | CONTINUE { result = LiteralNode.new(val[0]) }
104
105
  | ARGUMENTS { result = LiteralNode.new(val[0]) }
105
- | TRUE { result = LiteralNode.new(true) }
106
- | FALSE { result = LiteralNode.new(false) }
107
- | YES { result = LiteralNode.new(true) }
108
- | NO { result = LiteralNode.new(false) }
109
- | ON { result = LiteralNode.new(true) }
110
- | OFF { result = LiteralNode.new(false) }
106
+ | TRUE { result = LiteralNode.new(Value.new(true)) }
107
+ | FALSE { result = LiteralNode.new(Value.new(false)) }
108
+ | YES { result = LiteralNode.new(Value.new(true)) }
109
+ | NO { result = LiteralNode.new(Value.new(false)) }
110
+ | ON { result = LiteralNode.new(Value.new(true)) }
111
+ | OFF { result = LiteralNode.new(Value.new(false)) }
111
112
  ;
112
113
 
113
114
  # Assignment to a variable (or index).
@@ -178,6 +179,7 @@ rule
178
179
  | Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
179
180
  | Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
180
181
  | Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
182
+ | Expression '?' Expression { result = OpNode.new(val[1], val[0], val[2]) }
181
183
 
182
184
  | Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
183
185
  | Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
@@ -186,6 +188,7 @@ rule
186
188
  | Expression '%=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
187
189
  | Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
188
190
  | Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
191
+ | Expression '?=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
189
192
 
190
193
  | Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
191
194
  | Expression IN Expression { result = OpNode.new(val[1], val[0], val[2]) }
@@ -235,6 +238,7 @@ rule
235
238
  | Range { result = ValueNode.new(val[0]) }
236
239
  | Value Accessor { result = val[0] << val[1] }
237
240
  | Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
241
+ | THIS { result = ValueNode.new(ThisNode.new) }
238
242
  ;
239
243
 
240
244
  # Accessing into an object or array, through dot or index notation.
@@ -292,7 +296,7 @@ rule
292
296
 
293
297
  # Calling super.
294
298
  Super:
295
- SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
299
+ SUPER "(" ArgList ")" { result = CallNode.new(Value.new('super'), val[2]) }
296
300
  ;
297
301
 
298
302
  # The range literal.
@@ -13,10 +13,11 @@ module CoffeeScript
13
13
  "try", "catch", "finally", "throw",
14
14
  "break", "continue",
15
15
  "for", "in", "of", "by", "where", "while",
16
+ "delete", "instanceof", "typeof",
16
17
  "switch", "when",
17
18
  "super", "extends",
18
19
  "arguments",
19
- "delete", "instanceof", "typeof"]
20
+ "this"]
20
21
 
21
22
  # Token matching regexes.
22
23
  IDENTIFIER = /\A([a-zA-Z$_](\w|\$)*)/
@@ -24,7 +25,7 @@ module CoffeeScript
24
25
  STRING = /\A(""|''|"(.*?)([^\\]|\\\\)"|'(.*?)([^\\]|\\\\)')/m
25
26
  HEREDOC = /\A("{6}|'{6}|"{3}\n?(.*?)\n?(\s*)"{3}|'{3}\n?(.*?)\n?(\s*)'{3})/m
26
27
  JS = /\A(``|`(.*?)([^\\]|\\\\)`)/m
27
- OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
28
+ OPERATOR = /\A([+\*&|\/\-%=<>:!?]+)/
28
29
  WHITESPACE = /\A([ \t]+)/
29
30
  COMMENT = /\A(((\n?[ \t]*)?#.*$)+)/
30
31
  CODE = /\A(=?=>)/
@@ -20,28 +20,31 @@
20
20
  // Run a simple REPL, round-tripping to the CoffeeScript compiler for every
21
21
  // command.
22
22
  exports.run = function run(args) {
23
- var __a, i, path, result;
23
+ var __a, __b, i, path, result;
24
24
  if (args.length) {
25
25
  __a = args;
26
- for (i=0; i<__a.length; i++) {
26
+ for (i = 0; i < __a.length; i++) {
27
27
  path = __a[i];
28
28
  exports.evalCS(File.read(path));
29
29
  delete args[i];
30
30
  }
31
31
  return true;
32
32
  }
33
+ __b = [];
33
34
  while (true) {
34
- try {
35
- system.stdout.write('coffee> ').flush();
36
- result = exports.evalCS(Readline.readline(), ['--globals']);
37
- if (result !== undefined) {
38
- print(result);
35
+ __b.push((function() {
36
+ try {
37
+ system.stdout.write('coffee> ').flush();
38
+ result = exports.evalCS(Readline.readline(), ['--globals']);
39
+ if (result !== undefined) {
40
+ return print(result);
41
+ }
42
+ } catch (e) {
43
+ return print(e);
39
44
  }
40
- } catch (e) {
41
- print(e);
42
- }
45
+ })());
43
46
  }
44
- return null;
47
+ return __b;
45
48
  };
46
49
  // Compile a given CoffeeScript file into JavaScript.
47
50
  exports.compileFile = function compileFile(path) {
@@ -24,6 +24,19 @@ module CoffeeScript
24
24
  class_eval "def statement_only?; true; end"
25
25
  end
26
26
 
27
+ # This node needs to know if it's being compiled as a top-level statement,
28
+ # in order to compile without special expression conversion.
29
+ def self.top_sensitive
30
+ class_eval "def top_sensitive?; true; end"
31
+ end
32
+
33
+ # Provide a quick implementation of a children method.
34
+ def self.children(*attributes)
35
+ attr_reader(*attributes)
36
+ attrs = attributes.map {|a| "[@#{a}]" }.join(', ')
37
+ class_eval "def children; [#{attrs}].flatten.compact; end"
38
+ end
39
+
27
40
  def write(code)
28
41
  puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE']
29
42
  code
@@ -37,14 +50,18 @@ module CoffeeScript
37
50
  @options = o.dup
38
51
  @indent = o[:indent]
39
52
  top = self.top_sensitive? ? @options[:top] : @options.delete(:top)
40
- closure = statement? && !statement_only? && !top && !@options[:return]
53
+ closure = statement? && !statement_only? && !top && !@options[:return] && !self.is_a?(CommentNode)
54
+ closure &&= !contains? {|n| n.statement_only? }
41
55
  closure ? compile_closure(@options) : compile_node(@options)
42
56
  end
43
57
 
44
58
  def compile_closure(o={})
45
- indent = o[:indent]
46
- @indent = (o[:indent] = idt(1))
47
- "(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
59
+ indent = o[:indent]
60
+ @indent = (o[:indent] = idt(1))
61
+ pass_this = !o[:closure] && contains? {|node| node.is_a?(ThisNode) }
62
+ param = pass_this ? '__this' : ''
63
+ body = compile_node(o.merge(:return => true, :closure => true))
64
+ "(function(#{param}) {\n#{body}\n#{indent}})(#{pass_this ? 'this' : ''})"
48
65
  end
49
66
 
50
67
  # Quick short method for the current indentation level, plus tabbing in.
@@ -52,8 +69,18 @@ module CoffeeScript
52
69
  @indent + (TAB * tabs)
53
70
  end
54
71
 
72
+ # Does this node, or any of it's children, contain a node of a certain kind?
73
+ def contains?(&block)
74
+ children.each do |node|
75
+ return true if yield(node)
76
+ return true if node.is_a?(Node) && node.contains?(&block)
77
+ end
78
+ false
79
+ end
80
+
55
81
  # Default implementations of the common node methods.
56
82
  def unwrap; self; end
83
+ def children; []; end
57
84
  def statement?; false; end
58
85
  def statement_only?; false; end
59
86
  def top_sensitive?; false; end
@@ -62,10 +89,10 @@ module CoffeeScript
62
89
  # A collection of nodes, each one representing an expression.
63
90
  class Expressions < Node
64
91
  statement
65
- attr_reader :expressions
92
+ children :expressions
93
+ attr_accessor :function
66
94
 
67
95
  TRAILING_WHITESPACE = /\s+$/
68
- UPPERCASE = /[A-Z]/
69
96
 
70
97
  # Wrap up a node as an Expressions, unless it already is.
71
98
  def self.wrap(*nodes)
@@ -94,18 +121,17 @@ module CoffeeScript
94
121
  @expressions.length == 1 ? @expressions.first : self
95
122
  end
96
123
 
124
+ # Is this an empty block of code?
125
+ def empty?
126
+ @expressions.empty?
127
+ end
128
+
97
129
  # Is the node last in this block of expressions.
98
130
  def last?(node)
99
131
  @last_index ||= @expressions.last.is_a?(CommentNode) ? -2 : -1
100
132
  node == @expressions[@last_index]
101
133
  end
102
134
 
103
- # Determine if this is the expressions body within a constructor function.
104
- # Constructors are capitalized by CoffeeScript convention.
105
- def constructor?(o)
106
- o[:top] && o[:last_assign] && o[:last_assign][0..0][UPPERCASE]
107
- end
108
-
109
135
  def compile(o={})
110
136
  o[:scope] ? super(o) : compile_root(o)
111
137
  end
@@ -119,7 +145,7 @@ module CoffeeScript
119
145
  def compile_root(o={})
120
146
  indent = o[:no_wrap] ? '' : TAB
121
147
  @indent = indent
122
- o.merge!(:indent => indent, :scope => Scope.new(nil, self))
148
+ o.merge!(:indent => indent, :scope => Scope.new(nil, self, nil))
123
149
  code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
124
150
  code.gsub!(TRAILING_WHITESPACE, '')
125
151
  write(o[:no_wrap] ? code : "(function(){\n#{code}\n})();")
@@ -129,8 +155,11 @@ module CoffeeScript
129
155
  # at the top.
130
156
  def compile_with_declarations(o={})
131
157
  code = compile_node(o)
132
- code = "#{idt}var #{o[:scope].compiled_assignments};\n#{code}" if o[:scope].assignments?(self)
133
- code = "#{idt}var #{o[:scope].compiled_declarations};\n#{code}" if o[:scope].declarations?(self)
158
+ args = self.contains? {|n| n.is_a?(LiteralNode) && n.arguments? }
159
+ argv = args && o[:scope].check('arguments') ? '' : 'var '
160
+ code = "#{idt}#{argv}arguments = Array.prototype.slice.call(arguments, 0);\n#{code}" if args
161
+ code = "#{idt}var #{o[:scope].compiled_assignments};\n#{code}" if o[:scope].assignments?(self)
162
+ code = "#{idt}var #{o[:scope].compiled_declarations};\n#{code}" if o[:scope].declarations?(self)
134
163
  write(code)
135
164
  end
136
165
 
@@ -145,10 +174,10 @@ module CoffeeScript
145
174
  # If it's a statement, the node knows how to return itself.
146
175
  return node.compile(o.merge(:return => true)) if node.statement?
147
176
  # If it's not part of a constructor, we can just return the value of the expression.
148
- return "#{idt}return #{node.compile(o)};" unless constructor?(o)
177
+ return "#{idt}return #{node.compile(o)};" unless o[:scope].function && o[:scope].function.constructor?
149
178
  # It's the last line of a constructor, add a safety check.
150
179
  temp = o[:scope].free_variable
151
- "#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
180
+ "#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:scope].function.name} === this.constructor ? this : #{temp};"
152
181
  end
153
182
 
154
183
  end
@@ -156,17 +185,12 @@ module CoffeeScript
156
185
  # Literals are static values that have a Ruby representation, eg.: a string, a number,
157
186
  # true, false, nil, etc.
158
187
  class LiteralNode < Node
188
+ children :value
159
189
 
160
190
  # Values of a literal node that much be treated as a statement -- no
161
191
  # sense returning or assigning them.
162
192
  STATEMENTS = ['break', 'continue']
163
193
 
164
- # If we get handed a literal reference to an arguments object, convert
165
- # it to an array.
166
- ARG_ARRAY = 'Array.prototype.slice.call(arguments, 0)'
167
-
168
- attr_reader :value
169
-
170
194
  # Wrap up a compiler-generated string as a LiteralNode.
171
195
  def self.wrap(string)
172
196
  self.new(Value.new(string))
@@ -181,19 +205,21 @@ module CoffeeScript
181
205
  end
182
206
  alias_method :statement_only?, :statement?
183
207
 
208
+ def arguments?
209
+ @value.to_s == 'arguments'
210
+ end
211
+
184
212
  def compile_node(o)
185
- @value = ARG_ARRAY if @value.to_s.to_sym == :arguments
186
213
  indent = statement? ? idt : ''
187
214
  ending = statement? ? ';' : ''
188
- write "#{indent}#{@value}#{ending}"
215
+ "#{indent}#{@value}#{ending}"
189
216
  end
190
217
  end
191
218
 
192
219
  # Return an expression, or wrap it in a closure and return it.
193
220
  class ReturnNode < Node
194
221
  statement_only
195
-
196
- attr_reader :expression
222
+ children :expression
197
223
 
198
224
  def initialize(expression)
199
225
  @expression = expression
@@ -209,7 +235,7 @@ module CoffeeScript
209
235
  # Pass through CoffeeScript comments into JavaScript comments at the
210
236
  # same position.
211
237
  class CommentNode < Node
212
- statement_only
238
+ statement
213
239
 
214
240
  def initialize(lines)
215
241
  @lines = lines.value
@@ -225,47 +251,38 @@ module CoffeeScript
225
251
  # Node for a function invocation. Takes care of converting super() calls into
226
252
  # calls against the prototype's function of the same name.
227
253
  class CallNode < Node
228
- attr_reader :variable, :arguments
254
+ children :variable, :arguments
229
255
 
230
256
  def initialize(variable, arguments=[])
231
257
  @variable, @arguments = variable, arguments
258
+ @prefix = ''
232
259
  end
233
260
 
234
261
  def new_instance
235
- @new = true
262
+ @prefix = "new "
236
263
  self
237
264
  end
238
265
 
239
- def super?
240
- @variable == :super
241
- end
242
-
243
- def prefix
244
- @new ? "new " : ''
245
- end
246
-
247
- def splat?
248
- @arguments.any? {|a| a.is_a?(SplatNode) }
249
- end
250
-
251
266
  def <<(argument)
252
267
  @arguments << argument
268
+ self
253
269
  end
254
270
 
255
271
  # Compile a vanilla function call.
256
272
  def compile_node(o)
257
- return write(compile_splat(o)) if splat?
273
+ return write(compile_splat(o)) if @arguments.any? {|a| a.is_a?(SplatNode) }
258
274
  args = @arguments.map{|a| a.compile(o) }.join(', ')
259
- return write(compile_super(args, o)) if super?
260
- write("#{prefix}#{@variable.compile(o)}(#{args})")
275
+ return write(compile_super(args, o)) if @variable == 'super'
276
+ write("#{@prefix}#{@variable.compile(o)}(#{args})")
261
277
  end
262
278
 
263
279
  # Compile a call against the superclass's implementation of the current function.
264
280
  def compile_super(args, o)
265
- methname = o[:last_assign]
281
+ methname = o[:scope].function.name
266
282
  arg_part = args.empty? ? '' : ", #{args}"
267
- meth = o[:proto_assign] ? "#{o[:proto_assign]}.__superClass__.#{methname}" :
268
- "#{methname}.__superClass__.constructor"
283
+ meth = o[:scope].function.proto ?
284
+ "#{o[:scope].function.proto}.__superClass__.#{methname}" :
285
+ "#{methname}.__superClass__.constructor"
269
286
  "#{meth}.call(this#{arg_part})"
270
287
  end
271
288
 
@@ -278,15 +295,23 @@ module CoffeeScript
278
295
  code = arg.is_a?(SplatNode) ? code : "[#{code}]"
279
296
  arg.equal?(@arguments.first) ? code : ".concat(#{code})"
280
297
  end
281
- "#{prefix}#{meth}.apply(#{obj}, #{args.join('')})"
298
+ "#{@prefix}#{meth}.apply(#{obj}, #{args.join('')})"
299
+ end
300
+
301
+ # If the code generation wished to use the result of a function call
302
+ # in multiple places, ensure that the function is only ever called once.
303
+ def compile_reference(o)
304
+ reference = o[:scope].free_variable
305
+ call = ParentheticalNode.new(AssignNode.new(reference, self))
306
+ return call, reference
282
307
  end
283
308
  end
284
309
 
285
310
  # Node to extend an object's prototype with an ancestor object.
286
311
  # After goog.inherits from the Closure Library.
287
312
  class ExtendsNode < Node
313
+ children :sub_object, :super_object
288
314
  statement
289
- attr_reader :sub_object, :super_object
290
315
 
291
316
  def initialize(sub_object, super_object)
292
317
  @sub_object, @super_object = sub_object, super_object
@@ -307,7 +332,8 @@ module CoffeeScript
307
332
 
308
333
  # A value, indexed or dotted into, or vanilla.
309
334
  class ValueNode < Node
310
- attr_reader :base, :properties, :last, :source
335
+ children :base, :properties
336
+ attr_reader :last, :source
311
337
 
312
338
  def initialize(base, properties=[])
313
339
  @base, @properties = base, properties
@@ -334,6 +360,10 @@ module CoffeeScript
334
360
  properties? && @properties.last.is_a?(SliceNode)
335
361
  end
336
362
 
363
+ def unwrap
364
+ @properties.empty? ? @base : self
365
+ end
366
+
337
367
  # Values are statements if their base is a statement.
338
368
  def statement?
339
369
  @base.is_a?(Node) && @base.statement? && !properties?
@@ -352,7 +382,7 @@ module CoffeeScript
352
382
  # A dotted accessor into a part of a value, or the :: shorthand for
353
383
  # an accessor into the object's prototype.
354
384
  class AccessorNode < Node
355
- attr_reader :name
385
+ children :name
356
386
 
357
387
  def initialize(name, prototype=false)
358
388
  @name, @prototype = name, prototype
@@ -366,7 +396,7 @@ module CoffeeScript
366
396
 
367
397
  # An indexed accessor into a part of an array or object.
368
398
  class IndexNode < Node
369
- attr_reader :index
399
+ children :index
370
400
 
371
401
  def initialize(index)
372
402
  @index = index
@@ -377,10 +407,20 @@ module CoffeeScript
377
407
  end
378
408
  end
379
409
 
410
+ # A node to represent a reference to "this". Needs to be transformed into a
411
+ # reference to the correct value of "this", when used within a closure wrapper.
412
+ class ThisNode < Node
413
+
414
+ def compile_node(o)
415
+ write(o[:closure] ? "__this" : "this")
416
+ end
417
+
418
+ end
419
+
380
420
  # A range literal. Ranges can be used to extract portions (slices) of arrays,
381
421
  # or to specify a range for array comprehensions.
382
422
  class RangeNode < Node
383
- attr_reader :from, :to
423
+ children :from, :to
384
424
 
385
425
  def initialize(from, to, exclusive=false)
386
426
  @from, @to, @exclusive = from, to, exclusive
@@ -423,7 +463,7 @@ module CoffeeScript
423
463
  # specifies the index of the end of the slice (just like the first parameter)
424
464
  # is the index of the beginning.
425
465
  class SliceNode < Node
426
- attr_reader :range
466
+ children :range
427
467
 
428
468
  def initialize(range)
429
469
  @range = range
@@ -439,29 +479,35 @@ module CoffeeScript
439
479
 
440
480
  # Setting the value of a local variable, or the value of an object property.
441
481
  class AssignNode < Node
482
+ top_sensitive
483
+ children :variable, :value
484
+
442
485
  PROTO_ASSIGN = /\A(\S+)\.prototype/
443
486
  LEADING_DOT = /\A\.(prototype\.)?/
444
487
 
445
- attr_reader :variable, :value, :context
446
-
447
488
  def initialize(variable, value, context=nil)
448
489
  @variable, @value, @context = variable, value, context
449
490
  end
450
491
 
451
492
  def compile_node(o)
493
+ top = o.delete(:top)
452
494
  return compile_pattern_match(o) if statement?
453
495
  return compile_splice(o) if value? && @variable.splice?
454
- stmt = o.delete(:as_statement)
455
- name = @variable.compile(o)
456
- last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
457
- proto = name[PROTO_ASSIGN, 1]
458
- o = o.merge(:last_assign => last, :proto_assign => proto)
459
- o[:immediate_assign] = last if @value.is_a?(CodeNode) && last.match(Lexer::IDENTIFIER)
496
+ stmt = o.delete(:as_statement)
497
+ name = @variable.compile(o)
498
+ last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
499
+ proto = name[PROTO_ASSIGN, 1]
500
+ if @value.is_a?(CodeNode)
501
+ @value.name = last if last.match(Lexer::IDENTIFIER)
502
+ @value.proto = proto if proto
503
+ end
460
504
  return write("#{name}: #{@value.compile(o)}") if @context == :object
461
505
  o[:scope].find(name) unless value? && @variable.properties?
462
506
  val = "#{name} = #{@value.compile(o)}"
463
507
  return write("#{idt}#{val};") if stmt
464
- write(o[:return] ? "#{idt}return (#{val})" : val)
508
+ val = "(#{val})" if !top || o[:return]
509
+ val = "#{idt}return #{val}" if o[:return]
510
+ write(val)
465
511
  end
466
512
 
467
513
  def value?
@@ -485,7 +531,7 @@ module CoffeeScript
485
531
  if obj.is_a?(SplatNode)
486
532
  val = LiteralNode.wrap(obj.compile_value(o, val_var, @variable.base.objects.index(obj)))
487
533
  else
488
- val = ValueNode.new(Value.new(val_var), [access_class.new(Value.new(i.to_s))])
534
+ val = ValueNode.new(val_var, [access_class.new(Value.new(i.to_s))])
489
535
  end
490
536
  assigns << AssignNode.new(obj, val).compile(o)
491
537
  end
@@ -505,6 +551,10 @@ module CoffeeScript
505
551
  # Simple Arithmetic and logical operations. Performs some conversion from
506
552
  # CoffeeScript operations into their JavaScript equivalents.
507
553
  class OpNode < Node
554
+ children :first, :second
555
+ attr_reader :operator
556
+ attr_accessor :second
557
+
508
558
  CONVERSIONS = {
509
559
  :== => "===",
510
560
  :'!=' => "!==",
@@ -514,11 +564,10 @@ module CoffeeScript
514
564
  :isnt => "!==",
515
565
  :not => '!'
516
566
  }
517
- CONDITIONALS = [:'||=', :'&&=']
567
+ CHAINABLE = [:<, :>, :>=, :<=, :===, :'!===']
568
+ ASSIGNMENT = [:'||=', :'&&=', :'?=']
518
569
  PREFIX_OPERATORS = [:typeof, :delete]
519
570
 
520
- attr_reader :operator, :first, :second
521
-
522
571
  def initialize(operator, first, second=nil, flip=false)
523
572
  @first, @second, @flip = first, second, flip
524
573
  @operator = CONVERSIONS[operator.to_sym] || operator
@@ -528,18 +577,39 @@ module CoffeeScript
528
577
  @second.nil?
529
578
  end
530
579
 
580
+ def chainable?
581
+ CHAINABLE.include?(operator.to_sym)
582
+ end
583
+
531
584
  def compile_node(o)
532
- return write(compile_conditional(o)) if CONDITIONALS.include?(@operator.to_sym)
585
+ return write(compile_chain(o)) if chainable? && @first.unwrap.is_a?(OpNode) && @first.unwrap.chainable?
586
+ return write(compile_assignment(o)) if ASSIGNMENT.include?(@operator.to_sym)
533
587
  return write(compile_unary(o)) if unary?
588
+ return write(compile_existence(o)) if @operator == '?'
534
589
  write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
535
590
  end
536
591
 
537
- def compile_conditional(o)
592
+ # Mimic Python's chained comparisons. See:
593
+ # http://docs.python.org/reference/expressions.html#notin
594
+ def compile_chain(o)
595
+ shared = @first.unwrap.second
596
+ @first.second, shared = *shared.compile_reference(o) if shared.is_a?(CallNode)
597
+ "(#{@first.compile(o)}) && (#{shared.compile(o)} #{@operator} #{@second.compile(o)})"
598
+ end
599
+
600
+ def compile_assignment(o)
538
601
  first, second = @first.compile(o), @second.compile(o)
602
+ o[:scope].find(first) if @first.unwrap.is_a?(Value)
539
603
  sym = @operator[0..1]
604
+ return "#{first} = #{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}" if @operator == '?='
540
605
  "#{first} = #{first} #{sym} #{second}"
541
606
  end
542
607
 
608
+ def compile_existence(o)
609
+ first, second = @first.compile(o), @second.compile(o)
610
+ "#{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}"
611
+ end
612
+
543
613
  def compile_unary(o)
544
614
  space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : ''
545
615
  parts = [@operator.to_s, space, @first.compile(o)]
@@ -549,8 +619,14 @@ module CoffeeScript
549
619
  end
550
620
 
551
621
  # A function definition. The only node that creates a new Scope.
622
+ # A CodeNode does not have any children -- they're within the new scope.
552
623
  class CodeNode < Node
624
+ top_sensitive
553
625
  attr_reader :params, :body, :bound
626
+ attr_accessor :name, :proto
627
+
628
+ # Constructor functions start with an uppercase letter, by convention.
629
+ UPPERCASE = /[A-Z]/
554
630
 
555
631
  def initialize(params, body, tag=nil)
556
632
  @params = params
@@ -558,49 +634,48 @@ module CoffeeScript
558
634
  @bound = tag == :boundfunc
559
635
  end
560
636
 
561
- def statement?
562
- @bound
637
+ def constructor?
638
+ @name && @name[0..0][UPPERCASE]
563
639
  end
564
640
 
565
641
  def compile_node(o)
566
- if @bound
567
- o[:scope].assign("__this", "this")
568
- fvar = o[:scope].free_variable
569
- end
570
642
  shared_scope = o.delete(:shared_scope)
571
- o[:scope] = shared_scope || Scope.new(o[:scope], @body)
643
+ top = o.delete(:top)
644
+ o[:scope] = shared_scope || Scope.new(o[:scope], @body, self)
572
645
  o[:return] = true
573
646
  o[:top] = true
574
- o[:indent] = idt(1)
647
+ o[:indent] = idt(@bound ? 2 : 1)
575
648
  o.delete(:no_wrap)
576
649
  o.delete(:globals)
577
- name = o.delete(:immediate_assign)
650
+ o.delete(:closure)
578
651
  if @params.last.is_a?(SplatNode)
579
652
  splat = @params.pop
580
653
  splat.index = @params.length
581
654
  @body.unshift(splat)
582
655
  end
583
656
  @params.each {|id| o[:scope].parameter(id.to_s) }
584
- code = "\n#{@body.compile_with_declarations(o)}\n"
585
- name_part = name ? " #{name}" : ''
586
- func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt}}"
657
+ code = @body.empty? ? "" : "\n#{@body.compile_with_declarations(o)}\n"
658
+ name_part = @name ? " #{@name}" : ''
659
+ func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt(@bound ? 1 : 0)}}"
660
+ func = "(#{func})" if top && !@bound
587
661
  return write(func) unless @bound
588
- write("#{idt}#{fvar} = #{func};\n#{idt}#{o[:return] ? 'return ' : ''}(function#{name_part}() {\n#{idt(1)}return #{fvar}.apply(__this, arguments);\n#{idt}});")
662
+ inner = "(function#{name_part}() {\n#{idt(2)}return __func.apply(__this, arguments);\n#{idt(1)}});"
663
+ write("(function(__this) {\n#{idt(1)}var __func = #{func};\n#{idt(1)}return #{inner}\n#{idt}})(this)")
589
664
  end
590
665
  end
591
666
 
592
667
  # A splat, either as a parameter to a function, an argument to a call,
593
668
  # or in a destructuring assignment.
594
669
  class SplatNode < Node
670
+ children :name
595
671
  attr_accessor :index
596
- attr_reader :name
597
672
 
598
673
  def initialize(name)
599
674
  @name = name
600
675
  end
601
676
 
602
677
  def compile_node(o={})
603
- write(@index ? compile_param(o) : compile_arg(o))
678
+ write(@index ? compile_param(o) : @name.compile(o))
604
679
  end
605
680
 
606
681
  def compile_param(o)
@@ -608,10 +683,6 @@ module CoffeeScript
608
683
  "#{@name} = Array.prototype.slice.call(arguments, #{@index})"
609
684
  end
610
685
 
611
- def compile_arg(o)
612
- @name.compile(o)
613
- end
614
-
615
686
  def compile_value(o, name, index)
616
687
  "Array.prototype.slice.call(#{name}, #{index})"
617
688
  end
@@ -620,7 +691,7 @@ module CoffeeScript
620
691
 
621
692
  # An object literal.
622
693
  class ObjectNode < Node
623
- attr_reader :properties
694
+ children :properties
624
695
  alias_method :objects, :properties
625
696
 
626
697
  def initialize(properties = [])
@@ -647,7 +718,7 @@ module CoffeeScript
647
718
 
648
719
  # An array literal.
649
720
  class ArrayNode < Node
650
- attr_reader :objects
721
+ children :objects
651
722
 
652
723
  def initialize(objects=[])
653
724
  @objects = objects
@@ -669,9 +740,11 @@ module CoffeeScript
669
740
  # code generation to generate a quick "array.push(value)" tree of nodes.
670
741
  class PushNode
671
742
  def self.wrap(array, expressions)
743
+ expr = expressions.unwrap
744
+ return expressions if expr.statement_only? || expr.contains? {|n| n.statement_only? }
672
745
  Expressions.wrap(CallNode.new(
673
- ValueNode.new(LiteralNode.new(array), [AccessorNode.new('push')]),
674
- [expressions.unwrap]
746
+ ValueNode.new(LiteralNode.new(array), [AccessorNode.new(Value.new('push'))]),
747
+ [expr]
675
748
  ))
676
749
  end
677
750
  end
@@ -679,18 +752,14 @@ module CoffeeScript
679
752
  # A while loop, the only sort of low-level loop exposed by CoffeeScript. From
680
753
  # it, all other loops can be manufactured.
681
754
  class WhileNode < Node
755
+ top_sensitive
756
+ children :condition, :body
682
757
  statement
683
758
 
684
- attr_reader :condition, :body
685
-
686
759
  def initialize(condition, body)
687
760
  @condition, @body = condition, body
688
761
  end
689
762
 
690
- def top_sensitive?
691
- true
692
- end
693
-
694
763
  def compile_node(o)
695
764
  returns = o.delete(:return)
696
765
  top = o.delete(:top) && !returns
@@ -714,10 +783,11 @@ module CoffeeScript
714
783
  # of the comprehenion. Unlike Python array comprehensions, it's able to pass
715
784
  # the current index of the loop as a second parameter.
716
785
  class ForNode < Node
786
+ top_sensitive
787
+ children :body, :source, :filter
788
+ attr_reader :name, :index, :step
717
789
  statement
718
790
 
719
- attr_reader :body, :source, :name, :index, :filter, :step
720
-
721
791
  def initialize(body, source, name, index=nil)
722
792
  @body, @name, @index = body, name, index
723
793
  @source = source[:source]
@@ -727,10 +797,6 @@ module CoffeeScript
727
797
  @name, @index = @index, @name if @object
728
798
  end
729
799
 
730
- def top_sensitive?
731
- true
732
- end
733
-
734
800
  def compile_node(o)
735
801
  top_level = o.delete(:top) && !o[:return]
736
802
  range = @source.is_a?(ValueNode) && @source.base.is_a?(RangeNode) && @source.properties.empty?
@@ -771,10 +837,11 @@ module CoffeeScript
771
837
  if @object
772
838
  o[:scope].assign("__hasProp", "Object.prototype.hasOwnProperty", true)
773
839
  body = Expressions.wrap(IfNode.new(
774
- CallNode.new(ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]), [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]),
775
- Expressions.wrap(body),
776
- nil,
777
- {:statement => true}
840
+ CallNode.new(
841
+ ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]),
842
+ [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]
843
+ ),
844
+ Expressions.wrap(body), nil, {:statement => true}
778
845
  ))
779
846
  end
780
847
 
@@ -787,10 +854,10 @@ module CoffeeScript
787
854
 
788
855
  # A try/catch/finally block.
789
856
  class TryNode < Node
857
+ children :try, :recovery, :finally
858
+ attr_reader :error
790
859
  statement
791
860
 
792
- attr_reader :try, :error, :recovery, :finally
793
-
794
861
  def initialize(try, error, recovery, finally=nil)
795
862
  @try, @error, @recovery, @finally = try, error, recovery, finally
796
863
  end
@@ -807,10 +874,9 @@ module CoffeeScript
807
874
 
808
875
  # Throw an exception.
809
876
  class ThrowNode < Node
877
+ children :expression
810
878
  statement_only
811
879
 
812
- attr_reader :expression
813
-
814
880
  def initialize(expression)
815
881
  @expression = expression
816
882
  end
@@ -822,15 +888,20 @@ module CoffeeScript
822
888
 
823
889
  # Check an expression for existence (meaning not null or undefined).
824
890
  class ExistenceNode < Node
825
- attr_reader :expression
891
+ children :expression
892
+
893
+ def self.compile_test(o, variable)
894
+ first, second = variable, variable
895
+ first, second = *variable.compile_reference(o) if variable.is_a?(CallNode)
896
+ "(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)"
897
+ end
826
898
 
827
899
  def initialize(expression)
828
900
  @expression = expression
829
901
  end
830
902
 
831
903
  def compile_node(o)
832
- val = @expression.compile(o)
833
- write("(typeof #{val} !== \"undefined\" && #{val} !== null)")
904
+ write(ExistenceNode.compile_test(o, @expression))
834
905
  end
835
906
  end
836
907
 
@@ -838,7 +909,7 @@ module CoffeeScript
838
909
  # You can't wrap parentheses around bits that get compiled into JS statements,
839
910
  # unfortunately.
840
911
  class ParentheticalNode < Node
841
- attr_reader :expressions
912
+ children :expressions
842
913
 
843
914
  def initialize(expressions, line=nil)
844
915
  @expressions = expressions.unwrap
@@ -857,7 +928,7 @@ module CoffeeScript
857
928
  # Single-expression IfNodes are compiled into ternary operators if possible,
858
929
  # because ternaries are first-class returnable assignable expressions.
859
930
  class IfNode < Node
860
- attr_reader :condition, :body, :else_body
931
+ children :condition, :body, :else_body
861
932
 
862
933
  def initialize(condition, body, else_body=nil, tags={})
863
934
  @condition = condition
@@ -928,7 +999,8 @@ module CoffeeScript
928
999
  if_dent = child ? '' : idt
929
1000
  com_dent = child ? idt : ''
930
1001
  prefix = @comment ? @comment.compile(cond_o) + "\n#{com_dent}" : ''
931
- if_part = "#{prefix}#{if_dent}if (#{compile_condition(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{idt}}"
1002
+ body = Expressions.wrap(@body).compile(o)
1003
+ if_part = "#{prefix}#{if_dent}if (#{compile_condition(cond_o)}) {\n#{body}\n#{idt}}"
932
1004
  return if_part unless @else_body
933
1005
  else_part = chain? ?
934
1006
  " else #{@else_body.compile(o.merge(:indent => idt, :chain_child => true))}" :