coffee-script 0.2.1 → 0.2.2

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.
@@ -29,7 +29,7 @@ exports.run: args =>
29
29
  while true
30
30
  try
31
31
  system.stdout.write('coffee> ').flush()
32
- result: exports.evalCS(Readline.readline())
32
+ result: exports.evalCS(Readline.readline(), ['--globals'])
33
33
  print(result) if result isnt undefined
34
34
  catch e
35
35
  print(e)
@@ -41,15 +41,15 @@ exports.compileFile: path =>
41
41
  coffee.stdout.read()
42
42
 
43
43
  # Compile a string of CoffeeScript into JavaScript.
44
- exports.compile: source =>
45
- coffee: OS.popen([coffeePath, "--eval", "--no-wrap"])
44
+ exports.compile: source, flags =>
45
+ coffee: OS.popen([coffeePath, "--eval", "--no-wrap"].concat(flags or []))
46
46
  coffee.stdin.write(source).flush().close()
47
47
  checkForErrors(coffee)
48
48
  coffee.stdout.read()
49
49
 
50
50
  # Evaluating a string of CoffeeScript first compiles it externally.
51
- exports.evalCS: source =>
52
- eval(exports.compile(source))
51
+ exports.evalCS: source, flags =>
52
+ eval(exports.compile(source, flags))
53
53
 
54
54
  # Make a factory for the CoffeeScript environment.
55
55
  exports.makeNarwhalFactory: path =>
@@ -20,25 +20,24 @@
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, __b, __c, i, path, result;
23
+ var __a, __b, i, result;
24
24
  if (args.length) {
25
25
  __a = args;
26
- __b = [];
27
- for (i in __a) {
28
- if (__a.hasOwnProperty(i)) {
29
- path = __a[i];
30
- exports.evalCS(File.read(path));
31
- __c = delete args[i];
32
- __b.push(__c);
33
- }
26
+ __b = function(path, i) {
27
+ exports.evalCS(File.read(path));
28
+ delete args[i];
29
+ };
30
+ if (__a instanceof Array) {
31
+ for (i=0; i<__a.length; i++) __b(__a[i], i);
32
+ } else {
33
+ for (i in __a) { if (__a.hasOwnProperty(i)) __b(__a[i], i); }
34
34
  }
35
- __b;
36
35
  return true;
37
36
  }
38
37
  while (true) {
39
38
  try {
40
39
  system.stdout.write('coffee> ').flush();
41
- result = exports.evalCS(Readline.readline());
40
+ result = exports.evalCS(Readline.readline(), ['--globals']);
42
41
  if (result !== undefined) {
43
42
  print(result);
44
43
  }
@@ -46,6 +45,7 @@
46
45
  print(e);
47
46
  }
48
47
  }
48
+ return null;
49
49
  };
50
50
  // Compile a given CoffeeScript file into JavaScript.
51
51
  exports.compileFile = function compileFile(path) {
@@ -55,16 +55,16 @@
55
55
  return coffee.stdout.read();
56
56
  };
57
57
  // Compile a string of CoffeeScript into JavaScript.
