synvert-core 1.0.5 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/lib/synvert/core/array_ext.rb +9 -2
- data/lib/synvert/core/node_ext.rb +72 -151
- data/lib/synvert/core/node_query/compiler/array.rb +1 -1
- data/lib/synvert/core/node_query/compiler/expression.rb +69 -31
- data/lib/synvert/core/node_query/compiler/selector.rb +23 -18
- data/lib/synvert/core/node_query/compiler/string.rb +0 -11
- data/lib/synvert/core/node_query/lexer.rex +14 -2
- data/lib/synvert/core/node_query/lexer.rex.rb +31 -3
- data/lib/synvert/core/node_query/parser.racc.rb +223 -249
- data/lib/synvert/core/node_query/parser.y +9 -12
- data/lib/synvert/core/node_query.rb +1 -0
- data/lib/synvert/core/version.rb +1 -1
- data/spec/synvert/core/node_ext_spec.rb +41 -15
- data/spec/synvert/core/node_query/lexer_spec.rb +72 -5
- data/spec/synvert/core/node_query/parser_spec.rb +98 -23
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5bf36b2f9e986dd2baf0bbddf53f4671a3bf98af0db830c270960eb583b47ce
|
4
|
+
data.tar.gz: 7e8693858d73d11c09652076ee03d49fe69662386caff67cbd0a3cc37e47d0a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad0eb6125d82241c60f7f0e229b9735f7c253280f1e56d586dfd24e8afb9c25b2cf574ca9c21e252c485f529b1bdf5f6b26c2b59805781cdefd3bc07a76072d8
|
7
|
+
data.tar.gz: 37bd754087a17d55577a10338fef9da3c60733d8634e9c394666a826fe205dc7f895121570f1e9cca4e7e1284f0b3b11c16e532e197afe2cd0ce61424c5d1557
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 1.2.0 (2022-04-29)
|
4
|
+
|
5
|
+
* Remove comma in NQL array value
|
6
|
+
* Parse pseduo class without selector in NQL
|
7
|
+
* Parse multiple goto scope in NQL
|
8
|
+
* Parse `nil?` in NQL
|
9
|
+
|
10
|
+
## 1.1.1 (2022-04-27)
|
11
|
+
|
12
|
+
* Parse empty string properly in node query language
|
13
|
+
* Parse `[]` and `[]=` properly in node query language
|
14
|
+
|
15
|
+
## 1.1.0 (2022-04-26)
|
16
|
+
|
17
|
+
* Dynamic define Node methods by `TYPE_CHILDREN` const
|
18
|
+
* Add `Node#to_hash`
|
19
|
+
* Parse empty string in node query language
|
20
|
+
* Identifier value can contain `?`, `<`, `=`, `>` in node query language
|
21
|
+
|
3
22
|
## 1.0.0 (2022-04-25)
|
4
23
|
|
5
24
|
* Introduce new node query language
|
@@ -13,7 +13,7 @@ class Array
|
|
13
13
|
return child_direct_child_node if child_direct_child_node
|
14
14
|
|
15
15
|
raise Synvert::Core::MethodNotSupported,
|
16
|
-
"child_node_by_name is not handled for #{
|
16
|
+
"child_node_by_name is not handled for #{debug_info}, child_name: #{child_name}"
|
17
17
|
end
|
18
18
|
|
19
19
|
# Get the source range of child node.
|
@@ -35,7 +35,14 @@ class Array
|
|
35
35
|
)
|
36
36
|
else
|
37
37
|
raise Synvert::Core::MethodNotSupported,
|
38
|
-
"child_node_range is not handled for #{
|
38
|
+
"child_node_range is not handled for #{debug_info}, child_name: #{child_name}"
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
# Return the debug info.
|
43
|
+
#
|
44
|
+
# @return [String] file, line, source and node.
|
45
|
+
def debug_info
|
46
|
+
map(&:debug_info).join("\n")
|
47
|
+
end
|
41
48
|
end
|
@@ -27,11 +27,41 @@ module Parser::AST
|
|
27
27
|
# +type: 'send', receiver: { type: 'send', receiver: { type: 'send', message: 'config' }, message: 'active_record' }, message: 'identity_map='+
|
28
28
|
#
|
29
29
|
# Source Code to Ast Node
|
30
|
-
# {https://synvert-playground.xinminlabs.com
|
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
|
+
|
32
62
|
# Initialize a Node.
|
33
63
|
#
|
34
|
-
# It extends Parser::AST::Node and set parent for its child nodes.
|
64
|
+
# It extends {Parser::AST::Node} and set parent for its child nodes.
|
35
65
|
def initialize(type, children = [], properties = {})
|
36
66
|
@mutable_attributes = {}
|
37
67
|
super
|
@@ -62,88 +92,32 @@ module Parser::AST
|
|
62
92
|
parent.children[index + 1..]
|
63
93
|
end
|
64
94
|
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
73
|
-
def name
|
74
|
-
case type
|
75
|
-
when :class, :module, :def, :arg, :blockarg, :restarg, :lvar, :ivar, :cvar
|
76
|
-
children[0]
|
77
|
-
when :defs, :const
|
78
|
-
children[1]
|
79
|
-
when :mlhs
|
80
|
-
self
|
81
|
-
else
|
82
|
-
raise Synvert::Core::MethodNotSupported, "name is not handled for #{debug_info}"
|
83
|
-
end
|
84
|
-
end
|
95
|
+
# Dyamically defined method
|
96
|
+
# caller, key, left_value, message, name, parent_class, parent_const, receivr, rgith_value and value.
|
97
|
+
# based on const TYPE_CHILDREN.
|
98
|
+
%i[caller key left_value message name parent_class parent_const receiver right_value value].each do |method_name|
|
99
|
+
define_method(method_name) do
|
100
|
+
index = TYPE_CHILDREN[type]&.index(method_name)
|
101
|
+
return children[index] if index
|
85
102
|
|
86
|
-
|
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.
|
92
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
93
|
-
def parent_class
|
94
|
-
if :class == type
|
95
|
-
children[1]
|
96
|
-
else
|
97
|
-
raise Synvert::Core::MethodNotSupported, "parent_class is not handled for #{debug_info}"
|
103
|
+
raise Synvert::Core::MethodNotSupported, "#{method_name} is not handled for #{debug_info}"
|
98
104
|
end
|
99
105
|
end
|
100
106
|
|
101
|
-
#
|
102
|
-
# It supports :
|
107
|
+
# Return the left value of node.
|
108
|
+
# It supports :and, :cvagn, :lvasgn, :masgn, :or and :or_asgn nodes.
|
103
109
|
# @example
|
104
|
-
# node # s(:
|
105
|
-
# node.
|
106
|
-
# @return [Parser::AST::Node]
|
110
|
+
# node # s(:or_asgn, s(:lvasgn, :a), s(:int, 1))
|
111
|
+
# node.left_value # :a
|
112
|
+
# @return [Parser::AST::Node] left value of node.
|
107
113
|
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
108
|
-
def
|
109
|
-
if
|
110
|
-
children[0]
|
111
|
-
else
|
112
|
-
raise Synvert::Core::MethodNotSupported, "parent_const is not handled for #{debug_info}"
|
113
|
-
end
|
114
|
-
end
|
114
|
+
def left_value
|
115
|
+
return children[0].children[0] if type == :or_asgn
|
115
116
|
|
116
|
-
|
117
|
-
|
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.
|
122
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
123
|
-
def receiver
|
124
|
-
if %i[csend send].include?(type)
|
125
|
-
children[0]
|
126
|
-
else
|
127
|
-
raise Synvert::Core::MethodNotSupported, "receiver is not handled for #{debug_info}"
|
128
|
-
end
|
129
|
-
end
|
117
|
+
index = TYPE_CHILDREN[type]&.index(:left_value)
|
118
|
+
return children[index] if index
|
130
119
|
|
131
|
-
|
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.
|
137
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
138
|
-
def message
|
139
|
-
case type
|
140
|
-
when :super, :zsuper
|
141
|
-
:super
|
142
|
-
when :send, :csend
|
143
|
-
children[1]
|
144
|
-
else
|
145
|
-
raise Synvert::Core::MethodNotSupported, "message is not handled for #{debug_info}"
|
146
|
-
end
|
120
|
+
raise Synvert::Core::MethodNotSupported, "#{left_value} is not handled for #{debug_info}"
|
147
121
|
end
|
148
122
|
|
149
123
|
# Get arguments of node.
|
@@ -168,21 +142,6 @@ module Parser::AST
|
|
168
142
|
end
|
169
143
|
end
|
170
144
|
|
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.
|
177
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
178
|
-
def caller
|
179
|
-
if :block == type
|
180
|
-
children[0]
|
181
|
-
else
|
182
|
-
raise Synvert::Core::MethodNotSupported, "caller is not handled for #{debug_info}"
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
145
|
# Get body of node.
|
187
146
|
# It supports :begin, :block, :class, :def, :defs and :module node.
|
188
147
|
# @example
|
@@ -281,66 +240,6 @@ module Parser::AST
|
|
281
240
|
end
|
282
241
|
end
|
283
242
|
|
284
|
-
# Get key node of hash :pair node.
|
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.
|
289
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
290
|
-
def key
|
291
|
-
if :pair == type
|
292
|
-
children.first
|
293
|
-
else
|
294
|
-
raise Synvert::Core::MethodNotSupported, "key is not handled for #{debug_info}"
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
# Get value node of hash :pair node.
|
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.
|
303
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
304
|
-
def value
|
305
|
-
if :pair == type
|
306
|
-
children.last
|
307
|
-
else
|
308
|
-
raise Synvert::Core::MethodNotSupported, "value is not handled for #{debug_info}"
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
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.
|
318
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
319
|
-
def left_value
|
320
|
-
if %i[masgn lvasgn ivasgn cvasgn and or].include? type
|
321
|
-
children[0]
|
322
|
-
elsif :or_asgn == type
|
323
|
-
children[0].children[0]
|
324
|
-
else
|
325
|
-
raise Synvert::Core::MethodNotSupported, "left_value is not handled for #{debug_info}"
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
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.
|
335
|
-
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
336
|
-
def right_value
|
337
|
-
if %i[masgn lvasgn ivasgn cvasgn or_asgn and or].include? type
|
338
|
-
children[1]
|
339
|
-
else
|
340
|
-
raise Synvert::Core::MethodNotSupported, "right_value is not handled for #{debug_info}"
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
243
|
# Return the exact value of node.
|
345
244
|
# It supports :array, :begin, :erange, :false, :float, :irange, :int, :str, :sym and :true nodes.
|
346
245
|
# @example
|
@@ -717,6 +616,28 @@ module Parser::AST
|
|
717
616
|
end
|
718
617
|
end
|
719
618
|
|
619
|
+
# Convert node to a hash, so that it can be converted to a json.
|
620
|
+
def to_hash
|
621
|
+
result = { type: type }
|
622
|
+
if TYPE_CHILDREN[type]
|
623
|
+
TYPE_CHILDREN[type].each do |key|
|
624
|
+
value = send(key)
|
625
|
+
result[key] =
|
626
|
+
case value
|
627
|
+
when Array
|
628
|
+
value.map { |v| v.respond_to?(:to_hash) ? v.to_hash : v }
|
629
|
+
when Parser::AST::Node
|
630
|
+
value.to_hash
|
631
|
+
else
|
632
|
+
value
|
633
|
+
end
|
634
|
+
end
|
635
|
+
else
|
636
|
+
result[:children] = children.map { |c| c.respond_to?(:to_hash) ? c.to_hash : c }
|
637
|
+
end
|
638
|
+
result
|
639
|
+
end
|
640
|
+
|
720
641
|
private
|
721
642
|
|
722
643
|
# Compare actual value with expected value.
|
@@ -5,10 +5,12 @@ module Synvert::Core::NodeQuery::Compiler
|
|
5
5
|
class Expression
|
6
6
|
# Initialize a Expression.
|
7
7
|
# @param selector [Synvert::Core::NodeQuery::Compiler::Selector] the selector
|
8
|
+
# @param goto_scope [String] goto scope
|
8
9
|
# @param rest [Synvert::Core::NodeQuery::Compiler::Expression] the rest expression
|
9
10
|
# @param relationship [Symbol] the relationship between the selector and rest expression, it can be <code>:descendant</code>, <code>:child</code>, <code>:next_sibling</code>, <code>:subsequent_sibling</code> or <code>nil</code>.
|
10
|
-
def initialize(selector: nil, rest: nil, relationship: nil)
|
11
|
+
def initialize(selector: nil, goto_scope: nil, rest: nil, relationship: nil)
|
11
12
|
@selector = selector
|
13
|
+
@goto_scope = goto_scope
|
12
14
|
@rest = rest
|
13
15
|
@relationship = relationship
|
14
16
|
end
|
@@ -30,49 +32,85 @@ module Synvert::Core::NodeQuery::Compiler
|
|
30
32
|
# @param node [Parser::AST::Node] node to match
|
31
33
|
# @param descendant_match [Boolean] whether to match in descendant node
|
32
34
|
# @return [Array<Parser::AST::Node>] matching nodes.
|
33
|
-
def query_nodes(node, descendant_match
|
34
|
-
|
35
|
-
if @relationship.nil?
|
36
|
-
return matching_nodes
|
37
|
-
end
|
35
|
+
def query_nodes(node, descendant_match = true)
|
36
|
+
return find_nodes_by_goto_scope(node) if @goto_scope
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
nodes
|
47
|
-
when :child
|
48
|
-
matching_node.children.map { |child_node| @rest.query_nodes(child_node, descendant_match: false) }
|
49
|
-
.flatten
|
50
|
-
when :next_sibling
|
51
|
-
@rest.query_nodes(matching_node.siblings.first, descendant_match: false)
|
52
|
-
when :subsequent_sibling
|
53
|
-
matching_node.siblings.map { |sibling_node| @rest.query_nodes(sibling_node, descendant_match: false) }
|
54
|
-
.flatten
|
55
|
-
end
|
56
|
-
end.flatten
|
38
|
+
return find_nodes_by_relationship(node) if @relationship
|
39
|
+
|
40
|
+
matching_nodes = find_nodes_without_relationship(node, descendant_match)
|
41
|
+
return matching_nodes if @rest.nil?
|
42
|
+
|
43
|
+
matching_nodes.map { |matching_node| find_nodes_by_rest(matching_node, descendant_match) }
|
44
|
+
.flatten
|
57
45
|
end
|
58
46
|
|
59
47
|
def to_s
|
60
48
|
return @selector.to_s unless @rest
|
61
49
|
|
62
50
|
result = []
|
63
|
-
result << @selector if @selector
|
51
|
+
result << @selector.to_s if @selector
|
52
|
+
result << "<#{@goto_scope}>" if @goto_scope
|
64
53
|
case @relationship
|
65
|
-
when :child then result <<
|
66
|
-
when :subsequent_sibling then result <<
|
67
|
-
when :next_sibling then result <<
|
54
|
+
when :child then result << "> #{@rest}"
|
55
|
+
when :subsequent_sibling then result << "~ #{@rest}"
|
56
|
+
when :next_sibling then result << "+ #{@rest}"
|
57
|
+
when :has then result << ":has(#{@rest})"
|
58
|
+
when :not_has then result << ":not_has(#{@rest})"
|
59
|
+
else result << @rest.to_s
|
68
60
|
end
|
69
|
-
result
|
70
|
-
result.map(&:to_s).join(' ')
|
61
|
+
result.join(' ')
|
71
62
|
end
|
72
63
|
|
73
64
|
private
|
74
65
|
|
75
|
-
|
66
|
+
# Find nodes by @goto_scope
|
67
|
+
# @param node [Parser::AST::Node] node to match
|
68
|
+
def find_nodes_by_goto_scope(node)
|
69
|
+
@goto_scope.split('.').each { |scope| node = node.send(scope) }
|
70
|
+
@rest.query_nodes(node, false)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Find ndoes by @relationship
|
74
|
+
# @param node [Parser::AST::Node] node to match
|
75
|
+
def find_nodes_by_relationship(node)
|
76
|
+
case @relationship
|
77
|
+
when :child
|
78
|
+
if node.is_a?(::Array)
|
79
|
+
return node.map { |each_node| find_nodes_by_rest(each_node) }
|
80
|
+
.flatten
|
81
|
+
else
|
82
|
+
return node.children.map { |each_node| find_nodes_by_rest(each_node) }
|
83
|
+
.flatten
|
84
|
+
end
|
85
|
+
when :next_sibling
|
86
|
+
return find_nodes_by_rest(node.siblings.first)
|
87
|
+
when :subsequent_sibling
|
88
|
+
return node.siblings.map { |each_node| find_nodes_by_rest(each_node) }
|
89
|
+
.flatten
|
90
|
+
when :has
|
91
|
+
return @rest.match?(node) ? [node] : []
|
92
|
+
when :not_has
|
93
|
+
return !@rest.match?(node) ? [node] : []
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Find nodes by @rest
|
98
|
+
# @param node [Parser::AST::Node] node to match
|
99
|
+
# @param descendant_match [Boolean] whether to match in descendant node
|
100
|
+
def find_nodes_by_rest(node, descendant_match = false)
|
101
|
+
@rest.query_nodes(node, descendant_match)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Find nodes with nil relationship.
|
105
|
+
# @param node [Parser::AST::Node] node to match
|
106
|
+
# @param descendant_match [Boolean] whether to match in descendant node
|
107
|
+
def find_nodes_without_relationship(node, descendant_match = true)
|
108
|
+
if node.is_a?(::Array)
|
109
|
+
return node.map { |each_node|
|
110
|
+
find_nodes_without_relationship(each_node, descendant_match)
|
111
|
+
}.flatten
|
112
|
+
end
|
113
|
+
|
76
114
|
return [node] unless @selector
|
77
115
|
|
78
116
|
nodes = []
|
@@ -7,12 +7,14 @@ module Synvert::Core::NodeQuery::Compiler
|
|
7
7
|
# @param node_type [String] the node type
|
8
8
|
# @param attribute_list [Synvert::Core::NodeQuery::Compiler::AttributeList] the attribute list
|
9
9
|
# @param index [Integer] the index
|
10
|
-
# @param
|
11
|
-
|
10
|
+
# @param pseudo_class [String] the pseudo class, can be <code>has</code> or <code>not_has</code>
|
11
|
+
# @param pseudo_expression [Synvert::Core::NodeQuery::Compiler::Expression] the pseudo expression
|
12
|
+
def initialize(node_type: nil, attribute_list: nil, index: nil, pseudo_class: nil, pseudo_expression: nil)
|
12
13
|
@node_type = node_type
|
13
14
|
@attribute_list = attribute_list
|
14
15
|
@index = index
|
15
|
-
@
|
16
|
+
@pseudo_class = pseudo_class
|
17
|
+
@pseudo_expression = pseudo_expression
|
16
18
|
end
|
17
19
|
|
18
20
|
# Filter nodes by index.
|
@@ -27,25 +29,28 @@ module Synvert::Core::NodeQuery::Compiler
|
|
27
29
|
def match?(node, _operator = :==)
|
28
30
|
(!@node_type || (node.is_a?(::Parser::AST::Node) && @node_type.to_sym == node.type)) &&
|
29
31
|
(!@attribute_list || @attribute_list.match?(node)) &&
|
30
|
-
(!@
|
32
|
+
(!@pseudo_class || (@pseudo_class == 'has' && @pseudo_expression.match?(node)) || (@pseudo_class == 'not_has' && !@pseudo_expression.match?(node)))
|
31
33
|
end
|
32
34
|
|
33
35
|
def to_s
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
36
|
+
result = []
|
37
|
+
result << ".#{@node_type}" if @node_type
|
38
|
+
result << @attribute_list.to_s if @attribute_list
|
39
|
+
result << ":#{@pseudo_class}(#{@pseudo_expression})" if @pseudo_class
|
40
|
+
if @index
|
41
|
+
result <<
|
42
|
+
case @index
|
43
|
+
when 0
|
44
|
+
':first-child'
|
45
|
+
when -1
|
46
|
+
':last-child'
|
47
|
+
when (1..)
|
48
|
+
":nth-child(#{@index + 1})"
|
49
|
+
else # ...-1
|
50
|
+
":nth-last-child(#{-@index})"
|
51
|
+
end
|
48
52
|
end
|
53
|
+
result.join('')
|
49
54
|
end
|
50
55
|
end
|
51
56
|
end
|
@@ -16,17 +16,6 @@ module Synvert::Core::NodeQuery::Compiler
|
|
16
16
|
SIMPLE_VALID_OPERATORS
|
17
17
|
end
|
18
18
|
|
19
|
-
# Get the actual value of a node.
|
20
|
-
# @param node [Parser::AST::Node] the node
|
21
|
-
# @return [String] if node is a Parser::AST::Node, return the node source code, otherwise, return the string value.
|
22
|
-
def actual_value(node)
|
23
|
-
if node.is_a?(::Parser::AST::Node)
|
24
|
-
node.to_source
|
25
|
-
else
|
26
|
-
node.to_s
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
19
|
def to_s
|
31
20
|
"\"#{@value}\""
|
32
21
|
end
|
@@ -7,6 +7,8 @@ macros
|
|
7
7
|
CLOSE_ARRAY /\)/
|
8
8
|
OPEN_SELECTOR /\(/
|
9
9
|
CLOSE_SELECTOR /\)/
|
10
|
+
OPEN_GOTO_SCOPE /</
|
11
|
+
CLOSE_GOTO_SCOPE />/
|
10
12
|
OPEN_DYNAMIC_ATTRIBUTE /{{/
|
11
13
|
CLOSE_DYNAMIC_ATTRIBUTE /}}/
|
12
14
|
NODE_TYPE /\.[a-z]+/
|
@@ -31,14 +33,19 @@ rules
|
|
31
33
|
/:last-child/ { [:tINDEX, -1] }
|
32
34
|
/:nth-child\(\d+\)/ { [:tINDEX, text.sub(':nth-child(', '').to_i - 1] }
|
33
35
|
/:nth-last-child\(\d+\)/ { [:tINDEX, -text.sub(':nth-last-child(', '').to_i] }
|
34
|
-
/:has/ { [:
|
36
|
+
/:has/ { [:tPSEUDO_CLASS, text[1..-1]] }
|
37
|
+
/:not_has/ { [:tPSEUDO_CLASS, text[1..-1]] }
|
35
38
|
/#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
|
36
39
|
/>/ { [:tCHILD, text] }
|
37
40
|
/~/ { [:tSUBSEQUENT_SIBLING, text] }
|
38
41
|
/\+/ { [:tNEXT_SIBLING, text] }
|
39
42
|
/#{OPEN_SELECTOR}/ { [:tOPEN_SELECTOR, text] }
|
40
43
|
/#{CLOSE_SELECTOR}/ { [:tCLOSE_SELECTOR, text] }
|
44
|
+
/#{OPEN_GOTO_SCOPE}/ { @state = :GOTO_SCOPE; [:tOPEN_GOTO_SCOPE, text] }
|
41
45
|
/#{OPEN_ATTRIBUTE}/ { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
|
46
|
+
:GOTO_SCOPE /\s+/
|
47
|
+
:GOTO_SCOPE /#{IDENTIFIER}/ { [:tIDENTIFIER, text] }
|
48
|
+
:GOTO_SCOPE /#{CLOSE_GOTO_SCOPE}/ { @state = nil; [:tCLOSE_GOTO_SCOPE, text] }
|
42
49
|
:KEY /\s+/
|
43
50
|
:KEY /!=/ { @state = :VALUE; [:tNOT_EQUAL, text] }
|
44
51
|
:KEY /=~/ { @state = :VALUE; [:tMATCH, text] }
|
@@ -53,9 +60,14 @@ rules
|
|
53
60
|
:KEY /in/i { @state = :VALUE; [:tIN, text] }
|
54
61
|
:KEY /#{IDENTIFIER}/ { [:tKEY, text] }
|
55
62
|
:VALUE /\s+/
|
63
|
+
:VALUE /\[\]=/ { [:tIDENTIFIER_VALUE, text] }
|
64
|
+
:VALUE /\[\]/ { [:tIDENTIFIER_VALUE, text] }
|
65
|
+
:VALUE /:\[\]=/ { [:tSYMBOL, text[1..-1].to_sym] }
|
66
|
+
:VALUE /:\[\]/ { [:tSYMBOL, text[1..-1].to_sym] }
|
56
67
|
:VALUE /#{OPEN_DYNAMIC_ATTRIBUTE}/ { @state = :DYNAMIC_ATTRIBUTE; [:tOPEN_DYNAMIC_ATTRIBUTE, text] }
|
57
68
|
:VALUE /#{OPEN_ARRAY}/ { @state = :ARRAY_VALUE; [:tOPEN_ARRAY, text] }
|
58
69
|
:VALUE /#{CLOSE_ATTRIBUTE}/ { @nested_count -= 1; @state = @nested_count == 0 ? nil : :VALUE; [:tCLOSE_ATTRIBUTE, text] }
|
70
|
+
:VALUE /#{NIL}\?/ { [:tIDENTIFIER_VALUE, text] }
|
59
71
|
:VALUE /#{NIL}/ { [:tNIL, nil] }
|
60
72
|
:VALUE /#{TRUE}/ { [:tBOOLEAN, true] }
|
61
73
|
:VALUE /#{FALSE}/ { [:tBOOLEAN, false] }
|
@@ -71,8 +83,8 @@ rules
|
|
71
83
|
:DYNAMIC_ATTRIBUTE /#{CLOSE_DYNAMIC_ATTRIBUTE}/ { @state = :VALUE; [:tCLOSE_DYNAMIC_ATTRIBUTE, text] }
|
72
84
|
:DYNAMIC_ATTRIBUTE /#{IDENTIFIER}/ { [:tDYNAMIC_ATTRIBUTE, text] }
|
73
85
|
:ARRAY_VALUE /\s+/
|
74
|
-
:ARRAY_VALUE /,/ { [:tCOMMA, text] }
|
75
86
|
:ARRAY_VALUE /#{CLOSE_ARRAY}/ { @state = :VALUE; [:tCLOSE_ARRAY, text] }
|
87
|
+
:ARRAY_VALUE /#{NIL}\?/ { [:tIDENTIFIER_VALUE, text] }
|
76
88
|
:ARRAY_VALUE /#{NIL}/ { [:tNIL, nil] }
|
77
89
|
:ARRAY_VALUE /#{TRUE}/ { [:tBOOLEAN, true] }
|
78
90
|
:ARRAY_VALUE /#{FALSE}/ { [:tBOOLEAN, false] }
|