coffee-script 0.1.0

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.
@@ -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