58
- exports.compile = function compile(source) {
58
+ exports.compile = function compile(source, flags) {
59
59
  var coffee;
60
- coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
60
+ coffee = OS.popen([coffeePath, "--eval", "--no-wrap"].concat(flags || []));
61
61
  coffee.stdin.write(source).flush().close();
62
62
  checkForErrors(coffee);
63
63
  return coffee.stdout.read();
64
64
  };
65
65
  // Evaluating a string of CoffeeScript first compiles it externally.
66
- exports.evalCS = function evalCS(source) {
67
- return eval(exports.compile(source));
66
+ exports.evalCS = function evalCS(source, flags) {
67
+ return eval(exports.compile(source, flags));
68
68
  };
69
69
  // Make a factory for the CoffeeScript environment.
70
70
  exports.makeNarwhalFactory = function makeNarwhalFactory(path) {
@@ -8,9 +8,9 @@
8
8
  // Reload the coffee-script environment from source.
9
9
  reload: function reload(topId, path) {
10
10
  coffeescript = coffeescript || require('coffee-script');
11
- return (factories[topId] = function() {
11
+ return factories[topId] = function() {
12
12
  return coffeescript.makeNarwhalFactory(path);
13
- });
13
+ };
14
14
  },
15
15
  // Ensure that the coffee-script environment is loaded.
16
16
  load: function load(topId, path) {
@@ -29,6 +29,7 @@ module CoffeeScript
29
29
  # already been asked to return the result.
30
30
  def compile(o={})
31
31
  @options = o.dup
32
+ @indent = o[:indent]
32
33
  top = self.is_a?(ForNode) ? @options[:top] : @options.delete(:top)
33
34
  closure = statement? && !statement_only? && !top && !@options[:return]
34
35
  closure ? compile_closure(@options) : compile_node(@options)
@@ -36,10 +37,15 @@ module CoffeeScript
36
37
 
37
38
  def compile_closure(o={})
38
39
  indent = o[:indent]
39
- o[:indent] += TAB
40
+ @indent = (o[:indent] = idt(1))
40
41
  "(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
41
42
  end
42
43
 
44
+ # Quick method for the current indentation level, plus tabs out.
45
+ def idt(tabs=0)
46
+ @indent + (TAB * tabs)
47
+ end
48
+
43
49
  # Default implementations of the common node methods.
44
50
  def unwrap; self; end
45
51
  def statement?; false; end
@@ -95,16 +101,22 @@ module CoffeeScript
95
101
  def compile_node(options={})
96
102
  compiled = @expressions.map do |node|
97
103
  o = options.dup
104
+ @indent = o[:indent]
98
105
  returns = o.delete(:return)
99
106
  if last?(node) && returns && !node.statement_only?
100
107
  if node.statement?
101
108
  node.compile(o.merge(:return => true))
102
109
  else
103
- "#{o[:indent]}return #{node.compile(o)};"
110
+ if o[:top] && o[:last_assign] && o[:last_assign][0..0][/[A-Z]/]
111
+ temp = o[:scope].free_variable
112
+ "#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
113
+ else
114
+ "#{idt}return #{node.compile(o)};"
115
+ end
104
116
  end
105
117
  else
106
118
  ending = node.statement? ? '' : ';'
107
- indent = node.statement? ? '' : o[:indent]
119
+ indent = node.statement? ? '' : idt
108
120
  "#{indent}#{node.compile(o.merge(:top => true))}#{ending}"
109
121
  end
110
122
  end
@@ -114,8 +126,9 @@ module CoffeeScript
114
126
  # If this is the top-level Expressions, wrap everything in a safety closure.
115
127
  def compile_root(o={})
116
128
  indent = o[:no_wrap] ? '' : TAB
129
+ @indent = indent
117
130
  o.merge!(:indent => indent, :scope => Scope.new(nil, self))
118
- code = o[:no_wrap] ? compile_node(o) : compile_with_declarations(o)
131
+ code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
119
132
  code.gsub!(STRIP_TRAILING_WHITESPACE, '')
120
133
  o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
121
134
  end
@@ -123,7 +136,7 @@ module CoffeeScript
123
136
  def compile_with_declarations(o={})
124
137
  code = compile_node(o)
125
138
  decls = ''
126
- decls = "#{o[:indent]}var #{o[:scope].declared_variables.join(', ')};\n" if o[:scope].declarations?(self)
139
+ decls = "#{idt}var #{o[:scope].declared_variables.join(', ')};\n" if o[:scope].declarations?(self)
127
140
  decls + code
128
141
  end
129
142
 
@@ -140,6 +153,10 @@ module CoffeeScript
140
153
 
141
154
  attr_reader :value
142
155
 
156
+ def self.wrap(string)
157
+ self.new(Value.new(string))
158
+ end
159
+
143
160
  def initialize(value)
144
161
  @value = value
145
162
  end
@@ -151,7 +168,7 @@ module CoffeeScript
151
168
 
152
169
  def compile_node(o)
153
170
  val = CONVERSIONS[@value.to_s] || @value.to_s
154
- indent = statement? ? o[:indent] : ''
171
+ indent = statement? ? idt : ''
155
172
  ending = statement? ? ';' : ''
156
173
  write("#{indent}#{val}#{ending}")
157
174
  end
@@ -170,7 +187,7 @@ module CoffeeScript
170
187
  def compile_node(o)
171
188
  return write(@expression.compile(o.merge(:return => true))) if @expression.statement?
172
189
  compiled = @expression.compile(o)
173
- write(@expression.statement? ? "#{compiled}\n#{o[:indent]}return null;" : "#{o[:indent]}return #{compiled};")
190
+ write(@expression.statement? ? "#{compiled}\n#{idt}return null;" : "#{idt}return #{compiled};")
174
191
  end
175
192
  end
176
193
 
@@ -184,7 +201,7 @@ module CoffeeScript
184
201
  end
185
202
 
186
203
  def compile_node(o={})
187
- delimiter = "\n#{o[:indent]}//"
204
+ delimiter = "\n#{idt}//"
188
205
  comment = "#{delimiter}#{@lines.join(delimiter)}"
189
206
  write(comment)
190
207
  end
@@ -231,7 +248,9 @@ module CoffeeScript
231
248
  def compile_super(args, o)
232
249
  methname = o[:last_assign]
233
250
  arg_part = args.empty? ? '' : ", #{args}"
234
- "#{o[:proto_assign]}.__superClass__.#{methname}.call(this#{arg_part})"
251
+ meth = o[:proto_assign] ? "#{o[:proto_assign]}.__superClass__.#{methname}" :
252
+ "#{methname}.__superClass__.constructor"
253
+ "#{meth}.call(this#{arg_part})"
235
254
  end
236
255
 
237
256
  def compile_splat(o)
@@ -257,9 +276,12 @@ module CoffeeScript
257
276
  end
258
277
 
259
278
  def compile_node(o={})
279
+ constructor = o[:scope].free_variable
260
280
  sub, sup = @sub_object.compile(o), @super_object.compile(o)
261
- "#{o[:indent]}#{sub}.__superClass__ = #{sup}.prototype;\n#{o[:indent]}" +
262
- "#{sub}.prototype = new #{sup}();\n#{o[:indent]}" +
281
+ "#{idt}#{constructor} = function(){};\n#{idt}" +
282
+ "#{constructor}.prototype = #{sup}.prototype;\n#{idt}" +
283
+ "#{sub}.__superClass__ = #{sup}.prototype;\n#{idt}" +
284
+ "#{sub}.prototype = new #{constructor}();\n#{idt}" +
263
285
  "#{sub}.prototype.constructor = #{sub};"
264
286
  end
265
287
 
@@ -302,12 +324,13 @@ module CoffeeScript
302
324
  class AccessorNode < Node
303
325
  attr_reader :name
304
326
 
305
- def initialize(name)
306
- @name = name
327
+ def initialize(name, prototype=false)
328
+ @name, @prototype = name, prototype
307
329
  end
308
330
 
309
331
  def compile_node(o)
310
- write(".#{@name}")
332
+ proto = @prototype ? "prototype." : ''
333
+ write(".#{proto}#{@name}")
311
334
  end
312
335
  end
313
336
 
@@ -346,15 +369,15 @@ module CoffeeScript
346
369
  end
347
370
 
348
371
  def compile_variables(o)
349
- idt = o[:indent]
372
+ @indent = o[:indent]
350
373
  @from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
351
374
  from_val, to_val = @from.compile(o), @to.compile(o)
352
- write("#{idt}#{@from_var} = #{from_val};\n#{idt}#{@to_var} = #{to_val};\n#{idt}")
375
+ write("#{@from_var} = #{from_val}; #{@to_var} = #{to_val};\n#{idt}")
353
376
  end
354
377
 
355
378
  def compile_node(o)
356
379
  idx, step = o.delete(:index), o.delete(:step)
357
- raise SyntaxError, "unexpected range literal" unless idx
380
+ return compile_array(o) unless idx
358
381
  vars = "#{idx}=#{@from_var}"
359
382
  step = step ? step.compile(o) : '1'
360
383
  compare = "(#{@from_var} <= #{@to_var} ? #{idx} #{less_operator} #{@to_var} : #{idx} #{greater_operator} #{@to_var})"
@@ -362,6 +385,15 @@ module CoffeeScript
362
385
  write("#{vars}; #{compare}; #{incr}")
363
386
  end
364
387
 
388
+ # Expand the range into the equivalent array, if it's not being used as
389
+ # part of a comprehension, slice, or splice.
390
+ # TODO: This generates pretty ugly code ... shrink it.
391
+ def compile_array(o)
392
+ body = Expressions.wrap(LiteralNode.wrap('i'))
393
+ arr = Expressions.wrap(ForNode.new(body, {:source => ValueNode.new(self)}, Value.new('i')))
394
+ ParentheticalNode.new(CallNode.new(CodeNode.new([], arr))).compile(o)
395
+ end
396
+
365
397
  end
366
398
 
367
399
  # An array slice literal. Unlike JavaScript's Array#slice, the second parameter
@@ -385,7 +417,7 @@ module CoffeeScript
385
417
  # Setting the value of a local variable, or the value of an object property.
386
418
  class AssignNode < Node
387
419
  PROTO_ASSIGN = /\A(\S+)\.prototype/
388
- LEADING_DOT = /\A\./
420
+ LEADING_DOT = /\A\.(prototype\.)?/
389
421
 
390
422
  attr_reader :variable, :value, :context
391
423
 
@@ -403,7 +435,7 @@ module CoffeeScript
403
435
  return write("#{name}: #{@value.compile(o)}") if @context == :object
404
436
  o[:scope].find(name) unless @variable.properties?
405
437
  val = "#{name} = #{@value.compile(o)}"
406
- write(o[:return] ? "#{o[:indent]}return (#{val})" : val)
438
+ write(o[:return] ? "#{idt}return (#{val})" : val)
407
439
  end
408
440
 
409
441
  def compile_splice(o)
@@ -473,12 +505,12 @@ module CoffeeScript
473
505
 
474
506
  def compile_node(o)
475
507
  shared_scope = o.delete(:shared_scope)
476
- indent = o[:indent]
477
508
  o[:scope] = shared_scope || Scope.new(o[:scope], @body)
478
509
  o[:return] = true
479
510
  o[:top] = true
480
- o[:indent] += TAB
511
+ o[:indent] = idt(1)
481
512
  o.delete(:no_wrap)
513
+ o.delete(:globals)
482
514
  name = o.delete(:immediate_assign)
483
515
  if @params.last.is_a?(ParamSplatNode)
484
516
  splat = @params.pop
@@ -488,7 +520,7 @@ module CoffeeScript
488
520
  @params.each {|id| o[:scope].parameter(id.to_s) }
489
521
  code = @body.compile_with_declarations(o)
490
522
  name_part = name ? " #{name}" : ''
491
- write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
523
+ write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{idt}}")
492
524
  end
493
525
  end
494
526
 
@@ -532,18 +564,17 @@ module CoffeeScript
532
564
  # AssignNodes get interleaved correctly, with no trailing commas or
533
565
  # commas affixed to comments. TODO: Extract this and add it to ArrayNode.
534
566
  def compile_node(o)
535
- indent = o[:indent]
536
- o[:indent] += TAB
567
+ o[:indent] = idt(1)
537
568
  joins = Hash.new("\n")
538
569
  non_comments = @properties.select {|p| !p.is_a?(CommentNode) }
539
570
  non_comments.each {|p| joins[p] = p == non_comments.last ? "\n" : ",\n" }
540
571
  props = @properties.map { |prop|
541
572
  join = joins[prop]
542
573
  join = '' if prop == @properties.last
543
- idt = prop.is_a?(CommentNode) ? '' : o[:indent]
544
- "#{idt}#{prop.compile(o)}#{join}"
574
+ indent = prop.is_a?(CommentNode) ? '' : idt(1)
575
+ "#{indent}#{prop.compile(o)}#{join}"
545
576
  }.join('')
546
- write("{\n#{props}\n#{indent}}")
577
+ write("{\n#{props}\n#{idt}}")
547
578
  end
548
579
  end
549
580
 
@@ -556,14 +587,13 @@ module CoffeeScript
556
587
  end
557
588
 
558
589
  def compile_node(o)
559
- indent = o[:indent]
560
- o[:indent] += TAB
590
+ o[:indent] = idt(1)
561
591
  objects = @objects.map { |obj|
562
592
  code = obj.compile(o)
563
593
  obj.is_a?(CommentNode) ? "\n#{code}\n#{o[:indent]}" :
564
594
  obj == @objects.last ? code : "#{code}, "
565
595
  }.join('')
566
- ending = objects.include?("\n") ? "\n#{indent}]" : ']'
596
+ ending = objects.include?("\n") ? "\n#{idt}]" : ']'
567
597
  write("[#{objects}#{ending}")
568
598
  end
569
599
  end
@@ -581,12 +611,12 @@ module CoffeeScript
581
611
 
582
612
  def compile_node(o)
583
613
  returns = o.delete(:return)
584
- indent = o[:indent]
585
- o[:indent] += TAB
614
+ o[:indent] = idt(1)
586
615
  o[:top] = true
587
616
  cond = @condition.compile(o)
588
- post = returns ? "\n#{indent}return null;" : ''
589
- write("#{indent}while (#{cond}) {\n#{@body.compile(o)}\n#{indent}}#{post}")
617
+ post = returns ? "\n#{idt}return null;" : ''
618
+ return write("#{idt}while (#{cond}) null;#{post}") if @body.nil?
619
+ write("#{idt}while (#{cond}) {\n#{@body.compile(o)}\n#{idt}}#{post}")
590
620
  end
591
621
  end
592
622
 
@@ -604,56 +634,63 @@ module CoffeeScript
604
634
  @source = source[:source]
605
635
  @filter = source[:filter]
606
636
  @step = source[:step]
637
+ @object = !!source[:object]
638
+ @name, @index = @index, @name if @object
607
639
  end
608
640
 
609
641
  def compile_node(o)
610
642
  top_level = o.delete(:top) && !o[:return]
611
- range = @source.is_a?(RangeNode)
643
+ range = @source.is_a?(ValueNode) && @source.literal.is_a?(RangeNode) && @source.properties.empty?
644
+ source = range ? @source.literal : @source
612
645
  scope = o[:scope]
613
- name_found = scope.find(@name)
646
+ name_found = @name && scope.find(@name)
614
647
  index_found = @index && scope.find(@index)
648
+ body_dent = idt(1)
615
649
  svar = scope.free_variable
616
650
  ivar = range ? name : @index ? @index : scope.free_variable
617
651
  rvar = scope.free_variable unless top_level
618
- tvar = scope.free_variable
619
652
  if range
620
- body_dent = o[:indent] + TAB
621
- var_part, pre_cond, post_cond = '', '', ''
622
653
  index_var = scope.free_variable
623
- source_part = @source.compile_variables(o)
624
- for_part = "#{index_var}=0, #{@source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
654
+ source_part = source.compile_variables(o)
655
+ for_part = "#{index_var}=0, #{source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
656
+ var_part = ''
625
657
  else
626
658
  index_var = nil
627
- body_dent = o[:indent] + TAB + TAB
628
- source_part = "#{o[:indent]}#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
629
- for_part = "#{ivar} in #{svar}"
630
- pre_cond = "\n#{o[:indent] + TAB}if (#{svar}.hasOwnProperty(#{ivar})) {"
631
- var_part = "\n#{body_dent}#{@name} = #{svar}[#{ivar}];"
632
- post_cond = "\n#{o[:indent] + TAB}}"
659
+ source_part = "#{svar} = #{source.compile(o)};\n#{idt}"
660
+ for_part = "#{ivar}=0; #{ivar}<#{svar}.length; #{ivar}++"
661
+ for_part = "#{ivar} in #{svar}" if @object
662
+ var_part = @name ? "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" : ''
633
663
  end
634
664
  body = @body
635
- set_result = rvar ? "#{rvar} = [];\n#{o[:indent]}" : ''
665
+ set_result = rvar ? "#{idt}#{rvar} = []; " : idt
636
666
  return_result = rvar || ''
637
- temp_var = ValueNode.new(LiteralNode.new(tvar))
638
667
  if top_level
639
668
  body = Expressions.wrap(body)
640
669
  else
641
- body = Expressions.wrap(
642
- AssignNode.new(temp_var, @body.unwrap),
643
- CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [temp_var])
644
- )
670
+ body = Expressions.wrap(CallNode.new(
671
+ ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [body.unwrap]
672
+ ))
645
673
  end
646
674
  if o[:return]
647
675
  return_result = "return #{return_result}" if o[:return]
648
676
  o.delete(:return)
649
677
  body = IfNode.new(@filter, body, nil, :statement => true) if @filter
650
678
  elsif @filter
651
- body = Expressions.wrap(IfNode.new(@filter, @body))
679
+ body = Expressions.wrap(IfNode.new(@filter, body))
680
+ end
681
+ if @object
682
+ body = Expressions.wrap(IfNode.new(
683
+ CallNode.new(ValueNode.new(LiteralNode.wrap(svar), [AccessorNode.new(Value.new('hasOwnProperty'))]), [LiteralNode.wrap(ivar)]),
684
+ Expressions.wrap(body),
685
+ nil,
686
+ {:statement => true}
687
+ ))
652
688
  end
653
689
 
654
- return_result = "\n#{o[:indent]}#{return_result};" unless top_level
690
+ return_result = "\n#{idt}#{return_result};" unless top_level
655
691
  body = body.compile(o.merge(:indent => body_dent, :top => true))
656
- write("#{source_part}#{set_result}for (#{for_part}) {#{pre_cond}#{var_part}\n#{body}#{post_cond}\n#{o[:indent]}}#{return_result}")
692
+ vars = range ? @name : "#{@name}, #{ivar}"
693
+ return write(set_result + source_part + "for (#{for_part}) {\n#{var_part}#{body}\n#{idt}}\n#{idt}#{return_result}")
657
694
  end
658
695
  end
659
696
 
@@ -668,13 +705,12 @@ module CoffeeScript
668
705
  end
669
706
 
670
707
  def compile_node(o)
671
- indent = o[:indent]
672
- o[:indent] += TAB
708
+ o[:indent] = idt(1)
673
709
  o[:top] = true
674
710
  error_part = @error ? " (#{@error}) " : ' '
675
- catch_part = @recovery && " catch#{error_part}{\n#{@recovery.compile(o)}\n#{indent}}"
676
- finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:return => nil))}\n#{indent}}"
677
- write("#{indent}try {\n#{@try.compile(o)}\n#{indent}}#{catch_part}#{finally_part}")
711
+ catch_part = @recovery && " catch#{error_part}{\n#{@recovery.compile(o)}\n#{idt}}"
712
+ finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:return => nil))}\n#{idt}}"
713
+ write("#{idt}try {\n#{@try.compile(o)}\n#{idt}}#{catch_part}#{finally_part}")
678
714
  end
