coffee-script 0.1.4 → 0.1.5

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/README CHANGED
@@ -26,7 +26,7 @@
26
26
  gem install coffee-script
27
27
 
28
28
  Compile a script:
29
- coffee-script /path/to/script.coffee
29
+ coffee /path/to/script.coffee
30
30
 
31
31
  For documentation, usage, and examples, see:
32
32
  http://jashkenas.github.com/coffee-script/
File without changes
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'coffee-script'
3
- s.version = '0.1.4' # Keep version in sync with coffee-script.rb
4
- s.date = '2009-12-25'
3
+ s.version = '0.1.5' # Keep version in sync with coffee-script.rb
4
+ s.date = '2009-12-26'
5
5
 
6
6
  s.homepage = "http://jashkenas.github.com/coffee-script/"
7
7
  s.summary = "The CoffeeScript Compiler"
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.has_rdoc = false
21
21
 
22
22
  s.require_paths = ['lib']
23
- s.executables = ['coffee-script']
23
+ s.executables = ['coffee']
24
24
 
25
25
  s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README']
26
26
  end
data/examples/code.coffee CHANGED
@@ -132,7 +132,7 @@ wednesday: => eat_breakfast(); go_to_work(); eat_dinner(); .
132
132
 
133
133
  # Array slice literals.
134
134
  zero_to_nine: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
135
- three_to_six: zero_to_nine[3, 6]
135
+ three_to_six: zero_to_nine[3..6]
136
136
 
137
137
  # Multiline strings with inner quotes.
138
138
  story: "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit,
@@ -149,5 +149,5 @@ wipe_mutterings_from: sentence =>
149
149
  while sentence.indexOf('(') >= 0
150
150
  open: sentence.indexOf('(') - 1
151
151
  close: sentence.indexOf(')') + 1
152
- sentence: sentence[0, open] + sentence[close, sentence.length].
152
+ sentence: sentence[0..open] + sentence[close..sentence.length].
153
153
  sentence.
data/lib/coffee-script.rb CHANGED
@@ -9,7 +9,7 @@ require "coffee_script/parse_error"
9
9
  # Namespace for all CoffeeScript internal classes.
10
10
  module CoffeeScript
11
11
 
12
- VERSION = '0.1.4' # Keep in sync with the gemspec.
12
+ VERSION = '0.1.5' # Keep in sync with the gemspec.
13
13
 
14
14
  # Compile a script (String or IO) to JavaScript.
15
15
  def self.compile(script, options={})
@@ -234,7 +234,7 @@
234
234
  </dict>
235
235
  <dict>
236
236
  <key>match</key>
237
- <string>\b(debugger)\b</string>
237
+ <string>\b(debugger|\\)\b</string>
238
238
  <key>name</key>
239
239
  <string>keyword.other.coffee</string>
240
240
  </dict>
@@ -5,15 +5,15 @@ require File.expand_path(File.dirname(__FILE__) + '/../coffee-script')
5
5
 
6
6
  module CoffeeScript
7
7
 
8
- # The CommandLine handles all of the functionality of the `coffee-script`
8
+ # The CommandLine handles all of the functionality of the `coffee`
9
9
  # utility.
10
10
  class CommandLine
11
11
 
12
12
  BANNER = <<-EOS
13
- coffee-script compiles CoffeeScript source files into JavaScript.
13
+ coffee compiles CoffeeScript source files into JavaScript.
14
14
 
15
15
  Usage:
16
- coffee-script path/to/script.coffee
16
+ coffee path/to/script.coffee
17
17
  EOS
18
18
 
19
19
  # Seconds to pause between checks for changed source files.
@@ -125,11 +125,13 @@ Usage:
125
125
  end
126
126
 
127
127
  # Compile a single source file to JavaScript.
128
- def compile(script, source='')
128
+ def compile(script, source='error')
129
129
  begin
130
- CoffeeScript.compile(script, :no_wrap => @options[:no_wrap])
131
- rescue CoffeeScript::ParseError => e
132
- STDERR.puts e.message(source)
130
+ options = {}
131
+ options[:no_wrap] = true if @options[:no_wrap]
132
+ CoffeeScript.compile(script, options)
133
+ rescue CoffeeScript::ParseError, SyntaxError => e
134
+ STDERR.puts "#{source}: #{e.message}"
133
135
  exit(1) unless @options[:watch]
134
136
  nil
135
137
  end
