coffee-script 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,680 @@
1
+ module CoffeeScript
2
+
3
+ # The abstract base class for all CoffeeScript nodes.
4
+ class Node
5
+ # Tabs are two spaces for pretty-printing.
6
+ TAB = ' '
7
+
8
+ # Tag this node as a statement, meaning that it can't be used directly as
9
+ # the result of an expression.
10
+ def self.statement
11
+ class_eval "def statement?; true; end"
12
+ end
13
+
14
+ # Tag this node as having a custom return, meaning that instead of returning
15
+ # it from the outside, you ask it to return itself, and it obliges.
16
+ def self.custom_return
17
+ class_eval "def custom_return?; true; end"
18
+ end
19
+
20
+ # Tag this node as having a custom assignment, meaning that instead of
21
+ # assigning it to a variable name from the outside, you pass it the variable
22
+ # name and let it take care of it.
23
+ def self.custom_assign
24
+ class_eval "def custom_assign?; true; end"
25
+ end
26
+
27
+ def write(code)
28
+ puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE']
29
+ code
30
+ end
31
+
32
+ def compile(o={})
33
+ @options = o.dup
34
+ end
35
+
36
+ # Default implementations of the common node methods.
37
+ def unwrap; self; end
38
+ def line_ending; ';'; end
39
+ def statement?; false; end
40
+ def custom_return?; false; end
41
+ def custom_assign?; false; end
42
+ end
43
+
44
+ # A collection of nodes, each one representing an expression.
45
+ class Expressions < Node
46
+ statement
47
+ attr_reader :expressions
48
+
49
+ STRIP_TRAILING_WHITESPACE = /\s+$/
50
+
51
+ # Wrap up a node as an Expressions, unless it already is.
52
+ def self.wrap(node)
53
+ node.is_a?(Expressions) ? node : Expressions.new([node])
54
+ end
55
+
56
+ def initialize(nodes)
57
+ @expressions = nodes
58
+ end
59
+
60
+ # Tack an expression onto the end of this node.
61
+ def <<(node)
62
+ @expressions << node
63
+ self
64
+ end
65
+
66
+ # If this Expressions consists of a single node, pull it back out.
67
+ def unwrap
68
+ @expressions.length == 1 ? @expressions.first : self
69
+ end
70
+
71
+ # Is the node last in this block of expressions.
72
+ def last?(node)
73
+ @last_index ||= @expressions.last.is_a?(CommentNode) ? -2 : -1
74
+ node == @expressions[@last_index]
75
+ end
76
+
77
+ # If this is the top-level Expressions, wrap everything in a safety closure.
78
+ def root_compile
79
+ code = compile(:indent => TAB, :scope => Scope.new)
80
+ code.gsub!(STRIP_TRAILING_WHITESPACE, '')
81
+ "(function(){\n#{code}\n})();"
82
+ end
83
+
84
+ # The extra fancy is to handle pushing down returns and assignments
85
+ # recursively to the final lines of inner statements.
86
+ def compile(options={})
87
+ return root_compile unless options[:scope]
88
+ code = @expressions.map { |node|
89
+ o = super(options)
90
+ if last?(node) && (o[:return] || o[:assign])
91
+ if o[:return]
92
+ if node.statement? || node.custom_return?
93
+ "#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
94
+ else
95
+ "#{o[:indent]}return #{node.compile(o)}#{node.line_ending}"
96
+ end
97
+ elsif o[:assign]
98
+ if node.statement? || node.custom_assign?
99
+ "#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
100
+ else
101
+ "#{o[:indent]}#{AssignNode.new(ValueNode.new(LiteralNode.new(o[:assign])), node).compile(o)};"
102
+ end
103
+ end
104
+ else
105
+ o.delete(:return) and o.delete(:assign)
106
+ "#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
107
+ end
108
+ }.join("\n")
109
+ write(code)
110
+ end
111
+ end
112
+
113
+ # Literals are static values that have a Ruby representation, eg.: a string, a number,
114
+ # true, false, nil, etc.
115
+ class LiteralNode < Node
116
+ STATEMENTS = ['break', 'continue']
117
+
118
+ attr_reader :value
119
+
120
+ def initialize(value)
121
+ @value = value
122
+ end
123
+
124
+ def statement?
125
+ STATEMENTS.include?(@value.to_s)
126
+ end
127
+
128
+ def line_ending
129
+ @value.to_s[-1..-1] == ';' ? '' : ';'
130
+ end
131
+
132
+ def compile(o={})
133
+ o = super(o)
134
+ write(@value.to_s)
135
+ end
136
+ end
137
+
138
+ # Try to return your expression, or tell it to return itself.
139
+ class ReturnNode < Node
140
+ statement
141
+ custom_return
142
+
143
+ attr_reader :expression
144
+
145
+ def initialize(expression)
146
+ @expression = expression
147
+ end
148
+
149
+ def line_ending
150
+ @expression.custom_return? ? '' : ';'
151
+ end
152
+
153
+ def compile(o={})
154
+ o = super(o)
155
+ return write(@expression.compile(o.merge(:return => true))) if @expression.custom_return?
156
+ compiled = @expression.compile(o)
157
+ write(@expression.statement? ? "#{compiled}\n#{indent}return null" : "return #{compiled}")
158
+ end
159
+ end
160
+
161
+ # Pass through CoffeeScript comments into JavaScript comments at the
162
+ # same position.
163
+ class CommentNode < Node
164
+ statement
165
+
166
+ def initialize(lines)
167
+ @lines = lines.value
168
+ end
169
+
170
+ def line_ending
171
+ ''
172
+ end
173
+
174
+ def compile(o={})
175
+ delimiter = "\n#{o[:indent]}//"
176
+ comment = "#{delimiter}#{@lines.join(delimiter)}"
177
+ write(comment)
178
+ end
179
+
180
+ end
181
+
182
+ # Node for a function invocation. Takes care of converting super() calls into
183
+ # calls against the prototype's function of the same name.
184
+ class CallNode < Node
185
+ LEADING_DOT = /\A\./
186
+
187
+ attr_reader :variable, :arguments
188
+
189
+ def initialize(variable, arguments=[])
190
+ @variable, @arguments = variable, arguments
191
+ end
192
+
193
+ def new_instance
194
+ @new = true
195
+ self
196
+ end
197
+
198
+ def super?
199
+ @variable == :super
200
+ end
201
+
202
+ def compile(o={})
203
+ o = super(o)
204
+ args = @arguments.map{|a| a.compile(o.merge(:no_paren => true)) }.join(', ')
205
+ return write(compile_super(args, o)) if super?
206
+ prefix = @new ? "new " : ''
207
+ write("#{prefix}#{@variable.compile(o)}(#{args})")
208
+ end
209
+
210
+ def compile_super(args, o)
211
+ methname = o[:last_assign].sub(LEADING_DOT, '')
212
+ "this.constructor.prototype.#{methname}.call(this, #{args})"
213
+ end
214
+ end
215
+
216
+ # A value, indexed or dotted into, or vanilla.
217
+ class ValueNode < Node
218
+ attr_reader :literal, :properties, :last
219
+
220
+ def initialize(literal, properties=[])
221
+ @literal, @properties = literal, properties
222
+ end
223
+
224
+ def <<(other)
225
+ @properties << other
226
+ self
227
+ end
228
+
229
+ def properties?
230
+ return !@properties.empty?
231
+ end
232
+
233
+ def statement?
234
+ @literal.is_a?(Node) && @literal.statement? && !properties?
235
+ end
236
+
237
+ def custom_assign?
238
+ @literal.is_a?(Node) && @literal.custom_assign? && !properties?
239
+ end
240
+
241
+ def custom_return?
242
+ @literal.is_a?(Node) && @literal.custom_return? && !properties?
243
+ end
244
+
245
+ def compile(o={})
246
+ o = super(o)
247
+ parts = [@literal, @properties].flatten.map do |val|
248
+ val.respond_to?(:compile) ? val.compile(o) : val.to_s
249
+ end
250
+ @last = parts.last
251
+ write(parts.join(''))
252
+ end
253
+ end
254
+
255
+ # A dotted accessor into a part of a value.
256
+ class AccessorNode < Node
257
+ attr_reader :name
258
+
259
+ def initialize(name)
260
+ @name = name
261
+ end
262
+
263
+ def compile(o={})
264
+ o = super(o)
265
+ write(".#{@name}")
266
+ end
267
+ end
268
+
269
+ # An indexed accessor into a part of an array or object.
270
+ class IndexNode < Node
271
+ attr_reader :index
272
+
273
+ def initialize(index)
274
+ @index = index
275
+ end
276
+
277
+ def compile(o={})
278
+ o = super(o)
279
+ write("[#{@index.compile(o)}]")
280
+ end
281
+ end
282
+
283
+ # An array slice literal. Unlike JavaScript's Array#slice, the second parameter
284
+ # specifies the index of the end of the slice (just like the first parameter)
285
+ # is the index of the beginning.
286
+ class SliceNode < Node
287
+ attr_reader :from, :to
288
+
289
+ def initialize(from, to)
290
+ @from, @to = from, to
291
+ end
292
+
293
+ def compile(o={})
294
+ o = super(o)
295
+ write(".slice(#{@from.compile(o)}, #{@to.compile(o)} + 1)")
296
+ end
297
+ end
298
+
299
+ # Setting the value of a local variable, or the value of an object property.
300
+ class AssignNode < Node
301
+ LEADING_VAR = /\Avar\s+/
302
+
303
+ statement
304
+ custom_return
305
+
306
+ attr_reader :variable, :value, :context
307
+
308
+ def initialize(variable, value, context=nil)
309
+ @variable, @value, @context = variable, value, context
310
+ end
311
+
312
+ def line_ending
313
+ @value.custom_assign? ? '' : ';'
314
+ end
315
+
316
+ def compile(o={})
317
+ o = super(o)
318
+ name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable
319
+ last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
320
+ o = o.merge(:assign => name, :last_assign => last)
321
+ postfix = o[:return] ? ";\n#{o[:indent]}return #{name}" : ''
322
+ return write("#{@variable}: #{@value.compile(o)}") if @context == :object
323
+ return write("#{name} = #{@value.compile(o)}#{postfix}") if @variable.properties? && !@value.custom_assign?
324
+ defined = o[:scope].find(name)
325
+ def_part = defined || @variable.properties? ? "" : "var #{name};\n#{o[:indent]}"
326
+ return write(def_part + @value.compile(o)) if @value.custom_assign?
327
+ def_part = defined ? name : "var #{name}"
328
+ val_part = @value.compile(o).sub(LEADING_VAR, '')
329
+ write("#{def_part} = #{val_part}#{postfix}")
330
+ end
331
+ end
332
+
333
+ # Simple Arithmetic and logical operations. Performs some conversion from
334
+ # CoffeeScript operations into their JavaScript equivalents.
335
+ class OpNode < Node
336
+ CONVERSIONS = {
337
+ "==" => "===",
338
+ "!=" => "!==",
339
+ 'and' => '&&',
340
+ 'or' => '||',
341
+ 'is' => '===',
342
+ "aint" => "!==",
343
+ 'not' => '!',
344
+ }
345
+ CONDITIONALS = ['||:', '&&:']
346
+
347
+ attr_reader :operator, :first, :second
348
+
349
+ def initialize(operator, first, second=nil, flip=false)
350
+ @first, @second, @flip = first, second, flip
351
+ @operator = CONVERSIONS[operator] || operator
352
+ end
353
+
354
+ def unary?
355
+ @second.nil?
356
+ end
357
+
358
+ def compile(o={})
359
+ o = super(o)
360
+ return write(compile_conditional(o)) if CONDITIONALS.include?(@operator)
361
+ return write(compile_unary(o)) if unary?
362
+ write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
363
+ end
364
+
365
+ def compile_conditional(o)
366
+ first, second = @first.compile(o), @second.compile(o)
367
+ sym = @operator[0..1]
368
+ "#{first} = #{first} #{sym} #{second}"
369
+ end
370
+
371
+ def compile_unary(o)
372
+ space = @operator.to_s == 'delete' ? ' ' : ''
373
+ parts = [@operator.to_s, space, @first.compile(o)]
374
+ parts.reverse! if @flip
375
+ parts.join('')
376
+ end
377
+ end
378
+
379
+ # A function definition. The only node that creates a new Scope.
380
+ class CodeNode < Node
381
+ attr_reader :params, :body
382
+
383
+ def initialize(params, body)
384
+ @params = params
385
+ @body = body
386
+ end
387
+
388
+ def compile(o={})
389
+ o = super(o)
390
+ o[:scope] = Scope.new(o[:scope])
391
+ o[:return] = true
392
+ indent = o[:indent]
393
+ o[:indent] += TAB
394
+ o.delete(:assign)
395
+ @params.each {|id| o[:scope].find(id.to_s) }
396
+ code = @body.compile(o)
397
+ write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
398
+ end
399
+ end
400
+
401
+ # An object literal.
402
+ class ObjectNode < Node
403
+ attr_reader :properties
404
+
405
+ def initialize(properties = [])
406
+ @properties = properties
407
+ end
408
+
409
+ def compile(o={})
410
+ o = super(o)
411
+ indent = o[:indent]
412
+ o[:indent] += TAB
413
+ props = @properties.map { |prop|
414
+ joiner = prop == @properties.last ? '' : prop.is_a?(CommentNode) ? "\n" : ",\n"
415
+ o[:indent] + prop.compile(o) + joiner
416
+ }.join('')
417
+ write("{\n#{props}\n#{indent}}")
418
+ end
419
+ end
420
+
421
+ # An array literal.
422
+ class ArrayNode < Node
423
+ attr_reader :objects
424
+
425
+ def initialize(objects=[])
426
+ @objects = objects
427
+ end
428
+
429
+ def compile(o={})
430
+ o = super(o)
431
+ objects = @objects.map { |obj|
432
+ joiner = obj.is_a?(CommentNode) ? "\n#{o[:indent] + TAB}" : obj == @objects.last ? '' : ', '
433
+ obj.compile(o.merge(:indent => o[:indent] + TAB)) + joiner
434
+ }.join('')
435
+ ending = objects.include?("\n") ? "\n#{o[:indent]}]" : ']'
436
+ write("[#{objects}#{ending}")
437
+ end
438
+ end
439
+
440
+ # A while loop, the only sort of low-level loop exposed by CoffeeScript. From
441
+ # it, all other loops can be manufactured.
442
+ class WhileNode < Node
443
+ statement
444
+
445
+ attr_reader :condition, :body
446
+
447
+ def initialize(condition, body)
448
+ @condition, @body = condition, body
449
+ end
450
+
451
+ def line_ending
452
+ ''
453
+ end
454
+
455
+ def compile(o={})
456
+ o = super(o)
457
+ indent = o[:indent] + TAB
458
+ cond = @condition.compile(o.merge(:no_paren => true))
459
+ write("while (#{cond}) {\n#{@body.compile(o.merge(:indent => indent))}\n#{o[:indent]}}")
460
+ end
461
+ end
462
+
463
+ # The replacement for the for loop is an array comprehension (that compiles)
464
+ # into a for loop. Also acts as an expression, able to return the result
465
+ # of the comprehenion. Unlike Python array comprehensions, it's able to pass
466
+ # the current index of the loop as a second parameter.
467
+ class ForNode < Node
468
+ statement
469
+ custom_return
470
+ custom_assign
471
+
472
+ attr_reader :body, :source, :name, :filter, :index
473
+
474
+ def initialize(body, source, name, filter, index=nil)
475
+ @body, @source, @name, @filter, @index = body, source, name, filter, index
476
+ end
477
+
478
+ def line_ending
479
+ ''
480
+ end
481
+
482
+ def compile(o={})
483
+ o = super(o)
484
+ scope = o[:scope]
485
+ name_found = scope.find(@name)
486
+ index_found = @index && scope.find(@index)
487
+ svar = scope.free_variable
488
+ ivar = scope.free_variable
489
+ lvar = scope.free_variable
490
+ name_part = name_found ? @name : "var #{@name}"
491
+ index_name = @index ? (index_found ? @index : "var #{@index}") : nil
492
+ source_part = "var #{svar} = #{@source.compile(o)};"
493
+ for_part = "var #{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
494
+ var_part = "\n#{o[:indent] + TAB}#{name_part} = #{svar}[#{ivar}];\n"
495
+ index_part = @index ? "#{o[:indent] + TAB}#{index_name} = #{ivar};\n" : ''
496
+
497
+ set_result = ''
498
+ save_result = ''
499
+ return_result = ''
500
+ body = @body
501
+ suffix = ';'
502
+ if o[:return] || o[:assign]
503
+ rvar = scope.free_variable
504
+ set_result = "var #{rvar} = [];\n#{o[:indent]}"
505
+ save_result += "#{rvar}[#{ivar}] = "
506
+ return_result = rvar
507
+ return_result = "#{o[:assign]} = #{return_result};" if o[:assign]
508
+ return_result = "return #{return_result};" if o[:return]
509
+ return_result = "\n#{o[:indent]}#{return_result}"
510
+ if @filter
511
+ body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body])
512
+ body = IfNode.new(@filter, body, nil, :statement => true)
513
+ save_result = ''
514
+ suffix = ''
515
+ end
516
+ elsif @filter
517
+ body = IfNode.new(@filter, @body)
518
+ end
519
+
520
+ indent = o[:indent] + TAB
521
+ body = body.compile(o.merge(:indent => indent))
522
+ 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}")
523
+ end
524
+ end
525
+
526
+ # A try/catch/finally block.
527
+ class TryNode < Node
528
+ statement
529
+ custom_return
530
+ custom_assign
531
+
532
+ attr_reader :try, :error, :recovery, :finally
533
+
534
+ def initialize(try, error, recovery, finally=nil)
535
+ @try, @error, @recovery, @finally = try, error, recovery, finally
536
+ end
537
+
538
+ def line_ending
539
+ ''
540
+ end
541
+
542
+ def compile(o={})
543
+ o = super(o)
544
+ indent = o[:indent]
545
+ o[:indent] += TAB
546
+ error_part = @error ? " (#{@error}) " : ' '
547
+ catch_part = @recovery && " catch#{error_part}{\n#{@recovery.compile(o)}\n#{indent}}"
548
+ finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:assign => nil, :return => nil))}\n#{indent}}"
549
+ write("try {\n#{@try.compile(o)}\n#{indent}}#{catch_part}#{finally_part}")
550
+ end
551
+ end
552
+
553
+ # Throw an exception.
554
+ class ThrowNode < Node
555
+ statement
556
+
557
+ attr_reader :expression
558
+
559
+ def initialize(expression)
560
+ @expression = expression
561
+ end
562
+
563
+ def compile(o={})
564
+ o = super(o)
565
+ write("throw #{@expression.compile(o)}")
566
+ end
567
+ end
568
+
569
+ # An extra set of parenthesis, supplied by the script source.
570
+ class ParentheticalNode < Node
571
+ attr_reader :expressions
572
+
573
+ def initialize(expressions)
574
+ @expressions = expressions.unwrap
575
+ end
576
+
577
+ def statement?
578
+ @expressions.statement?
579
+ end
580
+
581
+ def custom_assign?
582
+ @expressions.custom_assign?
583
+ end
584
+
585
+ def custom_return?
586
+ @expressions.custom_return?
587
+ end
588
+
589
+ def compile(o={})
590
+ o = super(o)
591
+ compiled = @expressions.compile(o)
592
+ compiled = compiled[0...-1] if compiled[-1..-1] == ';'
593
+ write(o[:no_paren] || statement? ? compiled : "(#{compiled})")
594
+ end
595
+ end
596
+
597
+ # If/else statements. Switch/whens get compiled into these. Acts as an
598
+ # expression by pushing down requested returns to the expression bodies.
599
+ # Single-expression IfNodes are compiled into ternary operators if possible,
600
+ # because ternaries are first-class returnable assignable expressions.
601
+ class IfNode < Node
602
+ attr_reader :condition, :body, :else_body
603
+
604
+ def initialize(condition, body, else_body=nil, tags={})
605
+ @condition = condition
606
+ @body = body && body.unwrap
607
+ @else_body = else_body && else_body.unwrap
608
+ @tags = tags
609
+ @condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert]
610
+ end
611
+
612
+ def <<(else_body)
613
+ eb = else_body.unwrap
614
+ @else_body ? @else_body << eb : @else_body = eb
615
+ self
616
+ end
617
+
618
+ # Rewrite a chain of IfNodes with their switch condition for equality.
619
+ def rewrite_condition(expression)
620
+ @condition = OpNode.new("is", expression, @condition)
621
+ @else_body.rewrite_condition(expression) if chain?
622
+ self
623
+ end
624
+
625
+ # Rewrite a chain of IfNodes to add a default case as the final else.
626
+ def add_else(expressions)
627
+ chain? ? @else_body.add_else(expressions) : @else_body = expressions
628
+ self
629
+ end
630
+
631
+ # If the else_body is an IfNode itself, then we've got an if-else chain.
632
+ def chain?
633
+ @chain ||= @else_body && @else_body.is_a?(IfNode)
634
+ end
635
+
636
+ # The IfNode only compiles into a statement if either of the bodies needs
637
+ # to be a statement.
638
+ def statement?
639
+ @is_statement ||= !!(@tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
640
+ end
641
+
642
+ def custom_return?
643
+ statement?
644
+ end
645
+
646
+ def custom_assign?
647
+ statement?
648
+ end
649
+
650
+ def line_ending
651
+ statement? ? '' : ';'
652
+ end
653
+
654
+ def compile(o={})
655
+ o = super(o)
656
+ write(statement? ? compile_statement(o) : compile_ternary(o))
657
+ end
658
+
659
+ # Compile the IfNode as a regular if-else statement. Flattened chains
660
+ # force sub-else bodies into statement form.
661
+ def compile_statement(o)
662
+ indent = o[:indent]
663
+ o[:indent] += TAB
664
+ if_part = "if (#{@condition.compile(o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
665
+ return if_part unless @else_body
666
+ else_part = chain? ?
667
+ " else #{@else_body.compile(o.merge(:indent => indent))}" :
668
+ " else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{indent}}"
669
+ if_part + else_part
670
+ end
671
+
672
+ # Compile the IfNode into a ternary operator.
673
+ def compile_ternary(o)
674
+ if_part = "#{@condition.compile(o)} ? #{@body.compile(o)}"
675
+ else_part = @else_body ? "#{@else_body.compile(o)}" : 'null'
676
+ "#{if_part} : #{else_part}"
677
+ end
678
+ end
679
+
680
+ end