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.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/CHANGELOG.rdoc +26 -0
- data/Manifest.txt +199 -0
- data/README.rdoc +58 -0
- data/Rakefile +39 -0
- data/lib/parser.y +871 -0
- data/lib/rkelly.rb +14 -0
- data/lib/rkelly/constants.rb +3 -0
- data/lib/rkelly/generated_parser.rb +3274 -0
- data/lib/rkelly/js.rb +14 -0
- data/lib/rkelly/js/array.rb +15 -0
- data/lib/rkelly/js/base.rb +91 -0
- data/lib/rkelly/js/boolean.rb +21 -0
- data/lib/rkelly/js/function.rb +39 -0
- data/lib/rkelly/js/function_prototype.rb +15 -0
- data/lib/rkelly/js/global_object.rb +52 -0
- data/lib/rkelly/js/math.rb +10 -0
- data/lib/rkelly/js/nan.rb +18 -0
- data/lib/rkelly/js/number.rb +22 -0
- data/lib/rkelly/js/object.rb +30 -0
- data/lib/rkelly/js/object_prototype.rb +14 -0
- data/lib/rkelly/js/property.rb +20 -0
- data/lib/rkelly/js/scope.rb +6 -0
- data/lib/rkelly/js/string.rb +21 -0
- data/lib/rkelly/lexeme.rb +18 -0
- data/lib/rkelly/nodes.rb +25 -0
- data/lib/rkelly/nodes/binary_node.rb +18 -0
- data/lib/rkelly/nodes/bracket_accessor_node.rb +11 -0
- data/lib/rkelly/nodes/case_clause_node.rb +11 -0
- data/lib/rkelly/nodes/comma_node.rb +11 -0
- data/lib/rkelly/nodes/conditional_node.rb +11 -0
- data/lib/rkelly/nodes/dot_accessor_node.rb +11 -0
- data/lib/rkelly/nodes/for_in_node.rb +12 -0
- data/lib/rkelly/nodes/for_node.rb +13 -0
- data/lib/rkelly/nodes/function_call_node.rb +16 -0
- data/lib/rkelly/nodes/function_decl_node.rb +6 -0
- data/lib/rkelly/nodes/function_expr_node.rb +12 -0
- data/lib/rkelly/nodes/if_node.rb +12 -0
- data/lib/rkelly/nodes/label_node.rb +11 -0
- data/lib/rkelly/nodes/new_expr_node.rb +11 -0
- data/lib/rkelly/nodes/node.rb +88 -0
- data/lib/rkelly/nodes/not_strict_equal_node.rb +6 -0
- data/lib/rkelly/nodes/op_equal_node.rb +16 -0
- data/lib/rkelly/nodes/postfix_node.rb +11 -0
- data/lib/rkelly/nodes/prefix_node.rb +6 -0
- data/lib/rkelly/nodes/property_node.rb +13 -0
- data/lib/rkelly/nodes/resolve_node.rb +19 -0
- data/lib/rkelly/nodes/strict_equal_node.rb +6 -0
- data/lib/rkelly/nodes/try_node.rb +13 -0
- data/lib/rkelly/nodes/var_decl_node.rb +15 -0
- data/lib/rkelly/parser.rb +104 -0
- data/lib/rkelly/runtime.rb +36 -0
- data/lib/rkelly/runtime/ruby_function.rb +13 -0
- data/lib/rkelly/runtime/scope_chain.rb +57 -0
- data/lib/rkelly/syntax_error.rb +4 -0
- data/lib/rkelly/token.rb +19 -0
- data/lib/rkelly/tokenizer.rb +161 -0
- data/lib/rkelly/visitable.rb +16 -0
- data/lib/rkelly/visitors.rb +4 -0
- data/lib/rkelly/visitors/dot_visitor.rb +228 -0
- data/lib/rkelly/visitors/ecma_visitor.rb +322 -0
- data/lib/rkelly/visitors/enumerable_visitor.rb +18 -0
- data/lib/rkelly/visitors/evaluation_visitor.rb +419 -0
- data/lib/rkelly/visitors/function_visitor.rb +46 -0
- data/lib/rkelly/visitors/pointcut_visitor.rb +31 -0
- data/lib/rkelly/visitors/real_sexp_visitor.rb +16 -0
- data/lib/rkelly/visitors/sexp_visitor.rb +373 -0
- data/lib/rkelly/visitors/visitor.rb +136 -0
- data/test/ecma_script_test_case.rb +21 -0
- data/test/execute_test_case.rb +16 -0
- data/test/execution_contexts/test_10_1_3-1.rb +32 -0
- data/test/expressions/test_11_3_1.rb +64 -0
- data/test/expressions/test_11_3_2.rb +64 -0
- data/test/expressions/test_11_4_2.rb +13 -0
- data/test/expressions/test_11_4_3.rb +52 -0
- data/test/expressions/test_11_4_4.rb +68 -0
- data/test/expressions/test_11_4_5.rb +69 -0
- data/test/expressions/test_11_4_6.rb +88 -0
- data/test/expressions/test_11_4_8.rb +28 -0
- data/test/expressions/test_11_4_9.rb +103 -0
- data/test/expressions/test_11_5_1.rb +51 -0
- data/test/expressions/test_11_5_2.rb +80 -0
- data/test/expressions/test_11_5_3.rb +88 -0
- data/test/expressions/test_11_6_1-1.rb +19 -0
- data/test/expressions/test_11_9_1.rb +19 -0
- data/test/function/test_15_3_1_1-1.rb +34 -0
- data/test/global_object/test_15_1_1_1.rb +29 -0
- data/test/global_object/test_15_1_1_2.rb +17 -0
- data/test/global_object/test_15_1_1_3.rb +9 -0
- data/test/helper.rb +5 -0
- data/test/node_test_case.rb +11 -0
- data/test/object/test_15_2_1_1.rb +257 -0
- data/test/object/test_15_2_1_2.rb +21 -0
- data/test/object/test_15_2_2_1.rb +52 -0
- data/test/statements/test_12_5-1.rb +27 -0
- data/test/test_add_node.rb +8 -0
- data/test/test_arguments_node.rb +8 -0
- data/test/test_array_node.rb +9 -0
- data/test/test_assign_expr_node.rb +8 -0
- data/test/test_automatic_semicolon_insertion.rb +137 -0
- data/test/test_bit_and_node.rb +8 -0
- data/test/test_bit_or_node.rb +8 -0
- data/test/test_bit_x_or_node.rb +8 -0
- data/test/test_bitwise_not_node.rb +8 -0
- data/test/test_block_node.rb +14 -0
- data/test/test_bracket_accessor_node.rb +16 -0
- data/test/test_break_node.rb +11 -0
- data/test/test_case_block_node.rb +11 -0
- data/test/test_case_clause_node.rb +15 -0
- data/test/test_comma_node.rb +13 -0
- data/test/test_comments.rb +44 -0
- data/test/test_conditional_node.rb +17 -0
- data/test/test_const_statement_node.rb +14 -0
- data/test/test_continue_node.rb +11 -0
- data/test/test_delete_node.rb +8 -0
- data/test/test_divide_node.rb +8 -0
- data/test/test_do_while_node.rb +13 -0
- data/test/test_dot_accessor_node.rb +9 -0
- data/test/test_ecma_visitor.rb +210 -0
- data/test/test_element_node.rb +8 -0
- data/test/test_empty_statement_node.rb +8 -0
- data/test/test_equal_node.rb +8 -0
- data/test/test_evaluation_visitor.rb +66 -0
- data/test/test_expression_statement_node.rb +10 -0
- data/test/test_false_node.rb +8 -0
- data/test/test_for_in_node.rb +17 -0
- data/test/test_for_node.rb +24 -0
- data/test/test_function_body_node.rb +8 -0
- data/test/test_function_call_node.rb +10 -0
- data/test/test_function_decl_node.rb +16 -0
- data/test/test_function_expr_node.rb +16 -0
- data/test/test_function_visitor.rb +26 -0
- data/test/test_getter_property_node.rb +10 -0
- data/test/test_global_object.rb +49 -0
- data/test/test_greater_node.rb +8 -0
- data/test/test_greater_or_equal_node.rb +8 -0
- data/test/test_if_node.rb +17 -0
- data/test/test_in_node.rb +8 -0
- data/test/test_instance_of_node.rb +8 -0
- data/test/test_label_node.rb +13 -0
- data/test/test_left_shift_node.rb +8 -0
- data/test/test_less_node.rb +8 -0
- data/test/test_less_or_equal_node.rb +8 -0
- data/test/test_line_number.rb +23 -0
- data/test/test_logical_and_node.rb +8 -0
- data/test/test_logical_not_node.rb +8 -0
- data/test/test_logical_or_node.rb +8 -0
- data/test/test_modulus_node.rb +8 -0
- data/test/test_multiply_node.rb +8 -0
- data/test/test_new_expr_node.rb +9 -0
- data/test/test_not_equal_node.rb +8 -0
- data/test/test_not_strict_equal_node.rb +8 -0
- data/test/test_null_node.rb +8 -0
- data/test/test_number_node.rb +8 -0
- data/test/test_object_literal_node.rb +9 -0
- data/test/test_op_and_equal_node.rb +10 -0
- data/test/test_op_divide_equal_node.rb +10 -0
- data/test/test_op_equal_node.rb +10 -0
- data/test/test_op_l_shift_equal_node.rb +10 -0
- data/test/test_op_minus_equal_node.rb +10 -0
- data/test/test_op_mod_equal_node.rb +10 -0
- data/test/test_op_multiply_equal_node.rb +10 -0
- data/test/test_op_or_equal_node.rb +10 -0
- data/test/test_op_plus_equal_node.rb +10 -0
- data/test/test_op_r_shift_equal_node.rb +10 -0
- data/test/test_op_u_r_shift_equal_node.rb +10 -0
- data/test/test_op_x_or_equal_node.rb +10 -0
- data/test/test_parameter_node.rb +8 -0
- data/test/test_parser.rb +1361 -0
- data/test/test_pointcut_visitor.rb +34 -0
- data/test/test_postfix_node.rb +8 -0
- data/test/test_prefix_node.rb +8 -0
- data/test/test_property_node.rb +8 -0
- data/test/test_regexp_node.rb +8 -0
- data/test/test_resolve_node.rb +22 -0
- data/test/test_return_node.rb +11 -0
- data/test/test_right_shift_node.rb +8 -0
- data/test/test_rkelly.rb +19 -0
- data/test/test_runtime.rb +12 -0
- data/test/test_scope_chain.rb +50 -0
- data/test/test_setter_property_node.rb +10 -0
- data/test/test_source_elements.rb +9 -0
- data/test/test_strict_equal_node.rb +8 -0
- data/test/test_string_node.rb +8 -0
- data/test/test_subtract_node.rb +8 -0
- data/test/test_switch_node.rb +12 -0
- data/test/test_this_node.rb +8 -0
- data/test/test_throw_node.rb +7 -0
- data/test/test_tokenizer.rb +199 -0
- data/test/test_true_node.rb +8 -0
- data/test/test_try_node.rb +59 -0
- data/test/test_type_of_node.rb +8 -0
- data/test/test_unary_minus_node.rb +8 -0
- data/test/test_unary_plus_node.rb +8 -0
- data/test/test_unsigned_right_shift_node.rb +8 -0
- data/test/test_var_decl_node.rb +21 -0
- data/test/test_var_statement_node.rb +14 -0
- data/test/test_void_node.rb +8 -0
- data/test/test_while_node.rb +15 -0
- data/test/test_with_node.rb +8 -0
- 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,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
|
data/lib/rkelly/token.rb
ADDED
@@ -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,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
|