synvert-core 0.63.1 → 1.0.1
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 +1 -1
- data/.gitignore +4 -0
- data/CHANGELOG.md +9 -1
- data/Guardfile +11 -2
- data/README.md +74 -34
- data/Rakefile +15 -1
- data/lib/synvert/core/array_ext.rb +41 -0
- data/lib/synvert/core/configuration.rb +12 -0
- data/lib/synvert/core/engine/erb.rb +9 -8
- data/lib/synvert/core/exceptions.rb +0 -4
- data/lib/synvert/core/node_ext.rb +232 -128
- data/lib/synvert/core/node_query/compiler/array.rb +34 -0
- data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
- data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
- data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
- data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
- data/lib/synvert/core/node_query/compiler/float.rb +23 -0
- data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
- data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
- data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
- data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
- data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
- data/lib/synvert/core/node_query/compiler/string.rb +34 -0
- data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
- data/lib/synvert/core/node_query/compiler.rb +24 -0
- data/lib/synvert/core/node_query/lexer.rex +96 -0
- data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
- data/lib/synvert/core/node_query/parser.racc.rb +518 -0
- data/lib/synvert/core/node_query/parser.y +84 -0
- data/lib/synvert/core/node_query.rb +36 -0
- data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
- data/lib/synvert/core/rewriter/action/delete_action.rb +17 -8
- data/lib/synvert/core/rewriter/action/insert_action.rb +16 -7
- data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -2
- data/lib/synvert/core/rewriter/action/prepend_action.rb +3 -2
- data/lib/synvert/core/rewriter/action/remove_action.rb +16 -10
- data/lib/synvert/core/rewriter/action/replace_action.rb +15 -5
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +18 -11
- data/lib/synvert/core/rewriter/action/replace_with_action.rb +6 -5
- data/lib/synvert/core/rewriter/action/wrap_action.rb +16 -7
- data/lib/synvert/core/rewriter/action.rb +22 -10
- data/lib/synvert/core/rewriter/any_value.rb +1 -0
- data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition.rb +11 -3
- data/lib/synvert/core/rewriter/gem_spec.rb +6 -3
- data/lib/synvert/core/rewriter/helper.rb +7 -4
- data/lib/synvert/core/rewriter/instance.rb +217 -104
- data/lib/synvert/core/rewriter/ruby_version.rb +4 -4
- data/lib/synvert/core/rewriter/scope/goto_scope.rb +5 -6
- data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
- data/lib/synvert/core/rewriter/scope/within_scope.rb +10 -5
- data/lib/synvert/core/rewriter/scope.rb +8 -0
- data/lib/synvert/core/rewriter/warning.rb +1 -1
- data/lib/synvert/core/rewriter.rb +91 -43
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +22 -6
- data/spec/synvert/core/engine/erb_spec.rb +2 -2
- data/spec/synvert/core/node_ext_spec.rb +36 -12
- data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
- data/spec/synvert/core/node_query/parser_spec.rb +270 -0
- data/spec/synvert/core/rewriter/action_spec.rb +0 -4
- data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
- data/spec/synvert/core/rewriter/gem_spec_spec.rb +1 -1
- data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
- data/spec/synvert/core/rewriter/instance_spec.rb +31 -20
- data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
- data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
- data/spec/synvert/core/rewriter_spec.rb +4 -2
- data/synvert-core-ruby.gemspec +7 -2
- metadata +91 -4
@@ -1,11 +1,74 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Parser::AST
|
4
|
-
# Parser::AST::Node
|
4
|
+
# Extend Parser::AST::Node.
|
5
|
+
# {https://github.com/whitequark/parser/blob/master/lib/parser/ast/node.rb}
|
6
|
+
#
|
7
|
+
# Rules
|
8
|
+
#
|
9
|
+
# Synvert compares ast nodes with key / value pairs, each ast node has
|
10
|
+
# multiple attributes, e.g. +receiver+, +message+ and +arguments+, it
|
11
|
+
# matches only when all of key / value pairs match.
|
12
|
+
#
|
13
|
+
# +type: 'send', message: :include, arguments: ['FactoryGirl::Syntax::Methods']+
|
14
|
+
#
|
15
|
+
# Synvert does comparison based on the value type
|
16
|
+
#
|
17
|
+
# 1. if value is a symbol, then compares ast node value as symbol, e.g. +message: :include+
|
18
|
+
# 2. if value is a string, then compares ast node original source code, e.g. +name: 'Synvert::Application'+
|
19
|
+
# 3. if value is a regexp, then compares ast node original source code, e.g. +message: /find_all_by_/+
|
20
|
+
# 4. if value is an array, then compares each ast node, e.g. +arguments: ['FactoryGirl::Syntax::Methods']+
|
21
|
+
# 5. if value is nil, then check if ast node is nil, e.g. +arguments: [nil]+
|
22
|
+
# 6. if value is true or false, then check if ast node is :true or :false, e.g. +arguments: [false]+
|
23
|
+
# 7. if value is ast, then compare ast node directly, e.g. +to_ast: Parser::CurrentRuby.parse("self.class.serialized_attributes")+
|
24
|
+
#
|
25
|
+
# It can also compare nested key / value pairs, like
|
26
|
+
#
|
27
|
+
# +type: 'send', receiver: { type: 'send', receiver: { type: 'send', message: 'config' }, message: 'active_record' }, message: 'identity_map='+
|
28
|
+
#
|
29
|
+
# Source Code to Ast Node
|
30
|
+
# {https://synvert-playground.xinminlabs.com?language=ruby}
|
5
31
|
class Node
|
6
|
-
#
|
32
|
+
# Initialize a Node.
|
7
33
|
#
|
8
|
-
#
|
34
|
+
# It extends Parser::AST::Node and set parent for its child nodes.
|
35
|
+
def initialize(type, children = [], properties = {})
|
36
|
+
@mutable_attributes = {}
|
37
|
+
super
|
38
|
+
# children could be nil for s(:array)
|
39
|
+
Array(children).each do |child_node|
|
40
|
+
if child_node.is_a?(Parser::AST::Node)
|
41
|
+
child_node.parent = self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get the parent node.
|
47
|
+
# @return [Parser::AST::Node] parent node.
|
48
|
+
def parent
|
49
|
+
@mutable_attributes[:parent]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set the parent node.
|
53
|
+
# @param node [Parser::AST::Node] parent node.
|
54
|
+
def parent=(node)
|
55
|
+
@mutable_attributes[:parent] = node
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get the sibling nodes.
|
59
|
+
# @return [Array<Parser::AST::Node>] sibling nodes.
|
60
|
+
def siblings
|
61
|
+
index = parent.children.index(self)
|
62
|
+
parent.children[index + 1..]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get the name of node.
|
66
|
+
# It supports :arg, :blockarg, :class, :const, :cvar, :def, :defs, :ivar,
|
67
|
+
# :lvar, :mlhs, :module and :restarg nodes.
|
68
|
+
# @example
|
69
|
+
# node # => s(:class, s(:const, nil, :Synvert), nil, nil)
|
70
|
+
# node.name # => s(:const, nil, :Synvert)
|
71
|
+
# @return [Parser::AST::Node] name of node.
|
9
72
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
10
73
|
def name
|
11
74
|
case type
|
@@ -20,9 +83,12 @@ module Parser::AST
|
|
20
83
|
end
|
21
84
|
end
|
22
85
|
|
23
|
-
# Get parent_class
|
24
|
-
#
|
25
|
-
# @
|
86
|
+
# Get parent_class of node.
|
87
|
+
# It supports :class node.
|
88
|
+
# @example
|
89
|
+
# node # s(:class, s(:const, nil, :Post), s(:const, s(:const, nil, :ActiveRecord), :Base), nil)
|
90
|
+
# node.parent_class # s(:const, s(:const, nil, :ActiveRecord), :Base)
|
91
|
+
# @return [Parser::AST::Node] parent_class of node.
|
26
92
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
27
93
|
def parent_class
|
28
94
|
if :class == type
|
@@ -32,9 +98,12 @@ module Parser::AST
|
|
32
98
|
end
|
33
99
|
end
|
34
100
|
|
35
|
-
# Get parent constant
|
36
|
-
#
|
37
|
-
# @
|
101
|
+
# Get parent constant of node.
|
102
|
+
# It supports :const node.
|
103
|
+
# @example
|
104
|
+
# node # s(:const, s(:const, nil, :Synvert), :Node)
|
105
|
+
# node.parent_const # s(:const, nil, :Synvert)
|
106
|
+
# @return [Parser::AST::Node] parent const of node.
|
38
107
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
39
108
|
def parent_const
|
40
109
|
if :const == type
|
@@ -44,9 +113,12 @@ module Parser::AST
|
|
44
113
|
end
|
45
114
|
end
|
46
115
|
|
47
|
-
# Get receiver
|
48
|
-
#
|
49
|
-
# @
|
116
|
+
# Get receiver of node.
|
117
|
+
# It support :csend and :send nodes.
|
118
|
+
# @example
|
119
|
+
# node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post))
|
120
|
+
# node.receiver # s(:const, nil, :FactoryGirl)
|
121
|
+
# @return [Parser::AST::Node] receiver of node.
|
50
122
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
51
123
|
def receiver
|
52
124
|
if %i[csend send].include?(type)
|
@@ -56,9 +128,12 @@ module Parser::AST
|
|
56
128
|
end
|
57
129
|
end
|
58
130
|
|
59
|
-
# Get message
|
60
|
-
#
|
61
|
-
# @
|
131
|
+
# Get message of node.
|
132
|
+
# It support :csend, :send, :super and :zsuper nodes.
|
133
|
+
# @example
|
134
|
+
# node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post))
|
135
|
+
# node.message # :create
|
136
|
+
# @return [Symbol] mesage of node.
|
62
137
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
63
138
|
def message
|
64
139
|
case type
|
@@ -71,16 +146,19 @@ module Parser::AST
|
|
71
146
|
end
|
72
147
|
end
|
73
148
|
|
74
|
-
# Get arguments
|
75
|
-
#
|
76
|
-
# @
|
149
|
+
# Get arguments of node.
|
150
|
+
# It supports :block, :csend, :def, :defined?, :defs and :send nodes.
|
151
|
+
# @example
|
152
|
+
# node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post"))))
|
153
|
+
# node.arguments # [s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post")))]
|
154
|
+
# @return [Array<Parser::AST::Node>] arguments of node.
|
77
155
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
78
156
|
def arguments
|
79
157
|
case type
|
80
158
|
when :def, :block
|
81
|
-
children[1]
|
159
|
+
children[1].children
|
82
160
|
when :defs
|
83
|
-
children[2]
|
161
|
+
children[2].children
|
84
162
|
when :send, :csend
|
85
163
|
children[2..-1]
|
86
164
|
when :defined?
|
@@ -90,9 +168,12 @@ module Parser::AST
|
|
90
168
|
end
|
91
169
|
end
|
92
170
|
|
93
|
-
# Get caller
|
94
|
-
#
|
95
|
-
# @
|
171
|
+
# Get caller of node.
|
172
|
+
# It support :block node.
|
173
|
+
# @example
|
174
|
+
# node # s(:block, s(:send, s(:const, nil, :RSpec), :configure), s(:args, s(:arg, :config)), nil)
|
175
|
+
# node.caller # s(:send, s(:const, nil, :RSpec), :configure)
|
176
|
+
# @return [Parser::AST::Node] caller of node.
|
96
177
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
97
178
|
def caller
|
98
179
|
if :block == type
|
@@ -102,9 +183,12 @@ module Parser::AST
|
|
102
183
|
end
|
103
184
|
end
|
104
185
|
|
105
|
-
# Get body
|
106
|
-
#
|
107
|
-
# @
|
186
|
+
# Get body of node.
|
187
|
+
# It supports :begin, :block, :class, :def, :defs and :module node.
|
188
|
+
# @example
|
189
|
+
# 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)))
|
190
|
+
# node.body # [s(:send, nil, :include, s(:const, s(:const, nil, :EmailSpec), :Helpers))]
|
191
|
+
# @return [Array<Parser::AST::Node>] body of node.
|
108
192
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
109
193
|
def body
|
110
194
|
case type
|
@@ -123,9 +207,12 @@ module Parser::AST
|
|
123
207
|
end
|
124
208
|
end
|
125
209
|
|
126
|
-
# Get condition
|
127
|
-
#
|
128
|
-
# @
|
210
|
+
# Get condition of node.
|
211
|
+
# It supports :if node.
|
212
|
+
# @example
|
213
|
+
# node # s(:if, s(:defined?, s(:const, nil, :Bundler)), nil, nil)
|
214
|
+
# node.condition # s(:defined?, s(:const, nil, :Bundler))
|
215
|
+
# @return [Parser::AST::Node] condition of node.
|
129
216
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
130
217
|
def condition
|
131
218
|
if :if == type
|
@@ -135,9 +222,11 @@ module Parser::AST
|
|
135
222
|
end
|
136
223
|
end
|
137
224
|
|
138
|
-
# Get keys
|
139
|
-
#
|
140
|
-
#
|
225
|
+
# Get keys of :hash node.
|
226
|
+
# @example
|
227
|
+
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
|
228
|
+
# node.keys # [s(:sym, :foo), s(:str, "foo")]
|
229
|
+
# @return [Array<Parser::AST::Node>] keys of node.
|
141
230
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
142
231
|
def keys
|
143
232
|
if :hash == type
|
@@ -147,9 +236,11 @@ module Parser::AST
|
|
147
236
|
end
|
148
237
|
end
|
149
238
|
|
150
|
-
# Get values
|
151
|
-
#
|
152
|
-
#
|
239
|
+
# Get values of :hash node.
|
240
|
+
# @example
|
241
|
+
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
|
242
|
+
# node.values # [s(:sym, :bar), s(:str, "bar")]
|
243
|
+
# @return [Array<Parser::AST::Node>] values of node.
|
153
244
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
154
245
|
def values
|
155
246
|
if :hash == type
|
@@ -159,9 +250,11 @@ module Parser::AST
|
|
159
250
|
end
|
160
251
|
end
|
161
252
|
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
253
|
+
# Check if :hash node contains specified key.
|
254
|
+
# @example
|
255
|
+
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
|
256
|
+
# node.key?(:foo) # true
|
257
|
+
# @param [Symbol, String] key value.
|
165
258
|
# @return [Boolean] true if specified key exists.
|
166
259
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
167
260
|
def key?(key)
|
@@ -172,10 +265,12 @@ module Parser::AST
|
|
172
265
|
end
|
173
266
|
end
|
174
267
|
|
175
|
-
# Get hash value node according to specified key.
|
176
|
-
#
|
177
|
-
#
|
178
|
-
#
|
268
|
+
# Get :hash value node according to specified key.
|
269
|
+
# @example
|
270
|
+
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
|
271
|
+
# node.hash_value(:foo) # s(:sym, :bar)
|
272
|
+
# @param [Symbol, String] key value.
|
273
|
+
# @return [Parser::AST::Node] hash value of node.
|
179
274
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
180
275
|
def hash_value(key)
|
181
276
|
if :hash == type
|
@@ -187,8 +282,10 @@ module Parser::AST
|
|
187
282
|
end
|
188
283
|
|
189
284
|
# Get key node of hash :pair node.
|
190
|
-
#
|
191
|
-
#
|
285
|
+
# @example
|
286
|
+
# node # s(:pair, s(:sym, :foo), s(:str, "bar"))
|
287
|
+
# node.key # s(:sym, :foo)
|
288
|
+
# @return [Parser::AST::Node] key of node.
|
192
289
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
193
290
|
def key
|
194
291
|
if :pair == type
|
@@ -199,8 +296,10 @@ module Parser::AST
|
|
199
296
|
end
|
200
297
|
|
201
298
|
# Get value node of hash :pair node.
|
202
|
-
#
|
203
|
-
#
|
299
|
+
# @example
|
300
|
+
# node # s(:pair, s(:sym, :foo), s(:str, "bar"))
|
301
|
+
# node.value # s(:str, "bar")
|
302
|
+
# @return [Parser::AST::Node] value of node.
|
204
303
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
205
304
|
def value
|
206
305
|
if :pair == type
|
@@ -210,9 +309,12 @@ module Parser::AST
|
|
210
309
|
end
|
211
310
|
end
|
212
311
|
|
213
|
-
# Return the left value.
|
214
|
-
#
|
215
|
-
# @
|
312
|
+
# Return the left value of node.
|
313
|
+
# It supports :and, :cvagn, :lvasgn, :masgn, :or and :or_asgn nodes.
|
314
|
+
# @example
|
315
|
+
# node # s(:masgn, s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b)), s(:array, s(:int, 1), s(:int, 2)))
|
316
|
+
# node.left_value # s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b))
|
317
|
+
# @return [Parser::AST::Node] left value of node.
|
216
318
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
217
319
|
def left_value
|
218
320
|
if %i[masgn lvasgn ivasgn cvasgn and or].include? type
|
@@ -224,9 +326,12 @@ module Parser::AST
|
|
224
326
|
end
|
225
327
|
end
|
226
328
|
|
227
|
-
# Return the right value.
|
228
|
-
#
|
229
|
-
# @
|
329
|
+
# Return the right value of node.
|
330
|
+
# It supports :cvasgn, :ivasgn, :lvasgn, :masgn, :or and :or_asgn nodes.
|
331
|
+
# @example
|
332
|
+
# node # s(:masgn, s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b)), s(:array, s(:int, 1), s(:int, 2)))
|
333
|
+
# node.right_value # s(:array, s(:int, 1), s(:int, 2))
|
334
|
+
# @return [Array<Parser::AST::Node>] right value of node.
|
230
335
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
231
336
|
def right_value
|
232
337
|
if %i[masgn lvasgn ivasgn cvasgn or_asgn and or].include? type
|
@@ -236,8 +341,11 @@ module Parser::AST
|
|
236
341
|
end
|
237
342
|
end
|
238
343
|
|
239
|
-
# Return the exact value.
|
240
|
-
#
|
344
|
+
# Return the exact value of node.
|
345
|
+
# It supports :array, :begin, :erange, :false, :float, :irange, :int, :str, :sym and :true nodes.
|
346
|
+
# @example
|
347
|
+
# node # s(:array, s(:str, "str"), s(:sym, :str))
|
348
|
+
# node.to_value # ['str', :str]
|
241
349
|
# @return [Object] exact value.
|
242
350
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
243
351
|
def to_value
|
@@ -248,6 +356,8 @@ module Parser::AST
|
|
248
356
|
true
|
249
357
|
when :false
|
250
358
|
false
|
359
|
+
when :nil
|
360
|
+
nil
|
251
361
|
when :array
|
252
362
|
children.map(&:to_value)
|
253
363
|
when :irange
|
@@ -261,10 +371,11 @@ module Parser::AST
|
|
261
371
|
end
|
262
372
|
end
|
263
373
|
|
264
|
-
# Respond key value for hash node, e.g.
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
374
|
+
# Respond key value and source for hash node, e.g.
|
375
|
+
# @example
|
376
|
+
# node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)))
|
377
|
+
# node.foo_value # :bar
|
378
|
+
# node.foo_source # ":bar"
|
268
379
|
def method_missing(method_name, *args, &block)
|
269
380
|
if :args == type && children.respond_to?(method_name)
|
270
381
|
return children.send(method_name, *args, &block)
|
@@ -299,12 +410,9 @@ module Parser::AST
|
|
299
410
|
super
|
300
411
|
end
|
301
412
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
413
|
+
# Return the debug info.
|
414
|
+
#
|
415
|
+
# @return [String] file, line, source and node.
|
308
416
|
def debug_info
|
309
417
|
"\n" +
|
310
418
|
[
|
@@ -315,64 +423,49 @@ module Parser::AST
|
|
315
423
|
].join("\n")
|
316
424
|
end
|
317
425
|
|
318
|
-
# Get the file name of
|
426
|
+
# Get the file name of node.
|
319
427
|
#
|
320
428
|
# @return [String] file name.
|
321
429
|
def filename
|
322
430
|
loc.expression&.source_buffer.name
|
323
431
|
end
|
324
432
|
|
325
|
-
# Get the source code of
|
433
|
+
# Get the source code of node.
|
326
434
|
#
|
327
435
|
# @return [String] source code.
|
328
436
|
def to_source
|
329
437
|
loc.expression&.source
|
330
438
|
end
|
331
439
|
|
332
|
-
# Get the column of
|
440
|
+
# Get the column of node.
|
333
441
|
#
|
334
442
|
# @return [Integer] column.
|
335
443
|
def column
|
336
444
|
loc.expression.column
|
337
445
|
end
|
338
446
|
|
339
|
-
# Get the line of
|
447
|
+
# Get the line of node.
|
340
448
|
#
|
341
449
|
# @return [Integer] line.
|
342
450
|
def line
|
343
451
|
loc.expression.line
|
344
452
|
end
|
345
453
|
|
346
|
-
# Get child node by
|
454
|
+
# Get child node by the name.
|
347
455
|
#
|
348
|
-
# @param [String] name of child node.
|
456
|
+
# @param child_name [String] name of child node.
|
349
457
|
# @return [Parser::AST::Node] the child node.
|
350
458
|
def child_node_by_name(child_name)
|
351
459
|
direct_child_name, nested_child_name = child_name.to_s.split('.', 2)
|
352
460
|
if respond_to?(direct_child_name)
|
353
461
|
child_node = send(direct_child_name)
|
354
462
|
|
355
|
-
if nested_child_name
|
356
|
-
if child_node.is_a?(Array)
|
357
|
-
child_direct_child_name, child_nested_child_name = nested_child_name.split('.', 2)
|
358
|
-
child_direct_child_node = child_direct_child_name =~ /\A\d+\z/ ? child_node[child_direct_child_name.to_i - 1] : child_node.send(child_direct_child_name)
|
359
|
-
return child_direct_child_node.child_node_by_name(child_nested_child_name) if child_nested_child_name
|
360
|
-
return child_direct_child_node if child_direct_child_node
|
361
|
-
|
362
|
-
raise Synvert::Core::MethodNotSupported,
|
363
|
-
"child_node_by_name is not handled for #{debug_info}, child_name: #{child_name}"
|
364
|
-
end
|
365
|
-
|
366
|
-
return child_node.child_node_by_name(nested_child_name)
|
367
|
-
end
|
463
|
+
return child_node.child_node_by_name(nested_child_name) if nested_child_name
|
368
464
|
|
369
465
|
return nil if child_node.nil?
|
370
466
|
|
371
467
|
return child_node if child_node.is_a?(Parser::AST::Node)
|
372
468
|
|
373
|
-
# arguments
|
374
|
-
return nil if child_node.empty?
|
375
|
-
|
376
469
|
return child_node
|
377
470
|
end
|
378
471
|
|
@@ -382,12 +475,16 @@ module Parser::AST
|
|
382
475
|
|
383
476
|
# Get the source range of child node.
|
384
477
|
#
|
385
|
-
# @param [String] name of child node.
|
478
|
+
# @param child_name [String] name of child node.
|
386
479
|
# @return [Parser::Source::Range] source range of child node.
|
387
480
|
def child_node_range(child_name)
|
388
481
|
case [type, child_name.to_sym]
|
389
482
|
when %i[block pipes], %i[def parentheses], %i[defs parentheses]
|
390
|
-
Parser::Source::Range.new(
|
483
|
+
Parser::Source::Range.new(
|
484
|
+
'(string)',
|
485
|
+
arguments.first.loc.expression.begin_pos - 1,
|
486
|
+
arguments.last.loc.expression.end_pos + 1
|
487
|
+
)
|
391
488
|
when %i[block arguments], %i[def arguments], %i[defs arguments]
|
392
489
|
Parser::Source::Range.new(
|
393
490
|
'(string)',
|
@@ -417,28 +514,7 @@ module Parser::AST
|
|
417
514
|
if respond_to?(direct_child_name)
|
418
515
|
child_node = send(direct_child_name)
|
419
516
|
|
420
|
-
if nested_child_name
|
421
|
-
if child_node.is_a?(Array)
|
422
|
-
child_direct_child_name, child_nested_child_name = nested_child_name.split('.', 2)
|
423
|
-
child_direct_child_node = child_direct_child_name =~ /\A\d+\z/ ? child_node[child_direct_child_name.to_i - 1] : child_node.send(child_direct_child_name)
|
424
|
-
if child_nested_child_name
|
425
|
-
return child_direct_child_node.child_node_range(child_nested_child_name)
|
426
|
-
elsif child_direct_child_node
|
427
|
-
return (
|
428
|
-
Parser::Source::Range.new(
|
429
|
-
'(string)',
|
430
|
-
child_direct_child_node.loc.expression.begin_pos,
|
431
|
-
child_direct_child_node.loc.expression.end_pos
|
432
|
-
)
|
433
|
-
)
|
434
|
-
else
|
435
|
-
raise Synvert::Core::MethodNotSupported,
|
436
|
-
"child_node_range is not handled for #{debug_info}, child_name: #{child_name}"
|
437
|
-
end
|
438
|
-
end
|
439
|
-
|
440
|
-
return child_node.child_node_range(nested_child_name)
|
441
|
-
end
|
517
|
+
return child_node.child_node_range(nested_child_name) if nested_child_name
|
442
518
|
|
443
519
|
return nil if child_node.nil?
|
444
520
|
|
@@ -469,7 +545,7 @@ module Parser::AST
|
|
469
545
|
end
|
470
546
|
end
|
471
547
|
|
472
|
-
# Recursively iterate all child nodes of
|
548
|
+
# Recursively iterate all child nodes of node.
|
473
549
|
#
|
474
550
|
# @yield [child] Gives a child node.
|
475
551
|
# @yieldparam child [Parser::AST::Node] child node
|
@@ -482,8 +558,12 @@ module Parser::AST
|
|
482
558
|
end
|
483
559
|
end
|
484
560
|
|
485
|
-
# Match
|
486
|
-
#
|
561
|
+
# Match node with rules.
|
562
|
+
# It provides some additional keywords to match rules, +any+, +contain+, +not+, +in+, +not_in+, +gt+, +gte+, +lt+, +lte+.
|
563
|
+
# @example
|
564
|
+
# type: 'send', arguments: { any: 'Lifo::ShowExceptions' }
|
565
|
+
# type: { in: ['send', 'csend'] }
|
566
|
+
# type: :send, arguments: { length: { gt: 2 } }
|
487
567
|
# @param rules [Hash] rules to match.
|
488
568
|
# @return true if matches.
|
489
569
|
def match?(rules)
|
@@ -517,11 +597,10 @@ module Parser::AST
|
|
517
597
|
|
518
598
|
# Get rewritten source code.
|
519
599
|
# @example
|
520
|
-
# node.rewritten_source("create({{arguments}})")
|
521
|
-
#
|
600
|
+
# node.rewritten_source("create({{arguments}})") # "create(:post)"
|
522
601
|
# @param code [String] raw code.
|
523
|
-
# @return [String] rewritten code, replace string in block {{ }} in raw code.
|
524
|
-
# @raise [Synvert::Core::MethodNotSupported] if string in block {{ }} does not support.
|
602
|
+
# @return [String] rewritten code, replace string in block !{{ }} in raw code.
|
603
|
+
# @raise [Synvert::Core::MethodNotSupported] if string in block !{{ }} does not support.
|
525
604
|
def rewritten_source(code)
|
526
605
|
code.gsub(/{{(.*?)}}/m) do
|
527
606
|
old_code = Regexp.last_match(1)
|
@@ -563,47 +642,71 @@ module Parser::AST
|
|
563
642
|
end
|
564
643
|
end
|
565
644
|
|
566
|
-
#
|
645
|
+
# Strip curly braces for hash.
|
646
|
+
# @example
|
647
|
+
# node # s(:hash, s(:pair, s(:sym, :foo), s(:str, "bar")))
|
648
|
+
# node.strip_curly_braces # "foo: 'bar'"
|
649
|
+
# @return [String]
|
567
650
|
def strip_curly_braces
|
568
651
|
return to_source unless type == :hash
|
569
652
|
|
570
653
|
to_source.sub(/^{(.*)}$/) { Regexp.last_match(1).strip }
|
571
654
|
end
|
572
655
|
|
573
|
-
#
|
656
|
+
# Wrap curly braces for hash.
|
657
|
+
# @example
|
658
|
+
# node # s(:hash, s(:pair, s(:sym, :foo), s(:str, "bar")))
|
659
|
+
# node.wrap_curly_braces # "{ foo: 'bar' }"
|
660
|
+
# @return [String]
|
574
661
|
def wrap_curly_braces
|
575
662
|
return to_source unless type == :hash
|
576
663
|
|
577
664
|
"{ #{to_source} }"
|
578
665
|
end
|
579
666
|
|
580
|
-
#
|
667
|
+
# Get single quote string.
|
668
|
+
# @example
|
669
|
+
# node # s(:str, "foobar")
|
670
|
+
# node.to_single_quote # "'foobar'"
|
671
|
+
# @return [String]
|
581
672
|
def to_single_quote
|
582
673
|
return to_source unless type == :str
|
583
674
|
|
584
675
|
"'#{to_value}'"
|
585
676
|
end
|
586
677
|
|
587
|
-
#
|
678
|
+
# Convert string to symbol.
|
679
|
+
# @example
|
680
|
+
# node # s(:str, "foobar")
|
681
|
+
# node.to_symbol # ":foobar"
|
682
|
+
# @return [String]
|
588
683
|
def to_symbol
|
589
684
|
return to_source unless type == :str
|
590
685
|
|
591
686
|
":#{to_value}"
|
592
687
|
end
|
593
688
|
|
594
|
-
#
|
689
|
+
# Convert symbol to string.
|
690
|
+
# @example
|
691
|
+
# node # s(:sym, :foobar)
|
692
|
+
# node.to_string # "foobar"
|
693
|
+
# @return [String]
|
595
694
|
def to_string
|
596
695
|
return to_source unless type == :sym
|
597
696
|
|
598
697
|
to_value.to_s
|
599
698
|
end
|
600
699
|
|
601
|
-
#
|
700
|
+
# Convert lambda {} to -> {}
|
701
|
+
# @example
|
702
|
+
# node # s(:block, s(:send, nil, :lambda), s(:args), s(:send, nil, :foobar))
|
703
|
+
# node.to_lambda_literal # "-> { foobar }"
|
704
|
+
# @return [String]
|
602
705
|
def to_lambda_literal
|
603
706
|
if type == :block && caller.type == :send && caller.receiver.nil? && caller.message == :lambda
|
604
707
|
new_source = to_source
|
605
708
|
if arguments.size > 1
|
606
|
-
new_source = new_source[0...arguments.loc.
|
709
|
+
new_source = new_source[0...arguments.first.loc.expression.begin_pos - 2] + new_source[arguments.last.loc.expression.end_pos + 1..-1]
|
607
710
|
new_source = new_source.sub('lambda', "->(#{arguments.map(&:to_source).join(', ')})")
|
608
711
|
else
|
609
712
|
new_source = new_source.sub('lambda', '->')
|
@@ -620,7 +723,7 @@ module Parser::AST
|
|
620
723
|
#
|
621
724
|
# @param actual [Object] actual value.
|
622
725
|
# @param expected [Object] expected value.
|
623
|
-
# @return [
|
726
|
+
# @return [Boolean]
|
624
727
|
# @raise [Synvert::Core::MethodNotSupported] if expected class is not supported.
|
625
728
|
def match_value?(actual, expected)
|
626
729
|
return true if actual == expected
|
@@ -678,7 +781,7 @@ module Parser::AST
|
|
678
781
|
#
|
679
782
|
# @example
|
680
783
|
# flat_hash(type: 'block', caller: {type: 'send', receiver: 'RSpec'})
|
681
|
-
#
|
784
|
+
# # {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
|
682
785
|
# @param h [Hash] original hash.
|
683
786
|
# @return flatten hash.
|
684
787
|
def flat_hash(h, k = [])
|
@@ -695,8 +798,7 @@ module Parser::AST
|
|
695
798
|
|
696
799
|
# Get actual value from the node.
|
697
800
|
#
|
698
|
-
# @param
|
699
|
-
# @param multi_keys [Array<Symbol>]
|
801
|
+
# @param multi_keys [Array<Symbol, String>]
|
700
802
|
# @return [Object] actual value.
|
701
803
|
def actual_value(multi_keys)
|
702
804
|
multi_keys.inject(self) { |n, key| n.send(key) if n }
|
@@ -711,6 +813,7 @@ module Parser::AST
|
|
711
813
|
multi_keys.inject(rules) { |o, key| o[key] }
|
712
814
|
end
|
713
815
|
|
816
|
+
# Wrap the string with single or double quote.
|
714
817
|
def wrap_quote(string)
|
715
818
|
if string.include?("'")
|
716
819
|
"\"#{string}\""
|
@@ -719,6 +822,7 @@ module Parser::AST
|
|
719
822
|
end
|
720
823
|
end
|
721
824
|
|
825
|
+
# Unwrap the quote from the string.
|
722
826
|
def unwrap_quote(string)
|
723
827
|
if (string[0] == '"' && string[-1] == '"') || (string[0] == "'" && string[-1] == "'")
|
724
828
|
string[1...-1]
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Array represents a ruby array value.
|
5
|
+
class Array
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize an Array.
|
9
|
+
# @param value the first value of the array
|
10
|
+
# @param rest the rest value of the array
|
11
|
+
def initialize(value: nil, rest: nil)
|
12
|
+
@value = value
|
13
|
+
@rest = rest
|
14
|
+
end
|
15
|
+
|
16
|
+
# Get the expected value.
|
17
|
+
# @return [Array]
|
18
|
+
def expected_value
|
19
|
+
expected = []
|
20
|
+
expected.push(@value) if @value
|
21
|
+
expected += @rest.expected_value if @rest
|
22
|
+
expected
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get valid operators.
|
26
|
+
def valid_operators
|
27
|
+
ARRAY_VALID_OPERATORS
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
[@value, @rest].compact.join(', ')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|