coffee-script 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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))}" :