coffee-script 0.1.4 → 0.1.5

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