coffee-script 0.2.1 → 0.2.2

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