synvert-core 1.2.1 → 1.3.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 +6 -0
- data/Gemfile +9 -0
- data/lib/synvert/core/node_query/compiler/attribute.rb +6 -18
- data/lib/synvert/core/node_query/compiler/comparable.rb +26 -19
- data/lib/synvert/core/node_query/compiler/expression.rb +8 -76
- data/lib/synvert/core/node_query/compiler/identifier.rb +1 -1
- data/lib/synvert/core/node_query/compiler/regexp.rb +2 -2
- data/lib/synvert/core/node_query/compiler/selector.rb +102 -20
- data/lib/synvert/core/node_query/compiler/simple_selector.rb +29 -0
- data/lib/synvert/core/node_query/compiler/string.rb +1 -1
- data/lib/synvert/core/node_query/compiler.rb +1 -0
- data/lib/synvert/core/node_query/lexer.rex +19 -16
- data/lib/synvert/core/node_query/lexer.rex.rb +33 -27
- data/lib/synvert/core/node_query/parser.racc.rb +117 -279
- data/lib/synvert/core/node_query/parser.y +17 -34
- data/lib/synvert/core/version.rb +1 -1
- data/spec/synvert/core/node_query/lexer_spec.rb +48 -48
- data/spec/synvert/core/node_query/parser_spec.rb +138 -106
- data/spec/synvert/core/rewriter/instance_spec.rb +3 -3
- data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +3 -3
- data/synvert-core-ruby.gemspec +0 -10
- metadata +3 -128
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56b98032247ef28e548b34dfd8b2b49bad8744227cd61b4f300c7111b701ff91
|
4
|
+
data.tar.gz: ea75a1e4f442041665dbd9babdc2f7e4b55c4c78c87facef856e903f2d8c96d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 905c1f393a1702df43300432e210784790a30dff9a7bb0c798c742ec372a65ad8487b2097333f57afb1798c566f554ab9096ae92db8cd2541e5bb03769009882
|
7
|
+
data.tar.gz: bd03f1c285c5b5b42628df0b3c8777d6167d980c8c3e3b2f92badfc040b2bef5fe2bef3b848b5a6a166002a5e68f8f4f287ac6ee8fadc72a3ef6706fe191c821
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -7,7 +7,7 @@ module Synvert::Core::NodeQuery::Compiler
|
|
7
7
|
# @param key [String] the key
|
8
8
|
# @param value the value can be any class implement {Synvert::Core::NodeQuery::Compiler::Comparable}
|
9
9
|
# @param operator [Symbol] the operator
|
10
|
-
def initialize(key:, value:, operator:
|
10
|
+
def initialize(key:, value:, operator: '==')
|
11
11
|
@key = key
|
12
12
|
@value = value
|
13
13
|
@operator = operator
|
@@ -23,25 +23,13 @@ module Synvert::Core::NodeQuery::Compiler
|
|
23
23
|
|
24
24
|
def to_s
|
25
25
|
case @operator
|
26
|
-
when
|
27
|
-
"#{@key}
|
28
|
-
when
|
29
|
-
"#{@key}=~#{@value}"
|
30
|
-
when :!~
|
31
|
-
"#{@key}!~#{@value}"
|
32
|
-
when :>
|
33
|
-
"#{@key}>#{@value}"
|
34
|
-
when :>=
|
35
|
-
"#{@key}>=#{@value}"
|
36
|
-
when :<
|
37
|
-
"#{@key}<#{@value}"
|
38
|
-
when :<=
|
39
|
-
"#{@key}<=#{@value}"
|
40
|
-
when :in
|
26
|
+
when '^=', '$=', '*=', '!=', '=~', '!~', '>=', '>', '<=', '<'
|
27
|
+
"#{@key}#{@operator}#{@value}"
|
28
|
+
when 'in'
|
41
29
|
"#{@key} in (#{@value})"
|
42
|
-
when
|
30
|
+
when 'not_in'
|
43
31
|
"#{@key} not in (#{@value})"
|
44
|
-
when
|
32
|
+
when 'includes'
|
45
33
|
"#{@key} includes #{@value}"
|
46
34
|
else
|
47
35
|
"#{@key}=#{@value}"
|
@@ -3,52 +3,59 @@
|
|
3
3
|
module Synvert::Core::NodeQuery::Compiler
|
4
4
|
# Compare acutal value with expected value.
|
5
5
|
module Comparable
|
6
|
-
SIMPLE_VALID_OPERATORS = [
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
SIMPLE_VALID_OPERATORS = ['==', '!=', 'includes']
|
7
|
+
STRING_VALID_OPERATORS = ['==', '!=', '^=', '$=', '*=', 'includes']
|
8
|
+
NUMBER_VALID_OPERATORS = ['==', '!=', '>', '>=', '<', '<=', 'includes']
|
9
|
+
ARRAY_VALID_OPERATORS = ['==', '!=', 'in', 'not_in']
|
10
|
+
REGEXP_VALID_OPERATORS = ['=~', '!~']
|
10
11
|
|
11
12
|
# Check if the actual value matches the expected value.
|
12
13
|
#
|
13
14
|
# @param node [Parser::AST::Node] node to calculate actual value
|
14
|
-
# @param operator [Symbol] operator to compare with expected value, operator can be <code
|
15
|
+
# @param operator [Symbol] 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>
|
15
16
|
# @return [Boolean] true if actual value matches the expected value
|
16
17
|
# @raise [Synvert::Core::NodeQuery::Compiler::InvalidOperatorError] if operator is invalid
|
17
18
|
def match?(node, operator)
|
18
19
|
raise InvalidOperatorError, "invalid operator #{operator}" unless valid_operator?(operator)
|
19
20
|
|
20
21
|
case operator
|
21
|
-
when
|
22
|
+
when '!='
|
22
23
|
if expected_value.is_a?(::Array)
|
23
24
|
actual = actual_value(node)
|
24
25
|
!actual.is_a?(::Array) || actual.size != expected_value.size ||
|
25
|
-
actual.zip(expected_value).any? { |actual_node, expected_node| expected_node.match?(actual_node,
|
26
|
+
actual.zip(expected_value).any? { |actual_node, expected_node| expected_node.match?(actual_node, '!=') }
|
26
27
|
else
|
27
28
|
actual_value(node) != expected_value
|
28
29
|
end
|
29
|
-
when
|
30
|
+
when '=~'
|
30
31
|
actual_value(node) =~ expected_value
|
31
|
-
when
|
32
|
+
when '!~'
|
32
33
|
actual_value(node) !~ expected_value
|
33
|
-
when
|
34
|
+
when '^='
|
35
|
+
actual_value(node).start_with?(expected_value)
|
36
|
+
when '$='
|
37
|
+
actual_value(node).end_with?(expected_value)
|
38
|
+
when '*='
|
39
|
+
actual_value(node).include?(expected_value)
|
40
|
+
when '>'
|
34
41
|
actual_value(node) > expected_value
|
35
|
-
when
|
42
|
+
when '>='
|
36
43
|
actual_value(node) >= expected_value
|
37
|
-
when
|
44
|
+
when '<'
|
38
45
|
actual_value(node) < expected_value
|
39
|
-
when
|
46
|
+
when '<='
|
40
47
|
actual_value(node) <= expected_value
|
41
|
-
when
|
42
|
-
expected_value.any? { |expected| expected.match?(node,
|
43
|
-
when
|
44
|
-
expected_value.all? { |expected| expected.match?(node,
|
45
|
-
when
|
48
|
+
when 'in'
|
49
|
+
expected_value.any? { |expected| expected.match?(node, '==') }
|
50
|
+
when 'not_in'
|
51
|
+
expected_value.all? { |expected| expected.match?(node, '!=') }
|
52
|
+
when 'includes'
|
46
53
|
actual_value(node).any? { |actual| actual == expected_value }
|
47
54
|
else
|
48
55
|
if expected_value.is_a?(::Array)
|
49
56
|
actual = actual_value(node)
|
50
57
|
actual.is_a?(::Array) && actual.size == expected_value.size &&
|
51
|
-
actual.zip(expected_value).all? { |actual_node, expected_node| expected_node.match?(actual_node,
|
58
|
+
actual.zip(expected_value).all? { |actual_node, expected_node| expected_node.match?(actual_node, '==') }
|
52
59
|
else
|
53
60
|
actual_value(node) == expected_value
|
54
61
|
end
|
@@ -5,14 +5,10 @@ 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
|
9
8
|
# @param rest [Synvert::Core::NodeQuery::Compiler::Expression] the rest expression
|
10
|
-
|
11
|
-
def initialize(selector: nil, goto_scope: nil, rest: nil, relationship: nil)
|
9
|
+
def initialize(selector: nil, rest: nil)
|
12
10
|
@selector = selector
|
13
|
-
@goto_scope = goto_scope
|
14
11
|
@rest = rest
|
15
|
-
@relationship = relationship
|
16
12
|
end
|
17
13
|
|
18
14
|
# Check if the node matches the expression.
|
@@ -22,78 +18,27 @@ module Synvert::Core::NodeQuery::Compiler
|
|
22
18
|
!query_nodes(node).empty?
|
23
19
|
end
|
24
20
|
|
25
|
-
# Query nodes by the expression.
|
21
|
+
# Query nodes by the selector and the rest expression.
|
26
22
|
#
|
27
|
-
# * If relationship is nil, it will match in all recursive child nodes and return matching nodes.
|
28
|
-
# * If relationship is :decendant, it will match in all recursive child nodes.
|
29
|
-
# * If relationship is :child, it will match in direct child nodes.
|
30
|
-
# * If relationship is :next_sibling, it try to match next sibling node.
|
31
|
-
# * If relationship is :subsequent_sibling, it will match in all sibling nodes.
|
32
23
|
# @param node [Parser::AST::Node] node to match
|
33
24
|
# @param descendant_match [Boolean] whether to match in descendant node
|
34
25
|
# @return [Array<Parser::AST::Node>] matching nodes.
|
35
26
|
def query_nodes(node, descendant_match = true)
|
36
|
-
|
37
|
-
|
38
|
-
return find_nodes_by_relationship(node) if @relationship
|
39
|
-
|
40
|
-
matching_nodes = find_nodes_without_relationship(node, descendant_match)
|
27
|
+
matching_nodes = find_nodes_by_selector(node, descendant_match)
|
41
28
|
return matching_nodes if @rest.nil?
|
42
29
|
|
43
|
-
matching_nodes.
|
44
|
-
.flatten
|
30
|
+
matching_nodes.flat_map { |matching_node| find_nodes_by_rest(matching_node, descendant_match) }
|
45
31
|
end
|
46
32
|
|
47
33
|
def to_s
|
48
|
-
return @selector.to_s unless @rest
|
49
|
-
|
50
34
|
result = []
|
51
35
|
result << @selector.to_s if @selector
|
52
|
-
result <<
|
53
|
-
case @relationship
|
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
|
60
|
-
end
|
36
|
+
result << @rest.to_s if @rest
|
61
37
|
result.join(' ')
|
62
38
|
end
|
63
39
|
|
64
40
|
private
|
65
41
|
|
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
42
|
# Find nodes by @rest
|
98
43
|
# @param node [Parser::AST::Node] node to match
|
99
44
|
# @param descendant_match [Boolean] whether to match in descendant node
|
@@ -104,23 +49,10 @@ module Synvert::Core::NodeQuery::Compiler
|
|
104
49
|
# Find nodes with nil relationship.
|
105
50
|
# @param node [Parser::AST::Node] node to match
|
106
51
|
# @param descendant_match [Boolean] whether to match in descendant node
|
107
|
-
def
|
108
|
-
|
109
|
-
return node.map { |each_node|
|
110
|
-
find_nodes_without_relationship(each_node, descendant_match)
|
111
|
-
}.flatten
|
112
|
-
end
|
113
|
-
|
114
|
-
return [node] unless @selector
|
52
|
+
def find_nodes_by_selector(node, descendant_match = true)
|
53
|
+
return Array(node) if !@selector
|
115
54
|
|
116
|
-
|
117
|
-
nodes << node if @selector.match?(node)
|
118
|
-
if descendant_match
|
119
|
-
node.recursive_children do |child_node|
|
120
|
-
nodes << child_node if @selector.match?(child_node)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
@selector.filter(nodes)
|
55
|
+
@selector.query_nodes(node, descendant_match)
|
124
56
|
end
|
125
57
|
end
|
126
58
|
end
|
@@ -15,14 +15,14 @@ module Synvert::Core::NodeQuery::Compiler
|
|
15
15
|
# @param node [Parser::AST::Node] the node
|
16
16
|
# @param operator [Symbol] the operator
|
17
17
|
# @return [Boolean] true if the regexp value matches the node value, otherwise, false.
|
18
|
-
def match?(node, operator =
|
18
|
+
def match?(node, operator = '=~')
|
19
19
|
match =
|
20
20
|
if node.is_a?(::Parser::AST::Node)
|
21
21
|
@value.match(node.to_source)
|
22
22
|
else
|
23
23
|
@value.match(node.to_s)
|
24
24
|
end
|
25
|
-
operator ==
|
25
|
+
operator == '=~' ? match : !match
|
26
26
|
end
|
27
27
|
|
28
28
|
# Get valid operators.
|
@@ -4,39 +4,68 @@ module Synvert::Core::NodeQuery::Compiler
|
|
4
4
|
# Selector used to match nodes, it combines by node type and/or attribute list, plus index or has expression.
|
5
5
|
class Selector
|
6
6
|
# Initialize a Selector.
|
7
|
-
# @param
|
7
|
+
# @param goto_scope [String] goto scope
|
8
|
+
# @param relationship [Symbol] 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
|
+
# @param rest [Synvert::Core::NodeQuery::Compiler::Selector] the rest selector
|
10
|
+
# @param simple_selector [Synvert::Core::NodeQuery::Compiler::SimpleSelector] the simple selector
|
8
11
|
# @param attribute_list [Synvert::Core::NodeQuery::Compiler::AttributeList] the attribute list
|
9
12
|
# @param index [Integer] the index
|
10
13
|
# @param pseudo_class [String] the pseudo class, can be <code>has</code> or <code>not_has</code>
|
11
|
-
# @param
|
12
|
-
def initialize(
|
13
|
-
@
|
14
|
-
@
|
14
|
+
# @param pseudo_selector [Synvert::Core::NodeQuery::Compiler::Expression] the pseudo selector
|
15
|
+
def initialize(goto_scope: nil, relationship: nil, rest: nil, simple_selector: nil, index: nil, pseudo_class: nil, pseudo_selector: nil)
|
16
|
+
@goto_scope = goto_scope
|
17
|
+
@relationship = relationship
|
18
|
+
@rest = rest
|
19
|
+
@simple_selector = simple_selector
|
15
20
|
@index = index
|
16
21
|
@pseudo_class = pseudo_class
|
17
|
-
@
|
18
|
-
end
|
19
|
-
|
20
|
-
# Filter nodes by index.
|
21
|
-
def filter(nodes)
|
22
|
-
return nodes if @index.nil?
|
23
|
-
|
24
|
-
nodes[@index] ? [nodes[@index]] : []
|
22
|
+
@pseudo_selector = pseudo_selector
|
25
23
|
end
|
26
24
|
|
27
25
|
# Check if node matches the selector.
|
28
26
|
# @param node [Parser::AST::Node] the node
|
29
|
-
def match?(node
|
30
|
-
|
31
|
-
(!@
|
32
|
-
|
27
|
+
def match?(node)
|
28
|
+
node.is_a?(::Parser::AST::Node) &&
|
29
|
+
(!@simple_selector || @simple_selector.match?(node)) &&
|
30
|
+
match_pseudo_class?(node)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Query nodes by the selector.
|
34
|
+
#
|
35
|
+
# * If relationship is nil, it will match in all recursive child nodes and return matching nodes.
|
36
|
+
# * If relationship is decendant, it will match in all recursive child nodes.
|
37
|
+
# * If relationship is child, it will match in direct child nodes.
|
38
|
+
# * If relationship is next sibling, it try to match next sibling node.
|
39
|
+
# * If relationship is subsequent sibling, it will match in all sibling nodes.
|
40
|
+
# @param node [Parser::AST::Node] node to match
|
41
|
+
# @param descendant_match [Boolean] whether to match in descendant node
|
42
|
+
# @return [Array<Parser::AST::Node>] matching nodes.
|
43
|
+
def query_nodes(node, descendant_match = true)
|
44
|
+
return find_nodes_by_relationship(node) if @relationship
|
45
|
+
|
46
|
+
if node.is_a?(::Array)
|
47
|
+
return node.flat_map { |child_node| query_nodes(child_node, descendant_match) }
|
48
|
+
end
|
49
|
+
|
50
|
+
return find_nodes_by_goto_scope(node) if @goto_scope
|
51
|
+
|
52
|
+
nodes = []
|
53
|
+
nodes << node if match?(node)
|
54
|
+
if descendant_match && @simple_selector
|
55
|
+
node.recursive_children do |child_node|
|
56
|
+
nodes << child_node if match?(child_node)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
filter(nodes)
|
33
60
|
end
|
34
61
|
|
35
62
|
def to_s
|
36
63
|
result = []
|
37
|
-
result << "
|
38
|
-
result << @
|
39
|
-
result <<
|
64
|
+
result << "<#{@goto_scope}> " if @goto_scope
|
65
|
+
result << "#{@relationship} " if @relationship
|
66
|
+
result << @rest.to_s if @rest
|
67
|
+
result << @simple_selector.to_s if @simple_selector
|
68
|
+
result << ":#{@pseudo_class}(#{@pseudo_selector})" if @pseudo_class
|
40
69
|
if @index
|
41
70
|
result <<
|
42
71
|
case @index
|
@@ -52,5 +81,58 @@ module Synvert::Core::NodeQuery::Compiler
|
|
52
81
|
end
|
53
82
|
result.join('')
|
54
83
|
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# Find nodes by @goto_scope
|
88
|
+
# @param node [Parser::AST::Node] node to match
|
89
|
+
def find_nodes_by_goto_scope(node)
|
90
|
+
@goto_scope.split('.').each { |scope| node = node.send(scope) }
|
91
|
+
@rest.query_nodes(node, false)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Find ndoes by @relationship
|
95
|
+
# @param node [Parser::AST::Node] node to match
|
96
|
+
def find_nodes_by_relationship(node)
|
97
|
+
nodes = []
|
98
|
+
case @relationship
|
99
|
+
when '>'
|
100
|
+
if node.is_a?(::Array)
|
101
|
+
node.each do |child_node|
|
102
|
+
nodes << child_node if @rest.match?(child_node)
|
103
|
+
end
|
104
|
+
else
|
105
|
+
node.children.each do |child_node|
|
106
|
+
nodes << child_node if @rest.match?(child_node)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
when '+'
|
110
|
+
next_sibling = node.siblings.first
|
111
|
+
nodes << next_sibling if @rest.match?(next_sibling)
|
112
|
+
when '~'
|
113
|
+
node.siblings.each do |sibling_node|
|
114
|
+
nodes << sibling_node if @rest.match?(sibling_node)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
return filter(nodes)
|
118
|
+
end
|
119
|
+
|
120
|
+
def match_pseudo_class?(node)
|
121
|
+
case @pseudo_class
|
122
|
+
when 'has'
|
123
|
+
!@pseudo_selector.query_nodes(node).empty?
|
124
|
+
when 'not_has'
|
125
|
+
@pseudo_selector.query_nodes(node).empty?
|
126
|
+
else
|
127
|
+
true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Filter nodes by index.
|
132
|
+
def filter(nodes)
|
133
|
+
return nodes if @index.nil?
|
134
|
+
|
135
|
+
nodes[@index] ? [nodes[@index]] : []
|
136
|
+
end
|
55
137
|
end
|
56
138
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# SimpleSelector used to match nodes, it combines by node type and/or attribute list.
|
5
|
+
class SimpleSelector
|
6
|
+
# Initialize a SimpleSelector.
|
7
|
+
# @param node_type [String] the node type
|
8
|
+
# @param attribute_list [Synvert::Core::NodeQuery::Compiler::AttributeList] the attribute list
|
9
|
+
def initialize(node_type:, attribute_list: nil)
|
10
|
+
@node_type = node_type
|
11
|
+
@attribute_list = attribute_list
|
12
|
+
end
|
13
|
+
|
14
|
+
# Check if node matches the selector.
|
15
|
+
# @param node [Parser::AST::Node] the node
|
16
|
+
def match?(node, _operator = '==')
|
17
|
+
return false unless node
|
18
|
+
|
19
|
+
@node_type.to_sym == node.type && (!@attribute_list || @attribute_list.match?(node))
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
result = []
|
24
|
+
result << ".#{@node_type}" if @node_type
|
25
|
+
result << @attribute_list.to_s if @attribute_list
|
26
|
+
result.join('')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -8,6 +8,7 @@ module Synvert::Core::NodeQuery::Compiler
|
|
8
8
|
|
9
9
|
autoload :Expression, 'synvert/core/node_query/compiler/expression'
|
10
10
|
autoload :Selector, 'synvert/core/node_query/compiler/selector'
|
11
|
+
autoload :SimpleSelector, 'synvert/core/node_query/compiler/simple_selector'
|
11
12
|
autoload :AttributeList, 'synvert/core/node_query/compiler/attribute_list'
|
12
13
|
autoload :Attribute, 'synvert/core/node_query/compiler/attribute'
|
13
14
|
|
@@ -22,8 +22,8 @@ macros
|
|
22
22
|
REGEXP /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
|
23
23
|
SYMBOL /:[\w!\?<>=]+/
|
24
24
|
TRUE /true/
|
25
|
-
SINGLE_QUOTE_STRING /'
|
26
|
-
DOUBLE_QUOTE_STRING /"
|
25
|
+
SINGLE_QUOTE_STRING /'.*?'/
|
26
|
+
DOUBLE_QUOTE_STRING /".*?"/
|
27
27
|
|
28
28
|
rules
|
29
29
|
|
@@ -36,9 +36,9 @@ rules
|
|
36
36
|
/:has/ { [:tPSEUDO_CLASS, text[1..-1]] }
|
37
37
|
/:not_has/ { [:tPSEUDO_CLASS, text[1..-1]] }
|
38
38
|
/#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
|
39
|
-
/>/ { [:
|
40
|
-
/~/ { [:
|
41
|
-
/\+/ { [:
|
39
|
+
/>/ { [:tRELATIONSHIP, text] }
|
40
|
+
/~/ { [:tRELATIONSHIP, text] }
|
41
|
+
/\+/ { [:tRELATIONSHIP, text] }
|
42
42
|
/#{OPEN_SELECTOR}/ { [:tOPEN_SELECTOR, text] }
|
43
43
|
/#{CLOSE_SELECTOR}/ { [:tCLOSE_SELECTOR, text] }
|
44
44
|
/#{OPEN_GOTO_SCOPE}/ { @state = :GOTO_SCOPE; [:tOPEN_GOTO_SCOPE, text] }
|
@@ -47,17 +47,20 @@ rules
|
|
47
47
|
:GOTO_SCOPE /#{IDENTIFIER}/ { [:tIDENTIFIER, text] }
|
48
48
|
:GOTO_SCOPE /#{CLOSE_GOTO_SCOPE}/ { @state = nil; [:tCLOSE_GOTO_SCOPE, text] }
|
49
49
|
:KEY /\s+/
|
50
|
-
:KEY
|
51
|
-
:KEY
|
52
|
-
:KEY
|
53
|
-
:KEY
|
54
|
-
:KEY
|
55
|
-
:KEY
|
56
|
-
:KEY
|
57
|
-
:KEY
|
58
|
-
:KEY
|
59
|
-
:KEY
|
60
|
-
:KEY
|
50
|
+
:KEY /\^=/ { @state = :VALUE; [:tOPERATOR, '^='] }
|
51
|
+
:KEY /\$=/ { @state = :VALUE; [:tOPERATOR, '$='] }
|
52
|
+
:KEY /\*=/ { @state = :VALUE; [:tOPERATOR, '*='] }
|
53
|
+
:KEY /!=/ { @state = :VALUE; [:tOPERATOR, '!='] }
|
54
|
+
:KEY /=~/ { @state = :VALUE; [:tOPERATOR, '=~'] }
|
55
|
+
:KEY /!~/ { @state = :VALUE; [:tOPERATOR, '!~'] }
|
56
|
+
:KEY />=/ { @state = :VALUE; [:tOPERATOR, '>='] }
|
57
|
+
:KEY /<=/ { @state = :VALUE; [:tOPERATOR, '<='] }
|
58
|
+
:KEY />/ { @state = :VALUE; [:tOPERATOR, '>'] }
|
59
|
+
:KEY /</ { @state = :VALUE; [:tOPERATOR, '<'] }
|
60
|
+
:KEY /=/ { @state = :VALUE; [:tOPERATOR, '=='] }
|
61
|
+
:KEY /includes/i { @state = :VALUE; [:tOPERATOR, 'includes'] }
|
62
|
+
:KEY /not in/i { @state = :VALUE; [:tOPERATOR, 'not_in'] }
|
63
|
+
:KEY /in/i { @state = :VALUE; [:tOPERATOR, 'in'] }
|
61
64
|
:KEY /#{IDENTIFIER}/ { [:tKEY, text] }
|
62
65
|
:VALUE /\s+/
|
63
66
|
:VALUE /\[\]=/ { [:tIDENTIFIER_VALUE, text] }
|
@@ -35,8 +35,8 @@ class Synvert::Core::NodeQuery::Lexer
|
|
35
35
|
REGEXP = /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
|
36
36
|
SYMBOL = /:[\w!\?<>=]+/
|
37
37
|
TRUE = /true/
|
38
|
-
SINGLE_QUOTE_STRING = /'
|
39
|
-
DOUBLE_QUOTE_STRING = /"
|
38
|
+
SINGLE_QUOTE_STRING = /'.*?'/
|
39
|
+
DOUBLE_QUOTE_STRING = /".*?"/
|
40
40
|
# :startdoc:
|
41
41
|
# :stopdoc:
|
42
42
|
class LexerError < StandardError ; end
|
@@ -142,11 +142,11 @@ class Synvert::Core::NodeQuery::Lexer
|
|
142
142
|
when text = ss.scan(/#{NODE_TYPE}/) then
|
143
143
|
action { [:tNODE_TYPE, text[1..]] }
|
144
144
|
when text = ss.scan(/>/) then
|
145
|
-
action { [:
|
145
|
+
action { [:tRELATIONSHIP, text] }
|
146
146
|
when text = ss.scan(/~/) then
|
147
|
-
action { [:
|
147
|
+
action { [:tRELATIONSHIP, text] }
|
148
148
|
when text = ss.scan(/\+/) then
|
149
|
-
action { [:
|
149
|
+
action { [:tRELATIONSHIP, text] }
|
150
150
|
when text = ss.scan(/#{OPEN_SELECTOR}/) then
|
151
151
|
action { [:tOPEN_SELECTOR, text] }
|
152
152
|
when text = ss.scan(/#{CLOSE_SELECTOR}/) then
|
@@ -175,28 +175,34 @@ class Synvert::Core::NodeQuery::Lexer
|
|
175
175
|
case
|
176
176
|
when ss.skip(/\s+/) then
|
177
177
|
# do nothing
|
178
|
-
when
|
179
|
-
action { @state = :VALUE; [:
|
180
|
-
when
|
181
|
-
action { @state = :VALUE; [:
|
182
|
-
when
|
183
|
-
action { @state = :VALUE; [:
|
184
|
-
when
|
185
|
-
action { @state = :VALUE; [:
|
186
|
-
when
|
187
|
-
action { @state = :VALUE; [:
|
188
|
-
when
|
189
|
-
action { @state = :VALUE; [:
|
190
|
-
when
|
191
|
-
action { @state = :VALUE; [:
|
192
|
-
when
|
193
|
-
action { @state = :VALUE; [:
|
194
|
-
when
|
195
|
-
action { @state = :VALUE; [:
|
196
|
-
when
|
197
|
-
action { @state = :VALUE; [:
|
198
|
-
when
|
199
|
-
action { @state = :VALUE; [:
|
178
|
+
when ss.skip(/\^=/) then
|
179
|
+
action { @state = :VALUE; [:tOPERATOR, '^='] }
|
180
|
+
when ss.skip(/\$=/) then
|
181
|
+
action { @state = :VALUE; [:tOPERATOR, '$='] }
|
182
|
+
when ss.skip(/\*=/) then
|
183
|
+
action { @state = :VALUE; [:tOPERATOR, '*='] }
|
184
|
+
when ss.skip(/!=/) then
|
185
|
+
action { @state = :VALUE; [:tOPERATOR, '!='] }
|
186
|
+
when ss.skip(/=~/) then
|
187
|
+
action { @state = :VALUE; [:tOPERATOR, '=~'] }
|
188
|
+
when ss.skip(/!~/) then
|
189
|
+
action { @state = :VALUE; [:tOPERATOR, '!~'] }
|
190
|
+
when ss.skip(/>=/) then
|
191
|
+
action { @state = :VALUE; [:tOPERATOR, '>='] }
|
192
|
+
when ss.skip(/<=/) then
|
193
|
+
action { @state = :VALUE; [:tOPERATOR, '<='] }
|
194
|
+
when ss.skip(/>/) then
|
195
|
+
action { @state = :VALUE; [:tOPERATOR, '>'] }
|
196
|
+
when ss.skip(/</) then
|
197
|
+
action { @state = :VALUE; [:tOPERATOR, '<'] }
|
198
|
+
when ss.skip(/=/) then
|
199
|
+
action { @state = :VALUE; [:tOPERATOR, '=='] }
|
200
|
+
when ss.skip(/includes/i) then
|
201
|
+
action { @state = :VALUE; [:tOPERATOR, 'includes'] }
|
202
|
+
when ss.skip(/not in/i) then
|
203
|
+
action { @state = :VALUE; [:tOPERATOR, 'not_in'] }
|
204
|
+
when ss.skip(/in/i) then
|
205
|
+
action { @state = :VALUE; [:tOPERATOR, 'in'] }
|
200
206
|
when text = ss.scan(/#{IDENTIFIER}/) then
|
201
207
|
action { [:tKEY, text] }
|
202
208
|
else
|