synvert-core 1.4.0 → 1.6.0
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 +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
|