679
715
  end
680
716
 
@@ -689,7 +725,7 @@ module CoffeeScript
689
725
  end
690
726
 
691
727
  def compile_node(o)
692
- write("#{o[:indent]}throw #{@expression.compile(o)};")
728
+ write("#{idt}throw #{@expression.compile(o)};")
693
729
  end
694
730
  end
695
731
 
@@ -746,6 +782,11 @@ module CoffeeScript
746
782
  self
747
783
  end
748
784
 
785
+ def add_comment(comment)
786
+ @comment = comment
787
+ self
788
+ end
789
+
749
790
  def force_statement
750
791
  @tags[:statement] = true
751
792
  self
@@ -772,7 +813,7 @@ module CoffeeScript
772
813
  # The IfNode only compiles into a statement if either of the bodies needs
773
814
  # to be a statement.
774
815
  def statement?
775
- @is_statement ||= !!(@tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
816
+ @is_statement ||= !!(@comment || @tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
776
817
  end
777
818
 
778
819
  def compile_node(o)
@@ -782,18 +823,19 @@ module CoffeeScript
782
823
  # Compile the IfNode as a regular if-else statement. Flattened chains
783
824
  # force sub-else bodies into statement form.
784
825
  def compile_statement(o)
785
- indent = o[:indent]
786
- child = o.delete(:chain_child)
787
- cond_o = o.dup
826
+ child = o.delete(:chain_child)
827
+ cond_o = o.dup
788
828
  cond_o.delete(:return)
789
- o[:indent] += TAB
790
- o[:top] = true
791
- if_dent = child ? '' : indent
792
- if_part = "#{if_dent}if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
829
+ o[:indent] = idt(1)
830
+ o[:top] = true
831
+ if_dent = child ? '' : idt
832
+ com_dent = child ? idt : ''
833
+ prefix = @comment ? @comment.compile(cond_o) + "\n#{com_dent}" : ''
834
+ if_part = "#{prefix}#{if_dent}if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{idt}}"
793
835
  return if_part unless @else_body
794
836
  else_part = chain? ?
795
- " else #{@else_body.compile(o.merge(:indent => indent, :chain_child => true))}" :
796
- " else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{indent}}"
837
+ " else #{@else_body.compile(o.merge(:indent => idt, :chain_child => true))}" :
838
+ " else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{idt}}"
797
839
  if_part + else_part
798
840
  end
799
841