nscript 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,932 @@
1
+ module NScript
2
+
3
+ class Node
4
+ TAB = ' '
5
+
6
+ def self.statement
7
+ class_eval "def statement?; true; end"
8
+ end
9
+
10
+ def self.statement_only
11
+ statement
12
+ class_eval "def statement_only?; true; end"
13
+ end
14
+
15
+ def self.top_sensitive
16
+ class_eval "def top_sensitive?; true; end"
17
+ end
18
+
19
+ def self.children(*attributes)
20
+ attr_reader(*attributes)
21
+ attrs = attributes.map {|a| "[@#{a}]" }.join(', ')
22
+ class_eval "def children; [#{attrs}].flatten.compact; end"
23
+ end
24
+
25
+ def write(code)
26
+ puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE']
27
+ code
28
+ end
29
+
30
+ def compile(o={})
31
+ @options = o.dup
32
+ @indent = o[:indent]
33
+ top = self.top_sensitive? ? @options[:top] : @options.delete(:top)
34
+ closure = statement? && !statement_only? && !top && !@options[:return] && !self.is_a?(CommentNode)
35
+ closure &&= !contains? {|n| n.statement_only? }
36
+ closure ? compile_closure(@options) : compile_node(@options)
37
+ end
38
+
39
+ def compile_closure(o={})
40
+ @indent = o[:indent]
41
+ ClosureNode.wrap(self).compile(o.merge(:shared_scope => o[:scope]))
42
+ end
43
+
44
+ def idt(tabs=0)
45
+ @indent + (TAB * tabs)
46
+ end
47
+
48
+ def contains?(&block)
49
+ children.each do |node|
50
+ return true if yield(node)
51
+ return true if node.is_a?(Node) && node.contains?(&block)
52
+ end
53
+ false
54
+ end
55
+
56
+ def unwrap; self; end
57
+ def children; []; end
58
+ def statement?; false; end
59
+ def statement_only?; false; end
60
+ def top_sensitive?; false; end
61
+ end
62
+
63
+ class Expressions < Node
64
+ statement
65
+ children :expressions
66
+ attr_accessor :function
67
+
68
+ TRAILING_WHITESPACE = /\s+$/
69
+
70
+ def self.wrap(*nodes)
71
+ return nodes[0] if nodes.length == 1 && nodes[0].is_a?(Expressions)
72
+ Expressions.new(*nodes)
73
+ end
74
+
75
+ def initialize(*nodes)
76
+ @expressions = nodes.flatten
77
+ end
78
+
79
+ def <<(node)
80
+ @expressions << node
81
+ self
82
+ end
83
+
84
+ def unshift(node)
85
+ @expressions.unshift(node)
86
+ self
87
+ end
88
+
89
+ def unwrap
90
+ @expressions.length == 1 ? @expressions.first : self
91
+ end
92
+
93
+ def empty?
94
+ @expressions.empty?
95
+ end
96
+
97
+ def last?(node)
98
+ @last_index ||= @expressions.last.is_a?(CommentNode) ? -2 : -1
99
+ node == @expressions[@last_index]
100
+ end
101
+
102
+ def compile(o={})
103
+ o[:scope] ? super(o) : compile_root(o)
104
+ end
105
+
106
+ def compile_node(options={})
107
+ write(@expressions.map {|n| compile_expression(n, options.dup) }.join("\n"))
108
+ end
109
+
110
+ def compile_root(o={})
111
+ indent = o[:no_wrap] ? '' : TAB
112
+ @indent = indent
113
+ o.merge!(:indent => indent, :scope => Scope.new(nil, self, nil))
114
+ code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
115
+ code.gsub!(TRAILING_WHITESPACE, '')
116
+ write(o[:no_wrap] ? code : "(function(){\n#{code}\n})();")
117
+ end
118
+
119
+ def compile_with_declarations(o={})
120
+ code = compile_node(o)
121
+ args = self.contains? {|n| n.is_a?(ValueNode) && n.arguments? }
122
+ argv = args && o[:scope].check('arguments') ? '' : 'var '
123
+ code = "#{idt}#{argv}arguments = Array.prototype.slice.call(arguments, 0);\n#{code}" if args
124
+ code = "#{idt}var #{o[:scope].compiled_assignments};\n#{code}" if o[:scope].assignments?(self)
125
+ code = "#{idt}var #{o[:scope].compiled_declarations};\n#{code}" if o[:scope].declarations?(self)
126
+ write(code)
127
+ end
128
+
129
+ def compile_expression(node, o)
130
+ @indent = o[:indent]
131
+ stmt = node.statement?
132
+ # We need to return the result if this is the last node in the expressions body.
133
+ returns = o.delete(:return) && last?(node) && !node.statement_only?
134
+ # Return the regular compile of the node, unless we need to return the result.
135
+ return "#{stmt ? '' : idt}#{node.compile(o.merge(:top => true))}#{stmt ? '' : ';'}" unless returns
136
+ # If it's a statement, the node knows how to return itself.
137
+ return node.compile(o.merge(:return => true)) if node.statement?
138
+ # If it's not part of a constructor, we can just return the value of the expression.
139
+ return "#{idt}return #{node.compile(o)};" unless o[:scope].function && o[:scope].function.constructor?
140
+ # It's the last line of a constructor, add a safety check.
141
+ temp = o[:scope].free_variable
142
+ "#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:scope].function.name} === this.constructor ? this : #{temp};"
143
+ end
144
+ end
145
+
146
+ class LiteralNode < Node
147
+ children :value
148
+
149
+ STATEMENTS = ['break', 'continue']
150
+
151
+ def self.wrap(string)
152
+ self.new(Value.new(string))
153
+ end
154
+
155
+ def initialize(value)
156
+ @value = value
157
+ end
158
+
159
+ def statement?
160
+ STATEMENTS.include?(@value.to_s)
161
+ end
162
+ alias_method :statement_only?, :statement?
163
+
164
+ def compile_node(o)
165
+ indent = statement? ? idt : ''
166
+ ending = statement? ? ';' : ''
167
+ "#{indent}#{@value}#{ending}"
168
+ end
169
+ end
170
+
171
+ class ReturnNode < Node
172
+ statement_only
173
+ children :expression
174
+
175
+ def initialize(expression)
176
+ @expression = expression
177
+ end
178
+
179
+ def compile_node(o)
180
+ return write(@expression.compile(o.merge(:return => true))) if @expression.statement?
181
+ compiled = @expression.compile(o)
182
+ write(@expression.statement? ? "#{compiled}\n#{idt}return null;" : "#{idt}return #{compiled};")
183
+ end
184
+ end
185
+
186
+ class CommentNode < Node
187
+ statement
188
+
189
+ def initialize(lines)
190
+ @lines = lines.value
191
+ end
192
+
193
+ def compile_node(o={})
194
+ delimiter = "\n#{idt}//"
195
+ write("#{delimiter}#{@lines.join(delimiter)}")
196
+ end
197
+
198
+ end
199
+
200
+ class CallNode < Node
201
+ children :variable, :arguments
202
+
203
+ def initialize(variable, arguments=[])
204
+ @variable, @arguments = variable, arguments
205
+ @prefix = ''
206
+ end
207
+
208
+ def new_instance
209
+ @prefix = "new "
210
+ self
211
+ end
212
+
213
+ def <<(argument)
214
+ @arguments << argument
215
+ self
216
+ end
217
+
218
+ def compile_node(o)
219
+ return write(compile_splat(o)) if @arguments.any? {|a| a.is_a?(SplatNode) }
220
+ args = @arguments.map{|a| a.compile(o) }.join(', ')
221
+ return write(compile_super(args, o)) if @variable == 'super'
222
+ write("#{@prefix}#{@variable.compile(o)}(#{args})")
223
+ end
224
+
225
+ def compile_super(args, o)
226
+ methname = o[:scope].function.name
227
+ arg_part = args.empty? ? '' : ", #{args}"
228
+ meth = o[:scope].function.proto ?
229
+ "#{o[:scope].function.proto}.__superClass__.#{methname}" :
230
+ "#{methname}.__superClass__.constructor"
231
+ "#{meth}.call(this#{arg_part})"
232
+ end
233
+
234
+ def compile_splat(o)
235
+ meth = @variable.compile(o)
236
+ obj = @variable.source || 'this'
237
+ args = @arguments.map do |arg|
238
+ code = arg.compile(o)
239
+ code = arg.is_a?(SplatNode) ? code : "[#{code}]"
240
+ arg.equal?(@arguments.first) ? code : ".concat(#{code})"
241
+ end
242
+ "#{@prefix}#{meth}.apply(#{obj}, #{args.join('')})"
243
+ end
244
+
245
+ def compile_reference(o)
246
+ reference = o[:scope].free_variable
247
+ call = ParentheticalNode.new(AssignNode.new(reference, self))
248
+ return call, reference
249
+ end
250
+ end
251
+
252
+ class ExtendsNode < Node
253
+ children :sub_object, :super_object
254
+ statement
255
+
256
+ def initialize(sub_object, super_object)
257
+ @sub_object, @super_object = sub_object, super_object
258
+ end
259
+
260
+ def compile_node(o={})
261
+ constructor = o[:scope].free_variable
262
+ sub, sup = @sub_object.compile(o), @super_object.compile(o)
263
+ "#{idt}#{constructor} = function(){};\n#{idt}" +
264
+ "#{constructor}.prototype = #{sup}.prototype;\n#{idt}" +
265
+ "#{sub}.__superClass__ = #{sup}.prototype;\n#{idt}" +
266
+ "#{sub}.prototype = new #{constructor}();\n#{idt}" +
267
+ "#{sub}.prototype.constructor = #{sub};"
268
+ end
269
+ end
270
+
271
+ class ValueNode < Node
272
+ children :base, :properties
273
+ attr_reader :last, :source
274
+
275
+ SOAK = " == undefined ? undefined : "
276
+
277
+ def initialize(base, properties=[])
278
+ @base, @properties = base, [properties].flatten
279
+ end
280
+
281
+ def <<(other)
282
+ @properties << other
283
+ self
284
+ end
285
+
286
+ def properties?
287
+ return !@properties.empty? || @base.is_a?(ThisNode)
288
+ end
289
+
290
+ def array?
291
+ @base.is_a?(ArrayNode) && !properties?
292
+ end
293
+
294
+ def object?
295
+ @base.is_a?(ObjectNode) && !properties?
296
+ end
297
+
298
+ def splice?
299
+ properties? && @properties.last.is_a?(SliceNode)
300
+ end
301
+
302
+ def arguments?
303
+ @base.to_s == 'arguments'
304
+ end
305
+
306
+ def unwrap
307
+ @properties.empty? ? @base : self
308
+ end
309
+
310
+ def statement?
311
+ @base.is_a?(Node) && @base.statement? && !properties?
312
+ end
313
+
314
+ def compile_node(o)
315
+ soaked = false
316
+ only = o.delete(:only_first)
317
+ props = only ? @properties[0...-1] : @properties
318
+ baseline = @base.compile(o)
319
+ parts = [baseline.dup]
320
+ props.each do |prop|
321
+ if prop.is_a?(AccessorNode) && prop.soak
322
+ soaked = true
323
+ if @base.is_a?(CallNode) && prop == props.first
324
+ temp = o[:scope].free_variable
325
+ parts[-1] = "(#{temp} = #{baseline})#{SOAK}#{baseline = temp.to_s + prop.compile(o)}"
326
+ else
327
+ parts[-1] << "#{SOAK}#{baseline += prop.compile(o)}"
328
+ end
329
+ else
330
+ part = prop.compile(o)
331
+ baseline += part
332
+ parts << part
333
+ end
334
+ end
335
+ @last = parts.last
336
+ @source = parts.length > 1 ? parts[0...-1].join('') : nil
337
+ code = parts.join('').gsub(')())', '()))')
338
+ write(soaked ? "(#{code})" : code)
339
+ end
340
+ end
341
+
342
+ class AccessorNode < Node
343
+ children :name
344
+ attr_reader :soak
345
+
346
+ def initialize(name, tag=nil)
347
+ @name = name
348
+ @prototype = tag == :prototype
349
+ @soak = tag == :soak
350
+ end
351
+
352
+ def compile_node(o)
353
+ proto = @prototype ? "prototype." : ''
354
+ write(".#{proto}#{@name}")
355
+ end
356
+ end
357
+
358
+ class IndexNode < Node
359
+ children :index
360
+
361
+ def initialize(index)
362
+ @index = index
363
+ end
364
+
365
+ def compile_node(o)
366
+ write("[#{@index.compile(o)}]")
367
+ end
368
+ end
369
+
370
+ class ThisNode < Node
371
+ def initialize(property=nil)
372
+ @property = property
373
+ end
374
+
375
+ def compile_node(o)
376
+ prop = @property ? ".#{@property}" : ''
377
+ write("this#{prop}")
378
+ end
379
+ end
380
+
381
+ class RangeNode < Node
382
+ children :from, :to
383
+
384
+ def initialize(from, to, exclusive=false)
385
+ @from, @to, @exclusive = from, to, exclusive
386
+ end
387
+
388
+ def exclusive?
389
+ @exclusive
390
+ end
391
+
392
+ def compile_variables(o)
393
+ @indent = o[:indent]
394
+ @from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
395
+ from_val, to_val = @from.compile(o), @to.compile(o)
396
+ write("#{@from_var} = #{from_val}; #{@to_var} = #{to_val};\n#{idt}")
397
+ end
398
+
399
+ def compile_node(o)
400
+ return compile_array(o) unless o[:index]
401
+ idx, step = o.delete(:index), o.delete(:step)
402
+ vars = "#{idx}=#{@from_var}"
403
+ step = step ? step.compile(o) : '1'
404
+ equals = @exclusive ? '' : '='
405
+ compare = "(#{@from_var} <= #{@to_var} ? #{idx} <#{equals} #{@to_var} : #{idx} >#{equals} #{@to_var})"
406
+ incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
407
+ write("#{vars}; #{compare}; #{incr}")
408
+ end
409
+
410
+ def compile_array(o)
411
+ body = Expressions.wrap(LiteralNode.wrap('i'))
412
+ arr = Expressions.wrap(ForNode.new(body, {:source => ValueNode.new(self)}, Value.new('i')))
413
+ ParentheticalNode.new(CallNode.new(CodeNode.new([], arr))).compile(o)
414
+ end
415
+ end
416
+
417
+ class SliceNode < Node
418
+ children :range
419
+
420
+ def initialize(range)
421
+ @range = range
422
+ end
423
+
424
+ def compile_node(o)
425
+ from = @range.from.compile(o)
426
+ to = @range.to.compile(o)
427
+ plus_part = @range.exclusive? ? '' : ' + 1'
428
+ write(".slice(#{from}, #{to}#{plus_part})")
429
+ end
430
+ end
431
+
432
+ class AssignNode < Node
433
+ top_sensitive
434
+ children :variable, :value
435
+
436
+ PROTO_ASSIGN = /\A(\S+)\.prototype/
437
+ LEADING_DOT = /\A\.(prototype\.)?/
438
+
439
+ def initialize(variable, value, context=nil)
440
+ @variable, @value, @context = variable, value, context
441
+ end
442
+
443
+ def compile_node(o)
444
+ top = o.delete(:top)
445
+ return compile_pattern_match(o) if statement?
446
+ return compile_splice(o) if value? && @variable.splice?
447
+ stmt = o.delete(:as_statement)
448
+ name = @variable.compile(o)
449
+ last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
450
+ proto = name[PROTO_ASSIGN, 1]
451
+ if @value.is_a?(CodeNode)
452
+ @value.name = last if last.match(Lexer::IDENTIFIER)
453
+ @value.proto = proto if proto
454
+ end
455
+ return write("#{name}: #{@value.compile(o)}") if @context == :object
456
+ o[:scope].find(name) unless value? && @variable.properties?
457
+ val = "#{name} = #{@value.compile(o)}"
458
+ return write("#{idt}#{val};") if stmt
459
+ val = "(#{val})" if !top || o[:return]
460
+ val = "#{idt}return #{val}" if o[:return]
461
+ write(val)
462
+ end
463
+
464
+ def value?
465
+ @variable.is_a?(ValueNode)
466
+ end
467
+
468
+ def statement?
469
+ value? && (@variable.array? || @variable.object?)
470
+ end
471
+
472
+ def compile_pattern_match(o)
473
+ val_var = o[:scope].free_variable
474
+ assigns = ["#{idt}#{val_var} = #{@value.compile(o)};"]
475
+ o.merge!(:top => true, :as_statement => true)
476
+ @variable.base.objects.each_with_index do |obj, i|
477
+ obj, i = obj.value, obj.variable.base if @variable.object?
478
+ access_class = @variable.array? ? IndexNode : AccessorNode
479
+ if obj.is_a?(SplatNode)
480
+ val = LiteralNode.wrap(obj.compile_value(o, val_var, @variable.base.objects.index(obj)))
481
+ else
482
+ val = ValueNode.new(val_var, [access_class.new(Value.new(i.to_s))])
483
+ end
484
+ assigns << AssignNode.new(obj, val).compile(o)
485
+ end
486
+ write(assigns.join("\n"))
487
+ end
488
+
489
+ def compile_splice(o)
490
+ var = @variable.compile(o.merge(:only_first => true))
491
+ range = @variable.properties.last.range
492
+ plus = range.exclusive? ? '' : ' + 1'
493
+ from = range.from.compile(o)
494
+ to = "#{range.to.compile(o)} - #{from}#{plus}"
495
+ write("#{var}.splice.apply(#{var}, [#{from}, #{to}].concat(#{@value.compile(o)}))")
496
+ end
497
+ end
498
+
499
+ class OpNode < Node
500
+ children :first, :second
501
+ attr_reader :operator
502
+ attr_accessor :second
503
+
504
+ CONVERSIONS = {
505
+ :== => "===",
506
+ :'!=' => "!==",
507
+ :and => '&&',
508
+ :or => '||',
509
+ :is => '===',
510
+ :isnt => "!==",
511
+ :not => '!'
512
+ }
513
+ CHAINABLE = [:<, :>, :>=, :<=, :===, :'!===']
514
+ ASSIGNMENT = [:'||=', :'&&=', :'?=']
515
+ PREFIX_OPERATORS = [:typeof, :delete]
516
+
517
+ def initialize(operator, first, second=nil, flip=false)
518
+ @first, @second, @flip = first, second, flip
519
+ @operator = CONVERSIONS[operator.to_sym] || operator
520
+ end
521
+
522
+ def unary?
523
+ @second.nil?
524
+ end
525
+
526
+ def chainable?
527
+ CHAINABLE.include?(operator.to_sym)
528
+ end
529
+
530
+ def compile_node(o)
531
+ return write(compile_chain(o)) if chainable? && @first.unwrap.is_a?(OpNode) && @first.unwrap.chainable?
532
+ return write(compile_assignment(o)) if ASSIGNMENT.include?(@operator.to_sym)
533
+ return write(compile_unary(o)) if unary?
534
+ return write(compile_existence(o)) if @operator == '?'
535
+ write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
536
+ end
537
+
538
+ def compile_chain(o)
539
+ shared = @first.unwrap.second
540
+ @first.second, shared = *shared.compile_reference(o) if shared.is_a?(CallNode)
541
+ "(#{@first.compile(o)}) && (#{shared.compile(o)} #{@operator} #{@second.compile(o)})"
542
+ end
543
+
544
+ def compile_assignment(o)
545
+ first, second = @first.compile(o), @second.compile(o)
546
+ o[:scope].find(first) if @first.unwrap.is_a?(Value)
547
+ sym = @operator[0..1]
548
+ return "#{first} = #{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}" if @operator == '?='
549
+ "#{first} = #{first} #{sym} #{second}"
550
+ end
551
+
552
+ def compile_existence(o)
553
+ first, second = @first.compile(o), @second.compile(o)
554
+ "#{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}"
555
+ end
556
+
557
+ def compile_unary(o)
558
+ space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : ''
559
+ parts = [@operator.to_s, space, @first.compile(o)]
560
+ parts.reverse! if @flip
561
+ parts.join('')
562
+ end
563
+ end
564
+
565
+ class CodeNode < Node
566
+ top_sensitive
567
+ attr_reader :params, :body, :bound
568
+ attr_accessor :name, :proto
569
+
570
+ UPPERCASE = /[A-Z]/
571
+
572
+ def initialize(params, body, tag=nil)
573
+ @params = params
574
+ @body = body
575
+ @bound = tag == :boundfunc
576
+ end
577
+
578
+ def constructor?
579
+ @name && @name[0..0][UPPERCASE]
580
+ end
581
+
582
+ def compile_node(o)
583
+ shared_scope = o.delete(:shared_scope)
584
+ top = o.delete(:top)
585
+ o[:scope] = shared_scope || Scope.new(o[:scope], @body, self)
586
+ o[:return] = true
587
+ o[:top] = true
588
+ o[:indent] = idt(@bound ? 2 : 1)
589
+ o.delete(:no_wrap)
590
+ o.delete(:globals)
591
+ if @params.last.is_a?(SplatNode)
592
+ splat = @params.pop
593
+ splat.index = @params.length
594
+ @body.unshift(splat)
595
+ end
596
+ @params.each {|id| o[:scope].parameter(id.to_s) }
597
+ code = @body.empty? ? "" : "\n#{@body.compile_with_declarations(o)}\n"
598
+ name_part = @name ? " #{@name}" : ''
599
+ func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt(@bound ? 1 : 0)}}"
600
+ func = "(#{func})" if top && !@bound
601
+ return write(func) unless @bound
602
+ inner = "(function#{name_part}() {\n#{idt(2)}return __func.apply(__this, arguments);\n#{idt(1)}});"
603
+ write("(function(__this) {\n#{idt(1)}var __func = #{func};\n#{idt(1)}return #{inner}\n#{idt}})(this)")
604
+ end
605
+ end
606
+
607
+ class SplatNode < Node
608
+ children :name
609
+ attr_accessor :index
610
+
611
+ def initialize(name)
612
+ @name = name
613
+ end
614
+
615
+ def compile_node(o={})
616
+ write(@index ? compile_param(o) : @name.compile(o))
617
+ end
618
+
619
+ def compile_param(o)
620
+ o[:scope].find(@name)
621
+ "#{@name} = Array.prototype.slice.call(arguments, #{@index})"
622
+ end
623
+
624
+ def compile_value(o, name, index)
625
+ "Array.prototype.slice.call(#{name}, #{index})"
626
+ end
627
+ end
628
+
629
+ class ObjectNode < Node
630
+ children :properties
631
+ alias_method :objects, :properties
632
+
633
+ def initialize(properties = [])
634
+ @properties = properties
635
+ end
636
+
637
+ def compile_node(o)
638
+ o[:indent] = idt(1)
639
+ joins = Hash.new("\n")
640
+ non_comments = @properties.select {|p| !p.is_a?(CommentNode) }
641
+ non_comments.each {|p| joins[p] = p == non_comments.last ? "\n" : ",\n" }
642
+ props = @properties.map { |prop|
643
+ join = joins[prop]
644
+ join = '' if prop == @properties.last
645
+ indent = prop.is_a?(CommentNode) ? '' : idt(1)
646
+ "#{indent}#{prop.compile(o)}#{join}"
647
+ }.join('')
648
+ write("{\n#{props}\n#{idt}}")
649
+ end
650
+ end
651
+
652
+ class ArrayNode < Node
653
+ children :objects
654
+
655
+ def initialize(objects=[])
656
+ @objects = objects
657
+ end
658
+
659
+ def compile_node(o)
660
+ o[:indent] = idt(1)
661
+ objects = @objects.map { |obj|
662
+ code = obj.compile(o)
663
+ obj.is_a?(CommentNode) ? "\n#{code}\n#{o[:indent]}" :
664
+ obj == @objects.last ? code : "#{code}, "
665
+ }.join('')
666
+ ending = objects.include?("\n") ? "\n#{idt}]" : ']'
667
+ write("[#{objects}#{ending}")
668
+ end
669
+ end
670
+
671
+ class PushNode
672
+ def self.wrap(array, expressions)
673
+ expr = expressions.unwrap
674
+ return expressions if expr.statement_only? || expr.contains? {|n| n.statement_only? }
675
+ Expressions.wrap(CallNode.new(
676
+ ValueNode.new(LiteralNode.new(array), [AccessorNode.new(Value.new('push'))]),
677
+ [expr]
678
+ ))
679
+ end
680
+ end
681
+
682
+ class ClosureNode
683
+ def self.wrap(expressions, statement=false)
684
+ func = ParentheticalNode.new(CodeNode.new([], Expressions.wrap(expressions)))
685
+ call = CallNode.new(ValueNode.new(func, AccessorNode.new(Value.new('call'))), [Value.new('this')])
686
+ statement ? Expressions.wrap(call) : call
687
+ end
688
+ end
689
+
690
+ class WhileNode < Node
691
+ top_sensitive
692
+ children :condition, :body
693
+ statement
694
+
695
+ def initialize(condition, body)
696
+ @condition, @body = condition, body
697
+ end
698
+
699
+ def compile_node(o)
700
+ returns = o.delete(:return)
701
+ top = o.delete(:top) && !returns
702
+ o[:indent] = idt(1)
703
+ o[:top] = true
704
+ cond = @condition.compile(o)
705
+ set = ''
706
+ if !top
707
+ rvar = o[:scope].free_variable
708
+ set = "#{idt}#{rvar} = [];\n"
709
+ @body = PushNode.wrap(rvar, @body)
710
+ end
711
+ post = returns ? "\n#{idt}return #{rvar};" : ''
712
+ return write("#{set}#{idt}while (#{cond}) null;#{post}") if @body.nil?
713
+ write("#{set}#{idt}while (#{cond}) {\n#{@body.compile(o)}\n#{idt}}#{post}")
714
+ end
715
+ end
716
+
717
+ class ForNode < Node
718
+ top_sensitive
719
+ children :body, :source, :filter
720
+ attr_reader :name, :index, :step
721
+ statement
722
+
723
+ def initialize(body, source, name, index=nil)
724
+ @body, @name, @index = body, name, index
725
+ @source = source[:source]
726
+ @filter = source[:filter]
727
+ @step = source[:step]
728
+ @object = !!source[:object]
729
+ @name, @index = @index, @name if @object
730
+ end
731
+
732
+ def compile_node(o)
733
+ top_level = o.delete(:top) && !o[:return]
734
+ range = @source.is_a?(ValueNode) && @source.base.is_a?(RangeNode) && @source.properties.empty?
735
+ source = range ? @source.base : @source
736
+ scope = o[:scope]
737
+ name_found = @name && scope.find(@name)
738
+ index_found = @index && scope.find(@index)
739
+ body_dent = idt(1)
740
+ rvar = scope.free_variable unless top_level
741
+ svar = scope.free_variable
742
+ ivar = range ? name : @index ? @index : scope.free_variable
743
+ var_part = ''
744
+ body = Expressions.wrap(@body)
745
+ if range
746
+ index_var = scope.free_variable
747
+ source_part = source.compile_variables(o)
748
+ for_part = "#{index_var}=0, #{source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
749
+ else
750
+ index_var = nil
751
+ source_part = "#{svar} = #{@source.compile(o)};\n#{idt}"
752
+ step_part = @step ? "#{ivar} += #{@step.compile(o)}" : "#{ivar}++"
753
+ for_part = @object ? "#{ivar} in #{svar}" : "#{ivar} = 0; #{ivar} < #{svar}.length; #{step_part}"
754
+ var_part = "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" if @name
755
+ # body.unshift(AssignNode.new(@name, ValueNode.new(svar, [IndexNode.new(ivar)]))) if @name
756
+ end
757
+ set_result = rvar ? "#{idt}#{rvar} = []; " : idt
758
+ return_result = rvar || ''
759
+ body = ClosureNode.wrap(body, true) if top_level && contains? {|n| n.is_a? CodeNode }
760
+ body = PushNode.wrap(rvar, body) unless top_level
761
+ if o[:return]
762
+ return_result = "return #{return_result}" if o[:return]
763
+ o.delete(:return)
764
+ body = IfNode.new(@filter, body, nil, :statement => true) if @filter
765
+ elsif @filter
766
+ body = Expressions.wrap(IfNode.new(@filter, body))
767
+ end
768
+ if @object
769
+ o[:scope].assign("__hasProp", "Object.prototype.hasOwnProperty", true)
770
+ body = Expressions.wrap(IfNode.new(
771
+ CallNode.new(
772
+ ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]),
773
+ [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]
774
+ ),
775
+ Expressions.wrap(body), nil, {:statement => true}
776
+ ))
777
+ end
778
+
779
+ return_result = "\n#{idt}#{return_result};" unless top_level
780
+ body = body.compile(o.merge(:indent => body_dent, :top => true))
781
+ vars = range ? @name : "#{@name}, #{ivar}"
782
+ return write(set_result + source_part + "for (#{for_part}) {\n#{var_part}#{body}\n#{idt}}\n#{idt}#{return_result}")
783
+ end
784
+ end
785
+
786
+ class TryNode < Node
787
+ children :try, :recovery, :finally
788
+ attr_reader :error
789
+ statement
790
+
791
+ def initialize(try, error, recovery, finally=nil)
792
+ @try, @error, @recovery, @finally = try, error, recovery, finally
793
+ end
794
+
795
+ def compile_node(o)
796
+ o[:indent] = idt(1)
797
+ o[:top] = true
798
+ error_part = @error ? " (#{@error}) " : ' '
799
+ catch_part = @recovery && " catch#{error_part}{\n#{@recovery.compile(o)}\n#{idt}}"
800
+ finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:return => nil))}\n#{idt}}"
801
+ write("#{idt}try {\n#{@try.compile(o)}\n#{idt}}#{catch_part}#{finally_part}")
802
+ end
803
+ end
804
+
805
+ class ThrowNode < Node
806
+ children :expression
807
+ statement_only
808
+
809
+ def initialize(expression)
810
+ @expression = expression
811
+ end
812
+
813
+ def compile_node(o)
814
+ write("#{idt}throw #{@expression.compile(o)};")
815
+ end
816
+ end
817
+
818
+ class ExistenceNode < Node
819
+ children :expression
820
+
821
+ def self.compile_test(o, variable)
822
+ first, second = variable, variable
823
+ first, second = *variable.compile_reference(o) if variable.is_a?(CallNode)
824
+ "(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)"
825
+ end
826
+
827
+ def initialize(expression)
828
+ @expression = expression
829
+ end
830
+
831
+ def compile_node(o)
832
+ write(ExistenceNode.compile_test(o, @expression))
833
+ end
834
+ end
835
+
836
+ class ParentheticalNode < Node
837
+ children :expressions
838
+
839
+ def initialize(expressions, line=nil)
840
+ @expressions = expressions.unwrap
841
+ @line = line
842
+ end
843
+
844
+ def compile_node(o)
845
+ compiled = @expressions.compile(o)
846
+ compiled = compiled[0...-1] if compiled[-1..-1] == ';'
847
+ write("(#{compiled})")
848
+ end
849
+ end
850
+
851
+ class IfNode < Node
852
+ children :condition, :body, :else_body
853
+
854
+ def initialize(condition, body, else_body=nil, tags={})
855
+ @condition = condition
856
+ @body = body && body.unwrap
857
+ @else_body = else_body && else_body.unwrap
858
+ @tags = tags
859
+ @multiple = true if @condition.is_a?(Array)
860
+ @condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert]
861
+ end
862
+
863
+ def <<(else_body)
864
+ eb = else_body.unwrap
865
+ @else_body ? @else_body << eb : @else_body = eb
866
+ self
867
+ end
868
+
869
+ def add_comment(comment)
870
+ @comment = comment
871
+ self
872
+ end
873
+
874
+ def force_statement
875
+ @tags[:statement] = true
876
+ self
877
+ end
878
+
879
+ def rewrite_condition(expression)
880
+ @condition = @multiple ? @condition.map {|c| OpNode.new("is", expression, c) } :
881
+ OpNode.new("is", expression, @condition)
882
+ @else_body.rewrite_condition(expression) if chain?
883
+ self
884
+ end
885
+
886
+ def add_else(exprs)
887
+ chain? ? @else_body.add_else(exprs) : @else_body = (exprs && exprs.unwrap)
888
+ self
889
+ end
890
+
891
+ def chain?
892
+ @chain ||= @else_body && @else_body.is_a?(IfNode)
893
+ end
894
+
895
+ def statement?
896
+ @is_statement ||= !!(@comment || @tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
897
+ end
898
+
899
+ def compile_condition(o)
900
+ [@condition].flatten.map {|c| c.compile(o) }.join(' || ')
901
+ end
902
+
903
+ def compile_node(o)
904
+ write(statement? ? compile_statement(o) : compile_ternary(o))
905
+ end
906
+
907
+ def compile_statement(o)
908
+ child = o.delete(:chain_child)
909
+ cond_o = o.dup
910
+ cond_o.delete(:return)
911
+ o[:indent] = idt(1)
912
+ o[:top] = true
913
+ if_dent = child ? '' : idt
914
+ com_dent = child ? idt : ''
915
+ prefix = @comment ? @comment.compile(cond_o) + "\n#{com_dent}" : ''
916
+ body = Expressions.wrap(@body).compile(o)
917
+ if_part = "#{prefix}#{if_dent}if (#{compile_condition(cond_o)}) {\n#{body}\n#{idt}}"
918
+ return if_part unless @else_body
919
+ else_part = chain? ?
920
+ " else #{@else_body.compile(o.merge(:indent => idt, :chain_child => true))}" :
921
+ " else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{idt}}"
922
+ if_part + else_part
923
+ end
924
+
925
+ def compile_ternary(o)
926
+ if_part = "#{@condition.compile(o)} ? #{@body.compile(o)}"
927
+ else_part = @else_body ? "#{@else_body.compile(o)}" : 'null'
928
+ "#{if_part} : #{else_part}"
929
+ end
930
+ end
931
+
932
+ end # end - NScript