rkelly_for_reef 1.0.8

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 (202) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +26 -0
  4. data/Manifest.txt +199 -0
  5. data/README.rdoc +58 -0
  6. data/Rakefile +39 -0
  7. data/lib/parser.y +871 -0
  8. data/lib/rkelly.rb +14 -0
  9. data/lib/rkelly/constants.rb +3 -0
  10. data/lib/rkelly/generated_parser.rb +3274 -0
  11. data/lib/rkelly/js.rb +14 -0
  12. data/lib/rkelly/js/array.rb +15 -0
  13. data/lib/rkelly/js/base.rb +91 -0
  14. data/lib/rkelly/js/boolean.rb +21 -0
  15. data/lib/rkelly/js/function.rb +39 -0
  16. data/lib/rkelly/js/function_prototype.rb +15 -0
  17. data/lib/rkelly/js/global_object.rb +52 -0
  18. data/lib/rkelly/js/math.rb +10 -0
  19. data/lib/rkelly/js/nan.rb +18 -0
  20. data/lib/rkelly/js/number.rb +22 -0
  21. data/lib/rkelly/js/object.rb +30 -0
  22. data/lib/rkelly/js/object_prototype.rb +14 -0
  23. data/lib/rkelly/js/property.rb +20 -0
  24. data/lib/rkelly/js/scope.rb +6 -0
  25. data/lib/rkelly/js/string.rb +21 -0
  26. data/lib/rkelly/lexeme.rb +18 -0
  27. data/lib/rkelly/nodes.rb +25 -0
  28. data/lib/rkelly/nodes/binary_node.rb +18 -0
  29. data/lib/rkelly/nodes/bracket_accessor_node.rb +11 -0
  30. data/lib/rkelly/nodes/case_clause_node.rb +11 -0
  31. data/lib/rkelly/nodes/comma_node.rb +11 -0
  32. data/lib/rkelly/nodes/conditional_node.rb +11 -0
  33. data/lib/rkelly/nodes/dot_accessor_node.rb +11 -0
  34. data/lib/rkelly/nodes/for_in_node.rb +12 -0
  35. data/lib/rkelly/nodes/for_node.rb +13 -0
  36. data/lib/rkelly/nodes/function_call_node.rb +16 -0
  37. data/lib/rkelly/nodes/function_decl_node.rb +6 -0
  38. data/lib/rkelly/nodes/function_expr_node.rb +12 -0
  39. data/lib/rkelly/nodes/if_node.rb +12 -0
  40. data/lib/rkelly/nodes/label_node.rb +11 -0
  41. data/lib/rkelly/nodes/new_expr_node.rb +11 -0
  42. data/lib/rkelly/nodes/node.rb +88 -0
  43. data/lib/rkelly/nodes/not_strict_equal_node.rb +6 -0
  44. data/lib/rkelly/nodes/op_equal_node.rb +16 -0
  45. data/lib/rkelly/nodes/postfix_node.rb +11 -0
  46. data/lib/rkelly/nodes/prefix_node.rb +6 -0
  47. data/lib/rkelly/nodes/property_node.rb +13 -0
  48. data/lib/rkelly/nodes/resolve_node.rb +19 -0
  49. data/lib/rkelly/nodes/strict_equal_node.rb +6 -0
  50. data/lib/rkelly/nodes/try_node.rb +13 -0
  51. data/lib/rkelly/nodes/var_decl_node.rb +15 -0
  52. data/lib/rkelly/parser.rb +104 -0
  53. data/lib/rkelly/runtime.rb +36 -0
  54. data/lib/rkelly/runtime/ruby_function.rb +13 -0
  55. data/lib/rkelly/runtime/scope_chain.rb +57 -0
  56. data/lib/rkelly/syntax_error.rb +4 -0
  57. data/lib/rkelly/token.rb +19 -0
  58. data/lib/rkelly/tokenizer.rb +161 -0
  59. data/lib/rkelly/visitable.rb +16 -0
  60. data/lib/rkelly/visitors.rb +4 -0
  61. data/lib/rkelly/visitors/dot_visitor.rb +228 -0
  62. data/lib/rkelly/visitors/ecma_visitor.rb +322 -0
  63. data/lib/rkelly/visitors/enumerable_visitor.rb +18 -0
  64. data/lib/rkelly/visitors/evaluation_visitor.rb +419 -0
  65. data/lib/rkelly/visitors/function_visitor.rb +46 -0
  66. data/lib/rkelly/visitors/pointcut_visitor.rb +31 -0
  67. data/lib/rkelly/visitors/real_sexp_visitor.rb +16 -0
  68. data/lib/rkelly/visitors/sexp_visitor.rb +373 -0
  69. data/lib/rkelly/visitors/visitor.rb +136 -0
  70. data/test/ecma_script_test_case.rb +21 -0
  71. data/test/execute_test_case.rb +16 -0
  72. data/test/execution_contexts/test_10_1_3-1.rb +32 -0
  73. data/test/expressions/test_11_3_1.rb +64 -0
  74. data/test/expressions/test_11_3_2.rb +64 -0
  75. data/test/expressions/test_11_4_2.rb +13 -0
  76. data/test/expressions/test_11_4_3.rb +52 -0
  77. data/test/expressions/test_11_4_4.rb +68 -0
  78. data/test/expressions/test_11_4_5.rb +69 -0
  79. data/test/expressions/test_11_4_6.rb +88 -0
  80. data/test/expressions/test_11_4_8.rb +28 -0
  81. data/test/expressions/test_11_4_9.rb +103 -0
  82. data/test/expressions/test_11_5_1.rb +51 -0
  83. data/test/expressions/test_11_5_2.rb +80 -0
  84. data/test/expressions/test_11_5_3.rb +88 -0
  85. data/test/expressions/test_11_6_1-1.rb +19 -0
  86. data/test/expressions/test_11_9_1.rb +19 -0
  87. data/test/function/test_15_3_1_1-1.rb +34 -0
  88. data/test/global_object/test_15_1_1_1.rb +29 -0
  89. data/test/global_object/test_15_1_1_2.rb +17 -0
  90. data/test/global_object/test_15_1_1_3.rb +9 -0
  91. data/test/helper.rb +5 -0
  92. data/test/node_test_case.rb +11 -0
  93. data/test/object/test_15_2_1_1.rb +257 -0
  94. data/test/object/test_15_2_1_2.rb +21 -0
  95. data/test/object/test_15_2_2_1.rb +52 -0
  96. data/test/statements/test_12_5-1.rb +27 -0
  97. data/test/test_add_node.rb +8 -0
  98. data/test/test_arguments_node.rb +8 -0
  99. data/test/test_array_node.rb +9 -0
  100. data/test/test_assign_expr_node.rb +8 -0
  101. data/test/test_automatic_semicolon_insertion.rb +137 -0
  102. data/test/test_bit_and_node.rb +8 -0
  103. data/test/test_bit_or_node.rb +8 -0
  104. data/test/test_bit_x_or_node.rb +8 -0
  105. data/test/test_bitwise_not_node.rb +8 -0
  106. data/test/test_block_node.rb +14 -0
  107. data/test/test_bracket_accessor_node.rb +16 -0
  108. data/test/test_break_node.rb +11 -0
  109. data/test/test_case_block_node.rb +11 -0
  110. data/test/test_case_clause_node.rb +15 -0
  111. data/test/test_comma_node.rb +13 -0
  112. data/test/test_comments.rb +44 -0
  113. data/test/test_conditional_node.rb +17 -0
  114. data/test/test_const_statement_node.rb +14 -0
  115. data/test/test_continue_node.rb +11 -0
  116. data/test/test_delete_node.rb +8 -0
  117. data/test/test_divide_node.rb +8 -0
  118. data/test/test_do_while_node.rb +13 -0
  119. data/test/test_dot_accessor_node.rb +9 -0
  120. data/test/test_ecma_visitor.rb +210 -0
  121. data/test/test_element_node.rb +8 -0
  122. data/test/test_empty_statement_node.rb +8 -0
  123. data/test/test_equal_node.rb +8 -0
  124. data/test/test_evaluation_visitor.rb +66 -0
  125. data/test/test_expression_statement_node.rb +10 -0
  126. data/test/test_false_node.rb +8 -0
  127. data/test/test_for_in_node.rb +17 -0
  128. data/test/test_for_node.rb +24 -0
  129. data/test/test_function_body_node.rb +8 -0
  130. data/test/test_function_call_node.rb +10 -0
  131. data/test/test_function_decl_node.rb +16 -0
  132. data/test/test_function_expr_node.rb +16 -0
  133. data/test/test_function_visitor.rb +26 -0
  134. data/test/test_getter_property_node.rb +10 -0
  135. data/test/test_global_object.rb +49 -0
  136. data/test/test_greater_node.rb +8 -0
  137. data/test/test_greater_or_equal_node.rb +8 -0
  138. data/test/test_if_node.rb +17 -0
  139. data/test/test_in_node.rb +8 -0
  140. data/test/test_instance_of_node.rb +8 -0
  141. data/test/test_label_node.rb +13 -0
  142. data/test/test_left_shift_node.rb +8 -0
  143. data/test/test_less_node.rb +8 -0
  144. data/test/test_less_or_equal_node.rb +8 -0
  145. data/test/test_line_number.rb +23 -0
  146. data/test/test_logical_and_node.rb +8 -0
  147. data/test/test_logical_not_node.rb +8 -0
  148. data/test/test_logical_or_node.rb +8 -0
  149. data/test/test_modulus_node.rb +8 -0
  150. data/test/test_multiply_node.rb +8 -0
  151. data/test/test_new_expr_node.rb +9 -0
  152. data/test/test_not_equal_node.rb +8 -0
  153. data/test/test_not_strict_equal_node.rb +8 -0
  154. data/test/test_null_node.rb +8 -0
  155. data/test/test_number_node.rb +8 -0
  156. data/test/test_object_literal_node.rb +9 -0
  157. data/test/test_op_and_equal_node.rb +10 -0
  158. data/test/test_op_divide_equal_node.rb +10 -0
  159. data/test/test_op_equal_node.rb +10 -0
  160. data/test/test_op_l_shift_equal_node.rb +10 -0
  161. data/test/test_op_minus_equal_node.rb +10 -0
  162. data/test/test_op_mod_equal_node.rb +10 -0
  163. data/test/test_op_multiply_equal_node.rb +10 -0
  164. data/test/test_op_or_equal_node.rb +10 -0
  165. data/test/test_op_plus_equal_node.rb +10 -0
  166. data/test/test_op_r_shift_equal_node.rb +10 -0
  167. data/test/test_op_u_r_shift_equal_node.rb +10 -0
  168. data/test/test_op_x_or_equal_node.rb +10 -0
  169. data/test/test_parameter_node.rb +8 -0
  170. data/test/test_parser.rb +1361 -0
  171. data/test/test_pointcut_visitor.rb +34 -0
  172. data/test/test_postfix_node.rb +8 -0
  173. data/test/test_prefix_node.rb +8 -0
  174. data/test/test_property_node.rb +8 -0
  175. data/test/test_regexp_node.rb +8 -0
  176. data/test/test_resolve_node.rb +22 -0
  177. data/test/test_return_node.rb +11 -0
  178. data/test/test_right_shift_node.rb +8 -0
  179. data/test/test_rkelly.rb +19 -0
  180. data/test/test_runtime.rb +12 -0
  181. data/test/test_scope_chain.rb +50 -0
  182. data/test/test_setter_property_node.rb +10 -0
  183. data/test/test_source_elements.rb +9 -0
  184. data/test/test_strict_equal_node.rb +8 -0
  185. data/test/test_string_node.rb +8 -0
  186. data/test/test_subtract_node.rb +8 -0
  187. data/test/test_switch_node.rb +12 -0
  188. data/test/test_this_node.rb +8 -0
  189. data/test/test_throw_node.rb +7 -0
  190. data/test/test_tokenizer.rb +199 -0
  191. data/test/test_true_node.rb +8 -0
  192. data/test/test_try_node.rb +59 -0
  193. data/test/test_type_of_node.rb +8 -0
  194. data/test/test_unary_minus_node.rb +8 -0
  195. data/test/test_unary_plus_node.rb +8 -0
  196. data/test/test_unsigned_right_shift_node.rb +8 -0
  197. data/test/test_var_decl_node.rb +21 -0
  198. data/test/test_var_statement_node.rb +14 -0
  199. data/test/test_void_node.rb +8 -0
  200. data/test/test_while_node.rb +15 -0
  201. data/test/test_with_node.rb +8 -0
  202. metadata +406 -0
