synvert-core 0.63.1 → 1.0.2
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 +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 +525 -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
|