style-script 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1079 @@
1
+ module StyleScript
2
+
3
+ # The abstract base class for all StyleScript nodes.
4
+ # All nodes are implement a "compile_node" method, which performs the
5
+ # code generation for that node. To compile a node, call the "compile"
6
+ # method, which wraps "compile_node" in some extra smarts, to know when the
7
+ # generated code should be wrapped up in a closure. An options hash is passed
8
+ # and cloned throughout, containing messages from higher in the AST,
9
+ # information about the current scope, and indentation level.
10
+ class Node
11
+ # Tabs are two spaces for pretty-printing.
12
+ TAB = ' '
13
+
14
+ # Tag this node as a statement, meaning that it can't be used directly as
15
+ # the result of an expression.
16
+ def self.statement
17
+ class_eval "def statement?; true; end"
18
+ end
19
+
20
+ # Tag this node as a statement that cannot be transformed into an expression.
21
+ # (break, continue, etc.) It doesn't make sense to try to transform it.
22
+ def self.statement_only
23
+ statement
24
+ class_eval "def statement_only?; true; end"
25
+ end
26
+
27
+ # This node needs to know if it's being compiled as a top-level statement,
28
+ # in order to compile without special expression conversion.
29
+ def self.top_sensitive
30
+ class_eval "def top_sensitive?; true; end"
31
+ end
32
+
33
+ # Provide a quick implementation of a children method.
34
+ def self.children(*attributes)
35
+ attr_reader(*attributes)
36
+ attrs = attributes.map {|a| "[@#{a}]" }.join(', ')
37
+ class_eval "def children; [#{attrs}].flatten.compact; end"
38
+ end
39
+
40
+ def write(code)
41
+ puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE']
42
+ code
43
+ end
44
+
45
+ # This is extremely important -- we convert JS statements into expressions
46
+ # by wrapping them in a closure, only if it's possible, and we're not at
47
+ # the top level of a block (which would be unnecessary), and we haven't
48
+ # already been asked to return the result.
49
+ def compile(o={})
50
+ @options = o.dup
51
+ @indent = o[:indent]
52
+ top = self.top_sensitive? ? @options[:top] : @options.delete(:top)
53
+ closure = statement? && !statement_only? && !top && !@options[:return] && !self.is_a?(CommentNode)
54
+ closure &&= !contains? {|n| n.statement_only? }
55
+ closure ? compile_closure(@options) : compile_node(@options)
56
+ end
57
+
58
+ # Statements converted into expressions share scope with their parent
59
+ # closure, to preserve JavaScript-style lexical scope.
60
+ def compile_closure(o={})
61
+ @indent = o[:indent]
62
+ ClosureNode.wrap(self).compile(o.merge(:shared_scope => o[:scope]))
63
+ end
64
+
65
+ # Quick short method for the current indentation level, plus tabbing in.
66
+ def idt(tabs=0)
67
+ @indent + (TAB * tabs)
68
+ end
69
+
70
+ # Does this node, or any of its children, contain a node of a certain kind?
71
+ def contains?(&block)
72
+ children.each do |node|
73
+ return true if yield(node)
74
+ return true if node.is_a?(Node) && node.contains?(&block)
75
+ end
76
+ false
77
+ end
78
+
79
+ # Default implementations of the common node methods.
80
+ def unwrap; self; end
81
+ def children; []; end
82
+ def statement?; false; end
83
+ def statement_only?; false; end
84
+ def top_sensitive?; false; end
85
+ end
86
+
87
+ # A collection of nodes, each one representing an expression.
88
+ class Expressions < Node
89
+ statement
90
+ children :expressions
91
+ attr_accessor :function
92
+
93
+ TRAILING_WHITESPACE = /\s+$/
94
+
95
+ # Wrap up a node as an Expressions, unless it already is.
96
+ def self.wrap(*nodes)
97
+ return nodes[0] if nodes.length == 1 && nodes[0].is_a?(Expressions)
98
+ Expressions.new(*nodes)
99
+ end
100
+
101
+ def initialize(*nodes)
102
+ @expressions = nodes.flatten
103
+ end
104
+
105
+ # Tack an expression on to the end of this expression list.
106
+ def <<(node)
107
+ @expressions << node
108
+ self
109
+ end
110
+
111
+ # Tack an expression on to the beginning of this expression list.
112
+ def unshift(node)
113
+ @expressions.unshift(node)
114
+ self
115
+ end
116
+
117
+ # If this Expressions consists of a single node, pull it back out.
118
+ def unwrap
119
+ @expressions.length == 1 ? @expressions.first : self
120
+ end
121
+
122
+ # Is this an empty block of code?
123
+ def empty?
124
+ @expressions.empty?
125
+ end
126
+
127
+ # Is the node last in this block of expressions?
128
+ def last?(node)
129
+ @last_index ||= @expressions.last.is_a?(CommentNode) ? -2 : -1
130
+ node == @expressions[@last_index]
131
+ end
132
+
133
+ def compile(o={})
134
+ o[:scope] ? super(o) : compile_root(o)
135
+ end
136
+
137
+ # Compile each expression in the Expressions body.
138
+ def compile_node(o={})
139
+ write(@expressions.map {|n| compile_expression(n, o.dup) }.join("\n"))
140
+ end
141
+
142
+ # If this is the top-level Expressions, wrap everything in a safety closure.
143
+ def compile_root(o={})
144
+ indent = o[:no_wrap] ? '' : TAB
145
+ @indent = indent
146
+ o.merge!(:indent => indent, :scope => Scope.new(nil, self, nil))
147
+ code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
148
+ code.gsub!(TRAILING_WHITESPACE, '')
149
+ write(o[:no_wrap] ? code : "// Generated by StyleScript v1.0.0\n(function(){\n#{code}\n})();")
150
+ end
151
+
152
+ # Compile the expressions body, with declarations of all inner variables
153
+ # pushed up to the top.
154
+ def compile_with_declarations(o={})
155
+ code = compile_node(o)
156
+ args = self.contains? {|n| n.is_a?(ValueNode) && n.arguments? }
157
+ argv = args && o[:scope].check('arguments') ? '' : 'var '
158
+ code = "#{idt}#{argv}arguments = Array.prototype.slice.call(arguments, 0);\n#{code}" if args
159
+ code = "#{idt}var #{o[:scope].compiled_assignments};\n#{code}" if o[:scope].assignments?(self)
160
+ code = "#{idt}var #{o[:scope].compiled_declarations};\n#{code}" if o[:scope].declarations?(self)
161
+ write(code)
162
+ end
163
+
164
+ # Compiles a single expression within the expressions body.
165
+ def compile_expression(node, o)
166
+ @indent = o[:indent]
167
+ stmt = node.statement?
168
+ # We need to return the result if this is the last node in the expressions body.
169
+ returns = o.delete(:return) && last?(node) && !node.statement_only?
170
+ # Return the regular compile of the node, unless we need to return the result.
171
+ return "#{stmt ? '' : idt}#{node.compile(o.merge(:top => true))}#{stmt ? '' : ';'}" unless returns
172
+ # If it's a statement, the node knows how to return itself.
173
+ return node.compile(o.merge(:return => true)) if node.statement?
174
+ # If it's not part of a constructor, we can just return the value of the expression.
175
+ return "#{idt}return #{node.compile(o)};" unless o[:scope].function && o[:scope].function.constructor?
176
+ # It's the last line of a constructor, add a safety check.
177
+ temp = o[:scope].free_variable
178
+ "#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:scope].function.name} === this.constructor ? this : #{temp};"
179
+ end
180
+
181
+ end
182
+
183
+ # Literals are static values that can be passed through directly into
184
+ # JavaScript without translation, eg.: strings, numbers, true, false, null...
185
+ class LiteralNode < Node
186
+ children :value
187
+
188
+ # Values of a literal node that much be treated as a statement -- no
189
+ # sense returning or assigning them.
190
+ STATEMENTS = ['break', 'continue']
191
+
192
+ # Wrap up a compiler-generated string as a LiteralNode.
193
+ def self.wrap(string)
194
+ self.new(Value.new(string))
195
+ end
196
+
197
+ def initialize(value)
198
+ @value = value
199
+ end
200
+
201
+ def statement?
202
+ STATEMENTS.include?(@value.to_s)
203
+ end
204
+ alias_method :statement_only?, :statement?
205
+
206
+ def compile_node(o)
207
+ indent = statement? ? idt : ''
208
+ ending = statement? ? ';' : ''
209
+ "#{indent}#{@value}#{ending}"
210
+ end
211
+ end
212
+
213
+ # Return an expression, or wrap it in a closure and return it.
214
+ class ReturnNode < Node
215
+ statement_only
216
+ children :expression
217
+
218
+ def initialize(expression)
219
+ @expression = expression
220
+ end
221
+
222
+ def compile_node(o)
223
+ return write(@expression.compile(o.merge(:return => true))) if @expression.statement?
224
+ compiled = @expression.compile(o)
225
+ write("#{idt}return #{compiled};")
226
+ end
227
+ end
228
+
229
+ # A value, indexed or dotted into, or vanilla.
230
+ class ValueNode < Node
231
+ children :base, :properties
232
+ attr_reader :last, :source
233
+
234
+ # Soak up undefined properties and call attempts.
235
+ SOAK = " == undefined ? undefined : "
236
+
237
+ def initialize(base, properties=[])
238
+ @base, @properties = base, [properties].flatten
239
+ end
240
+
241
+ def <<(other)
242
+ @properties << other
243
+ self
244
+ end
245
+
246
+ def properties?
247
+ return !@properties.empty? || @base.is_a?(ThisNode)
248
+ end
249
+
250
+ def array?
251
+ @base.is_a?(ArrayNode) && !properties?
252
+ end
253
+
254
+ def object?
255
+ @base.is_a?(ObjectNode) && !properties?
256
+ end
257
+
258
+ def splice?
259
+ properties? && @properties.last.is_a?(SliceNode)
260
+ end
261
+
262
+ def arguments?
263
+ @base.to_s == 'arguments'
264
+ end
265
+
266
+ def unwrap
267
+ @properties.empty? ? @base : self
268
+ end
269
+
270
+ # Values are statements if their base is a statement.
271
+ def statement?
272
+ @base.is_a?(Node) && @base.statement? && !properties?
273
+ end
274
+
275
+ def compile_node(o)
276
+ soaked = false
277
+ only = o.delete(:only_first)
278
+ props = only ? @properties[0...-1] : @properties
279
+ baseline = @base.compile(o)
280
+ parts = [baseline.dup]
281
+ props.each do |prop|
282
+ if prop.is_a?(AccessorNode) && prop.soak
283
+ soaked = true
284
+ if @base.is_a?(CallNode) && prop == props.first
285
+ temp = o[:scope].free_variable
286
+ parts[-1] = "(#{temp} = #{baseline})#{SOAK}#{baseline = temp.to_s + prop.compile(o)}"
287
+ else
288
+ parts[-1] << "#{SOAK}#{baseline += prop.compile(o)}"
289
+ end
290
+ else
291
+ part = prop.compile(o)
292
+ baseline += part
293
+ parts << part
294
+ end
295
+ end
296
+ @last = parts.last
297
+ @source = parts.length > 1 ? parts[0...-1].join('') : nil
298
+ code = parts.join('').gsub(')())', '()))')
299
+ write(soaked ? "(#{code})" : code)
300
+ end
301
+ end
302
+
303
+ # Pass through StyleScript comments into JavaScript comments at the
304
+ # same position.
305
+ class CommentNode < Node
306
+ statement
307
+
308
+ def initialize(lines)
309
+ @lines = lines.value
310
+ end
311
+
312
+ def compile_node(o={})
313
+ delimiter = "\n#{idt}//"
314
+ write("#{delimiter}#{@lines.join(delimiter)}")
315
+ end
316
+
317
+ end
318
+
319
+ # Node for a function invocation. Takes care of converting super() calls into
320
+ # calls against the prototype's function of the same name.
321
+ class CallNode < Node
322
+ children :variable, :arguments
323
+
324
+ def initialize(variable, arguments=[])
325
+ @variable, @arguments = variable, arguments
326
+ @prefix = ''
327
+ end
328
+
329
+ def new_instance
330
+ @prefix = "new "
331
+ self
332
+ end
333
+
334
+ def <<(argument)
335
+ @arguments << argument
336
+ self
337
+ end
338
+
339
+ # Compile a vanilla function call.
340
+ def compile_node(o)
341
+ return write(compile_splat(o)) if @arguments.any? {|a| a.is_a?(SplatNode) }
342
+ args = @arguments.map{|a| a.compile(o) }.join(', ')
343
+ return write(compile_super(args, o)) if @variable == 'super'
344
+ write("#{@prefix}#{@variable.compile(o)}(#{args})")
345
+ end
346
+
347
+ # Compile a call against the superclass's implementation of the current function.
348
+ def compile_super(args, o)
349
+ methname = o[:scope].function.name
350
+ arg_part = args.empty? ? '' : ", #{args}"
351
+ meth = o[:scope].function.proto ?
352
+ "#{o[:scope].function.proto}.__superClass__.#{methname}" :
353
+ "#{methname}.__superClass__.constructor"
354
+ "#{meth}.call(this#{arg_part})"
355
+ end
356
+
357
+ # Compile a function call being passed variable arguments.
358
+ def compile_splat(o)
359
+ meth = @variable.compile(o)
360
+ obj = @variable.source || 'this'
361
+ args = @arguments.map do |arg|
362
+ code = arg.compile(o)
363
+ code = arg.is_a?(SplatNode) ? code : "[#{code}]"
364
+ arg.equal?(@arguments.first) ? code : ".concat(#{code})"
365
+ end
366
+ "#{@prefix}#{meth}.apply(#{obj}, #{args.join('')})"
367
+ end
368
+
369
+ # If the code generation wished to use the result of a function call
370
+ # in multiple places, ensure that the function is only ever called once.
371
+ def compile_reference(o)
372
+ reference = o[:scope].free_variable
373
+ call = ParentheticalNode.new(AssignNode.new(reference, self))
374
+ return call, reference
375
+ end
376
+ end
377
+
378
+ # Node to extend an object's prototype with an ancestor object.
379
+ # After goog.inherits from the Closure Library.
380
+ class ExtendsNode < Node
381
+ children :sub_object, :super_object
382
+ statement
383
+
384
+ def initialize(sub_object, super_object)
385
+ @sub_object, @super_object = sub_object, super_object
386
+ end
387
+
388
+ # Hooking one constructor into another's prototype chain.
389
+ def compile_node(o={})
390
+ constructor = o[:scope].free_variable
391
+ sub, sup = @sub_object.compile(o), @super_object.compile(o)
392
+ "#{idt}#{constructor} = function(){};\n#{idt}" +
393
+ "#{constructor}.prototype = #{sup}.prototype;\n#{idt}" +
394
+ "#{sub}.__superClass__ = #{sup}.prototype;\n#{idt}" +
395
+ "#{sub}.prototype = new #{constructor}();\n#{idt}" +
396
+ "#{sub}.prototype.constructor = #{sub};"
397
+ end
398
+
399
+ end
400
+
401
+ # A dotted accessor into a part of a value, or the :: shorthand for
402
+ # an accessor into the object's prototype.
403
+ class AccessorNode < Node
404
+ children :name
405
+ attr_reader :soak
406
+
407
+ def initialize(name, tag=nil)
408
+ @name = name
409
+ @prototype = tag == :prototype
410
+ @soak = tag == :soak
411
+ end
412
+
413
+ def compile_node(o)
414
+ proto = @prototype ? "prototype." : ''
415
+ write(".#{proto}#{@name}")
416
+ end
417
+ end
418
+
419
+ # An indexed accessor into a part of an array or object.
420
+ class IndexNode < Node
421
+ children :index
422
+
423
+ def initialize(index)
424
+ @index = index
425
+ end
426
+
427
+ def compile_node(o)
428
+ write("[#{@index.compile(o)}]")
429
+ end
430
+ end
431
+
432
+ # A this-reference, using '@'.
433
+ class ThisNode < Node
434
+ def initialize(property=nil)
435
+ @property = property
436
+ end
437
+
438
+ def compile_node(o)
439
+ prop = @property ? ".#{@property}" : ''
440
+ write("this#{prop}")
441
+ end
442
+ end
443
+
444
+ # A range literal. Ranges can be used to extract portions (slices) of arrays,
445
+ # or to specify a range for array comprehensions.
446
+ class RangeNode < Node
447
+ children :from, :to
448
+
449
+ def initialize(from, to, exclusive=false)
450
+ @from, @to, @exclusive = from, to, exclusive
451
+ end
452
+
453
+ def exclusive?
454
+ @exclusive
455
+ end
456
+
457
+ def compile_variables(o)
458
+ @indent = o[:indent]
459
+ @from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
460
+ from_val, to_val = @from.compile(o), @to.compile(o)
461
+ write("#{@from_var} = #{from_val}; #{@to_var} = #{to_val};\n#{idt}")
462
+ end
463
+
464
+ def compile_node(o)
465
+ return compile_array(o) unless o[:index]
466
+ idx, step = o.delete(:index), o.delete(:step)
467
+ vars = "#{idx}=#{@from_var}"
468
+ step = step ? step.compile(o) : '1'
469
+ equals = @exclusive ? '' : '='
470
+ compare = "(#{@from_var} <= #{@to_var} ? #{idx} <#{equals} #{@to_var} : #{idx} >#{equals} #{@to_var})"
471
+ incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
472
+ write("#{vars}; #{compare}; #{incr}")
473
+ end
474
+
475
+ # Expand the range into the equivalent array, if it's not being used as
476
+ # part of a comprehension, slice, or splice.
477
+ # TODO: This generates pretty ugly code ... shrink it.
478
+ def compile_array(o)
479
+ body = Expressions.wrap(LiteralNode.wrap('i'))
480
+ arr = Expressions.wrap(ForNode.new(body, {:source => ValueNode.new(self)}, Value.new('i')))
481
+ ParentheticalNode.new(CallNode.new(CodeNode.new([], arr))).compile(o)
482
+ end
483
+
484
+ end
485
+
486
+ # An array slice literal. Unlike JavaScript's Array#slice, the second parameter
487
+ # specifies the index of the end of the slice (just like the first parameter)
488
+ # is the index of the beginning.
489
+ class SliceNode < Node
490
+ children :range
491
+
492
+ def initialize(range)
493
+ @range = range
494
+ end
495
+
496
+ def compile_node(o)
497
+ from = @range.from.compile(o)
498
+ to = @range.to.compile(o)
499
+ plus_part = @range.exclusive? ? '' : ' + 1'
500
+ write(".slice(#{from}, #{to}#{plus_part})")
501
+ end
502
+ end
503
+
504
+ # Setting the value of a local variable, or the value of an object property.
505
+ class AssignNode < Node
506
+ top_sensitive
507
+ children :variable, :value
508
+
509
+ PROTO_ASSIGN = /\A(\S+)\.prototype/
510
+ LEADING_DOT = /\A\.(prototype\.)?/
511
+
512
+ def initialize(variable, value, context=nil)
513
+ @variable, @value, @context = variable, value, context
514
+ end
515
+
516
+ def compile_node(o)
517
+ top = o.delete(:top)
518
+ return compile_pattern_match(o) if statement?
519
+ return compile_splice(o) if value? && @variable.splice?
520
+ stmt = o.delete(:as_statement)
521
+ name = @variable.compile(o)
522
+ last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
523
+ proto = name[PROTO_ASSIGN, 1]
524
+ if @value.is_a?(CodeNode)
525
+ @value.name = last if last.match(Lexer::IDENTIFIER)
526
+ @value.proto = proto if proto
527
+ end
528
+ return write("#{name}: #{@value.compile(o)}") if @context == :object
529
+ o[:scope].find(name) unless value? && @variable.properties?
530
+ val = "#{name} = #{@value.compile(o)}"
531
+ return write("#{idt}#{val};") if stmt
532
+ val = "(#{val})" if !top || o[:return]
533
+ val = "#{idt}return #{val}" if o[:return]
534
+ write(val)
535
+ end
536
+
537
+ def value?
538
+ @variable.is_a?(ValueNode)
539
+ end
540
+
541
+ def statement?
542
+ value? && (@variable.array? || @variable.object?)
543
+ end
544
+
545
+ # Implementation of recursive pattern matching, when assigning array or
546
+ # object literals to a value. Peeks at their properties to assign inner names.
547
+ # See: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring
548
+ def compile_pattern_match(o)
549
+ val_var = o[:scope].free_variable
550
+ assigns = ["#{idt}#{val_var} = #{@value.compile(o)};"]
551
+ o.merge!(:top => true, :as_statement => true)
552
+ @variable.base.objects.each_with_index do |obj, i|
553
+ obj, i = obj.value, obj.variable.base if @variable.object?
554
+ access_class = @variable.array? ? IndexNode : AccessorNode
555
+ if obj.is_a?(SplatNode)
556
+ val = LiteralNode.wrap(obj.compile_value(o, val_var, @variable.base.objects.index(obj)))
557
+ else
558
+ val = ValueNode.new(val_var, [access_class.new(Value.new(i.to_s))])
559
+ end
560
+ assigns << AssignNode.new(obj, val).compile(o)
561
+ end
562
+ write(assigns.join("\n"))
563
+ end
564
+
565
+ def compile_splice(o)
566
+ var = @variable.compile(o.merge(:only_first => true))
567
+ range = @variable.properties.last.range
568
+ plus = range.exclusive? ? '' : ' + 1'
569
+ from = range.from.compile(o)
570
+ to = "#{range.to.compile(o)} - #{from}#{plus}"
571
+ write("#{var}.splice.apply(#{var}, [#{from}, #{to}].concat(#{@value.compile(o)}))")
572
+ end
573
+ end
574
+
575
+ # Simple Arithmetic and logical operations. Performs some conversion from
576
+ # StyleScript operations into their JavaScript equivalents.
577
+ class OpNode < Node
578
+ children :first, :second
579
+ attr_reader :operator
580
+ attr_accessor :second
581
+
582
+ CONVERSIONS = {
583
+ :== => "===",
584
+ :'!=' => "!==",
585
+ :and => '&&',
586
+ :or => '||',
587
+ :is => '===',
588
+ :isnt => "!==",
589
+ :not => '!'
590
+ }
591
+ CHAINABLE = [:<, :>, :>=, :<=, :===, :'!===']
592
+ ASSIGNMENT = [:'||=', :'&&=', :'?=']
593
+ PREFIX_OPERATORS = [:typeof, :delete]
594
+
595
+ def initialize(operator, first, second=nil, flip=false)
596
+ @first, @second, @flip = first, second, flip
597
+ @operator = CONVERSIONS[operator.to_sym] || operator
598
+ end
599
+
600
+ def unary?
601
+ @second.nil?
602
+ end
603
+
604
+ def chainable?
605
+ CHAINABLE.include?(operator.to_sym)
606
+ end
607
+
608
+ def compile_node(o)
609
+ return write(compile_chain(o)) if chainable? && @first.unwrap.is_a?(OpNode) && @first.unwrap.chainable?
610
+ return write(compile_assignment(o)) if ASSIGNMENT.include?(@operator.to_sym)
611
+ return write(compile_unary(o)) if unary?
612
+ return write(compile_existence(o)) if @operator == '?'
613
+ write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
614
+ end
615
+
616
+ # Mimic Python's chained comparisons. See:
617
+ # http://docs.python.org/reference/expressions.html#notin
618
+ def compile_chain(o)
619
+ shared = @first.unwrap.second
620
+ @first.second, shared = *shared.compile_reference(o) if shared.is_a?(CallNode)
621
+ "(#{@first.compile(o)}) && (#{shared.compile(o)} #{@operator} #{@second.compile(o)})"
622
+ end
623
+
624
+ def compile_assignment(o)
625
+ first, second = @first.compile(o), @second.compile(o)
626
+ o[:scope].find(first) if @first.unwrap.is_a?(Value)
627
+ sym = @operator[0..1]
628
+ return "#{first} = #{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}" if @operator == '?='
629
+ "#{first} = #{first} #{sym} #{second}"
630
+ end
631
+
632
+ def compile_existence(o)
633
+ first, second = @first.compile(o), @second.compile(o)
634
+ "#{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}"
635
+ end
636
+
637
+ def compile_unary(o)
638
+ space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : ''
639
+ parts = [@operator.to_s, space, @first.compile(o)]
640
+ parts.reverse! if @flip
641
+ parts.join('')
642
+ end
643
+ end
644
+
645
+ # A function definition. The only node that creates a new Scope.
646
+ # A CodeNode does not have any children -- they're within the new scope.
647
+ class CodeNode < Node
648
+ top_sensitive
649
+ attr_reader :params, :body, :bound
650
+ attr_accessor :name, :proto
651
+
652
+ # Constructor functions start with an uppercase letter, by convention.
653
+ UPPERCASE = /[A-Z]/
654
+
655
+ def initialize(params, body, tag=nil)
656
+ @params = params
657
+ @body = body
658
+ @bound = tag == :boundfunc
659
+ end
660
+
661
+ def constructor?
662
+ @name && @name[0..0][UPPERCASE]
663
+ end
664
+
665
+ def compile_node(o)
666
+ shared_scope = o.delete(:shared_scope)
667
+ top = o.delete(:top)
668
+ o[:scope] = shared_scope || Scope.new(o[:scope], @body, self)
669
+ o[:return] = true
670
+ o[:top] = true
671
+ o[:indent] = idt(@bound ? 2 : 1)
672
+ o.delete(:no_wrap)
673
+ o.delete(:globals)
674
+ if @params.last.is_a?(SplatNode)
675
+ splat = @params.pop
676
+ splat.index = @params.length
677
+ @body.unshift(splat)
678
+ end
679
+ @params.each {|id| o[:scope].parameter(id.to_s) }
680
+ code = @body.empty? ? "" : "\n#{@body.compile_with_declarations(o)}\n"
681
+ name_part = @name ? " #{@name}" : ''
682
+ func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt(@bound ? 1 : 0)}}"
683
+ func = "(#{func})" if top && !@bound
684
+ return write(func) unless @bound
685
+ inner = "// Generated by StyleScript v1.0.0\n(function#{name_part}() {\n#{idt(2)}return __func.apply(__this, arguments);\n#{idt(1)}});"
686
+ write("// Generated by StyleScript v1.0.0\n(function(__this) {\n#{idt(1)}var __func = #{func};\n#{idt(1)}return #{inner}\n#{idt}})(this)")
687
+ end
688
+ end
689
+
690
+ # A splat, either as a parameter to a function, an argument to a call,
691
+ # or in a destructuring assignment.
692
+ class SplatNode < Node
693
+ children :name
694
+ attr_accessor :index
695
+
696
+ def initialize(name)
697
+ @name = name
698
+ end
699
+
700
+ def compile_node(o={})
701
+ write(@index ? compile_param(o) : @name.compile(o))
702
+ end
703
+
704
+ def compile_param(o)
705
+ o[:scope].find(@name)
706
+ "#{@name} = Array.prototype.slice.call(arguments, #{@index})"
707
+ end
708
+
709
+ def compile_value(o, name, index)
710
+ "Array.prototype.slice.call(#{name}, #{index})"
711
+ end
712
+
713
+ end
714
+
715
+ # An object literal.
716
+ class ObjectNode < Node
717
+ children :properties
718
+ alias_method :objects, :properties
719
+
720
+ def initialize(properties = [])
721
+ @properties = properties
722
+ end
723
+
724
+ # All the mucking about with commas is to make sure that CommentNodes and
725
+ # AssignNodes get interleaved correctly, with no trailing commas or
726
+ # commas affixed to comments. TODO: Extract this and add it to ArrayNode.
727
+ def compile_node(o)
728
+ o[:indent] = idt(1)
729
+ joins = Hash.new("\n")
730
+ non_comments = @properties.select {|p| !p.is_a?(CommentNode) }
731
+ non_comments.each {|p| joins[p] = p == non_comments.last ? "\n" : ",\n" }
732
+ props = @properties.map { |prop|
733
+ join = joins[prop]
734
+ join = '' if prop == @properties.last
735
+ indent = prop.is_a?(CommentNode) ? '' : idt(1)
736
+ "#{indent}#{prop.compile(o)}#{join}"
737
+ }.join('')
738
+ write("{\n#{props}\n#{idt}}")
739
+ end
740
+ end
741
+
742
+ # An array literal.
743
+ class ArrayNode < Node
744
+ children :objects
745
+
746
+ def initialize(objects=[])
747
+ @objects = objects
748
+ end
749
+
750
+ def compile_node(o)
751
+ o[:indent] = idt(1)
752
+ objects = @objects.map { |obj|
753
+ code = obj.compile(o)
754
+ obj.is_a?(CommentNode) ? "\n#{code}\n#{o[:indent]}" :
755
+ obj == @objects.last ? code : "#{code}, "
756
+ }.join('')
757
+ ending = objects.include?("\n") ? "\n#{idt}]" : ']'
758
+ write("[#{objects}#{ending}")
759
+ end
760
+ end
761
+
762
+ # A faux-node that is never created by the grammar, but is used during
763
+ # code generation to generate a quick "array.push(value)" tree of nodes.
764
+ class PushNode
765
+ def self.wrap(array, expressions)
766
+ expr = expressions.unwrap
767
+ return expressions if expr.statement_only? || expr.contains? {|n| n.statement_only? }
768
+ Expressions.wrap(CallNode.new(
769
+ ValueNode.new(LiteralNode.new(array), [AccessorNode.new(Value.new('push'))]),
770
+ [expr]
771
+ ))
772
+ end
773
+ end
774
+
775
+ # A faux-node used to wrap an expressions body in a closure.
776
+ class ClosureNode
777
+ def self.wrap(expressions, statement=false)
778
+ func = ParentheticalNode.new(CodeNode.new([], Expressions.wrap(expressions)))
779
+ call = CallNode.new(ValueNode.new(func, AccessorNode.new(Value.new('call'))), [Value.new('this')])
780
+ statement ? Expressions.wrap(call) : call
781
+ end
782
+ end
783
+
784
+ # A while loop, the only sort of low-level loop exposed by StyleScript. From
785
+ # it, all other loops can be manufactured.
786
+ class WhileNode < Node
787
+ top_sensitive
788
+ children :condition, :body
789
+ statement
790
+
791
+ def initialize(condition, body)
792
+ @condition, @body = condition, body
793
+ end
794
+
795
+ def compile_node(o)
796
+ returns = o.delete(:return)
797
+ top = o.delete(:top) && !returns
798
+ o[:indent] = idt(1)
799
+ o[:top] = true
800
+ cond = @condition.compile(o)
801
+ set = ''
802
+ if !top
803
+ rvar = o[:scope].free_variable
804
+ set = "#{idt}#{rvar} = [];\n"
805
+ @body = PushNode.wrap(rvar, @body)
806
+ end
807
+ post = returns ? "\n#{idt}return #{rvar};" : ''
808
+ return write("#{set}#{idt}while (#{cond}) null;#{post}") if @body.nil?
809
+ write("#{set}#{idt}while (#{cond}) {\n#{@body.compile(o)}\n#{idt}}#{post}")
810
+ end
811
+ end
812
+
813
+ # A until loop, the only sort of low-level loop exposed by StyleScript. From
814
+ # it, all other loops can be manufactured.
815
+ class UntilNode < Node
816
+ top_sensitive
817
+ children :condition, :body
818
+ statement
819
+
820
+ def initialize(condition, body)
821
+ @condition, @body = condition, body
822
+ end
823
+
824
+ def compile_node(o)
825
+ returns = o.delete(:return)
826
+ top = o.delete(:top) && !returns
827
+ o[:indent] = idt(1)
828
+ o[:top] = true
829
+ cond = @condition.compile(o)
830
+ set = ''
831
+ if !top
832
+ rvar = o[:scope].free_variable
833
+ set = "#{idt}#{rvar} = [];\n"
834
+ @body = PushNode.wrap(rvar, @body)
835
+ end
836
+ post = returns ? "\n#{idt}return #{rvar};" : ''
837
+ return write("#{set}#{idt}while (!(#{cond})) null;#{post}") if @body.nil?
838
+ write("#{set}#{idt}while (!(#{cond})) {\n#{@body.compile(o)}\n#{idt}}#{post}")
839
+ end
840
+ end
841
+
842
+ # The replacement for the for loop is an array comprehension (that compiles)
843
+ # into a for loop. Also acts as an expression, able to return the result
844
+ # of the comprehenion. Unlike Python array comprehensions, it's able to pass
845
+ # the current index of the loop as a second parameter.
846
+ class ForNode < Node
847
+ top_sensitive
848
+ children :body, :source, :filter
849
+ attr_reader :name, :index, :step
850
+ statement
851
+
852
+ def initialize(body, source, name, index=nil)
853
+ @body, @name, @index = body, name, index
854
+ @source = source[:source]
855
+ @filter = source[:filter]
856
+ @step = source[:step]
857
+ @object = !!source[:object]
858
+ @name, @index = @index, @name if @object
859
+ end
860
+
861
+ def compile_node(o)
862
+ top_level = o.delete(:top) && !o[:return]
863
+ range = @source.is_a?(ValueNode) && @source.base.is_a?(RangeNode) && @source.properties.empty?
864
+ source = range ? @source.base : @source
865
+ scope = o[:scope]
866
+ name_found = @name && scope.find(@name)
867
+ index_found = @index && scope.find(@index)
868
+ body_dent = idt(1)
869
+ rvar = scope.free_variable unless top_level
870
+ svar = scope.free_variable
871
+ ivar = range ? name : @index ? @index : scope.free_variable
872
+ var_part = ''
873
+ body = Expressions.wrap(@body)
874
+ if range
875
+ index_var = scope.free_variable
876
+ source_part = source.compile_variables(o)
877
+ for_part = "#{index_var}=0, #{source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
878
+ else
879
+ index_var = nil
880
+ source_part = "#{svar} = #{@source.compile(o)};\n#{idt}"
881
+ step_part = @step ? "#{ivar} += #{@step.compile(o)}" : "#{ivar}++"
882
+ for_part = @object ? "#{ivar} in #{svar}" : "#{ivar} = 0; #{ivar} < #{svar}.length; #{step_part}"
883
+ var_part = "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" if @name
884
+ # body.unshift(AssignNode.new(@name, ValueNode.new(svar, [IndexNode.new(ivar)]))) if @name
885
+ end
886
+ set_result = rvar ? "#{idt}#{rvar} = []; " : idt
887
+ return_result = rvar || ''
888
+ body = ClosureNode.wrap(body, true) if top_level && contains? {|n| n.is_a? CodeNode }
889
+ body = PushNode.wrap(rvar, body) unless top_level
890
+ if o[:return]
891
+ return_result = "return #{return_result}" if o[:return]
892
+ o.delete(:return)
893
+ body = IfNode.new(@filter, body, nil, :statement => true) if @filter
894
+ elsif @filter
895
+ body = Expressions.wrap(IfNode.new(@filter, body))
896
+ end
897
+ if @object
898
+ o[:scope].assign("__hasProp", "Object.prototype.hasOwnProperty", true)
899
+ body = Expressions.wrap(IfNode.new(
900
+ CallNode.new(
901
+ ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]),
902
+ [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]
903
+ ),
904
+ Expressions.wrap(body), nil, {:statement => true}
905
+ ))
906
+ end
907
+
908
+ return_result = "\n#{idt}#{return_result};" unless top_level
909
+ body = body.compile(o.merge(:indent => body_dent, :top => true))
910
+ vars = range ? @name : "#{@name}, #{ivar}"
911
+ return write(set_result + source_part + "for (#{for_part}) {\n#{var_part}#{body}\n#{idt}}\n#{idt}#{return_result}")
912
+ end
913
+ end
914
+
915
+ # A try/catch/finally block.
916
+ class TryNode < Node
917
+ children :try, :recovery, :finally
918
+ attr_reader :error
919
+ statement
920
+
921
+ def initialize(try, error, recovery, finally=nil)
922
+ @try, @error, @recovery, @finally = try, error, recovery, finally
923
+ end
924
+
925
+ def compile_node(o)
926
+ o[:indent] = idt(1)
927
+ o[:top] = true
928
+ error_part = @error ? " (#{@error}) " : ' '
929
+ catch_part = @recovery && " catch#{error_part}{\n#{@recovery.compile(o)}\n#{idt}}"
930
+ finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:return => nil))}\n#{idt}}"
931
+ write("#{idt}try {\n#{@try.compile(o)}\n#{idt}}#{catch_part}#{finally_part}")
932
+ end
933
+ end
934
+
935
+ # Throw an exception.
936
+ class ThrowNode < Node
937
+ children :expression
938
+ statement_only
939
+
940
+ def initialize(expression)
941
+ @expression = expression
942
+ end
943
+
944
+ def compile_node(o)
945
+ write("#{idt}throw #{@expression.compile(o)};")
946
+ end
947
+ end
948
+
949
+ # Check an expression for existence (meaning not null or undefined).
950
+ class ExistenceNode < Node
951
+ children :expression
952
+
953
+ def self.compile_test(o, variable)
954
+ first, second = variable, variable
955
+ first, second = *variable.compile_reference(o) if variable.is_a?(CallNode)
956
+ "(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)"
957
+ end
958
+
959
+ def initialize(expression)
960
+ @expression = expression
961
+ end
962
+
963
+ def compile_node(o)
964
+ write(ExistenceNode.compile_test(o, @expression))
965
+ end
966
+ end
967
+
968
+ # An extra set of parentheses, supplied by the script source.
969
+ # You can't wrap parentheses around bits that get compiled into JS statements,
970
+ # unfortunately.
971
+ class ParentheticalNode < Node
972
+ children :expressions
973
+
974
+ def initialize(expressions, line=nil)
975
+ @expressions = expressions.unwrap
976
+ @line = line
977
+ end
978
+
979
+ def compile_node(o)
980
+ compiled = @expressions.compile(o)
981
+ compiled = compiled[0...-1] if compiled[-1..-1] == ';'
982
+ write("(#{compiled})")
983
+ end
984
+ end
985
+
986
+ # If/else statements. Switch/whens get compiled into these. Acts as an
987
+ # expression by pushing down requested returns to the expression bodies.
988
+ # Single-expression IfNodes are compiled into ternary operators if possible,
989
+ # because ternaries are first-class returnable assignable expressions.
990
+ class IfNode < Node
991
+ children :condition, :body, :else_body
992
+
993
+ def initialize(condition, body, else_body=nil, tags={})
994
+ @condition = condition
995
+ @body = body && body.unwrap
996
+ @else_body = else_body && else_body.unwrap
997
+ @tags = tags
998
+ @multiple = true if @condition.is_a?(Array)
999
+ @condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert]
1000
+ end
1001
+
1002
+ def <<(else_body)
1003
+ eb = else_body.unwrap
1004
+ @else_body ? @else_body << eb : @else_body = eb
1005
+ self
1006
+ end
1007
+
1008
+ def add_comment(comment)
1009
+ @comment = comment
1010
+ self
1011
+ end
1012
+
1013
+ def force_statement
1014
+ @tags[:statement] = true
1015
+ self
1016
+ end
1017
+
1018
+ # Rewrite a chain of IfNodes with their switch condition for equality.
1019
+ def rewrite_condition(expression)
1020
+ @condition = @multiple ? @condition.map {|c| OpNode.new("is", expression, c) } :
1021
+ OpNode.new("is", expression, @condition)
1022
+ @else_body.rewrite_condition(expression) if chain?
1023
+ self
1024
+ end
1025
+
1026
+ # Rewrite a chain of IfNodes to add a default case as the final else.
1027
+ def add_else(exprs)
1028
+ chain? ? @else_body.add_else(exprs) : @else_body = (exprs && exprs.unwrap)
1029
+ self
1030
+ end
1031
+
1032
+ # If the else_body is an IfNode itself, then we've got an if-else chain.
1033
+ def chain?
1034
+ @chain ||= @else_body && @else_body.is_a?(IfNode)
1035
+ end
1036
+
1037
+ # The IfNode only compiles into a statement if either of the bodies needs
1038
+ # to be a statement.
1039
+ def statement?
1040
+ @is_statement ||= !!(@comment || @tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
1041
+ end
1042
+
1043
+ def compile_condition(o)
1044
+ [@condition].flatten.map {|c| c.compile(o) }.join(' || ')
1045
+ end
1046
+
1047
+ def compile_node(o)
1048
+ write(statement? ? compile_statement(o) : compile_ternary(o))
1049
+ end
1050
+
1051
+ # Compile the IfNode as a regular if-else statement. Flattened chains
1052
+ # force sub-else bodies into statement form.
1053
+ def compile_statement(o)
1054
+ child = o.delete(:chain_child)
1055
+ cond_o = o.dup
1056
+ cond_o.delete(:return)
1057
+ o[:indent] = idt(1)
1058
+ o[:top] = true
1059
+ if_dent = child ? '' : idt
1060
+ com_dent = child ? idt : ''
1061
+ prefix = @comment ? @comment.compile(cond_o) + "\n#{com_dent}" : ''
1062
+ body = Expressions.wrap(@body).compile(o)
1063
+ if_part = "#{prefix}#{if_dent}if (#{compile_condition(cond_o)}) {\n#{body}\n#{idt}}"
1064
+ return if_part unless @else_body
1065
+ else_part = chain? ?
1066
+ " else #{@else_body.compile(o.merge(:indent => idt, :chain_child => true))}" :
1067
+ " else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{idt}}"
1068
+ if_part + else_part
1069
+ end
1070
+
1071
+ # Compile the IfNode into a ternary operator.
1072
+ def compile_ternary(o)
1073
+ if_part = "#{@condition.compile(o)} ? #{@body.compile(o)}"
1074
+ else_part = @else_body ? "#{@else_body.compile(o)}" : 'null'
1075
+ "#{if_part} : #{else_part}"
1076
+ end
1077
+ end
1078
+
1079
+ end