jbarnette-johnson 1.0.0.200806240111

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +5 -0
  2. data/MANIFEST +385 -0
  3. data/MINGW32.mk +124 -0
  4. data/README.rdoc +51 -0
  5. data/Rakefile +166 -0
  6. data/bin/johnson +107 -0
  7. data/cross-compile.txt +38 -0
  8. data/ext/spidermonkey/context.c +122 -0
  9. data/ext/spidermonkey/context.h +19 -0
  10. data/ext/spidermonkey/conversions.c +286 -0
  11. data/ext/spidermonkey/conversions.h +18 -0
  12. data/ext/spidermonkey/debugger.c +208 -0
  13. data/ext/spidermonkey/debugger.h +9 -0
  14. data/ext/spidermonkey/extconf.rb +25 -0
  15. data/ext/spidermonkey/extensions.c +37 -0
  16. data/ext/spidermonkey/extensions.h +12 -0
  17. data/ext/spidermonkey/global.c +40 -0
  18. data/ext/spidermonkey/global.h +11 -0
  19. data/ext/spidermonkey/idhash.c +16 -0
  20. data/ext/spidermonkey/idhash.h +8 -0
  21. data/ext/spidermonkey/immutable_node.c.erb +522 -0
  22. data/ext/spidermonkey/immutable_node.h +22 -0
  23. data/ext/spidermonkey/jroot.h +187 -0
  24. data/ext/spidermonkey/js_land_proxy.c +609 -0
  25. data/ext/spidermonkey/js_land_proxy.h +20 -0
  26. data/ext/spidermonkey/ruby_land_proxy.c +537 -0
  27. data/ext/spidermonkey/ruby_land_proxy.h +17 -0
  28. data/ext/spidermonkey/runtime.c +304 -0
  29. data/ext/spidermonkey/runtime.h +25 -0
  30. data/ext/spidermonkey/spidermonkey.c +20 -0
  31. data/ext/spidermonkey/spidermonkey.h +29 -0
  32. data/js/johnson/browser.js +9 -0
  33. data/js/johnson/browser/env.js +687 -0
  34. data/js/johnson/browser/jquery.js +3444 -0
  35. data/js/johnson/browser/xmlsax.js +1564 -0
  36. data/js/johnson/browser/xmlw3cdom.js +4189 -0
  37. data/js/johnson/cli.js +30 -0
  38. data/js/johnson/prelude.js +80 -0
  39. data/js/johnson/template.js +29 -0
  40. data/lib/hoe.rb +748 -0
  41. data/lib/johnson.rb +46 -0
  42. data/lib/johnson/cli.rb +7 -0
  43. data/lib/johnson/cli/options.rb +56 -0
  44. data/lib/johnson/error.rb +4 -0
  45. data/lib/johnson/nodes.rb +7 -0
  46. data/lib/johnson/nodes/binary_node.rb +64 -0
  47. data/lib/johnson/nodes/for.rb +14 -0
  48. data/lib/johnson/nodes/for_in.rb +12 -0
  49. data/lib/johnson/nodes/function.rb +13 -0
  50. data/lib/johnson/nodes/list.rb +27 -0
  51. data/lib/johnson/nodes/node.rb +68 -0
  52. data/lib/johnson/nodes/ternary_node.rb +20 -0
  53. data/lib/johnson/parser.rb +21 -0
  54. data/lib/johnson/parser/syntax_error.rb +13 -0
  55. data/lib/johnson/runtime.rb +55 -0
  56. data/lib/johnson/spidermonkey/context.rb +10 -0
  57. data/lib/johnson/spidermonkey/debugger.rb +67 -0
  58. data/lib/johnson/spidermonkey/immutable_node.rb +280 -0
  59. data/lib/johnson/spidermonkey/js_land_proxy.rb +62 -0
  60. data/lib/johnson/spidermonkey/mutable_tree_visitor.rb +233 -0
  61. data/lib/johnson/spidermonkey/ruby_land_proxy.rb +52 -0
  62. data/lib/johnson/spidermonkey/runtime.rb +94 -0
  63. data/lib/johnson/version.rb +4 -0
  64. data/lib/johnson/visitable.rb +16 -0
  65. data/lib/johnson/visitors.rb +4 -0
  66. data/lib/johnson/visitors/dot_visitor.rb +167 -0
  67. data/lib/johnson/visitors/ecma_visitor.rb +315 -0
  68. data/lib/johnson/visitors/enumerating_visitor.rb +115 -0
  69. data/lib/johnson/visitors/sexp_visitor.rb +172 -0
  70. data/lib/rails/init.rb +37 -0
  71. data/test/assets/index.html +38 -0
  72. data/test/assets/jquery_test.html +186 -0
  73. data/test/helper.rb +58 -0
  74. data/test/johnson/browser_test.rb +38 -0
  75. data/test/johnson/conversions/array_test.rb +32 -0
  76. data/test/johnson/conversions/boolean_test.rb +17 -0
  77. data/test/johnson/conversions/callable_test.rb +34 -0
  78. data/test/johnson/conversions/file_test.rb +15 -0
  79. data/test/johnson/conversions/nil_test.rb +20 -0
  80. data/test/johnson/conversions/number_test.rb +34 -0
  81. data/test/johnson/conversions/regexp_test.rb +24 -0
  82. data/test/johnson/conversions/string_test.rb +26 -0
  83. data/test/johnson/conversions/struct_test.rb +15 -0
  84. data/test/johnson/conversions/symbol_test.rb +19 -0
  85. data/test/johnson/conversions/thread_test.rb +24 -0
  86. data/test/johnson/error_test.rb +9 -0
  87. data/test/johnson/extensions_test.rb +56 -0
  88. data/test/johnson/nodes/array_literal_test.rb +57 -0
  89. data/test/johnson/nodes/array_node_test.rb +26 -0
  90. data/test/johnson/nodes/binary_node_test.rb +61 -0
  91. data/test/johnson/nodes/bracket_access_test.rb +16 -0
  92. data/test/johnson/nodes/delete_test.rb +11 -0
  93. data/test/johnson/nodes/do_while_test.rb +12 -0
  94. data/test/johnson/nodes/dot_accessor_test.rb +15 -0
  95. data/test/johnson/nodes/export_test.rb +9 -0
  96. data/test/johnson/nodes/for_test.rb +54 -0
  97. data/test/johnson/nodes/function_test.rb +71 -0
  98. data/test/johnson/nodes/if_test.rb +41 -0
  99. data/test/johnson/nodes/import_test.rb +13 -0
  100. data/test/johnson/nodes/label_test.rb +19 -0
  101. data/test/johnson/nodes/object_literal_test.rb +110 -0
  102. data/test/johnson/nodes/return_test.rb +16 -0
  103. data/test/johnson/nodes/semi_test.rb +8 -0
  104. data/test/johnson/nodes/switch_test.rb +55 -0
  105. data/test/johnson/nodes/ternary_test.rb +25 -0
  106. data/test/johnson/nodes/throw_test.rb +9 -0
  107. data/test/johnson/nodes/try_node_test.rb +59 -0
  108. data/test/johnson/nodes/typeof_test.rb +11 -0
  109. data/test/johnson/nodes/unary_node_test.rb +23 -0
  110. data/test/johnson/nodes/void_test.rb +11 -0
  111. data/test/johnson/nodes/while_test.rb +26 -0
  112. data/test/johnson/nodes/with_test.rb +10 -0
  113. data/test/johnson/prelude_test.rb +56 -0
  114. data/test/johnson/runtime_test.rb +46 -0
  115. data/test/johnson/spidermonkey/context_test.rb +21 -0
  116. data/test/johnson/spidermonkey/immutable_node_test.rb +34 -0
  117. data/test/johnson/spidermonkey/js_land_proxy_test.rb +236 -0
  118. data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +225 -0
  119. data/test/johnson/spidermonkey/runtime_test.rb +17 -0
  120. data/test/johnson/version_test.rb +13 -0
  121. data/test/johnson/visitors/dot_visitor_test.rb +39 -0
  122. data/test/johnson/visitors/enumerating_visitor_test.rb +12 -0
  123. data/test/johnson_test.rb +16 -0
  124. data/test/jquery_units/test.js +27 -0
  125. data/test/jquery_units/test_helper.js +197 -0
  126. data/test/jquery_units/units/ajax.js +795 -0
  127. data/test/jquery_units/units/core.js +1563 -0
  128. data/test/jquery_units/units/event.js +299 -0
  129. data/test/jquery_units/units/fx.js +427 -0
  130. data/test/jquery_units/units/offset.js +112 -0
  131. data/test/jquery_units/units/selector.js +224 -0
  132. data/test/jspec/helper.js +7 -0
  133. data/test/jspec/jspec.js +192 -0
  134. data/test/jspec/simple_spec.js +68 -0
  135. data/test/parser_test.rb +276 -0
  136. data/todo/.keep +0 -0
  137. metadata +501 -0
