synvert-core 1.4.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +0 -1
- data/.gitignore +0 -5
- data/CHANGELOG.md +12 -0
- data/Gemfile +0 -3
- data/Gemfile.lock +101 -0
- data/Guardfile +0 -9
- data/README.md +31 -13
- data/Rakefile +1 -15
- data/lib/synvert/core/engine/erb.rb +1 -1
- data/lib/synvert/core/engine.rb +1 -1
- data/lib/synvert/core/node_ext.rb +0 -639
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +20 -17
- data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +1 -5
- data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +1 -1
- data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +1 -5
- data/lib/synvert/core/rewriter/condition.rb +5 -1
- data/lib/synvert/core/rewriter/instance.rb +91 -140
- data/lib/synvert/core/rewriter/scope/query_scope.rb +8 -6
- data/lib/synvert/core/rewriter/scope/within_scope.rb +4 -87
- data/lib/synvert/core/rewriter.rb +0 -10
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +4 -6
- data/spec/synvert/core/engine/erb_spec.rb +3 -3
- data/spec/synvert/core/node_ext_spec.rb +0 -965
- data/spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb +21 -1
- data/spec/synvert/core/rewriter/instance_spec.rb +64 -131
- data/spec/synvert/core/rewriter/scope/goto_scope_spec.rb +1 -4
- data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +1 -16
- data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +22 -13
- data/synvert-core-ruby.gemspec +5 -3
- metadata +46 -62
- data/lib/synvert/core/array_ext.rb +0 -48
- data/lib/synvert/core/node_query/compiler/array.rb +0 -34
- data/lib/synvert/core/node_query/compiler/attribute.rb +0 -39
- data/lib/synvert/core/node_query/compiler/attribute_list.rb +0 -24
- data/lib/synvert/core/node_query/compiler/basic_selector.rb +0 -28
- data/lib/synvert/core/node_query/compiler/boolean.rb +0 -23
- data/lib/synvert/core/node_query/compiler/comparable.rb +0 -86
- data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +0 -51
- data/lib/synvert/core/node_query/compiler/expression.rb +0 -41
- data/lib/synvert/core/node_query/compiler/float.rb +0 -23
- data/lib/synvert/core/node_query/compiler/identifier.rb +0 -41
- data/lib/synvert/core/node_query/compiler/integer.rb +0 -23
- data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +0 -7
- data/lib/synvert/core/node_query/compiler/nil.rb +0 -23
- data/lib/synvert/core/node_query/compiler/parse_error.rb +0 -7
- data/lib/synvert/core/node_query/compiler/regexp.rb +0 -37
- data/lib/synvert/core/node_query/compiler/selector.rb +0 -113
- data/lib/synvert/core/node_query/compiler/string.rb +0 -23
- data/lib/synvert/core/node_query/compiler/symbol.rb +0 -23
- data/lib/synvert/core/node_query/compiler.rb +0 -25
- data/lib/synvert/core/node_query/lexer.rex +0 -99
- data/lib/synvert/core/node_query/lexer.rex.rb +0 -299
- data/lib/synvert/core/node_query/parser.racc.rb +0 -306
- data/lib/synvert/core/node_query/parser.y +0 -60
- data/lib/synvert/core/node_query.rb +0 -36
- data/lib/synvert/core/rewriter/action/append_action.rb +0 -28
- data/lib/synvert/core/rewriter/action/delete_action.rb +0 -34
- data/lib/synvert/core/rewriter/action/insert_action.rb +0 -34
- data/lib/synvert/core/rewriter/action/insert_after_action.rb +0 -22
- data/lib/synvert/core/rewriter/action/prepend_action.rb +0 -44
- data/lib/synvert/core/rewriter/action/remove_action.rb +0 -56
- data/lib/synvert/core/rewriter/action/replace_action.rb +0 -33
- data/lib/synvert/core/rewriter/action/replace_with_action.rb +0 -36
- data/lib/synvert/core/rewriter/action/wrap_action.rb +0 -37
- data/lib/synvert/core/rewriter/action.rb +0 -102
- data/spec/synvert/core/node_query/lexer_spec.rb +0 -580
- data/spec/synvert/core/node_query/parser_spec.rb +0 -337
- data/spec/synvert/core/rewriter/action/append_action_spec.rb +0 -70
- data/spec/synvert/core/rewriter/action/delete_action_spec.rb +0 -26
- data/spec/synvert/core/rewriter/action/insert_action_spec.rb +0 -70
- data/spec/synvert/core/rewriter/action/insert_after_action_spec.rb +0 -26
- data/spec/synvert/core/rewriter/action/prepend_action_spec.rb +0 -175
- data/spec/synvert/core/rewriter/action/remove_action_spec.rb +0 -26
- data/spec/synvert/core/rewriter/action/replace_action_spec.rb +0 -28
- data/spec/synvert/core/rewriter/action/replace_with_action_spec.rb +0 -59
- data/spec/synvert/core/rewriter/action/wrap_action_spec.rb +0 -31
- data/spec/synvert/core/rewriter/action_spec.rb +0 -14
@@ -29,311 +29,6 @@ module Parser::AST
|
|
29
29
|
# Source Code to Ast Node
|
30
30
|
# {https://synvert-playground.xinminlabs.com/ruby}
|
31
31
|
class Node
|
32
|
-
TYPE_CHILDREN = {
|
33
|
-
and: %i[left_value right_value],
|
34
|
-
arg: %i[name],
|
35
|
-
begin: %i[body],
|
36
|
-
block: %i[caller arguments body],
|
37
|
-
blockarg: %i[name],
|
38
|
-
const: %i[parent_const name],
|
39
|
-
class: %i[name parent_class body],
|
40
|
-
csend: %i[receiver message arguments],
|
41
|
-
cvasgn: %i[left_value right_value],
|
42
|
-
cvar: %i[name],
|
43
|
-
def: %i[name arguments body],
|
44
|
-
definded?: %i[arguments],
|
45
|
-
defs: %i[self name arguments body],
|
46
|
-
hash: %i[pairs],
|
47
|
-
ivasgn: %i[left_value right_value],
|
48
|
-
ivar: %i[name],
|
49
|
-
lvar: %i[name],
|
50
|
-
lvasgn: %i[left_value right_value],
|
51
|
-
masgn: %i[left_value right_value],
|
52
|
-
module: %i[name body],
|
53
|
-
or: %i[left_value right_value],
|
54
|
-
or_asgn: %i[left_value right_value],
|
55
|
-
pair: %i[key value],
|
56
|
-
restarg: %i[name],
|
57
|
-
send: %i[receiver message arguments],
|
58
|
-
super: %i[arguments],
|
59
|
-
zsuper: %i[]
|
60
|
-
}
|
61
|
-
|
62
|
-
# Initialize a Node.
|
63
|
-
#
|
64
|
-
# It extends {Parser::AST::Node} and set parent for its child nodes.
|
65
|
-
def initialize(type, children = [], properties = {})
|
66
|
-
@mutable_attributes = {}
|
67
|
-
super
|
68
|
-
# children could be nil for s(:array)
|
69
|
-
Array(children).each do |child_node|
|
70
|
-
if child_node.is_a?(Parser::AST::Node)
|
71
|
-
child_node.parent = self
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Get the parent node.
|
77
|
-
# @return [Parser::AST::Node] parent node.
|
78
|
-
def parent
|
79
|
-
@mutable_attributes[:parent]
|
80
|
-
end
|
81
|
-
|
82
|
-
# Set the parent node.
|
83
|
-
# @param node [Parser::AST::Node] parent node.
|
84
|
-
def parent=(node)
|
85
|
-
@mutable_attributes[:parent] = node
|
86
|
-
end
|
87
|
-
|
88
|
-
# Get the sibling nodes.
|
89
|
-
# @return [Array<Parser::AST::Node>] sibling nodes.
|
90
|
-
def siblings
|
91
|
-
index = parent.children.index(self)
|
92
|
-
parent.children[index + 1..]
|
93
|
-
end
|
94
|
-
|
95
|
-
# Dyamically defined method
|
96
|
-
# caller, key, left_value, message, name, pairs, parent_class, parent_const, receivr, rgith_value and value.
|
97
|
-
# based on const TYPE_CHILDREN.
|
98
|
-
%i[
|
99
|
-
caller
|
100
|
-
key
|
101
|
-
left_value
|
102
|
-
message
|
103
|
-
name
|
104
|
-
pairs
|
105
|
-
parent_class
|
106
|
-
parent_const
|
107
|
-
receiver
|
108
|
-
right_value
|
109
|
-
value
|
110
|
-
].each do |method_name|
|
111
|
-
define_method(method_name) do
|
112
|
-
index = TYPE_CHILDREN[type]&.index(method_name)
|
113
|
-
return children[index] if index
|
114
|
-
|
115
|
-
raise Synvert::Core::MethodNotSupported, "#{method_name} is not handled for #{debug_info}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Return the left value of node.
|
120
|
-
# It supports :and, :cvagn, :lvasgn, :masgn, :or and :or_asgn nodes.
|
121
|
-
# @example
|
122
|
-
# node # s(:or_asgn, s(:lvasgn, :a), s(:int, 1))
|
123
|
-
# node.left_value # :a
|
124
|
-
# @return [Parser::AST::Node] left value of node.
|
125
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
126
|
-
def left_value
|
127
|
-
return children[0].children[0] if type == :or_asgn
|
128
|
-
|
129
|
-
index = TYPE_CHILDREN[type]&.index(:left_value)
|
130
|
-
return children[index] if index
|
131
|
-
|
132
|
-
raise Synvert::Core::MethodNotSupported, "#{left_value} is not handled for #{debug_info}"
|
133
|
-
end
|
134
|
-
|
135
|
-
# Get arguments of node.
|
136
|
-
# It supports :block, :csend, :def, :defined?, :defs and :send nodes.
|
137
|
-
# @example
|
138
|
-
# node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post"))))
|
139
|
-
# node.arguments # [s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post")))]
|
140
|
-
# @return [Array<Parser::AST::Node>] arguments of node.
|
141
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
142
|
-
def arguments
|
143
|
-
case type
|
144
|
-
when :def, :block
|
145
|
-
children[1].children
|
146
|
-
when :defs
|
147
|
-
children[2].children
|
148
|
-
when :send, :csend
|
149
|
-
children[2..-1]
|
150
|
-
when :defined?
|
151
|
-
children
|
152
|
-
else
|
153
|
-
raise Synvert::Core::MethodNotSupported, "arguments is not handled for #{debug_info}"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# Get body of node.
|
158
|
-
# It supports :begin, :block, :class, :def, :defs and :module node.
|
159
|
-
# @example
|
160
|
-
# node # s(:block, s(:send, s(:const, nil, :RSpec), :configure), s(:args, s(:arg, :config)), s(:send, nil, :include, s(:const, s(:const, nil, :EmailSpec), :Helpers)))
|
161
|
-
# node.body # [s(:send, nil, :include, s(:const, s(:const, nil, :EmailSpec), :Helpers))]
|
162
|
-
# @return [Array<Parser::AST::Node>] body of node.
|
163
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
164
|
-
def body
|
165
|
-
case type
|
166
|
-
when :begin
|
167
|
-
children
|
168
|
-
when :def, :block, :class, :module
|
169
|
-
return [] if children[2].nil?
|
170
|
-
|
171
|
-
:begin == children[2].type ? children[2].body : children[2..-1]
|
172
|
-
when :defs
|
173
|
-
return [] if children[3].nil?
|
174
|
-
|
175
|
-
:begin == children[3].type ? children[3].body : children[3..-1]
|
176
|
-
else
|
177
|
-
raise Synvert::Core::MethodNotSupported, "body is not handled for #{debug_info}"
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
# Get condition of node.
|
182
|
-
# It supports :if node.
|
183
|
-
# @example
|
184
|
-
# node # s(:if, s(:defined?, s(:const, nil, :Bundler)), nil, nil)
|
185
|
-
# node.condition # s(:defined?, s(:const, nil, :Bundler))
|
186
|
-
# @return [Parser::AST::Node] condition of node.
|
187
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
188
|
-
def condition
|
189
|
-
if :if == type
|
190
|
-
children[0]
|
191
|
-
else
|
192
|
-
raise Synvert::Core::MethodNotSupported, "condition is not handled for #{debug_info}"
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
# Get keys of :hash node.
|
197
|
-
# @example
|
198
|
-
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
|
199
|
-
# node.keys # [s(:sym, :foo), s(:str, "foo")]
|
200
|
-
# @return [Array<Parser::AST::Node>] keys of node.
|
201
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
202
|
-
def keys
|
203
|
-
if :hash == type
|
204
|
-
children.map { |child| child.children[0] }
|
205
|
-
else
|
206
|
-
raise Synvert::Core::MethodNotSupported, "keys is not handled for #{debug_info}"
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# Get values of :hash node.
|
211
|
-
# @example
|
212
|
-
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
|
213
|
-
# node.values # [s(:sym, :bar), s(:str, "bar")]
|
214
|
-
# @return [Array<Parser::AST::Node>] values of node.
|
215
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
216
|
-
def values
|
217
|
-
if :hash == type
|
218
|
-
children.map { |child| child.children[1] }
|
219
|
-
else
|
220
|
-
raise Synvert::Core::MethodNotSupported, "keys is not handled for #{debug_info}"
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# Check if :hash node contains specified key.
|
225
|
-
# @example
|
226
|
-
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
|
227
|
-
# node.key?(:foo) # true
|
228
|
-
# @param [Symbol, String] key value.
|
229
|
-
# @return [Boolean] true if specified key exists.
|
230
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
231
|
-
def key?(key)
|
232
|
-
if :hash == type
|
233
|
-
children.any? { |pair_node| pair_node.key.to_value == key }
|
234
|
-
else
|
235
|
-
raise Synvert::Core::MethodNotSupported, "key? is not handled for #{debug_info}"
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
# Get :hash value node according to specified key.
|
240
|
-
# @example
|
241
|
-
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
|
242
|
-
# node.hash_value(:foo) # s(:sym, :bar)
|
243
|
-
# @param [Symbol, String] key value.
|
244
|
-
# @return [Parser::AST::Node] hash value of node.
|
245
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
246
|
-
def hash_value(key)
|
247
|
-
if :hash == type
|
248
|
-
value_node = children.find { |pair_node| pair_node.key.to_value == key }
|
249
|
-
value_node&.value
|
250
|
-
else
|
251
|
-
raise Synvert::Core::MethodNotSupported, "hash_value is not handled for #{debug_info}"
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
# Return the exact value of node.
|
256
|
-
# It supports :array, :begin, :erange, :false, :float, :irange, :int, :str, :sym and :true nodes.
|
257
|
-
# @example
|
258
|
-
# node # s(:array, s(:str, "str"), s(:sym, :str))
|
259
|
-
# node.to_value # ['str', :str]
|
260
|
-
# @return [Object] exact value.
|
261
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
262
|
-
def to_value
|
263
|
-
case type
|
264
|
-
when :int, :float, :str, :sym
|
265
|
-
children.last
|
266
|
-
when :true
|
267
|
-
true
|
268
|
-
when :false
|
269
|
-
false
|
270
|
-
when :nil
|
271
|
-
nil
|
272
|
-
when :array
|
273
|
-
children.map(&:to_value)
|
274
|
-
when :irange
|
275
|
-
(children.first.to_value..children.last.to_value)
|
276
|
-
when :erange
|
277
|
-
(children.first.to_value...children.last.to_value)
|
278
|
-
when :begin
|
279
|
-
children.first.to_value
|
280
|
-
else
|
281
|
-
self
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
# Respond key value and source for hash node, e.g.
|
286
|
-
# @example
|
287
|
-
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
|
288
|
-
# node.foo_value # :bar
|
289
|
-
# node.foo_source # ":bar"
|
290
|
-
def method_missing(method_name, *args, &block)
|
291
|
-
if :args == type && children.respond_to?(method_name)
|
292
|
-
return children.send(method_name, *args, &block)
|
293
|
-
elsif :hash == type && method_name.to_s.include?('_value')
|
294
|
-
key = method_name.to_s.sub('_value', '')
|
295
|
-
return hash_value(key.to_sym)&.to_value if key?(key.to_sym)
|
296
|
-
return hash_value(key.to_s)&.to_value if key?(key.to_s)
|
297
|
-
|
298
|
-
return nil
|
299
|
-
elsif :hash == type && method_name.to_s.include?('_source')
|
300
|
-
key = method_name.to_s.sub('_source', '')
|
301
|
-
return hash_value(key.to_sym)&.to_source if key?(key.to_sym)
|
302
|
-
return hash_value(key.to_s)&.to_source if key?(key.to_s)
|
303
|
-
|
304
|
-
return nil
|
305
|
-
end
|
306
|
-
|
307
|
-
super
|
308
|
-
end
|
309
|
-
|
310
|
-
def respond_to_missing?(method_name, *args)
|
311
|
-
if :args == type && children.respond_to?(method_name)
|
312
|
-
return true
|
313
|
-
elsif :hash == type && method_name.to_s.include?('_value')
|
314
|
-
key = method_name.to_s.sub('_value', '')
|
315
|
-
return true if key?(key.to_sym) || key?(key.to_s)
|
316
|
-
elsif :hash == type && method_name.to_s.include?('_source')
|
317
|
-
key = method_name.to_s.sub('_source', '')
|
318
|
-
return true if key?(key.to_sym) || key?(key.to_s)
|
319
|
-
end
|
320
|
-
|
321
|
-
super
|
322
|
-
end
|
323
|
-
|
324
|
-
# Return the debug info.
|
325
|
-
#
|
326
|
-
# @return [String] file, line, source and node.
|
327
|
-
def debug_info
|
328
|
-
"\n" +
|
329
|
-
[
|
330
|
-
"file: #{loc.expression.source_buffer.name}",
|
331
|
-
"line: #{loc.expression.line}",
|
332
|
-
"source: #{to_source}",
|
333
|
-
"node: #{inspect}"
|
334
|
-
].join("\n")
|
335
|
-
end
|
336
|
-
|
337
32
|
# Get the file name of node.
|
338
33
|
#
|
339
34
|
# @return [String] file name.
|
@@ -341,13 +36,6 @@ module Parser::AST
|
|
341
36
|
loc.expression&.source_buffer.name
|
342
37
|
end
|
343
38
|
|
344
|
-
# Get the source code of node.
|
345
|
-
#
|
346
|
-
# @return [String] source code.
|
347
|
-
def to_source
|
348
|
-
loc.expression&.source
|
349
|
-
end
|
350
|
-
|
351
39
|
# Get the column of node.
|
352
40
|
#
|
353
41
|
# @return [Integer] column.
|
@@ -362,197 +50,6 @@ module Parser::AST
|
|
362
50
|
loc.expression.line
|
363
51
|
end
|
364
52
|
|
365
|
-
# Get child node by the name.
|
366
|
-
#
|
367
|
-
# @param child_name [String] name of child node.
|
368
|
-
# @return [Parser::AST::Node] the child node.
|
369
|
-
def child_node_by_name(child_name)
|
370
|
-
direct_child_name, nested_child_name = child_name.to_s.split('.', 2)
|
371
|
-
if respond_to?(direct_child_name)
|
372
|
-
child_node = send(direct_child_name)
|
373
|
-
|
374
|
-
return child_node.child_node_by_name(nested_child_name) if nested_child_name
|
375
|
-
|
376
|
-
return nil if child_node.nil?
|
377
|
-
|
378
|
-
return child_node if child_node.is_a?(Parser::AST::Node)
|
379
|
-
|
380
|
-
return child_node
|
381
|
-
end
|
382
|
-
|
383
|
-
raise Synvert::Core::MethodNotSupported,
|
384
|
-
"child_node_by_name is not handled for #{debug_info}, child_name: #{child_name}"
|
385
|
-
end
|
386
|
-
|
387
|
-
# Get the source range of child node.
|
388
|
-
#
|
389
|
-
# @param child_name [String] name of child node.
|
390
|
-
# @return [Parser::Source::Range] source range of child node.
|
391
|
-
def child_node_range(child_name)
|
392
|
-
case [type, child_name.to_sym]
|
393
|
-
when %i[block pipes], %i[def parentheses], %i[defs parentheses]
|
394
|
-
Parser::Source::Range.new(
|
395
|
-
'(string)',
|
396
|
-
arguments.first.loc.expression.begin_pos - 1,
|
397
|
-
arguments.last.loc.expression.end_pos + 1
|
398
|
-
)
|
399
|
-
when %i[block arguments], %i[def arguments], %i[defs arguments]
|
400
|
-
Parser::Source::Range.new(
|
401
|
-
'(string)',
|
402
|
-
arguments.first.loc.expression.begin_pos,
|
403
|
-
arguments.last.loc.expression.end_pos
|
404
|
-
)
|
405
|
-
when %i[class name], %i[def name], %i[defs name]
|
406
|
-
loc.name
|
407
|
-
when %i[defs dot]
|
408
|
-
loc.operator
|
409
|
-
when %i[defs self]
|
410
|
-
Parser::Source::Range.new('(string)', loc.operator.begin_pos - 'self'.length, loc.operator.begin_pos)
|
411
|
-
when %i[send dot], %i[csend dot]
|
412
|
-
loc.dot
|
413
|
-
when %i[send message], %i[csend message]
|
414
|
-
if loc.operator
|
415
|
-
Parser::Source::Range.new('(string)', loc.selector.begin_pos, loc.operator.end_pos)
|
416
|
-
else
|
417
|
-
loc.selector
|
418
|
-
end
|
419
|
-
when %i[send parentheses], %i[csend parentheses]
|
420
|
-
if loc.begin && loc.end
|
421
|
-
Parser::Source::Range.new('(string)', loc.begin.begin_pos, loc.end.end_pos)
|
422
|
-
end
|
423
|
-
else
|
424
|
-
direct_child_name, nested_child_name = child_name.to_s.split('.', 2)
|
425
|
-
if respond_to?(direct_child_name)
|
426
|
-
child_node = send(direct_child_name)
|
427
|
-
|
428
|
-
return child_node.child_node_range(nested_child_name) if nested_child_name
|
429
|
-
|
430
|
-
return nil if child_node.nil?
|
431
|
-
|
432
|
-
if child_node.is_a?(Parser::AST::Node)
|
433
|
-
return(
|
434
|
-
Parser::Source::Range.new(
|
435
|
-
'(string)',
|
436
|
-
child_node.loc.expression.begin_pos,
|
437
|
-
child_node.loc.expression.end_pos
|
438
|
-
)
|
439
|
-
)
|
440
|
-
end
|
441
|
-
|
442
|
-
# arguments
|
443
|
-
return nil if child_node.empty?
|
444
|
-
|
445
|
-
return(
|
446
|
-
Parser::Source::Range.new(
|
447
|
-
'(string)',
|
448
|
-
child_node.first.loc.expression.begin_pos,
|
449
|
-
child_node.last.loc.expression.end_pos
|
450
|
-
)
|
451
|
-
)
|
452
|
-
end
|
453
|
-
|
454
|
-
raise Synvert::Core::MethodNotSupported,
|
455
|
-
"child_node_range is not handled for #{debug_info}, child_name: #{child_name}"
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
# Recursively iterate all child nodes of node.
|
460
|
-
#
|
461
|
-
# @yield [child] Gives a child node.
|
462
|
-
# @yieldparam child [Parser::AST::Node] child node
|
463
|
-
def recursive_children(&block)
|
464
|
-
children.each do |child|
|
465
|
-
if child.is_a?(Parser::AST::Node)
|
466
|
-
stop = yield child
|
467
|
-
child.recursive_children(&block) unless stop == :stop
|
468
|
-
end
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
# Match node with rules.
|
473
|
-
# It provides some additional keywords to match rules, +any+, +contain+, +not+, +in+, +not_in+, +gt+, +gte+, +lt+, +lte+.
|
474
|
-
# @example
|
475
|
-
# type: 'send', arguments: { any: 'Lifo::ShowExceptions' }
|
476
|
-
# type: { in: ['send', 'csend'] }
|
477
|
-
# type: :send, arguments: { length: { gt: 2 } }
|
478
|
-
# @param rules [Hash] rules to match.
|
479
|
-
# @return true if matches.
|
480
|
-
def match?(rules)
|
481
|
-
keywords = %i[any contain not in not_in gt gte lt lte]
|
482
|
-
flat_hash(rules).keys.all? do |multi_keys|
|
483
|
-
last_key = multi_keys.last
|
484
|
-
actual = keywords.include?(last_key) ? actual_value(multi_keys[0...-1]) : actual_value(multi_keys)
|
485
|
-
expected = expected_value(rules, multi_keys)
|
486
|
-
case last_key
|
487
|
-
when :any, :contain
|
488
|
-
actual.any? { |actual_value| match_value?(actual_value, expected) }
|
489
|
-
when :not
|
490
|
-
!match_value?(actual, expected)
|
491
|
-
when :in
|
492
|
-
expected.any? { |expected_value| match_value?(actual, expected_value) }
|
493
|
-
when :not_in
|
494
|
-
expected.all? { |expected_value| !match_value?(actual, expected_value) }
|
495
|
-
when :gt
|
496
|
-
actual > expected
|
497
|
-
when :gte
|
498
|
-
actual >= expected
|
499
|
-
when :lt
|
500
|
-
actual < expected
|
501
|
-
when :lte
|
502
|
-
actual <= expected
|
503
|
-
else
|
504
|
-
match_value?(actual, expected)
|
505
|
-
end
|
506
|
-
end
|
507
|
-
end
|
508
|
-
|
509
|
-
# Get rewritten source code.
|
510
|
-
# @example
|
511
|
-
# node.rewritten_source("create({{arguments}})") # "create(:post)"
|
512
|
-
# @param code [String] raw code.
|
513
|
-
# @return [String] rewritten code, replace string in block !{{ }} in raw code.
|
514
|
-
# @raise [Synvert::Core::MethodNotSupported] if string in block !{{ }} does not support.
|
515
|
-
def rewritten_source(code)
|
516
|
-
code.gsub(/{{(.*?)}}/m) do
|
517
|
-
old_code = Regexp.last_match(1)
|
518
|
-
if respond_to?(old_code.split('.').first)
|
519
|
-
evaluated = child_node_by_name(old_code)
|
520
|
-
case evaluated
|
521
|
-
when Parser::AST::Node
|
522
|
-
if evaluated.type == :args
|
523
|
-
evaluated.loc.expression.source[1...-1]
|
524
|
-
else
|
525
|
-
evaluated.loc.expression.source
|
526
|
-
end
|
527
|
-
when Array
|
528
|
-
if evaluated.size > 0
|
529
|
-
file_source = evaluated.first.loc.expression.source_buffer.source
|
530
|
-
source = file_source[evaluated.first.loc.expression.begin_pos...evaluated.last.loc.expression.end_pos]
|
531
|
-
lines = source.split "\n"
|
532
|
-
lines_count = lines.length
|
533
|
-
if lines_count > 1 && lines_count == evaluated.size
|
534
|
-
new_code = []
|
535
|
-
lines.each_with_index { |line, index|
|
536
|
-
new_code << (index == 0 ? line : line[evaluated.first.indent - 2..-1])
|
537
|
-
}
|
538
|
-
new_code.join("\n")
|
539
|
-
else
|
540
|
-
source
|
541
|
-
end
|
542
|
-
end
|
543
|
-
when String, Symbol, Integer, Float
|
544
|
-
evaluated
|
545
|
-
when NilClass
|
546
|
-
'nil'
|
547
|
-
else
|
548
|
-
raise Synvert::Core::MethodNotSupported, "rewritten_source is not handled for #{evaluated.inspect}"
|
549
|
-
end
|
550
|
-
else
|
551
|
-
"{{#{old_code}}}"
|
552
|
-
end
|
553
|
-
end
|
554
|
-
end
|
555
|
-
|
556
53
|
# Strip curly braces for hash.
|
557
54
|
# @example
|
558
55
|
# node # s(:hash, s(:pair, s(:sym, :foo), s(:str, "bar")))
|
@@ -627,141 +124,5 @@ module Parser::AST
|
|
627
124
|
to_source
|
628
125
|
end
|
629
126
|
end
|
630
|
-
|
631
|
-
# Convert node to a hash, so that it can be converted to a json.
|
632
|
-
def to_hash
|
633
|
-
result = { type: type }
|
634
|
-
if TYPE_CHILDREN[type]
|
635
|
-
TYPE_CHILDREN[type].each do |key|
|
636
|
-
value = send(key)
|
637
|
-
result[key] =
|
638
|
-
case value
|
639
|
-
when Array
|
640
|
-
value.map { |v| v.respond_to?(:to_hash) ? v.to_hash : v }
|
641
|
-
when Parser::AST::Node
|
642
|
-
value.to_hash
|
643
|
-
else
|
644
|
-
value
|
645
|
-
end
|
646
|
-
end
|
647
|
-
else
|
648
|
-
result[:children] = children.map { |c| c.respond_to?(:to_hash) ? c.to_hash : c }
|
649
|
-
end
|
650
|
-
result
|
651
|
-
end
|
652
|
-
|
653
|
-
private
|
654
|
-
|
655
|
-
# Compare actual value with expected value.
|
656
|
-
#
|
657
|
-
# @param actual [Object] actual value.
|
658
|
-
# @param expected [Object] expected value.
|
659
|
-
# @return [Boolean]
|
660
|
-
# @raise [Synvert::Core::MethodNotSupported] if expected class is not supported.
|
661
|
-
def match_value?(actual, expected)
|
662
|
-
return true if actual == expected
|
663
|
-
|
664
|
-
case expected
|
665
|
-
when Symbol
|
666
|
-
if actual.is_a?(Parser::AST::Node)
|
667
|
-
actual.to_source == ":#{expected}" || actual.to_source == expected.to_s
|
668
|
-
else
|
669
|
-
actual.to_sym == expected
|
670
|
-
end
|
671
|
-
when String
|
672
|
-
if actual.is_a?(Parser::AST::Node)
|
673
|
-
actual.to_source == expected || actual.to_source == unwrap_quote(expected) ||
|
674
|
-
unwrap_quote(actual.to_source) == expected || unwrap_quote(actual.to_source) == unwrap_quote(expected)
|
675
|
-
else
|
676
|
-
actual.to_s == expected || wrap_quote(actual.to_s) == expected
|
677
|
-
end
|
678
|
-
when Regexp
|
679
|
-
if actual.is_a?(Parser::AST::Node)
|
680
|
-
actual.to_source =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
681
|
-
else
|
682
|
-
actual.to_s =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
683
|
-
end
|
684
|
-
when Array
|
685
|
-
return false unless expected.length == actual.length
|
686
|
-
|
687
|
-
actual.zip(expected).all? { |a, e| match_value?(a, e) }
|
688
|
-
when NilClass
|
689
|
-
if actual.is_a?(Parser::AST::Node)
|
690
|
-
:nil == actual.type
|
691
|
-
else
|
692
|
-
actual.nil?
|
693
|
-
end
|
694
|
-
when Numeric
|
695
|
-
if actual.is_a?(Parser::AST::Node)
|
696
|
-
actual.children[0] == expected
|
697
|
-
else
|
698
|
-
actual == expected
|
699
|
-
end
|
700
|
-
when TrueClass
|
701
|
-
:true == actual.type
|
702
|
-
when FalseClass
|
703
|
-
:false == actual.type
|
704
|
-
when Parser::AST::Node
|
705
|
-
actual == expected
|
706
|
-
when Synvert::Core::Rewriter::AnyValue
|
707
|
-
!actual.nil?
|
708
|
-
else
|
709
|
-
raise Synvert::Core::MethodNotSupported, "#{expected.class} is not handled for match_value?"
|
710
|
-
end
|
711
|
-
end
|
712
|
-
|
713
|
-
# Convert a hash to flat one.
|
714
|
-
#
|
715
|
-
# @example
|
716
|
-
# flat_hash(type: 'block', caller: {type: 'send', receiver: 'RSpec'})
|
717
|
-
# # {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
|
718
|
-
# @param h [Hash] original hash.
|
719
|
-
# @return flatten hash.
|
720
|
-
def flat_hash(h, k = [])
|
721
|
-
new_hash = {}
|
722
|
-
h.each_pair do |key, val|
|
723
|
-
if val.is_a?(Hash)
|
724
|
-
new_hash.merge!(flat_hash(val, k + [key]))
|
725
|
-
else
|
726
|
-
new_hash[k + [key]] = val
|
727
|
-
end
|
728
|
-
end
|
729
|
-
new_hash
|
730
|
-
end
|
731
|
-
|
732
|
-
# Get actual value from the node.
|
733
|
-
#
|
734
|
-
# @param multi_keys [Array<Symbol, String>]
|
735
|
-
# @return [Object] actual value.
|
736
|
-
def actual_value(multi_keys)
|
737
|
-
multi_keys.inject(self) { |n, key| n.send(key) if n }
|
738
|
-
end
|
739
|
-
|
740
|
-
# Get expected value from rules.
|
741
|
-
#
|
742
|
-
# @param rules [Hash]
|
743
|
-
# @param multi_keys [Array<Symbol>]
|
744
|
-
# @return [Object] expected value.
|
745
|
-
def expected_value(rules, multi_keys)
|
746
|
-
multi_keys.inject(rules) { |o, key| o[key] }
|
747
|
-
end
|
748
|
-
|
749
|
-
# Wrap the string with single or double quote.
|
750
|
-
def wrap_quote(string)
|
751
|
-
if string.include?("'")
|
752
|
-
"\"#{string}\""
|
753
|
-
else
|
754
|
-
"'#{string}'"
|
755
|
-
end
|
756
|
-
end
|
757
|
-
|
758
|
-
# Unwrap the quote from the string.
|
759
|
-
def unwrap_quote(string)
|
760
|
-
if (string[0] == '"' && string[-1] == '"') || (string[0] == "'" && string[-1] == "'")
|
761
|
-
string[1...-1]
|
762
|
-
else
|
763
|
-
string
|
764
|
-
end
|
765
|
-
end
|
766
127
|
end
|
767
128
|
end
|