node_query 1.6.1 → 1.8.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 +9 -0
- data/Gemfile.lock +2 -2
- data/README.md +19 -5
- data/lib/node_query/compiler/array_value.rb +2 -2
- data/lib/node_query/compiler/attribute.rb +3 -3
- data/lib/node_query/compiler/attribute_list.rb +3 -2
- data/lib/node_query/compiler/basic_selector.rb +3 -2
- data/lib/node_query/compiler/comparable.rb +34 -31
- data/lib/node_query/compiler/regexp.rb +2 -1
- data/lib/node_query/compiler/selector.rb +36 -14
- data/lib/node_query/compiler/string.rb +5 -5
- data/lib/node_query/helper.rb +1 -1
- data/lib/node_query/node_rules.rb +3 -3
- data/lib/node_query/version.rb +1 -1
- data/lib/node_query.rb +5 -5
- data/lib/node_query_lexer.rex +2 -0
- data/lib/node_query_lexer.rex.rb +4 -0
- data/lib/node_query_parser.racc.rb +100 -93
- data/lib/node_query_parser.y +3 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3638d51ca40eb891535cc2320377e3492023439946460a1c0a81af02f211c3ce
|
4
|
+
data.tar.gz: 4919be334676677f9ad144db4358be354ca81f8244294e3ea1fbf07d6d0a9845
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af54774545d5b284e3d6bd1977094f108553526fc9613ceebe0c96e46be9ac26c5c66db2e4301cef483cf66e4c02f04f19e1e8180e8025d360e5c2b0437c2efa
|
7
|
+
data.tar.gz: ca74ee091e6ec5d603ce4efaed4927e4d08a123563f95f7e09807e7c8433447ef53390ba727f7500037d60fc74ea5fb1601dfeba52e2cc569e54bd25eea14fc9
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 1.8.0 (2022-10-14)
|
4
|
+
|
5
|
+
* Support `:first-child` and `:last-child`
|
6
|
+
|
7
|
+
## 1.7.0 (2022-10-01)
|
8
|
+
|
9
|
+
* Better regexp to match evaluated value
|
10
|
+
* Make `base_node` as the root matching node
|
11
|
+
|
3
12
|
## 1.6.1 (2022-09-28)
|
4
13
|
|
5
14
|
* Do not handle `erange` and `irange` in `actual_value`
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
node_query (1.
|
4
|
+
node_query (1.8.0)
|
5
5
|
activesupport (< 7.0.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -78,7 +78,7 @@ GEM
|
|
78
78
|
thor (1.2.1)
|
79
79
|
tzinfo (2.0.5)
|
80
80
|
concurrent-ruby (~> 1.0)
|
81
|
-
zeitwerk (2.6.
|
81
|
+
zeitwerk (2.6.1)
|
82
82
|
|
83
83
|
PLATFORMS
|
84
84
|
x86_64-darwin-21
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# NodeQuery
|
2
2
|
|
3
|
-
NodeQuery defines
|
4
|
-
it supports other ast parser if it implements `NodeQuery::Adapter`.
|
3
|
+
NodeQuery defines a NQL (node query language) and node rules to query AST nodes.
|
5
4
|
|
6
5
|
## Table of Contents
|
7
6
|
|
@@ -25,7 +24,8 @@ it supports other ast parser if it implements `NodeQuery::Adapter`.
|
|
25
24
|
- [Adjacent sibling combinator](#adjacent-sibling-combinator)
|
26
25
|
- [General sibling combinator](#general-sibling-combinator)
|
27
26
|
- [nql matches goto scope](#nql-matches-goto-scope)
|
28
|
-
- [nql matches pseudo selector](#nql-matches-pseudo-selector)
|
27
|
+
- [nql matches :has and :not_has pseudo selector](#nql-matches-has-and-not_has-pseudo-selector)
|
28
|
+
- [nql matches :first-child and :last-child pseudo selector](#nql-matches-first-child-and-last-child-pseudo-selector)
|
29
29
|
- [nql matches multiple expressions](#nql-matches-multiple-expressions)
|
30
30
|
- [Node Rules](#node-rules)
|
31
31
|
- [rules matches node type](#rules-matches-node-type)
|
@@ -61,7 +61,7 @@ Or install it yourself as:
|
|
61
61
|
It provides two apis: `query_nodes` and `match_node?`
|
62
62
|
|
63
63
|
```ruby
|
64
|
-
node_query = NodeQuery.new(
|
64
|
+
node_query = NodeQuery.new(nql_or_rules: String | Hash) # Initialize NodeQuery
|
65
65
|
node_query.query_nodes(node: Node, options = { including_self: true, stop_at_first_match: false, recursive: true }): Node[] # Get the matching nodes.
|
66
66
|
node_query.match_node?(node: Node): boolean # Check if the node matches nql or rules.
|
67
67
|
```
|
@@ -286,7 +286,7 @@ It matches send node only if it is follows the send node whose left value is @id
|
|
286
286
|
|
287
287
|
It matches send node who is in the body of def node.
|
288
288
|
|
289
|
-
### nql matches pseudo selector
|
289
|
+
### nql matches :has and :not_has pseudo selector
|
290
290
|
|
291
291
|
```
|
292
292
|
.class:has(.def[name=initialize])
|
@@ -300,6 +300,20 @@ It matches class node who has an initialize def node.
|
|
300
300
|
|
301
301
|
It matches class node who does not have an initialize def node.
|
302
302
|
|
303
|
+
### nql matches :first-child and :last-child pseudo selector
|
304
|
+
|
305
|
+
```
|
306
|
+
.def:first-child
|
307
|
+
```
|
308
|
+
|
309
|
+
It matches the first def node.
|
310
|
+
|
311
|
+
```
|
312
|
+
.def:last-child
|
313
|
+
```
|
314
|
+
|
315
|
+
It matches the last def node.
|
316
|
+
|
303
317
|
### nql matches multiple expressions
|
304
318
|
|
305
319
|
```
|
@@ -15,10 +15,10 @@ module NodeQuery::Compiler
|
|
15
15
|
|
16
16
|
# Get the expected value.
|
17
17
|
# @return [Array]
|
18
|
-
def expected_value
|
18
|
+
def expected_value(base_node)
|
19
19
|
expected = []
|
20
20
|
expected.push(@value) if @value
|
21
|
-
expected += @rest.expected_value if @rest
|
21
|
+
expected += @rest.expected_value(base_node) if @rest
|
22
22
|
expected
|
23
23
|
end
|
24
24
|
|
@@ -15,10 +15,10 @@ module NodeQuery::Compiler
|
|
15
15
|
|
16
16
|
# Check if the node matches the attribute.
|
17
17
|
# @param node [Node] the node
|
18
|
+
# @param base_node [Node] the bae node for evaluated value
|
18
19
|
# @return [Boolean]
|
19
|
-
def match?(node)
|
20
|
-
|
21
|
-
node && @value.match?(NodeQuery::Helper.get_target_node(node, @key), @operator)
|
20
|
+
def match?(node, base_node)
|
21
|
+
node && @value.match?(NodeQuery::Helper.get_target_node(node, @key), base_node, @operator)
|
22
22
|
end
|
23
23
|
|
24
24
|
def to_s
|
@@ -13,9 +13,10 @@ module NodeQuery::Compiler
|
|
13
13
|
|
14
14
|
# Check if the node matches the attribute list.
|
15
15
|
# @param node [Node] the node
|
16
|
+
# @param base_node [Node] the base node for evaluated value
|
16
17
|
# @return [Boolean]
|
17
|
-
def match?(node)
|
18
|
-
@attribute.match?(node) && (!@rest || @rest.match?(node))
|
18
|
+
def match?(node, base_node)
|
19
|
+
@attribute.match?(node, base_node) && (!@rest || @rest.match?(node, base_node))
|
19
20
|
end
|
20
21
|
|
21
22
|
def to_s
|
@@ -13,11 +13,12 @@ module NodeQuery::Compiler
|
|
13
13
|
|
14
14
|
# Check if node matches the selector.
|
15
15
|
# @param node [Node] the node
|
16
|
+
# @param base_node [Node] the base node for evaluated value
|
16
17
|
# @return [Boolean]
|
17
|
-
def match?(node, _operator = '==')
|
18
|
+
def match?(node, base_node, _operator = '==')
|
18
19
|
return false unless node
|
19
20
|
|
20
|
-
@node_type.to_sym == NodeQuery.adapter.get_node_type(node) && (!@attribute_list || @attribute_list.match?(node))
|
21
|
+
@node_type.to_sym == NodeQuery.adapter.get_node_type(node) && (!@attribute_list || @attribute_list.match?(node, base_node))
|
21
22
|
end
|
22
23
|
|
23
24
|
def to_s
|
@@ -12,69 +12,71 @@ module NodeQuery::Compiler
|
|
12
12
|
# Check if the actual value matches the expected value.
|
13
13
|
#
|
14
14
|
# @param node [Node] node to calculate actual value
|
15
|
+
# @param base_node [Node] the base node for evaluated value
|
15
16
|
# @param operator [String] operator to compare with expected value, operator can be <code>'=='</code>, <code>'!='</code>, <code>'>'</code>, <code>'>='</code>, <code>'<'</code>, <code>'<='</code>, <code>'includes'</code>, <code>'in'</code>, <code>'not_in'</code>, <code>'=~'</code>, <code>'!~'</code>
|
16
17
|
# @return [Boolean] true if actual value matches the expected value
|
17
18
|
# @raise [NodeQuery::Compiler::InvalidOperatorError] if operator is invalid
|
18
|
-
def match?(node, operator)
|
19
|
+
def match?(node, base_node, operator)
|
19
20
|
raise InvalidOperatorError, "invalid operator #{operator}" unless valid_operator?(operator)
|
20
21
|
|
22
|
+
actual = actual_value(node)
|
23
|
+
expected = expected_value(base_node)
|
21
24
|
case operator
|
22
25
|
when '!='
|
23
|
-
if
|
24
|
-
actual
|
25
|
-
|
26
|
-
actual.zip(expected_value).any? { |actual_node, expected_node| expected_node.match?(actual_node, '!=') }
|
26
|
+
if expected.is_a?(::Array)
|
27
|
+
!actual.is_a?(::Array) || actual.size != expected.size ||
|
28
|
+
actual.zip(expected).any? { |actual_child, expected_child| expected_child.match?(actual_child, base_node, '!=') }
|
27
29
|
else
|
28
|
-
!is_equal?(
|
30
|
+
!is_equal?(actual, expected)
|
29
31
|
end
|
30
32
|
when '=~'
|
31
|
-
|
33
|
+
actual =~ expected
|
32
34
|
when '!~'
|
33
|
-
|
35
|
+
actual !~ expected
|
34
36
|
when '^='
|
35
|
-
|
37
|
+
actual.start_with?(expected)
|
36
38
|
when '$='
|
37
|
-
|
39
|
+
actual.end_with?(expected)
|
38
40
|
when '*='
|
39
|
-
|
41
|
+
actual.include?(expected)
|
40
42
|
when '>'
|
41
|
-
|
43
|
+
actual > expected
|
42
44
|
when '>='
|
43
|
-
|
45
|
+
actual >= expected
|
44
46
|
when '<'
|
45
|
-
|
47
|
+
actual < expected
|
46
48
|
when '<='
|
47
|
-
|
49
|
+
actual <= expected
|
48
50
|
when 'in'
|
49
51
|
if node.is_a?(Array)
|
50
|
-
node.all? { |child|
|
52
|
+
node.all? { |child| expected.any? { |expected_child| expected_child.match?(child, base_node, '==') } }
|
51
53
|
else
|
52
|
-
|
54
|
+
expected.any? { |expected_child| expected_child.match?(node, base_node, '==') }
|
53
55
|
end
|
54
56
|
when 'not_in'
|
55
57
|
if node.is_a?(Array)
|
56
|
-
node.all? { |child|
|
58
|
+
node.all? { |child| expected.all? { |expected_child| expected_child.match?(child, base_node, '!=') } }
|
57
59
|
else
|
58
|
-
|
60
|
+
expected.all? { |expected_child| expected_child.match?(node, base_node, '!=') }
|
59
61
|
end
|
60
62
|
when 'includes'
|
61
|
-
|
63
|
+
actual.any? { |actual_child| actual_child == expected }
|
62
64
|
else
|
63
|
-
if
|
64
|
-
actual
|
65
|
-
|
66
|
-
actual.zip(expected_value).all? { |actual_node, expected_node| expected_node.match?(actual_node, '==') }
|
65
|
+
if expected.is_a?(::Array)
|
66
|
+
actual.is_a?(::Array) && actual.size == expected.size &&
|
67
|
+
actual.zip(expected).all? { |actual_child, expected_child| expected_child.match?(actual_child, base_node, '==') }
|
67
68
|
else
|
68
|
-
is_equal?(
|
69
|
+
is_equal?(actual, expected)
|
69
70
|
end
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
|
-
# Check if the actual value equals the
|
74
|
-
# @param
|
75
|
-
# @
|
76
|
-
|
77
|
-
|
74
|
+
# Check if the actual value equals the expected value.
|
75
|
+
# @param acutal
|
76
|
+
# @param expected
|
77
|
+
# @return [Boolean] true if the actual value equals the expected value.
|
78
|
+
def is_equal?(actual, expected)
|
79
|
+
actual == expected
|
78
80
|
end
|
79
81
|
|
80
82
|
# Get the actual value from ast node.
|
@@ -104,8 +106,9 @@ module NodeQuery::Compiler
|
|
104
106
|
end
|
105
107
|
|
106
108
|
# Get the expected value
|
109
|
+
# @param base_node [Node] the base node for evaluated value
|
107
110
|
# @return expected value, could be integer, float, string, boolean, nil, range, and etc.
|
108
|
-
def expected_value
|
111
|
+
def expected_value(base_node)
|
109
112
|
@value
|
110
113
|
end
|
111
114
|
|
@@ -13,9 +13,10 @@ module NodeQuery::Compiler
|
|
13
13
|
|
14
14
|
# Check if the regexp value matches the node value.
|
15
15
|
# @param node [Node] the node
|
16
|
+
# @param _base_node [Node] the base node for evaluated value
|
16
17
|
# @param operator [String] the operator
|
17
18
|
# @return [Boolean] true if the regexp value matches the node value, otherwise, false.
|
18
|
-
def match?(node, operator = '=~')
|
19
|
+
def match?(node, _base_node, operator = '=~')
|
19
20
|
match =
|
20
21
|
if NodeQuery.adapter.is_node?(node)
|
21
22
|
@value.match(NodeQuery.adapter.get_source(node))
|
@@ -8,22 +8,25 @@ module NodeQuery::Compiler
|
|
8
8
|
# @param relationship [String] the relationship between the selectors, it can be descendant <code>nil</code>, child <code>></code>, next sibling <code>+</code> or subsequent sibing <code>~</code>.
|
9
9
|
# @param rest [NodeQuery::Compiler::Selector] the rest selector
|
10
10
|
# @param basic_selector [NodeQuery::Compiler::BasicSelector] the simple selector
|
11
|
+
# @param position [String] the position of the node
|
11
12
|
# @param attribute_list [NodeQuery::Compiler::AttributeList] the attribute list
|
12
13
|
# @param pseudo_class [String] the pseudo class, can be <code>has</code> or <code>not_has</code>
|
13
14
|
# @param pseudo_selector [NodeQuery::Compiler::Expression] the pseudo selector
|
14
|
-
def initialize(goto_scope: nil, relationship: nil, rest: nil, basic_selector: nil, pseudo_class: nil, pseudo_selector: nil)
|
15
|
+
def initialize(goto_scope: nil, relationship: nil, rest: nil, basic_selector: nil, position: nil, pseudo_class: nil, pseudo_selector: nil)
|
15
16
|
@goto_scope = goto_scope
|
16
17
|
@relationship = relationship
|
17
18
|
@rest = rest
|
18
19
|
@basic_selector = basic_selector
|
20
|
+
@position = position
|
19
21
|
@pseudo_class = pseudo_class
|
20
22
|
@pseudo_selector = pseudo_selector
|
21
23
|
end
|
22
24
|
|
23
25
|
# Check if node matches the selector.
|
24
26
|
# @param node [Parser::AST::Node] the node
|
25
|
-
|
26
|
-
|
27
|
+
# @param base_node [Parser::AST::Node] the base node for evaluated node
|
28
|
+
def match?(node, base_node)
|
29
|
+
NodeQuery.adapter.is_node?(node) && (!@basic_selector || @basic_selector.match?(node, base_node)) && match_pseudo_class?(node)
|
27
30
|
end
|
28
31
|
|
29
32
|
# Query nodes by the selector.
|
@@ -49,32 +52,32 @@ module NodeQuery::Compiler
|
|
49
52
|
return find_nodes_by_goto_scope(node) if @goto_scope
|
50
53
|
|
51
54
|
if options[:including_self] && !options[:recursive]
|
52
|
-
return match?(node) ? [node] : []
|
55
|
+
return match?(node, node) ? [node] : []
|
53
56
|
end
|
54
57
|
|
55
58
|
nodes = []
|
56
|
-
if options[:including_self] && match?(node)
|
59
|
+
if options[:including_self] && match?(node, node)
|
57
60
|
nodes << node
|
58
61
|
return nodes if options[:stop_at_first_match]
|
59
62
|
end
|
60
63
|
if @basic_selector
|
61
64
|
if options[:recursive]
|
62
65
|
NodeQuery::Helper.handle_recursive_child(node) do |child_node|
|
63
|
-
if match?(child_node)
|
66
|
+
if match?(child_node, child_node)
|
64
67
|
nodes << child_node
|
65
68
|
break if options[:stop_at_first_match]
|
66
69
|
end
|
67
70
|
end
|
68
71
|
else
|
69
72
|
NodeQuery.adapter.get_children(node).each do |child_node|
|
70
|
-
if match?(child_node)
|
73
|
+
if match?(child_node, child_node)
|
71
74
|
nodes << child_node
|
72
75
|
break if options[:stop_at_first_match]
|
73
76
|
end
|
74
77
|
end
|
75
78
|
end
|
76
79
|
end
|
77
|
-
nodes
|
80
|
+
filter_by_position(nodes)
|
78
81
|
end
|
79
82
|
|
80
83
|
def to_s
|
@@ -83,10 +86,29 @@ module NodeQuery::Compiler
|
|
83
86
|
result << "#{@relationship} " if @relationship
|
84
87
|
result << @rest.to_s if @rest
|
85
88
|
result << @basic_selector.to_s if @basic_selector
|
89
|
+
result << ":#{@position}" if @position
|
86
90
|
result << ":#{@pseudo_class}(#{@pseudo_selector})" if @pseudo_class
|
87
91
|
result.join('')
|
88
92
|
end
|
89
93
|
|
94
|
+
protected
|
95
|
+
|
96
|
+
# Filter nodes by position.
|
97
|
+
# @param nodes [Array<Node>] nodes to filter
|
98
|
+
# @return [Array<Node>|Node] first node or last node or nodes
|
99
|
+
def filter_by_position(nodes)
|
100
|
+
return nodes unless @position
|
101
|
+
|
102
|
+
case @position
|
103
|
+
when 'first-child'
|
104
|
+
[nodes.first]
|
105
|
+
when 'last-child'
|
106
|
+
[nodes.last]
|
107
|
+
else
|
108
|
+
nodes
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
90
112
|
private
|
91
113
|
|
92
114
|
# Find nodes by @goto_scope
|
@@ -106,28 +128,28 @@ module NodeQuery::Compiler
|
|
106
128
|
when '>'
|
107
129
|
if node.is_a?(::Array)
|
108
130
|
node.each do |child_node|
|
109
|
-
nodes << child_node if @rest.match?(child_node)
|
131
|
+
nodes << child_node if @rest.match?(child_node, child_node)
|
110
132
|
end
|
111
133
|
else
|
112
134
|
node.children.each do |child_node|
|
113
135
|
if NodeQuery.adapter.is_node?(child_node) && :begin == NodeQuery.adapter.get_node_type(child_node)
|
114
136
|
child_node.children.each do |child_child_node|
|
115
|
-
nodes << child_child_node if @rest.match?(child_child_node)
|
137
|
+
nodes << child_child_node if @rest.match?(child_child_node, child_child_node)
|
116
138
|
end
|
117
|
-
elsif @rest.match?(child_node)
|
139
|
+
elsif @rest.match?(child_node, child_node)
|
118
140
|
nodes << child_node
|
119
141
|
end
|
120
142
|
end
|
121
143
|
end
|
122
144
|
when '+'
|
123
145
|
next_sibling = node.siblings.first
|
124
|
-
nodes << next_sibling if @rest.match?(next_sibling)
|
146
|
+
nodes << next_sibling if @rest.match?(next_sibling, next_sibling)
|
125
147
|
when '~'
|
126
148
|
node.siblings.each do |sibling_node|
|
127
|
-
nodes << sibling_node if @rest.match?(sibling_node)
|
149
|
+
nodes << sibling_node if @rest.match?(sibling_node, sibling_node)
|
128
150
|
end
|
129
151
|
end
|
130
|
-
nodes
|
152
|
+
@rest.filter_by_position(nodes)
|
131
153
|
end
|
132
154
|
|
133
155
|
# Check if it matches pseudo class.
|
@@ -5,8 +5,6 @@ module NodeQuery::Compiler
|
|
5
5
|
class String
|
6
6
|
include Comparable
|
7
7
|
|
8
|
-
attr_accessor :base_node
|
9
|
-
|
10
8
|
# Initialize a String.
|
11
9
|
# @param value [String] the string value
|
12
10
|
def initialize(value:)
|
@@ -18,16 +16,18 @@ module NodeQuery::Compiler
|
|
18
16
|
# if the source code of the node is @id = id,
|
19
17
|
# and the @value is "@{{right_vaue}}",
|
20
18
|
# then it returns "@id".
|
19
|
+
# @param base_node [Node] the base node for evaluated value
|
21
20
|
# @return [String] the expected string, if it contains evaluated value, evaluate the node value.
|
22
|
-
def expected_value
|
21
|
+
def expected_value(base_node)
|
23
22
|
NodeQuery::Helper.evaluate_node_value(base_node, @value)
|
24
23
|
end
|
25
24
|
|
26
25
|
# Check if the actual value equals the node value.
|
27
26
|
# @param node [Node] the node
|
27
|
+
# @param base_node [Node] the base node for evaluated value
|
28
28
|
# @return [Boolean] true if the actual value equals the node value.
|
29
|
-
def is_equal?(
|
30
|
-
NodeQuery::Helper.to_string(
|
29
|
+
def is_equal?(actual, expected)
|
30
|
+
NodeQuery::Helper.to_string(actual) == expected
|
31
31
|
end
|
32
32
|
|
33
33
|
# Get valid operators.
|
data/lib/node_query/helper.rb
CHANGED
@@ -48,7 +48,7 @@ class NodeQuery::Helper
|
|
48
48
|
# @param str [String] string to be evaluated
|
49
49
|
# @return [String] evaluated string
|
50
50
|
def evaluate_node_value(node, str)
|
51
|
-
str.scan(/{{(
|
51
|
+
str.scan(/{{(.+?)}}/).each do |match_data|
|
52
52
|
target_node = NodeQuery::Helper.get_target_node(node, match_data.first)
|
53
53
|
str = str.sub("{{#{match_data.first}}}", to_string(target_node))
|
54
54
|
end
|
@@ -144,8 +144,8 @@ class NodeQuery::NodeRules
|
|
144
144
|
# Convert a hash to flat one.
|
145
145
|
#
|
146
146
|
# @example
|
147
|
-
# flat_hash(
|
148
|
-
# # {[:
|
147
|
+
# flat_hash(node_type: 'block', caller: { node_type: 'send', receiver: 'RSpec' })
|
148
|
+
# # {[:node_type] => 'block', [:caller, :node_type] => 'send', [:caller, :receiver] => 'RSpec'}
|
149
149
|
# @param h [Hash] original hash.
|
150
150
|
# @return flatten hash.
|
151
151
|
def flat_hash(h, k = [])
|
@@ -190,4 +190,4 @@ class NodeQuery::NodeRules
|
|
190
190
|
string
|
191
191
|
end
|
192
192
|
end
|
193
|
-
end
|
193
|
+
end
|
data/lib/node_query/version.rb
CHANGED
data/lib/node_query.rb
CHANGED
@@ -27,12 +27,12 @@ class NodeQuery
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Initialize a NodeQuery.
|
30
|
-
# @param
|
31
|
-
def initialize(
|
32
|
-
if
|
33
|
-
@expression = NodeQueryParser.new.parse(
|
30
|
+
# @param nql_or_ruls [String | Hash] node query language or node rules
|
31
|
+
def initialize(nql_or_ruls)
|
32
|
+
if nql_or_ruls.is_a?(String)
|
33
|
+
@expression = NodeQueryParser.new.parse(nql_or_ruls)
|
34
34
|
else
|
35
|
-
@rules = NodeRules.new(
|
35
|
+
@rules = NodeRules.new(nql_or_ruls)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
data/lib/node_query_lexer.rex
CHANGED
@@ -26,6 +26,8 @@ rules
|
|
26
26
|
# [:state] pattern [actions]
|
27
27
|
/\s+/
|
28
28
|
/,/ { [:tCOMMA, text] }
|
29
|
+
/:first-child/ { [:tPOSITION, text[1..-1]] }
|
30
|
+
/:last-child/ { [:tPOSITION, text[1..-1]] }
|
29
31
|
/:has/ { [:tPSEUDO_CLASS, text[1..-1]] }
|
30
32
|
/:not_has/ { [:tPSEUDO_CLASS, text[1..-1]] }
|
31
33
|
/#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
|
data/lib/node_query_lexer.rex.rb
CHANGED
@@ -125,6 +125,10 @@ class NodeQueryLexer
|
|
125
125
|
# do nothing
|
126
126
|
when text = ss.scan(/,/) then
|
127
127
|
action { [:tCOMMA, text] }
|
128
|
+
when text = ss.scan(/:first-child/) then
|
129
|
+
action { [:tPOSITION, text[1..-1]] }
|
130
|
+
when text = ss.scan(/:last-child/) then
|
131
|
+
action { [:tPOSITION, text[1..-1]] }
|
128
132
|
when text = ss.scan(/:has/) then
|
129
133
|
action { [:tPSEUDO_CLASS, text[1..-1]] }
|
130
134
|
when text = ss.scan(/:not_has/) then
|
@@ -22,85 +22,86 @@ class NodeQueryParser < Racc::Parser
|
|
22
22
|
##### State transition tables begin ###
|
23
23
|
|
24
24
|
racc_action_table = [
|
25
|
-
8, 8, 7, 9, 10,
|
26
|
-
|
27
|
-
33, 34, 35, 8,
|
28
|
-
8, 5, 6, nil,
|
29
|
-
30, 31, 32, 33, 34, 35,
|
30
|
-
33, 34, 35, 8, 7, 8, 7, 8,
|
31
|
-
6, 5, 6, 5, 6, 8, 7, nil,
|
32
|
-
nil, 5, 6 ]
|
25
|
+
8, 8, 7, 9, 10, 37, 12, 5, 6, 13,
|
26
|
+
28, 17, 18, 22, 23, 24, 25, 30, 31, 32,
|
27
|
+
33, 34, 35, 36, 8, 17, 8, 7, 41, 37,
|
28
|
+
nil, 8, 5, 6, nil, 38, 37, nil, nil, nil,
|
29
|
+
nil, 30, 31, 32, 33, 34, 35, 36, 30, 31,
|
30
|
+
32, 33, 34, 35, 36, 8, 7, 8, 7, 8,
|
31
|
+
7, 5, 6, 5, 6, 5, 6, 8, 7, nil,
|
32
|
+
nil, nil, nil, 5, 6 ]
|
33
33
|
|
34
34
|
racc_action_check = [
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
6, 7, 7, 10, 10,
|
42
|
-
nil,
|
35
|
+
25, 0, 0, 1, 2, 25, 4, 0, 0, 5,
|
36
|
+
25, 8, 9, 17, 20, 21, 22, 25, 25, 25,
|
37
|
+
25, 25, 25, 25, 28, 24, 3, 3, 39, 28,
|
38
|
+
nil, 40, 3, 3, nil, 28, 40, nil, nil, nil,
|
39
|
+
nil, 28, 28, 28, 28, 28, 28, 28, 40, 40,
|
40
|
+
40, 40, 40, 40, 40, 6, 6, 7, 7, 10,
|
41
|
+
10, 6, 6, 7, 7, 10, 10, 13, 13, nil,
|
42
|
+
nil, nil, nil, 13, 13 ]
|
43
43
|
|
44
44
|
racc_action_pointer = [
|
45
|
-
-2, 3, 2,
|
46
|
-
|
47
|
-
|
48
|
-
nil, nil, nil, nil, nil, nil, nil, nil,
|
49
|
-
nil, nil ]
|
45
|
+
-2, 3, 2, 23, -11, -6, 52, 54, 0, 12,
|
46
|
+
56, nil, nil, 64, nil, nil, nil, 7, nil, nil,
|
47
|
+
-2, 3, -2, nil, 14, -3, nil, nil, 21, nil,
|
48
|
+
nil, nil, nil, nil, nil, nil, nil, nil, nil, 14,
|
49
|
+
28, nil, nil ]
|
50
50
|
|
51
51
|
racc_action_default = [
|
52
|
-
-
|
53
|
-
-
|
54
|
-
-
|
55
|
-
-20, -21, -22, -23, -24, -25, -26, -
|
56
|
-
-
|
52
|
+
-28, -28, -2, -4, -6, -28, -28, -28, -10, -28,
|
53
|
+
-28, -3, -5, -28, -8, -9, -11, -28, 43, -1,
|
54
|
+
-28, -28, -28, -7, -13, -28, -12, -14, -28, -19,
|
55
|
+
-20, -21, -22, -23, -24, -25, -26, -27, -15, -28,
|
56
|
+
-18, -16, -17 ]
|
57
57
|
|
58
58
|
racc_goto_table = [
|
59
|
-
|
60
|
-
|
59
|
+
16, 39, 29, 11, 1, 29, 21, 27, 14, 15,
|
60
|
+
nil, nil, nil, 42, 19, 20, 26, 29 ]
|
61
61
|
|
62
62
|
racc_goto_check = [
|
63
|
-
|
64
|
-
|
63
|
+
5, 8, 4, 2, 1, 4, 6, 7, 3, 3,
|
64
|
+
nil, nil, nil, 8, 1, 3, 5, 4 ]
|
65
65
|
|
66
66
|
racc_goto_pointer = [
|
67
|
-
nil,
|
67
|
+
nil, 4, 0, 2, -23, -8, -11, -18, -27 ]
|
68
68
|
|
69
69
|
racc_goto_default = [
|
70
|
-
nil, nil, 2, 3, 4, nil, nil,
|
70
|
+
nil, nil, 2, 3, 4, nil, nil, 40, nil ]
|
71
71
|
|
72
72
|
racc_reduce_table = [
|
73
73
|
0, 0, :racc_error,
|
74
|
-
3,
|
75
|
-
1,
|
76
|
-
2,
|
77
|
-
1,
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
2,
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
74
|
+
3, 28, :_reduce_1,
|
75
|
+
1, 28, :_reduce_2,
|
76
|
+
2, 29, :_reduce_3,
|
77
|
+
1, 29, :_reduce_4,
|
78
|
+
2, 30, :_reduce_5,
|
79
|
+
1, 30, :_reduce_6,
|
80
|
+
4, 30, :_reduce_7,
|
81
|
+
2, 30, :_reduce_8,
|
82
|
+
2, 30, :_reduce_9,
|
83
|
+
1, 31, :_reduce_10,
|
84
|
+
2, 31, :_reduce_11,
|
85
|
+
4, 32, :_reduce_12,
|
86
86
|
3, 32, :_reduce_13,
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
1,
|
92
|
-
1,
|
93
|
-
1,
|
94
|
-
1,
|
95
|
-
1,
|
96
|
-
1,
|
97
|
-
1,
|
98
|
-
1,
|
99
|
-
1,
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
87
|
+
3, 33, :_reduce_14,
|
88
|
+
4, 33, :_reduce_15,
|
89
|
+
5, 33, :_reduce_16,
|
90
|
+
2, 35, :_reduce_17,
|
91
|
+
1, 35, :_reduce_18,
|
92
|
+
1, 34, :_reduce_none,
|
93
|
+
1, 34, :_reduce_20,
|
94
|
+
1, 34, :_reduce_21,
|
95
|
+
1, 34, :_reduce_22,
|
96
|
+
1, 34, :_reduce_23,
|
97
|
+
1, 34, :_reduce_24,
|
98
|
+
1, 34, :_reduce_25,
|
99
|
+
1, 34, :_reduce_26,
|
100
|
+
1, 34, :_reduce_27 ]
|
101
|
+
|
102
|
+
racc_reduce_n = 28
|
103
|
+
|
104
|
+
racc_shift_n = 43
|
104
105
|
|
105
106
|
racc_token_table = {
|
106
107
|
false => 0,
|
@@ -120,17 +121,18 @@ racc_token_table = {
|
|
120
121
|
:tCLOSE_ARRAY => 14,
|
121
122
|
:tOPEN_SELECTOR => 15,
|
122
123
|
:tCLOSE_SELECTOR => 16,
|
123
|
-
:
|
124
|
-
:
|
125
|
-
:
|
126
|
-
:
|
127
|
-
:
|
128
|
-
:
|
129
|
-
:
|
130
|
-
:
|
131
|
-
:
|
132
|
-
|
133
|
-
|
124
|
+
:tPOSITION => 17,
|
125
|
+
:tOPERATOR => 18,
|
126
|
+
:tARRAY_VALUE => 19,
|
127
|
+
:tBOOLEAN => 20,
|
128
|
+
:tFLOAT => 21,
|
129
|
+
:tINTEGER => 22,
|
130
|
+
:tNIL => 23,
|
131
|
+
:tREGEXP => 24,
|
132
|
+
:tSTRING => 25,
|
133
|
+
:tSYMBOL => 26 }
|
134
|
+
|
135
|
+
racc_nt_base = 27
|
134
136
|
|
135
137
|
racc_use_result_var = false
|
136
138
|
|
@@ -168,6 +170,7 @@ Racc_token_to_s_table = [
|
|
168
170
|
"tCLOSE_ARRAY",
|
169
171
|
"tOPEN_SELECTOR",
|
170
172
|
"tCLOSE_SELECTOR",
|
173
|
+
"tPOSITION",
|
171
174
|
"tOPERATOR",
|
172
175
|
"tARRAY_VALUE",
|
173
176
|
"tBOOLEAN",
|
@@ -210,88 +213,92 @@ def _reduce_4(val, _values)
|
|
210
213
|
end
|
211
214
|
|
212
215
|
def _reduce_5(val, _values)
|
213
|
-
NodeQuery::Compiler::Selector.new(basic_selector: val[0])
|
216
|
+
NodeQuery::Compiler::Selector.new(basic_selector: val[0], position: val[1] )
|
214
217
|
end
|
215
218
|
|
216
219
|
def _reduce_6(val, _values)
|
217
|
-
NodeQuery::Compiler::Selector.new(
|
220
|
+
NodeQuery::Compiler::Selector.new(basic_selector: val[0])
|
218
221
|
end
|
219
222
|
|
220
223
|
def _reduce_7(val, _values)
|
221
|
-
NodeQuery::Compiler::Selector.new(
|
224
|
+
NodeQuery::Compiler::Selector.new(pseudo_class: val[0], pseudo_selector: val[2])
|
222
225
|
end
|
223
226
|
|
224
227
|
def _reduce_8(val, _values)
|
225
|
-
NodeQuery::Compiler::Selector.new(
|
228
|
+
NodeQuery::Compiler::Selector.new(relationship: val[0], rest: val[1])
|
226
229
|
end
|
227
230
|
|
228
231
|
def _reduce_9(val, _values)
|
229
|
-
NodeQuery::Compiler::
|
232
|
+
NodeQuery::Compiler::Selector.new(goto_scope: val[0], rest: val[1])
|
230
233
|
end
|
231
234
|
|
232
235
|
def _reduce_10(val, _values)
|
233
|
-
NodeQuery::Compiler::BasicSelector.new(node_type: val[0]
|
236
|
+
NodeQuery::Compiler::BasicSelector.new(node_type: val[0])
|
234
237
|
end
|
235
238
|
|
236
239
|
def _reduce_11(val, _values)
|
237
|
-
NodeQuery::Compiler::
|
240
|
+
NodeQuery::Compiler::BasicSelector.new(node_type: val[0], attribute_list: val[1])
|
238
241
|
end
|
239
242
|
|
240
243
|
def _reduce_12(val, _values)
|
241
|
-
NodeQuery::Compiler::AttributeList.new(attribute: val[1])
|
244
|
+
NodeQuery::Compiler::AttributeList.new(attribute: val[1], rest: val[3])
|
242
245
|
end
|
243
246
|
|
244
247
|
def _reduce_13(val, _values)
|
245
|
-
NodeQuery::Compiler::
|
248
|
+
NodeQuery::Compiler::AttributeList.new(attribute: val[1])
|
246
249
|
end
|
247
250
|
|
248
251
|
def _reduce_14(val, _values)
|
249
|
-
NodeQuery::Compiler::Attribute.new(key: val[0], value:
|
252
|
+
NodeQuery::Compiler::Attribute.new(key: val[0], value: val[2], operator: val[1])
|
250
253
|
end
|
251
254
|
|
252
255
|
def _reduce_15(val, _values)
|
253
|
-
NodeQuery::Compiler::Attribute.new(key: val[0], value:
|
256
|
+
NodeQuery::Compiler::Attribute.new(key: val[0], value: NodeQuery::Compiler::ArrayValue.new, operator: val[1])
|
254
257
|
end
|
255
258
|
|
256
259
|
def _reduce_16(val, _values)
|
257
|
-
NodeQuery::Compiler::
|
260
|
+
NodeQuery::Compiler::Attribute.new(key: val[0], value: val[3], operator: val[1])
|
258
261
|
end
|
259
262
|
|
260
263
|
def _reduce_17(val, _values)
|
264
|
+
NodeQuery::Compiler::ArrayValue.new(value: val[0], rest: val[1])
|
265
|
+
end
|
266
|
+
|
267
|
+
def _reduce_18(val, _values)
|
261
268
|
NodeQuery::Compiler::ArrayValue.new(value: val[0])
|
262
269
|
end
|
263
270
|
|
264
|
-
# reduce
|
271
|
+
# reduce 19 omitted
|
265
272
|
|
266
|
-
def
|
273
|
+
def _reduce_20(val, _values)
|
267
274
|
NodeQuery::Compiler::Boolean.new(value: val[0])
|
268
275
|
end
|
269
276
|
|
270
|
-
def
|
277
|
+
def _reduce_21(val, _values)
|
271
278
|
NodeQuery::Compiler::Float.new(value: val[0])
|
272
279
|
end
|
273
280
|
|
274
|
-
def
|
281
|
+
def _reduce_22(val, _values)
|
275
282
|
NodeQuery::Compiler::Integer.new(value: val[0])
|
276
283
|
end
|
277
284
|
|
278
|
-
def
|
285
|
+
def _reduce_23(val, _values)
|
279
286
|
NodeQuery::Compiler::Nil.new(value: val[0])
|
280
287
|
end
|
281
288
|
|
282
|
-
def
|
289
|
+
def _reduce_24(val, _values)
|
283
290
|
NodeQuery::Compiler::Regexp.new(value: val[0])
|
284
291
|
end
|
285
292
|
|
286
|
-
def
|
293
|
+
def _reduce_25(val, _values)
|
287
294
|
NodeQuery::Compiler::String.new(value: val[0])
|
288
295
|
end
|
289
296
|
|
290
|
-
def
|
297
|
+
def _reduce_26(val, _values)
|
291
298
|
NodeQuery::Compiler::Symbol.new(value: val[0])
|
292
299
|
end
|
293
300
|
|
294
|
-
def
|
301
|
+
def _reduce_27(val, _values)
|
295
302
|
NodeQuery::Compiler::Identifier.new(value: val[0])
|
296
303
|
end
|
297
304
|
|
data/lib/node_query_parser.y
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class NodeQueryParser
|
2
2
|
options no_result_var
|
3
3
|
token tCOMMA tNODE_TYPE tGOTO_SCOPE tATTRIBUTE tKEY tIDENTIFIER tIDENTIFIER_VALUE tPSEUDO_CLASS tRELATIONSHIP
|
4
|
-
tOPEN_ATTRIBUTE tCLOSE_ATTRIBUTE tOPEN_ARRAY tCLOSE_ARRAY tOPEN_SELECTOR tCLOSE_SELECTOR
|
4
|
+
tOPEN_ATTRIBUTE tCLOSE_ATTRIBUTE tOPEN_ARRAY tCLOSE_ARRAY tOPEN_SELECTOR tCLOSE_SELECTOR tPOSITION
|
5
5
|
tOPERATOR tARRAY_VALUE tBOOLEAN tFLOAT tINTEGER tNIL tREGEXP tSTRING tSYMBOL
|
6
6
|
rule
|
7
7
|
expression_list
|
@@ -13,7 +13,8 @@ rule
|
|
13
13
|
| selector { NodeQuery::Compiler::Expression.new(selector: val[0]) }
|
14
14
|
|
15
15
|
selector
|
16
|
-
: basic_selector { NodeQuery::Compiler::Selector.new(basic_selector: val[0]) }
|
16
|
+
: basic_selector tPOSITION { NodeQuery::Compiler::Selector.new(basic_selector: val[0], position: val[1] ) }
|
17
|
+
| basic_selector { NodeQuery::Compiler::Selector.new(basic_selector: val[0]) }
|
17
18
|
| tPSEUDO_CLASS tOPEN_SELECTOR selector tCLOSE_SELECTOR { NodeQuery::Compiler::Selector.new(pseudo_class: val[0], pseudo_selector: val[2]) }
|
18
19
|
| tRELATIONSHIP selector { NodeQuery::Compiler::Selector.new(relationship: val[0], rest: val[1]) }
|
19
20
|
| tGOTO_SCOPE selector { NodeQuery::Compiler::Selector.new(goto_scope: val[0], rest: val[1]) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: node_query
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
91
|
- !ruby/object:Gem::Version
|
92
92
|
version: '0'
|
93
93
|
requirements: []
|
94
|
-
rubygems_version: 3.3.
|
94
|
+
rubygems_version: 3.3.22
|
95
95
|
signing_key:
|
96
96
|
specification_version: 4
|
97
97
|
summary: ast node query language
|