@@ -0,0 +1,52 @@
1
+ module Johnson #:nodoc:
2
+ module SpiderMonkey #:nodoc:
3
+ class RubyLandProxy # native
4
+ include Enumerable
5
+
6
+ def initialize
7
+ raise Johnson::Error, "#{self.class.name} is an internal support class."
8
+ end
9
+
10
+ private :initialize
11
+
12
+ alias_method :size, :length
13
+
14
+ def to_proc
15
+ @proc ||= Proc.new { |*args| call(*args) }
16
+ end
17
+
18
+ def call(*args)
19
+ call_using(runtime.global, *args)
20
+ end
21
+
22
+ def call_using(this, *args)
23
+ native_call(this, *args)
24
+ end
25
+
26
+ def inspect
27
+ toString
28
+ end
29
+
30
+ def method_missing(sym, *args, &block)
31
+ args << block if block_given?
32
+
33
+ name = sym.to_s
34
+ assignment = "=" == name[-1, 1]
35
+
36
+ # default behavior if the slot's not there
37
+ return super unless assignment || respond_to?(sym)
38
+
39
+ unless function_property?(name)
40
+ # for arity 0, treat it as a get
41
+ return self[name] if args.empty?
42
+
43
+ # arity 1 and quacking like an assignment, treat it as a set
44
+ return self[name[0..-2]] = args[0] if assignment && 1 == args.size
45
+ end
46
+
47
+ # okay, must really be a function
48
+ call_function_property(name, *args)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,94 @@
1
+ module Johnson #:nodoc:
2
+ module SpiderMonkey #:nodoc:
3
+ class Runtime # native
4
+ CONTEXT_MAP_KEY = :johnson_context_map
5
+
6
+ def initialize(options={})
7
+ @debugger = nil
8
+ @compiled_scripts = {}
9
+ @gcthings = {}
10
+ initialize_native(options)
11
+ self["Ruby"] = Object
12
+ end
13
+
14
+ # called from js_land_proxy.c:make_js_land_proxy
15
+ def add_gcthing(thing)
16
+ @gcthings[thing.object_id] = thing
17
+ end
18
+
19
+ # called from js_land_proxy.c:finalize
20
+ def remove_gcthing(thing)
21
+ @gcthings.delete(thing.object_id)
22
+ end
23
+
24
+
25
+ def current_context
26
+ contexts = (Thread.current[CONTEXT_MAP_KEY] ||= {})
27
+ contexts[self.object_id] ||= Context.new(self)
28
+ end
29
+
30
+ def [](key)
31
+ global[key]
32
+ end
33
+
34
+ def []=(key, value)
35
+ global[key] = value
36
+ end
37
+
38
+ ###
39
+ # Evaluate +script+ with +filename+ and +linenum+
40
+ def evaluate(script, filename = nil, linenum = nil)
41
+ compiled_script = compile(script, filename, linenum)
42
+ evaluate_compiled_script(compiled_script)
43
+ end
44
+
45
+ ###
46
+ # Compile +script+ with +filename+ and +linenum+
47
+ def compile(script, filename=nil, linenum=nil)
48
+ filename ||= 'none'
49
+ linenum ||= 1
50
+ @compiled_scripts[filename] = native_compile(script, filename, linenum)
51
+ end
52
+
53
+ ###
54
+ # Yield to +block+ in +filename+ at +linenum+
55
+ def break(filename, linenum, &block)
56
+ raise "#{filename} has not been compiled" unless @compiled_scripts.key?(filename)
57
+
58
+ compiled_script = @compiled_scripts[filename]
59
+ set_trap(compiled_script, linenum, block)
60
+ end
61
+
62
+ class << self
63
+ def raise_js_exception(jsex)
64
+ raise jsex if Exception === jsex
65
+ raise Johnson::Error.new(jsex.to_s) unless Johnson::SpiderMonkey::RubyLandProxy === jsex
66
+
67
+ stack = jsex.stack rescue nil
68
+
69
+ message = jsex['message'] || jsex.to_s
70
+ at = "(#{jsex['fileName']}):#{jsex['lineNumber']}"
71
+ ex = Johnson::Error.new("#{message} at #{at}")
72
+ if stack
73
+ js_caller = stack.split("\n").find_all { |x| x != '@:0' }
74
+ ex.set_backtrace(js_caller + caller)
75
+ else
76
+ ex.set_backtrace(caller)
77
+ end
78
+
79
+ raise ex
80
+ end
81
+ end
82
+
83
+ private
84
+ # Called by SpiderMonkey's garbage collector to determine whether or
85
+ # not it should GC
86
+ def should_sm_gc?
87
+ return false if Thread.list.find_all { |t|
88
+ t.key?(CONTEXT_MAP_KEY)
89
+ }.length > 1
90
+ true
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,4 @@
1
+ module Johnson #:nodoc:
2
+ # FIXME: Don't add the timestamp for "release" versions
3
+ VERSION = "1.0.0.#{Time.now.strftime("%Y%m%d%H%M")}".freeze
4
+ end
@@ -0,0 +1,16 @@
1
+ module Johnson
2
+ module Visitable
3
+ # Based off the visitor pattern from RubyGarden
4
+ def accept(visitor, &block)
5
+ klass = self.class.ancestors.find { |ancestor|
6
+ visitor.respond_to?("visit_#{ancestor.name.split(/::/)[-1]}")
7
+ }
8
+
9
+ if klass
10
+ visitor.send(:"visit_#{klass.name.split(/::/)[-1]}", self, &block)
11
+ else
12
+ raise "No visitor for '#{self.class}' at line #{line}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ require 'johnson/visitors/sexp_visitor'
2
+ require 'johnson/visitors/ecma_visitor'
3
+ require 'johnson/visitors/dot_visitor'
4
+ require 'johnson/visitors/enumerating_visitor'
@@ -0,0 +1,167 @@
1
+ module Johnson
2
+ module Visitors
3
+ class DotVisitor
4
+ attr_accessor :nodes, :links
5
+
6
+ ESCAPE = /([<>"\\])/
7
+
8
+ class Node < Struct.new(:node_id, :label, :fields)
9
+ def to_s
10
+ f = fields.map { |field|
11
+ field.to_s.gsub(ESCAPE, '\\\\\1').gsub(/[\r\n]/, ' ')
12
+ }.join('\n')
13
+ f = "\\n#{f}" if f.length > 0
14
+ "\"#{node_id}\" [ label = \"#{label}#{f}\" ];"
15
+ end
16
+ end
17
+ class Link < Struct.new(:from, :to, :attributes)
18
+ def to_s
19
+ attrs = ''
20
+ if attributes
21
+ attrs = " [" + attributes.map { |attribute|
22
+ attribute.join(' = ')
23
+ }.join("; ") + "]"
24
+ end
25
+ "\"#{from}\" -> \"#{to}\"#{attrs};"
26
+ end
27
+ end
28
+
29
+ def initialize
30
+ @nodes = []
31
+ @links = []
32
+ @color = 'lightblue2'
33
+ @style = 'filled'
34
+ yield self if block_given?
35
+ end
36
+
37
+ def to_s
38
+ "digraph parsetree {\n" +
39
+ " node [color=#{@color}, style=#{@style}];\n" +
40
+ @nodes.map { |n| n.to_s }.join("\n") +
41
+ @links.map { |n| n.to_s }.join("\n") +
42
+ "\n}"
43
+ end
44
+
45
+ def visit_SourceElements(o)
46
+ @nodes << Node.new(o.object_id, 'SourceElements', [])
47
+ o.value.each { |x|
48
+ @links << Link.new(o.object_id, x.object_id)
49
+ x.accept(self)
50
+ }
51
+ end
52
+
53
+ %w{
54
+ VarStatement
55
+ Comma
56
+ ObjectLiteral
57
+ ArrayLiteral
58
+ New
59
+ FunctionCall
60
+ Import
61
+ Export
62
+ }.each do |type|
63
+ define_method(:"visit_#{type}") do |o|
64
+ @nodes << Node.new(o.object_id, type, [])
65
+ o.value.each { |x|
66
+ @links << Link.new(o.object_id, x.object_id)
67
+ x.accept(self)
68
+ }
69
+ end
70
+ end
71
+
72
+ %w{ Name Number Regexp String }.each do |type|
73
+ define_method(:"visit_#{type}") do |o|
74
+ @nodes << Node.new(o.object_id, type, [o.value])
75
+ end
76
+ end
77
+
78
+ %w{ Break Continue False Null This True }.each do |type|
79
+ define_method(:"visit_#{type}") do |o|
80
+ @nodes << Node.new(o.object_id, type, [])
81
+ end
82
+ end
83
+
84
+ def visit_Try(o)
85
+ @nodes << Node.new(o.object_id, 'Try', [])
86
+ @links << Link.new(o.object_id, o.cond.object_id, {'label' => 'cond' })
87
+ o.cond.accept(self)
88
+ if o.b_then
89
+ o.b_then.each { |x|
90
+ @links << Link.new(o.object_id, x.object_id, {'label' => 'b_then' })
91
+ x.accept(self)
92
+ }
93
+ end
94
+ if x = o.b_else
95
+ @links << Link.new(o.object_id, x.object_id, {'label' => 'b_else' })
96
+ x.accept(self)
97
+ end
98
+ end
99
+
100
+ {
101
+ 'For' => [:init, :cond, :update, :body],
102
+ 'ForIn' => [:in_cond, :body],
103
+ 'Ternary' => [:cond, :b_then, :b_else],
104
+ 'If' => [:cond, :b_then, :b_else],
105
+ 'Catch' => [:cond, :b_then, :b_else],
106
+ }.each do |type,attrs|
107
+ define_method(:"visit_#{type}") do |o|
108
+ @nodes << Node.new(o.object_id, type, [])
109
+ attrs.each do |method|
110
+ if x = o.send(method)
111
+ @links << Link.new(o.object_id, x.object_id, {'label' => method })
112
+ x.accept(self)
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ ### UNARY NODES ###
119
+ %w{
120
+ BitwiseNot Delete Not Parenthesis PostfixDecrement PostfixIncrement
121
+ PrefixDecrement PrefixIncrement Return Throw Typeof UnaryNegative
122
+ UnaryPositive Void
123
+ }.each do |node|
124
+ define_method(:"visit_#{node}") do |o|
125
+ @nodes << Node.new(o.object_id, node, [])
126
+ if x = o.value
127
+ @links << Link.new(o.object_id, x.object_id)
128
+ x.accept(self)
129
+ end
130
+ end
131
+ end
132
+
133
+ ### FUNCTION NODES ###
134
+ def visit_Function(o)
135
+ @nodes << Node.new(o.object_id, 'Function', [o.arguments.join(', ')])
136
+ @links << Link.new(o.object_id, o.body.object_id, { 'label' => 'body' })
137
+ o.body.accept(self)
138
+ end
139
+
140
+ ### BINARY NODES ###
141
+ %w{
142
+ And AssignExpr BracketAccess Case Default DoWhile DotAccessor
143
+ Equal GetterProperty GreaterThan GreaterThanOrEqual In
144
+ InstanceOf Label LessThan LessThanOrEqual NotEqual OpAdd OpAddEqual
145
+ OpBitAnd OpBitAndEqual OpBitOr OpBitOrEqual OpBitXor OpBitXorEqual
146
+ OpDivide OpDivideEqual OpEqual OpLShift OpLShiftEqual OpMod
147
+ OpModEqual OpMultiply OpMultiplyEqual OpRShift OpRShiftEqual
148
+ OpSubtract OpSubtractEqual OpURShift OpURShiftEqual Or Property
149
+ SetterProperty StrictEqual StrictNotEqual Switch While With
150
+ }.each do |node|
151
+ define_method(:"visit_#{node}") do |o|
152
+ @nodes << Node.new(o.object_id, node, [])
153
+ [:left, :right].each do |side|
154
+ if x = o.send(side)
155
+ @links << Link.new(o.object_id, x.object_id, { 'label' => side })
156
+ x.accept(self)
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ def accept(target)
163
+ target.accept(self)
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,315 @@
1
+ module Johnson
2
+ module Visitors
3
+ class EcmaVisitor
4
+ def initialize
5
+ @depth = 0
6
+ end
7
+
8
+ def visit_SourceElements(o)
9
+ newline = o.value.length > 0 ? "\n" : ' '
10
+ (@depth == 0 ? '' : "{#{newline}") +
11
+ indent {
12
+ o.value.map { |x|
13
+ code = x.accept(self)
14
+ semi = case x
15
+ when Nodes::Function, Nodes::While, Nodes::If, Nodes::Try, Nodes::Switch, Nodes::Case, Nodes::Default, Nodes::For, Nodes::ForIn
16
+ code =~ /\}\Z/ ? '' : ';'
17
+ else
18
+ ';'
19
+ end
20
+ "#{indent}#{code}#{semi}"
21
+ }.join("\n")
22
+ } +
23
+ (@depth == 0 ? '' : "#{newline}}")
24
+ end
25
+
26
+ def visit_For(o)
27
+ "for(#{o.init ? o.init.accept(self) : ' '};" \
28
+ " #{o.cond && o.cond.accept(self)};" \
29
+ " #{o.update && o.update.accept(self)}) #{o.body.accept(self)}"
30
+ end
31
+
32
+ def visit_ForIn(o)
33
+ "for(#{o.in_cond.accept(self)}) #{o.body.accept(self)}"
34
+ end
35
+
36
+ def visit_Ternary(o)
37
+ "#{o.cond.accept(self)} ? #{o.b_then.accept(self)} : " \
38
+ "#{o.b_else.accept(self)}"
39
+ end
40
+
41
+ def visit_VarStatement(o)
42
+ "var #{o.value.map { |x| x.accept(self) }.join(', ')}"
43
+ end
44
+
45
+ def visit_ArrayLiteral(o)
46
+ "[#{o.value.map { |x| x.accept(self) }.join(', ')}]"
47
+ end
48
+
49
+ def visit_New(o)
50
+ rest = o.value.slice(1..-1)
51
+ "new #{o.value.first.accept(self)}"\
52
+ "(#{rest && rest.map { |x| x.accept(self) }.join(', ')})"
53
+ end
54
+
55
+ def visit_FunctionCall(o)
56
+ rest = o.value.slice(1..-1)
57
+ stmt =
58
+ if o.value.first.is_a?(Nodes::Function)
59
+ "(#{o.value.first.accept(self)})"
60
+ else
61
+ "#{o.value.first.accept(self)}"
62
+ end
63
+ "#{stmt}(#{rest && rest.map { |x| x.accept(self) }.join(', ')})"
64
+ end
65
+
66
+ def visit_Comma(o)
67
+ "#{o.value.map { |x| x.accept(self) }.join(', ') }"
68
+ end
69
+
70
+ %w{ Name Number Regexp }.each do |type|
71
+ define_method(:"visit_#{type}") do |o|
72
+ o.value
73
+ end
74
+ end
75
+
76
+ def visit_If(o)
77
+ semi = ''
78
+ semi = ';' if o.b_else && !o.b_then.is_a?(Nodes::SourceElements)
79
+
80
+ stmt = "if(#{o.cond.accept(self)}) #{o.b_then.accept(self)}#{semi}"
81
+ stmt += " else #{o.b_else.accept(self)}" if o.b_else
82
+ stmt
83
+ end
84
+
85
+ def visit_Function(o)
86
+ "function#{o.name && ' '}#{o.name}(#{o.arguments.join(', ')}) #{o.body.accept(self)}"
87
+ end
88
+
89
+ def visit_String(o)
90
+ h = {
91
+ "\b" => '\b',
92
+ "\t" => '\t',
93
+ "\n" => '\n',
94
+ "\f" => '\f',
95
+ "\r" => '\r',
96
+ }
97
+ "\"#{o.value.gsub(/[\\]/, '\\\\\\').gsub(/"/, '\"').gsub(/[\b\t\n\f\r]/) { |m| h[m] }}\""
98
+ end
99
+
100
+ {
101
+ 'Break' => 'break',
102
+ 'Continue' => 'continue',
103
+ 'Null' => 'null',
104
+ 'True' => 'true',
105
+ 'False' => 'false',
106
+ 'This' => 'this',
107
+ }.each do |type,sym|
108
+ define_method(:"visit_#{type}") do |o|
109
+ sym
110
+ end
111
+ end
112
+
113
+ def visit_BracketAccess(o)
114
+ "#{o.left.accept(self)}[#{o.right.accept(self)}]"
115
+ end
116
+
117
+ def visit_DoWhile(o)
118
+ semi = o.left.is_a?(Nodes::SourceElements) ? '' : ';'
119
+ "do #{o.left.accept(self)}#{semi} while(#{o.right.accept(self)})"
120
+ end
121
+
122
+ def visit_Try(o)
123
+ stmt = "try #{o.cond.accept(self)}"
124
+ o.b_then.each do |node|
125
+ stmt << " #{node.accept(self)}"
126
+ end if o.b_then
127
+ stmt << "#{o.b_else && ' finally '}" \
128
+ "#{o.b_else && o.b_else.accept(self)}" if o.b_else
129
+ stmt
130
+ end
131
+
132
+ def visit_Catch(o)
133
+ "catch(#{o.cond.accept(self)}) #{o.b_else.accept(self)}"
134
+ end
135
+
136
+ def visit_Delete(o)
137
+ "delete #{o.value.accept(self)}"
138
+ end
139
+
140
+ def visit_Export(o)
141
+ "export #{o.value.map { |x| x.accept(self) }.join(', ')}"
142
+ end
143
+
144
+ def visit_Import(o)
145
+ "import #{o.value.map { |x| x.accept(self) }.join(', ')}"
146
+ end
147
+
148
+ def visit_Throw(o)
149
+ "throw #{o.value.accept(self)}"
150
+ end
151
+
152
+ def visit_Void(o)
153
+ "void #{o.value.accept(self)}"
154
+ end
155
+
156
+ def visit_Return(o)
157
+ "return#{o.value && ' '}#{o.value && o.value.accept(self)}"
158
+ end
159
+
160
+ def visit_Typeof(o)
161
+ "typeof #{o.value.accept(self)}"
162
+ end
163
+
164
+ {
165
+ 'UnaryPositive' => '+',
166
+ 'UnaryNegative' => '-',
167
+ 'BitwiseNot' => '~',
168
+ 'Not' => '!',
169
+ }.each do |type,op|
170
+ define_method(:"visit_#{type}") do |o|
171
+ "#{op}#{o.value.accept(self)}"
172
+ end
173
+ end
174
+
175
+ def visit_Parenthesis(o)
176
+ "(#{o.value.accept(self)})"
177
+ end
178
+
179
+ def visit_While(o)
180
+ "while(#{o.left.accept(self)}) #{o.right.accept(self)}"
181
+ end
182
+
183
+ def visit_With(o)
184
+ "with(#{o.left.accept(self)}) #{o.right.accept(self)}"
185
+ end
186
+
187
+ def visit_Switch(o)
188
+ "switch(#{o.left.accept(self)}) #{o.right.accept(self)}"
189
+ end
190
+
191
+ def visit_Case(o)
192
+ "case #{o.left.accept(self)}: #{o.right.accept(self)}"
193
+ end
194
+
195
+ def visit_Default(o)
196
+ "default: #{o.right.accept(self)}"
197
+ end
198
+
199
+ def visit_Label(o)
200
+ "#{o.left.accept(self)}: #{o.right.accept(self)}"
201
+ end
202
+ alias :visit_Property :visit_Label
203
+
204
+ def visit_DotAccessor(o)
205
+ stmt =
206
+ if o.right.is_a?(Nodes::Function)
207
+ "(#{o.right.accept(self)})"
208
+ else
209
+ "#{o.right.accept(self)}"
210
+ end
211
+
212
+ rhs = o.left.accept(self)
213
+ if rhs =~ /\A\w+$/
214
+ stmt << ".#{rhs}"
215
+ else
216
+ stmt << "['#{rhs}']"
217
+ end
218
+ stmt
219
+ end
220
+
221
+ def visit_GetterProperty(o)
222
+ "get #{o.left.accept(self)}#{o.right.accept(self).gsub(/function/, '')}"
223
+ end
224
+
225
+ def visit_SetterProperty(o)
226
+ "set #{o.left.accept(self)}#{o.right.accept(self).gsub(/function/, '')}"
227
+ end
228
+
229
+ def visit_ObjectLiteral(o)
230
+ indent {
231
+ "{ #{o.value.map { |x| x.accept(self) }.join(",\n#{indent}")} }"
232
+ }
233
+ end
234
+
235
+ {
236
+ 'PostfixIncrement' => '++',
237
+ 'PostfixDecrement' => '--',
238
+ }.each do |type,op|
239
+ define_method(:"visit_#{type}") do |o|
240
+ "#{o.value.accept(self)}#{op}"
241
+ end
242
+ end
243
+
244
+ {
245
+ 'PrefixIncrement' => '++',
246
+ 'PrefixDecrement' => '--',
247
+ }.each do |type,op|
248
+ define_method(:"visit_#{type}") do |o|
249
+ "#{op}#{o.value.accept(self)}"
250
+ end
251
+ end
252
+
253
+ {
254
+ 'OpEqual' => '=',
255
+ 'StrictNotEqual' => '!==',
256
+ 'StrictEqual' => '===',
257
+ 'Or' => '||',
258
+ 'OpURShift' => '>>>',
259
+ 'OpURShiftEqual' => '>>>=',
260
+ 'OpSubtract' => '-',
261
+ 'OpSubtractEqual' => '-=',
262
+ 'OpRShift' => '>>',
263
+ 'OpRShiftEqual' => '>>=',
264
+ 'OpMultiply' => '*',
265
+ 'OpMultiplyEqual' => '*=',
266
+ 'OpMod' => '%',
267
+ 'OpModEqual' => '%=',
268
+ 'OpLShift' => '<<',
269
+ 'OpLShiftEqual' => '<<=',
270
+ 'OpDivide' => '/',
271
+ 'OpDivideEqual' => '/=',
272
+ 'OpBitXor' => '^',
273
+ 'OpBitXorEqual' => '^=',
274
+ 'OpBitOr' => '|',
275
+ 'OpBitOrEqual' => '|=',
276
+ 'OpBitAnd' => '&',
277
+ 'OpBitAndEqual' => '&=',
278
+ 'OpAdd' => '+',
279
+ 'OpAddEqual' => '+=',
280
+ 'NotEqual' => '!=',
281
+ 'LessThan' => '<',
282
+ 'LessThanOrEqual' => '<=',
283
+ 'GreaterThan' => '>',
284
+ 'GreaterThanOrEqual' => '>=',
285
+ 'And' => '&&',
286
+ 'InstanceOf' => 'instanceof',
287
+ 'In' => 'in',
288
+ 'Equal' => '==',
289
+ 'AssignExpr' => '=',
290
+ }.each do |type,op|
291
+ define_method(:"visit_#{type}") do |o|
292
+ "#{o.left && o.left.accept(self)}" \
293
+ " #{op} " \
294
+ "#{o.right && o.right.accept(self)}"
295
+ end
296
+ end
297
+
298
+ def accept(target)
299
+ target.accept(self)
300
+ end
301
+
302
+ private
303
+ def indent
304
+ if block_given?
305
+ @depth += 1
306
+ x = yield
307
+ @depth -= 1
308
+ x
309
+ else
310
+ ' ' * (@depth - 1) * 2
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end