node_query 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|