jbarnette-johnson 1.0.0.200806240111

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