Repository navigation
Expand file tree
/
Copy pathdst_extractor.rb
More file actions
172 lines (156 loc) · 6 KB
/
Copy pathdst_extractor.rb
File metadata and controls
172 lines (156 loc) · 6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
require 'ap'
module Monocle
class DSTExtractor
def initialize snippet
@snippet = snippet
@template_ast = RubyParser.parse(snippet.sample_code)
end
def extract_from(ast)
match_nodes?(ast, @template_ast)
end
def match_placeholder?(id, node)
if !@snippet.respond_to?(:"match_#{id}?")
puts @snippet.sample_code
end
@snippet.send(:"match_#{id}?", node)
end
def match_nodes?(node, template)
if $extensive_mutator_logging
puts "--------------- compare nodes -----------------"
ap node
puts "template: -------------------------------------"
ap template
puts "-----------------------------------------------"
end
if template.is_a?(Symbol)
match_symbol?(node, template)
elsif template.is_a?(Astrolabe::Node)
meth = :"match_#{template.type}_node?"
if respond_to?(meth)
send(meth, node, template)
else
if template.type != node&.type
raise 'unexpected node type mismatch: #{template.type} != #{node&.type}'
end
template.type == node&.type && match_children?(node, template)
end
else
node == template
end
end
def match_children? node, template, range=0..-1
node.children[range].each_with_index do |child, index|
return false unless match_nodes?(child, template.children[range][index])
end
true
end
def match_symbol? node, template
if id = Monocle.extract_placeholder_id(template)
match_placeholder?(id, node)
else
node == template
end
end
def match_const_node? node, template
return unless template.type == node.type
if id = Monocle.extract_placeholder_id(template.children.last)
match_placeholder?(id, node.children.last) &&
match_children?(node, template, 0..-2)
else
match_children? node, template
end
end
def match_ivar_node? node, template
return unless template.type == node.type
if id = Monocle.extract_placeholder_id(template.children.first)
match_placeholder?(id, node)
else
node == template
end
end
def match_block_arg_node? node, template
return unless template.type == node.type
if id = Monocle.extract_placeholder_id(template.children.first)
match_placeholder?(id, node)
else
node == template
end
end
def match_send_node? node, template
if id = Monocle.extract_placeholder_id(template.children[1])
raise if template.children.size > 2
match_placeholder?(id, node)
elsif node&.type == :lvar
# This code deals with the following edgecase:
#
# `a.each {|b| b}` when parsing this, the second `b` will be an
# lvar node, because the parser knows there is a local var with
# that name. When parsing the block contents separately, it'll
# become a send node, because the parser doesn't know there is a
# local var. So, if a child-snippet contains block contents, any
# local vars will be send nodes in the snippet ast, but lvar nodes
# in the real ast.
return unless template.children[0] || template.children.size < 3
node.children.first == template.children[1]
else
return unless template&.type == node&.type
first_arg = template.children[2]
# TODO: if type is send, and there is a child of send which happens to
# be a placeholder and the placeholder ast2dst_meta method tells us
# that it's child snippets that have link_to_array, then we need to
# match each of the ast elements to that placeholder, while keeping
# track of an index (to construct the data_path prefix for the
# elmement in the linked_array) # in a snippet instance variable
if first_arg&.type == :send &&
(id = Monocle.extract_placeholder_id(first_arg.children[1])) &&
@snippet.respond_to?(:"match_options_#{id}") &&
@snippet.send(:"match_options_#{id}")[:type] == :list_of_symbols
match_children?(node, template, 0..1) && match_placeholder?(id, node.children[2..-1])
else
match_children? node, template
end
end
end
def match_class_or_block? node, template
return unless template.type == node.type
return unless match_children?(node, template, 0..1)
current_ast_node = 0
children = node.children[2]&.children || []
snippet_children = template.children[2]&.children || []
snippet_children.each do |snippet_child|
if id = Monocle.extract_placeholder_id(snippet_child.type == :send && snippet_child.children[1])
# TODO: Implement a ast2dst_meta2 method that tells us if we have to match *all* methods.
@snippet.ast_for_snippet_count = 0 # is used in the snippet when dealing with linked_array
while children.size > current_ast_node && match_placeholder?(id, children[current_ast_node])
current_ast_node += 1
@snippet.ast_for_snippet_count += 1
end
else
return unless match_nodes?(children[current_ast_node], snippet_child)
current_ast_node += 1
end
end
current_ast_node == children.size
end
alias_method :match_class_node?, :match_class_or_block?
alias_method :match_block_node?, :match_class_or_block?
def match_ivasgn_node? node, template
return unless template.type == node.type
if id = Monocle.extract_placeholder_id(template.children.first)
match_placeholder?(id, node) &&
match_children?(node, template, 1..-1)
else
match_children? node, template
end
end
def match_def_node? node, template
return unless template.type == node.type
if id = Monocle.extract_placeholder_id(template.children.first)
match_placeholder?(id, node) &&
match_children?(node, template, 1..-1)
else
match_children? node, template
end
end
end
end