@@ -0,0 +1,36 @@
1
+ require 'rkelly/js'
2
+ require 'rkelly/runtime/scope_chain'
3
+
4
+ module RKelly
5
+ class Runtime
6
+ UNDEFINED = RKelly::JS::Property.new(:undefined, :undefined)
7
+
8
+ def initialize
9
+ @parser = Parser.new
10
+ @scope = ScopeChain.new
11
+ end
12
+
13
+ # Execute +js+
14
+ def execute(js)
15
+ function_visitor = Visitors::FunctionVisitor.new(@scope)
16
+ eval_visitor = Visitors::EvaluationVisitor.new(@scope)
17
+ tree = @parser.parse(js)
18
+ function_visitor.accept(tree)
19
+ eval_visitor.accept(tree)
20
+ @scope
21
+ end
22
+
23
+ def call_function(function_name, *args)
24
+ function = @scope[function_name].value
25
+ @scope.new_scope { |chain|
26
+ function.js_call(chain, *(args.map { |x|
27
+ RKelly::JS::Property.new(:param, x)
28
+ }))
29
+ }.value
30
+ end
31
+
32
+ def define_function(function, &block)
33
+ @scope[function.to_s].function = block
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ module RKelly
2
+ class Runtime
3
+ class RubyFunction
4
+ def initialize(&block)
5
+ @code = block
6
+ end
7
+
8
+ def call(chain, *args)
9
+ @code.call(*args)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,57 @@
1
+ module RKelly
2
+ class Runtime
3
+ class ScopeChain
4
+ include RKelly::JS
5
+
6
+ def initialize(scope = Scope.new)
7
+ @chain = [GlobalObject.new]
8
+ end
9
+
10
+ def <<(scope)
11
+ @chain << scope
12
+ end
13
+
14
+ def has_property?(name)
15
+ scope = @chain.reverse.find { |x|
16
+ x.has_property?(name)
17
+ }
18
+ scope ? scope[name] : nil
19
+ end
20
+
21
+ def [](name)
22
+ property = has_property?(name)
23
+ return property if property
24
+ @chain.last.properties[name]
25
+ end
26
+
27
+ def []=(name, value)
28
+ @chain.last.properties[name] = value
29
+ end
30
+
31
+ def pop
32
+ @chain.pop
33
+ end
34
+
35
+ def this
36
+ @chain.last
37
+ end
38
+
39
+ def new_scope(&block)
40
+ @chain << Scope.new
41
+ result = yield(self)
42
+ @chain.pop
43
+ result
44
+ end
45
+
46
+ def return=(value)
47
+ @chain.last.return = value
48
+ end
49
+
50
+ def return; @chain.last.return; end
51
+
52
+ def returned?
53
+ @chain.last.returned?
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,4 @@
1
+ module RKelly
2
+ class SyntaxError < ::SyntaxError
3
+ end
4
+ end
@@ -0,0 +1,19 @@
1
+ module RKelly
2
+ class Token
3
+ attr_accessor :name, :value, :transformer, :line
4
+ def initialize(name, value, &transformer)
5
+ @name = name
6
+ @value = value
7
+ @transformer = transformer
8
+ end
9
+
10
+ def to_racc_token
11
+ return transformer.call(name, value) if transformer
12
+ [name, value]
13
+ end
14
+
15
+ def to_s
16
+ return "#{self.name}: #{self.value}"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,161 @@
1
+ require 'rkelly/lexeme'
2
+
3
+ module RKelly
4
+ class Tokenizer
5
+ KEYWORDS = %w{
6
+ break case catch continue default delete do else finally for function
7
+ if in instanceof new return switch this throw try typeof var void while
8
+ with
9
+
10
+ const true false null debugger
11
+ }
12
+
13
+ RESERVED = %w{
14
+ abstract boolean byte char class double enum export extends
15
+ final float goto implements import int interface long native package
16
+ private protected public short static super synchronized throws
17
+ transient volatile
18
+ }
19
+
20
+ LITERALS = {
21
+ # Punctuators
22
+ '==' => :EQEQ,
23
+ '!=' => :NE,
24
+ '===' => :STREQ,
25
+ '!==' => :STRNEQ,
26
+ '<=' => :LE,
27
+ '>=' => :GE,
28
+ '||' => :OR,
29
+ '&&' => :AND,
30
+ '++' => :PLUSPLUS,
31
+ '--' => :MINUSMINUS,
32
+ '<<' => :LSHIFT,
33
+ '<<=' => :LSHIFTEQUAL,
34
+ '>>' => :RSHIFT,
35
+ '>>=' => :RSHIFTEQUAL,
36
+ '>>>' => :URSHIFT,
37
+ '>>>='=> :URSHIFTEQUAL,
38
+ '&=' => :ANDEQUAL,
39
+ '%=' => :MODEQUAL,
40
+ '^=' => :XOREQUAL,
41
+ '|=' => :OREQUAL,
42
+ '+=' => :PLUSEQUAL,
43
+ '-=' => :MINUSEQUAL,
44
+ '*=' => :MULTEQUAL,
45
+ '/=' => :DIVEQUAL,
46
+ }
47
+
48
+ # Some keywords can be followed by regular expressions (eg, return and throw).
49
+ # Others can be followed by division.
50
+ KEYWORDS_THAT_IMPLY_DIVISION = %w{
51
+ this true false null
52
+ }
53
+
54
+ KEYWORDS_THAT_IMPLY_REGEX = KEYWORDS - KEYWORDS_THAT_IMPLY_DIVISION
55
+
56
+ SINGLE_CHARS_THAT_IMPLY_DIVISION = [')', ']', '}']
57
+
58
+ def initialize(&block)
59
+ @lexemes = []
60
+
61
+ token(:COMMENT, /\A\/(?:\*(?:.)*?\*\/|\/[^\n]*)/m)
62
+ token(:STRING, /\A"(?:[^"\\]*(?:\\.[^"\\]*)*)"|\A'(?:[^'\\]*(?:\\.[^'\\]*)*)'/m)
63
+
64
+ # A regexp to match floating point literals (but not integer literals).
65
+ token(:NUMBER, /\A\d+\.\d*(?:[eE][-+]?\d+)?|\A\d+(?:\.\d*)?[eE][-+]?\d+|\A\.\d+(?:[eE][-+]?\d+)?/m) do |type, value|
66
+ value.gsub!(/\.(\D)/, '.0\1') if value =~ /\.\w/
67
+ value.gsub!(/\.$/, '.0') if value =~ /\.$/
68
+ value.gsub!(/^\./, '0.') if value =~ /^\./
69
+ [type, eval(value)]
70
+ end
71
+ token(:NUMBER, /\A0[xX][\da-fA-F]+|\A0[0-7]*|\A\d+/) do |type, value|
72
+ [type, eval(value)]
73
+ end
74
+
75
+ token(:LITERALS,
76
+ Regexp.new(LITERALS.keys.sort_by { |x|
77
+ x.length
78
+ }.reverse.map { |x| "\\A#{x.gsub(/([|+*^])/, '\\\\\1')}" }.join('|')
79
+ )) do |type, value|
80
+ [LITERALS[value], value]
81
+ end
82
+
83
+ token(:RAW_IDENT, /\A([_\$A-Za-z][_\$0-9A-Za-z]*)/) do |type,value|
84
+ if KEYWORDS.include?(value)
85
+ [value.upcase.to_sym, value]
86
+ elsif RESERVED.include?(value)
87
+ [:RESERVED, value]
88
+ else
89
+ [:IDENT, value]
90
+ end
91
+ end
92
+
93
+ # To distinguish regular expressions from comments, we require that
94
+ # regular expressions start with a non * character (ie, not look like
95
+ # /*foo*/). Note that we can't depend on the length of the match to
96
+ # correctly distinguish, since `/**/i` is longer if matched as a regular
97
+ # expression than as matched as a comment.
98
+ # Incidentally, we're also not matching empty regular expressions
99
+ # (eg, // and //g). Here we could depend on match length and priority to
100
+ # determine that these are actually comments, but it turns out to be
101
+ # easier to not match them in the first place.
102
+ token(:REGEXP, /\A\/(?:[^\/\r\n\\*]|\\[^\r\n])[^\/\r\n\\]*(?:\\[^\r\n][^\/\r\n\\]*)*\/[gim]*/)
103
+ token(:S, /\A[\s\r\n]*/m)
104
+
105
+ token(:SINGLE_CHAR, /\A./) do |type, value|
106
+ [value, value]
107
+ end
108
+ end
109
+
110
+ def tokenize(string)
111
+ raw_tokens(string).map { |x| x.to_racc_token }
112
+ end
113
+
114
+ def raw_tokens(string)
115
+ tokens = []
116
+ line_number = 1
117
+ accepting_regexp = true
118
+ while string.length > 0
119
+ longest_token = nil
120
+
121
+ @lexemes.each { |lexeme|
122
+ next if lexeme.name == :REGEXP && !accepting_regexp
123
+
124
+ match = lexeme.match(string)
125
+ next if match.nil?
126
+ longest_token = match if longest_token.nil?
127
+ next if longest_token.value.length >= match.value.length
128
+ longest_token = match
129
+ }
130
+
131
+ if longest_token.name != :S
132
+ accepting_regexp = followable_by_regex(longest_token)
133
+ end
134
+
135
+ longest_token.line = line_number
136
+ line_number += longest_token.value.scan(/\n/).length
137
+ string = string.slice(Range.new(longest_token.value.length, -1))
138
+ tokens << longest_token
139
+ end
140
+ tokens
141
+ end
142
+
143
+ private
144
+ def token(name, pattern = nil, &block)
145
+ @lexemes << Lexeme.new(name, pattern, &block)
146
+ end
147
+
148
+ def followable_by_regex(current_token)
149
+ case current_token.name
150
+ when :RAW_IDENT
151
+ KEYWORDS_THAT_IMPLY_REGEX.include?(current_token.value)
152
+ when :NUMBER
153
+ false
154
+ when :SINGLE_CHAR
155
+ !SINGLE_CHARS_THAT_IMPLY_DIVISION.include?(current_token.value)
156
+ else
157
+ true
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,16 @@
1
+ module RKelly
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}'"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ require 'rkelly/visitors/visitor'
2
+ Dir[File.join(File.dirname(__FILE__), "visitors/*_visitor.rb")].each do |file|
3
+ require file[/rkelly\/visitors\/.*/]
4
+ end
@@ -0,0 +1,228 @@
1
+ module RKelly
2
+ module Visitors
3
+ class DotVisitor < Visitor
4
+ class Node < Struct.new(:node_id, :fields)
5
+ ESCAPE = /([<>"\\])/
6
+ def to_s
7
+ counter = 0
8
+ label = fields.map { |f|
9
+ s = "<f#{counter}> #{f.to_s.gsub(ESCAPE, '\\\\\1').gsub(/[\r\n]/,' ')}"
10
+ counter += 1
11
+ s
12
+ }.join('|')
13
+ "\"#{node_id}\" [\nlabel = \"#{label}\"\nshape = \"record\"\n];"
14
+ end
15
+ end
16
+
17
+ class Arrow < Struct.new(:from, :to, :label)
18
+ def to_s
19
+ "\"#{from.node_id}\":f0 -> \"#{to.node_id}\":f0"
20
+ end
21
+ end
22
+
23
+ attr_reader :nodes, :arrows
24
+ def initialize
25
+ @stack = []
26
+ @node_index = 0
27
+ @nodes = []
28
+ @arrows = []
29
+ end
30
+
31
+ ## Terminal nodes
32
+ %w{
33
+ BreakNode ContinueNode EmptyStatementNode FalseNode
34
+ NullNode NumberNode ParameterNode RegexpNode ResolveNode StringNode
35
+ ThisNode TrueNode
36
+ }.each do |type|
37
+ define_method(:"visit_#{type}") do |o|
38
+ node = Node.new(@node_index += 1, [type, o.value].compact)
39
+ add_arrow_for(node)
40
+ @nodes << node
41
+ end
42
+ end
43
+ ## End Terminal nodes
44
+
45
+ # Single value nodes
46
+ %w{
47
+ AssignExprNode BitwiseNotNode BlockNode DeleteNode ElementNode
48
+ ExpressionStatementNode FunctionBodyNode LogicalNotNode ReturnNode
49
+ ThrowNode TypeOfNode UnaryMinusNode UnaryPlusNode VoidNode
50
+ }.each do |type|
51
+ define_method(:"visit_#{type}") do |o|
52
+ node = Node.new(@node_index += 1, [type])
53
+ add_arrow_for(node)
54
+ @nodes << node
55
+ @stack.push(node)
56
+ o.value && o.value.accept(self)
57
+ @stack.pop
58
+ end
59
+ end
60
+ # End Single value nodes
61
+
62
+ # Binary nodes
63
+ %w{
64
+ AddNode BitAndNode BitOrNode BitXOrNode CaseClauseNode CommaNode
65
+ DivideNode DoWhileNode EqualNode GreaterNode GreaterOrEqualNode InNode
66
+ InstanceOfNode LeftShiftNode LessNode LessOrEqualNode LogicalAndNode
67
+ LogicalOrNode ModulusNode MultiplyNode NotEqualNode NotStrictEqualNode
68
+ OpAndEqualNode OpDivideEqualNode OpEqualNode OpLShiftEqualNode
69
+ OpMinusEqualNode OpModEqualNode OpMultiplyEqualNode OpOrEqualNode
70
+ OpPlusEqualNode OpRShiftEqualNode OpURShiftEqualNode OpXOrEqualNode
71
+ RightShiftNode StrictEqualNode SubtractNode SwitchNode
72
+ UnsignedRightShiftNode WhileNode WithNode
73
+ }.each do |type|
74
+ define_method(:"visit_#{type}") do |o|
75
+ node = Node.new(@node_index += 1, [type])
76
+ add_arrow_for(node)
77
+ @nodes << node
78
+ @stack.push(node)
79
+ o.left && o.left.accept(self)
80
+ o.value && o.value.accept(self)
81
+ @stack.pop
82
+ end
83
+ end
84
+ # End Binary nodes
85
+
86
+ # Array Value Nodes
87
+ %w{
88
+ ArgumentsNode ArrayNode CaseBlockNode ConstStatementNode
89
+ ObjectLiteralNode SourceElementsNode VarStatementNode
90
+ }.each do |type|
91
+ define_method(:"visit_#{type}") do |o|
92
+ node = Node.new(@node_index += 1, [type])
93
+ add_arrow_for(node)
94
+ @nodes << node
95
+ @stack.push(node)
96
+ o.value && o.value.each { |v| v && v.accept(self) }
97
+ @stack.pop
98
+ end
99
+ end
100
+ # END Array Value Nodes
101
+
102
+ # Name and Value Nodes
103
+ %w{
104
+ LabelNode PropertyNode GetterPropertyNode SetterPropertyNode VarDeclNode
105
+ }.each do |type|
106
+ define_method(:"visit_#{type}") do |o|
107
+ node = Node.new(@node_index += 1, [type, o.name || 'NULL'])
108
+ add_arrow_for(node)
109
+ @nodes << node
110
+ @stack.push(node)
111
+ o.value && o.value.accept(self)
112
+ @stack.pop
113
+ end
114
+ end
115
+ # END Name and Value Nodes
116
+
117
+ %w{ PostfixNode PrefixNode }.each do |type|
118
+ define_method(:"visit_#{type}") do |o|
119
+ node = Node.new(@node_index += 1, [type, o.value])
120
+ add_arrow_for(node)
121
+ @nodes << node
122
+ @stack.push(node)
123
+ o.operand && o.operand.accept(self)
124
+ @stack.pop
125
+ end
126
+ end
127
+
128
+ def visit_ForNode(o)
129
+ node = Node.new(@node_index += 1, ['ForNode'])
130
+ add_arrow_for(node)
131
+ @nodes << node
132
+ @stack.push(node)
133
+ [:init, :test, :counter, :value].each do |method|
134
+ o.send(method) && o.send(method).accept(self)
135
+ end
136
+ @stack.pop
137
+ end
138
+
139
+ %w{ IfNode ConditionalNode }.each do |type|
140
+ define_method(:"visit_#{type}") do |o|
141
+ node = Node.new(@node_index += 1, [type])
142
+ add_arrow_for(node)
143
+ @nodes << node
144
+ @stack.push(node)
145
+ [:conditions, :value, :else].each do |method|
146
+ o.send(method) && o.send(method).accept(self)
147
+ end
148
+ @stack.pop
149
+ end
150
+ end
151
+
152
+ def visit_ForInNode(o)
153
+ node = Node.new(@node_index += 1, ['ForInNode'])
154
+ add_arrow_for(node)
155
+ @nodes << node
156
+ @stack.push(node)
157
+ [:left, :right, :value].each do |method|
158
+ o.send(method) && o.send(method).accept(self)
159
+ end
160
+ @stack.pop
161
+ end
162
+
163
+ def visit_TryNode(o)
164
+ node = Node.new(@node_index += 1, ['TryNode', o.catch_var || 'NULL'])
165
+ add_arrow_for(node)
166
+ @nodes << node
167
+ @stack.push(node)
168
+ [:value, :catch_block, :finally_block].each do |method|
169
+ o.send(method) && o.send(method).accept(self)
170
+ end
171
+ @stack.pop
172
+ end
173
+
174
+ def visit_BracketAccessorNode(o)
175
+ node = Node.new(@node_index += 1, ['BracketAccessorNode'])
176
+ add_arrow_for(node)
177
+ @nodes << node
178
+ @stack.push(node)
179
+ [:value, :accessor].each do |method|
180
+ o.send(method) && o.send(method).accept(self)
181
+ end
182
+ @stack.pop
183
+ end
184
+
185
+ %w{ NewExprNode FunctionCallNode }.each do |type|
186
+ define_method(:"visit_#{type}") do |o|
187
+ node = Node.new(@node_index += 1, [type])
188
+ add_arrow_for(node)
189
+ @nodes << node
190
+ @stack.push(node)
191
+ [:value, :arguments].each do |method|
192
+ o.send(method) && o.send(method).accept(self)
193
+ end
194
+ @stack.pop
195
+ end
196
+ end
197
+
198
+ %w{ FunctionExprNode FunctionDeclNode }.each do |type|
199
+ define_method(:"visit_#{type}") do |o|
200
+ node = Node.new(@node_index += 1, [type, o.value || 'NULL'])
201
+ add_arrow_for(node)
202
+ @nodes << node
203
+ @stack.push(node)
204
+ o.arguments.each { |a| a && a.accept(self) }
205
+ o.function_body && o.function_body.accept(self)
206
+ @stack.pop
207
+ end
208
+ end
209
+
210
+ def visit_DotAccessorNode(o)
211
+ node = Node.new(@node_index += 1, ['DotAccessorNode', o.accessor])
212
+ add_arrow_for(node)
213
+ @nodes << node
214
+ @stack.push(node)
215
+ [:value].each do |method|
216
+ o.send(method) && o.send(method).accept(self)
217
+ end
218
+ @stack.pop
219
+ end
220
+
221
+ private
222
+ def add_arrow_for(node, label = nil)
223
+ @arrows << Arrow.new(@stack.last, node, label) if @stack.length > 0
224
+ end
225
+
226
+ end
227
+ end
228
+ end