@@ -188,8 +190,8 @@ Usage:
188
190
  install_bundle
189
191
  exit
190
192
  end
191
- opts.on_tail('--version', 'display coffee-script version') do
192
- puts "coffee-script version #{CoffeeScript::VERSION}"
193
+ opts.on_tail('--version', 'display CoffeeScript version') do
194
+ puts "CoffeeScript version #{CoffeeScript::VERSION}"
193
195
  exit
194
196
  end
195
197
  opts.on_tail('-h', '--help', 'display this help message') do
@@ -64,11 +64,11 @@ rule
64
64
 
65
65
  # The parts that are natural JavaScript expressions.
66
66
  PureExpression:
67
- Literal
68
- | Value
67
+ Value
69
68
  | Call
70
69
  | Code
71
70
  | Operation
71
+ | Range
72
72
  ;
73
73
 
74
74
  # We have to take extra care to convert these statements into expressions.
@@ -120,8 +120,8 @@ rule
120
120
 
121
121
  # Assignment within an object literal.
122
122
  AssignObj:
123
- IDENTIFIER ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) }
124
- | STRING ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) }
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) }
125
125
  | Comment { result = val[0] }
126
126
  ;
127
127
 
@@ -144,10 +144,10 @@ rule
144
144
  | '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
145
145
  | NOT Expression { result = OpNode.new(val[0], val[1]) }
146
146
  | '~' Expression { result = OpNode.new(val[0], val[1]) }
147
- | '--' Expression { result = OpNode.new(val[0], val[1]) }
148
- | '++' Expression { result = OpNode.new(val[0], val[1]) }
149
- | Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
150
- | Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
147
+ | '--' Expression { result = OpNode.new(val[0], val[1]) }
148
+ | '++' Expression { result = OpNode.new(val[0], val[1]) }
149
+ | Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
150
+ | Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
151
151
 
152
152
  | Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
153
153
  | Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
@@ -213,6 +213,7 @@ rule
213
213
  # Expressions that can be treated as values.
214
214
  Value:
215
215
  IDENTIFIER { result = ValueNode.new(val[0]) }
216
+ | Literal { result = ValueNode.new(val[0]) }
216
217
  | Array { result = ValueNode.new(val[0]) }
217
218
  | Object { result = ValueNode.new(val[0]) }
218
219
  | Parenthetical { result = ValueNode.new(val[0]) }
@@ -224,7 +225,7 @@ rule
224
225
  Accessor:
225
226
  PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
226
227
  | Index { result = val[0] }
227
- | Slice { result = val[0] }
228
+ | Range { result = SliceNode.new(val[0]) }
228
229
  ;
229
230
 
230
231
  # Indexing into an object or array.
@@ -232,11 +233,6 @@ rule
232
233
  "[" Expression "]" { result = IndexNode.new(val[1]) }
233
234
  ;
234
235
 
235
- # Array slice literal.
236
- Slice:
237
- "[" Expression "," Expression "]" { result = SliceNode.new(val[1], val[3]) }
238
- ;
239
-
240
236
  # An object literal.
241
237
  Object:
242
238
  "{" AssignList "}" { result = ObjectNode.new(val[1]) }
@@ -273,6 +269,12 @@ rule
273
269
  SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
274
270
  ;
275
271
 
272
+ # The range literal.
273
+ Range:
274
+ "[" Value "." "." Value "]" { result = RangeNode.new(val[1], val[4]) }
275
+ | "[" Value "." "." "." Value "]" { result = RangeNode.new(val[1], val[5], true) }
276
+ ;
277
+
276
278
  # The array literal.
277
279
  Array:
278
280
  "[" ArgList "]" { result = ArrayNode.new(val[1]) }
@@ -19,7 +19,7 @@ module CoffeeScript
19
19
 
20
20
  # Token matching regexes.
21
21
  IDENTIFIER = /\A([a-zA-Z$_]\w*)/
22
- NUMBER = /\A\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b/i
22
+ NUMBER = /\A((\b|-)((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
23
23
  STRING = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/m
24
24
  JS = /\A(``|`(.*?)[^\\]`)/m
25
25
  OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
@@ -75,7 +75,7 @@ module CoffeeScript
75
75
  # Keywords are special identifiers tagged with their own name, 'if' will result
76
76
  # in an [:IF, "if"] token
77
77
  tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER
78
- @tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.'
78
+ @tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.')
79
79
  token(tag, identifier)
80
80
  @i += identifier.length
81
81
  end
@@ -130,11 +130,13 @@ module CoffeeScript
130
130
  # We treat all other single characters as a token. Eg.: ( ) , . !
131
131
  # Multi-character operators are also literal tokens, so that Racc can assign
132
132
  # the proper order of operations. Multiple newlines get merged together.
133
+ # Use a trailing \ to escape newlines.
133
134
  def literal_token
134
135
  value = @chunk[NEWLINE, 1]
135
136
  if value
136
137
  @line += value.length
137
- token("\n", "\n") unless last_value == "\n"
138
+ token("\n", "\n") unless ["\n", "\\"].include?(last_value)
139
+ @tokens.pop if last_value == "\\"
138
140
  return @i += value.length
139
141
  end
140
142
  value = @chunk[OPERATOR, 1]
@@ -1,6 +1,6 @@
1
1
  # This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee
2
2
 
3
- # Executes the `coffee-script` Ruby program to convert from CoffeeScript
3
+ # Executes the `coffee` Ruby program to convert from CoffeeScript
4
4
  # to Javascript. Eventually this will hopefully happen entirely within JS.
5
5
 
6
6
  # Require external dependencies.
@@ -9,13 +9,13 @@ File: require('file')
9
9
  Readline: require('readline')
10
10
 
11
11
  # The path to the CoffeeScript Compiler.
12
- coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee-script')
12
+ coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee')
13
13
 
14
14
  # Our general-purpose error handler.
15
15
  checkForErrors: coffeeProcess =>
16
16
  return true if coffeeProcess.wait() is 0
17
17
  system.stderr.print(coffeeProcess.stderr.read())
18
- throw new Error("coffee-script compile error").
18
+ throw new Error("CoffeeScript compile error").
19
19
 
20
20
  # Run a simple REPL, round-tripping to the CoffeeScript compiler for every
21
21
  # command.
@@ -25,7 +25,7 @@ exports.run: args =>
25
25
 
26
26
  while true
27
27
  try
28
- system.stdout.write('cs> ').flush()
28
+ system.stdout.write('coffee> ').flush()
29
29
  result: exports.evalCS(Readline.readline())
30
30
  print(result) if result isnt undefined
31
31
  catch e
@@ -1,31 +1,32 @@
1
1
  (function(){
2
-
3
- // This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee Executes the `coffee-script` Ruby program to convert from CoffeeScript
2
+ var File, OS, Readline, checkForErrors, coffeePath;
3
+ // This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee Executes the `coffee` Ruby program to convert from CoffeeScript
4
4
  // to Javascript. Eventually this will hopefully happen entirely within JS. Require external dependencies.
5
- var OS = require('os');
6
- var File = require('file');
7
- var Readline = require('readline');
5
+ OS = require('os');
6
+ File = require('file');
7
+ Readline = require('readline');
8
8
  // The path to the CoffeeScript Compiler.
9
- var coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee-script');
9
+ coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee');
10
10
  // Our general-purpose error handler.
11
- var checkForErrors = function(coffeeProcess) {
11
+ checkForErrors = function(coffeeProcess) {
12
12
  if (coffeeProcess.wait() === 0) {
13
13
  return true;
14
14
  }
15
15
  system.stderr.print(coffeeProcess.stderr.read());
16
- throw new Error("coffee-script compile error");
16
+ throw new Error("CoffeeScript compile error");
17
17
  };
18
18
  // Run a simple REPL, round-tripping to the CoffeeScript compiler for every
19
19
  // command.
20
20
  exports.run = function(args) {
21
+ var result;
21
22
  args.shift();
22
23
  if (args.length) {
23
24
  return require(File.absolute(args[0]));
24
25
  }
25
26
  while (true) {
26
27
  try {
27
- system.stdout.write('cs> ').flush();
28
- var result = exports.evalCS(Readline.readline());
28
+ system.stdout.write('coffee> ').flush();
29
+ result = exports.evalCS(Readline.readline());
29
30
  if (result !== undefined) {
30
31
  print(result);
31
32
  }
@@ -36,13 +37,15 @@
36
37
  };
37
38
  // Compile a given CoffeeScript file into JavaScript.
38
39
  exports.compileFile = function(path) {
39
- var coffee = OS.popen([coffeePath, "--print", "--no-wrap", path]);
40
+ var coffee;
41
+ coffee = OS.popen([coffeePath, "--print", "--no-wrap", path]);
40
42
  checkForErrors(coffee);
41
43
  return coffee.stdout.read();
42
44
  };
43
45
  // Compile a string of CoffeeScript into JavaScript.
44
46
  exports.compile = function(source) {
45
- var coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
47
+ var coffee;
48
+ coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
46
49
  coffee.stdin.write(source).flush().close();
47
50
  checkForErrors(coffee);
48
51
  return coffee.stdout.read();
@@ -53,8 +56,9 @@
53
56
  };
54
57
  // Make a factory for the CoffeeScript environment.
55
58
  exports.makeNarwhalFactory = function(path) {
56
- var code = exports.compileFile(path);
57
- var factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
59
+ var code, factoryText;
60
+ code = exports.compileFile(path);
61
+ factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
58
62
  if (system.engine === "rhino") {
59
63
  return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
60
64
  } else {
@@ -1,10 +1,10 @@
1
1
  (function(){
2
-
2
+ var coffeescript, factories, loader;
3
3
  // This (javascript) file is generated from lib/coffee_script/narwhal/loader.coffee
4
- var coffeescript = null;
5
- var factories = {
4
+ coffeescript = null;
5
+ factories = {
6
6
  };
7
- var loader = {
7
+ loader = {
8
8
  // Reload the coffee-script environment from source.
9
9
  reload: function(topId, path) {
10
10
  coffeescript = coffeescript || require('coffee-script');
@@ -77,16 +77,18 @@ module CoffeeScript
77
77
  # If this is the top-level Expressions, wrap everything in a safety closure.
78
78
  def root_compile(o={})
79
79
  indent = o[:no_wrap] ? '' : TAB
80
- code = compile(o.merge(:indent => indent, :scope => Scope.new))
80
+ code = compile(o.merge(:indent => indent, :scope => Scope.new), o[:no_wrap] ? nil : :code)
81
81
  code.gsub!(STRIP_TRAILING_WHITESPACE, '')
82
82
  o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
83
83
  end
84
84
 
85
85
  # The extra fancy is to handle pushing down returns and assignments
86
86
  # recursively to the final lines of inner statements.
87
- def compile(options={})
87
+ # Variables first defined within the Expressions body have their
88
+ # declarations pushed up to the top scope.
89
+ def compile(options={}, parent=nil)
88
90
  return root_compile(options) unless options[:scope]
89
- code = @expressions.map { |node|
91
+ compiled = @expressions.map do |node|
90
92
  o = super(options)
91
93
  if last?(node) && (o[:return] || o[:assign])
92
94
  if o[:return]
@@ -99,14 +101,17 @@ module CoffeeScript
99
101
  if node.statement? || node.custom_assign?
100
102
  "#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
101
103
  else
102
- "#{o[:indent]}#{AssignNode.new(ValueNode.new(LiteralNode.new(o[:assign])), node).compile(o)};"
104
+ "#{o[:indent]}#{AssignNode.new(o[:assign], node).compile(o)};"
103
105
  end
104
106
  end
105
107
  else
106
108
  o.delete(:return) and o.delete(:assign)
107
109
  "#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
108
110
  end
109
- }.join("\n")
111
+ end
112
+ scope = options[:scope]
113
+ declarations = scope.any_declared? && parent == :code ? "#{options[:indent]}var #{scope.declared_variables.join(', ')};\n" : ''
114
+ code = declarations + compiled.join("\n")
110
115
  write(code)
111
116
  end
112
117
  end
@@ -202,7 +207,7 @@ module CoffeeScript
202
207
 
203
208
  def compile(o={})
204
209
  o = super(o)
205
- args = @arguments.map{|a| a.compile(o.merge(:no_paren => true)) }.join(', ')
210
+ args = @arguments.map{|a| a.compile(o) }.join(', ')
206
211
  return write(compile_super(args, o)) if super?
207
212
  prefix = @new ? "new " : ''
208
213
  write("#{prefix}#{@variable.compile(o)}(#{args})")
@@ -300,28 +305,60 @@ module CoffeeScript
300
305
  end
301
306
  end
302
307
 
308
+ # A range literal. Ranges can be used to extract portions (slices) of arrays,
309
+ # or to specify a range for array comprehensions.
310
+ class RangeNode
311
+ attr_reader :from, :to
312
+
313
+ def initialize(from, to, exclusive=false)
314
+ @from, @to, @exclusive = from, to, exclusive
315
+ end
316
+
317
+ def exclusive?
318
+ @exclusive
319
+ end
320
+
321
+ def less_operator
322
+ @exclusive ? '<' : '<='
323
+ end
324
+
325
+ def greater_operator
326
+ @exclusive ? '>' : '>='
327
+ end
328
+
329
+ def compile(o, fv, tv)
330
+ fvv, tvv = @from.compile(o), @to.compile(o)
331
+ vars = "#{fv}=#{fvv}, #{tv}=#{tvv}"
332
+ compare = "(#{fvv} <= #{tvv} ? #{fv} #{less_operator} #{tv} : #{fv} #{greater_operator} #{tv})"
333
+ incr = "(#{fvv} <= #{tvv} ? #{fv} += 1 : #{fv} -= 1)"
334
+ "#{vars}; #{compare}; #{incr}"
335
+ end
336
+
337
+ end
338
+
303
339
  # An array slice literal. Unlike JavaScript's Array#slice, the second parameter
304
340
  # specifies the index of the end of the slice (just like the first parameter)
305
341
  # is the index of the beginning.
306
342
  class SliceNode < Node
307
- attr_reader :from, :to
343
+ attr_reader :range
308
344
 
309
- def initialize(from, to)
310
- @from, @to = from, to
345
+ def initialize(range)
346
+ @range = range
311
347
  end
312
348
 
313
349
  def compile(o={})
314
- o = super(o)
315
- write(".slice(#{@from.compile(o)}, #{@to.compile(o)} + 1)")
350
+ o = super(o)
351
+ from = @range.from.compile(o)
352
+ to = @range.to.compile(o)
353
+ plus_part = @range.exclusive? ? '' : ' + 1'
354
+ write(".slice(#{from}, #{to}#{plus_part})")
316
355
  end
317
356
  end
318
357
 
319
358
  # Setting the value of a local variable, or the value of an object property.
320
359
  class AssignNode < Node
321
- LEADING_VAR = /\Avar\s+/
322
360
  PROTO_ASSIGN = /\A(\S+)\.prototype/
323
361
 
324
- statement
325
362
  custom_return
326
363
 
327
364
  attr_reader :variable, :value, :context
@@ -336,19 +373,16 @@ module CoffeeScript
336
373
 
337
374
  def compile(o={})
338
375
  o = super(o)
339
- name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable.to_s
340
- last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
376
+ name = @variable.compile(o)
377
+ last = @variable.last.to_s
341
378
  proto = name[PROTO_ASSIGN, 1]
342
- o = o.merge(:assign => name, :last_assign => last, :proto_assign => proto)
379
+ o = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto)
343
380
  postfix = o[:return] ? ";\n#{o[:indent]}return #{name}" : ''
344
- return write("#{@variable}: #{@value.compile(o)}") if @context == :object
381
+ return write("#{name}: #{@value.compile(o)}") if @context == :object
345
382
  return write("#{name} = #{@value.compile(o)}#{postfix}") if @variable.properties? && !@value.custom_assign?
346
- defined = o[:scope].find(name)
347
- def_part = defined || @variable.properties? || o[:no_wrap] ? "" : "var #{name};\n#{o[:indent]}"
348
- return write(def_part + @value.compile(o)) if @value.custom_assign?
349
- def_part = defined || o[:no_wrap] ? name : "var #{name}"
350
- val_part = @value.compile(o).sub(LEADING_VAR, '')
351
- write("#{def_part} = #{val_part}#{postfix}")
383
+ o[:scope].find(name) unless @variable.properties?
384
+ return write(@value.compile(o)) if @value.custom_assign?
385
+ write("#{name} = #{@value.compile(o)}#{postfix}")
352
386
  end
353
387
  end
354
388
 
@@ -416,8 +450,8 @@ module CoffeeScript
416
450
  o[:indent] += TAB
417
451
  o.delete(:assign)
418
452
  o.delete(:no_wrap)
419
- @params.each {|id| o[:scope].find(id.to_s) }
420
- code = @body.compile(o)
453
+ @params.each {|id| o[:scope].parameter(id.to_s) }
454
+ code = @body.compile(o, :code)
421
455
  write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
422
456
  end
423
457
  end
@@ -480,7 +514,7 @@ module CoffeeScript
480
514
  o = super(o)
481
515
  o.delete(:return)
482
516
  indent = o[:indent] + TAB
483
- cond = @condition.compile(o.merge(:no_paren => true))
517
+ cond = @condition.compile(o)
484
518
  write("while (#{cond}) {\n#{@body.compile(o.merge(:indent => indent))}\n#{o[:indent]}}")
485
519
  end
486
520
  end
@@ -506,27 +540,36 @@ module CoffeeScript
506
540
 
507
541
  def compile(o={})
508
542
  o = super(o)
543
+ range = @source.is_a?(RangeNode)
509
544
  scope = o[:scope]
510
545
  name_found = scope.find(@name)
511
546
  index_found = @index && scope.find(@index)
512
547
  svar = scope.free_variable
513
- ivar = scope.free_variable
548
+ ivar = range ? name : scope.free_variable
514
549
  lvar = scope.free_variable
515
550
  rvar = scope.free_variable
516
- name_part = name_found ? @name : "var #{@name}"
517
- index_name = @index ? (index_found ? @index : "var #{@index}") : nil
518
- source_part = "var #{svar} = #{@source.compile(o)};"
519
- for_part = "var #{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
520
- var_part = "\n#{o[:indent] + TAB}#{name_part} = #{svar}[#{ivar}];\n"
521
- index_part = @index ? "#{o[:indent] + TAB}#{index_name} = #{ivar};\n" : ''
551
+ index_name = @index ? @index : nil
552
+ if range
553
+ source_part = ''
554
+ var_part = ''
555
+ index_part = ''
556
+ index_var = scope.free_variable
557
+ for_part = "#{index_var}=0, #{@source.compile(o, ivar, lvar)}, #{index_var}++"
558
+ else
559
+ index_var = nil
560
+ source_part = "#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
561
+ for_part = "#{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
562
+ var_part = "\n#{o[:indent] + TAB}#{@name} = #{svar}[#{ivar}];"
563
+ index_part = @index ? "\n#{o[:indent] + TAB}#{index_name} = #{ivar};" : ''
564
+ end
522
565
  body = @body
523
566
  suffix = ';'
524
- set_result = "var #{rvar} = [];\n#{o[:indent]}"
525
- save_result = "#{rvar}[#{ivar}] = "
567
+ set_result = "#{rvar} = [];\n#{o[:indent]}"
568
+ save_result = "#{rvar}[#{index_var || ivar}] = "
526
569
  return_result = rvar
527
570
 
528
571
  if o[:return] || o[:assign]
529
- return_result = "#{o[:assign]} = #{return_result}" if o[:assign]
572
+ return_result = "#{o[:assign].compile(o)} = #{return_result}" if o[:assign]
530
573
  return_result = "return #{return_result}" if o[:return]
531
574
  if @filter
532
575
  body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body])
@@ -541,7 +584,7 @@ module CoffeeScript
541
584
  return_result = "\n#{o[:indent]}#{return_result};"
542
585
  indent = o[:indent] + TAB
543
586
  body = body.compile(o.merge(:indent => indent))
544
- write("#{source_part}\n#{o[:indent]}#{set_result}for (#{for_part}) {#{var_part}#{index_part}#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
587
+ write("#{source_part}#{set_result}for (#{for_part}) {#{var_part}#{index_part}\n#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
545
588
  end
546
589
  end
547
590
 
@@ -588,7 +631,9 @@ module CoffeeScript
588
631
  end
589
632
  end
590
633
 
591
- # An extra set of parenthesis, supplied by the script source.
634
+ # An extra set of parentheses, supplied by the script source.
635
+ # You can't wrap parentheses around bits that get compiled into JS statements,
636
+ # unfortunately.
592
637
  class ParentheticalNode < Node
593
638
  attr_reader :expressions
594
639
 
@@ -597,7 +642,7 @@ module CoffeeScript
597
642
  end
598
643
 
599
644
  def statement?
600
- @expressions.statement?
645
+ @expressions.unwrap.statement?
601
646
  end
602
647
 
603
648
  def custom_assign?
@@ -609,10 +654,11 @@ module CoffeeScript
609
654
  end
610
655
 
611
656
  def compile(o={})
657
+ raise SyntaxError, "parentheses can't be wrapped around a statement" if statement?
612
658
  o = super(o)
613
659
  compiled = @expressions.compile(o)
614
660
  compiled = compiled[0...-1] if compiled[-1..-1] == ';'
615
- write(o[:no_paren] || statement? ? compiled : "(#{compiled})")
661
+ write("(#{compiled})")
616
662
  end
617
663
  end
618
664