node_query 1.2.0 → 1.4.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 +15 -0
- data/Gemfile.lock +9 -7
- data/README.md +437 -3
- data/lib/node_query/compiler/attribute.rb +1 -1
- data/lib/node_query/compiler/comparable.rb +10 -3
- data/lib/node_query/compiler/expression.rb +7 -10
- data/lib/node_query/compiler/expression_list.rb +14 -3
- data/lib/node_query/compiler/identifier.rb +1 -1
- data/lib/node_query/compiler/regexp.rb +7 -10
- data/lib/node_query/compiler/selector.rb +32 -6
- data/lib/node_query/compiler/string.rb +19 -0
- data/lib/node_query/compiler.rb +0 -1
- data/lib/node_query/helper.rb +47 -29
- data/lib/node_query/node_rules.rb +189 -0
- data/lib/node_query/version.rb +1 -1
- data/lib/node_query.rb +34 -6
- data/lib/node_query_lexer.rex +2 -5
- data/lib/node_query_lexer.rex.rb +19 -23
- data/lib/node_query_parser.racc.rb +74 -81
- data/lib/node_query_parser.y +1 -2
- data/node_query.gemspec +1 -1
- data/sig/node_query.rbs +4 -2
- metadata +7 -7
- data/lib/node_query/compiler/evaluated_value.rb +0 -50
@@ -23,7 +23,7 @@ module NodeQuery::Compiler
|
|
23
23
|
# Check if node matches the selector.
|
24
24
|
# @param node [Parser::AST::Node] the node
|
25
25
|
def match?(node)
|
26
|
-
|
26
|
+
NodeQuery.adapter.is_node?(node) && (!@basic_selector || @basic_selector.match?(node)) && match_pseudo_class?(node)
|
27
27
|
end
|
28
28
|
|
29
29
|
# Query nodes by the selector.
|
@@ -33,8 +33,13 @@ module NodeQuery::Compiler
|
|
33
33
|
# * If relationship is next sibling, it try to match next sibling node.
|
34
34
|
# * If relationship is subsequent sibling, it will match in all sibling nodes.
|
35
35
|
# @param node [Node] node to match
|
36
|
+
# @param options [Hash] if query the current node
|
37
|
+
# @option options [boolean] :including_self if query the current node, default is ture
|
38
|
+
# @option options [boolean] :stop_on_match if stop on first match, default is false
|
39
|
+
# @option options [boolean] :recursive if stop on first match, default is true
|
36
40
|
# @return [Array<Node>] matching nodes.
|
37
|
-
def query_nodes(node)
|
41
|
+
def query_nodes(node, options = {})
|
42
|
+
options = { including_self: true, stop_on_match: false, recursive: true }.merge(options)
|
38
43
|
return find_nodes_by_relationship(node) if @relationship
|
39
44
|
|
40
45
|
if node.is_a?(::Array)
|
@@ -44,10 +49,25 @@ module NodeQuery::Compiler
|
|
44
49
|
return find_nodes_by_goto_scope(node) if @goto_scope
|
45
50
|
|
46
51
|
nodes = []
|
47
|
-
|
52
|
+
if options[:including_self] && match?(node)
|
53
|
+
nodes << node
|
54
|
+
return matching_nodes if options[:stop_on_match]
|
55
|
+
end
|
48
56
|
if @basic_selector
|
49
|
-
|
50
|
-
|
57
|
+
if options[:recursive]
|
58
|
+
NodeQuery::Helper.handle_recursive_child(node) do |child_node|
|
59
|
+
if match?(child_node)
|
60
|
+
nodes << child_node
|
61
|
+
break if options[:stop_on_match]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
NodeQuery.adapter.get_children(node).each do |child_node|
|
66
|
+
if match?(child_node)
|
67
|
+
nodes << child_node
|
68
|
+
break if options[:stop_on_match]
|
69
|
+
end
|
70
|
+
end
|
51
71
|
end
|
52
72
|
end
|
53
73
|
nodes
|
@@ -86,7 +106,13 @@ module NodeQuery::Compiler
|
|
86
106
|
end
|
87
107
|
else
|
88
108
|
node.children.each do |child_node|
|
89
|
-
|
109
|
+
if NodeQuery.adapter.is_node?(child_node) && :begin == NodeQuery.adapter.get_node_type(child_node)
|
110
|
+
child_node.children.each do |child_child_node|
|
111
|
+
nodes << child_child_node if @rest.match?(child_child_node)
|
112
|
+
end
|
113
|
+
elsif @rest.match?(child_node)
|
114
|
+
nodes << child_node
|
115
|
+
end
|
90
116
|
end
|
91
117
|
end
|
92
118
|
when '+'
|
@@ -5,12 +5,31 @@ module NodeQuery::Compiler
|
|
5
5
|
class String
|
6
6
|
include Comparable
|
7
7
|
|
8
|
+
attr_accessor :base_node
|
9
|
+
|
8
10
|
# Initialize a String.
|
9
11
|
# @param value [String] the string value
|
10
12
|
def initialize(value:)
|
11
13
|
@value = value
|
12
14
|
end
|
13
15
|
|
16
|
+
# Get the expected value.
|
17
|
+
# @example
|
18
|
+
# if the source code of the node is @id = id,
|
19
|
+
# and the @value is "@{{right_vaue}}",
|
20
|
+
# then it returns "@id".
|
21
|
+
# @return [String] the expected string, if it contains evaluated value, evaluate the node value.
|
22
|
+
def expected_value
|
23
|
+
NodeQuery::Helper.evaluate_node_value(base_node, @value)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check if the actual value equals the node value.
|
27
|
+
# @param node [Node] the node
|
28
|
+
# @return [Boolean] true if the actual value equals the node value.
|
29
|
+
def is_equal?(node)
|
30
|
+
actual_value(node).to_s == expected_value
|
31
|
+
end
|
32
|
+
|
14
33
|
# Get valid operators.
|
15
34
|
# @return [Array] valid operators
|
16
35
|
def valid_operators
|
data/lib/node_query/compiler.rb
CHANGED
@@ -15,7 +15,6 @@ module NodeQuery::Compiler
|
|
15
15
|
|
16
16
|
autoload :ArrayValue, 'node_query/compiler/array_value'
|
17
17
|
autoload :Boolean, 'node_query/compiler/boolean'
|
18
|
-
autoload :EvaluatedValue, 'node_query/compiler/evaluated_value'
|
19
18
|
autoload :Float, 'node_query/compiler/float'
|
20
19
|
autoload :Identifier, 'node_query/compiler/identifier'
|
21
20
|
autoload :Integer, 'node_query/compiler/integer'
|
data/lib/node_query/helper.rb
CHANGED
@@ -1,40 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class NodeQuery::Helper
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
class <<self
|
5
|
+
# Get target node by the keys.
|
6
|
+
# @param node [Node] ast node
|
7
|
+
# @param keys [String|Array] keys of child node.
|
8
|
+
# @return [Node|] the target node.
|
9
|
+
def get_target_node(node, keys)
|
10
|
+
return unless node
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
first_key, rest_keys = keys.to_s.split('.', 2)
|
13
|
+
if node.is_a?(Array) && first_key === "*"
|
14
|
+
return node.map { |child_node| get_target_node(child_node, rest_keys) }
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
if node.is_a?(Array) && first_key =~ /\d+/
|
18
|
+
child_node = node[first_key.to_i]
|
19
|
+
elsif node.respond_to?(first_key)
|
20
|
+
child_node = node.send(first_key)
|
21
|
+
elsif first_key == "nodeType"
|
22
|
+
child_node = NodeQuery.adapter.get_node_type(node)
|
23
|
+
end
|
22
24
|
|
23
|
-
|
25
|
+
return child_node unless rest_keys
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
return get_target_node(child_node, rest_keys)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Recursively handle child nodes.
|
31
|
+
# @param node [Node] ast node
|
32
|
+
# @yield [child] Gives a child node.
|
33
|
+
# @yieldparam child [Parser::AST::Node] child node
|
34
|
+
def handle_recursive_child(node, &block)
|
35
|
+
NodeQuery.adapter.get_children(node).each do |child|
|
36
|
+
if NodeQuery.adapter.is_node?(child)
|
37
|
+
block.call(child)
|
38
|
+
handle_recursive_child(child, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
27
42
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
43
|
+
# Evaluate node value.
|
44
|
+
# @example
|
45
|
+
# source code of the node is @id = id
|
46
|
+
# evaluated_node_value(node, "@{{right_value}}") # => @id
|
47
|
+
# @param node [Node] ast node
|
48
|
+
# @param str [String] string to be evaluated
|
49
|
+
# @return [String] evaluated string
|
50
|
+
def evaluate_node_value(node, str)
|
51
|
+
str.scan(/{{(.*?)}}/).each do |match_data|
|
52
|
+
target_node = NodeQuery::Helper.get_target_node(node, match_data.first)
|
53
|
+
str = str.sub("{{#{match_data.first}}}", NodeQuery.adapter.get_source(target_node))
|
37
54
|
end
|
55
|
+
str
|
38
56
|
end
|
39
57
|
end
|
40
|
-
end
|
58
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NodeQuery::NodeRules
|
4
|
+
KEYWORDS = %i[any includes not in not_in gt gte lt lte]
|
5
|
+
|
6
|
+
# Initialize a NodeRules.
|
7
|
+
# @param rules [Hash] the nod rules
|
8
|
+
def initialize(rules)
|
9
|
+
@rules = rules
|
10
|
+
end
|
11
|
+
|
12
|
+
# Query nodes by the rules.
|
13
|
+
# @param node [Node] node to query
|
14
|
+
# @param options [Hash] if query the current node
|
15
|
+
# @option options [boolean] :including_self if query the current node, default is ture
|
16
|
+
# @option options [boolean] :stop_on_match if stop on first match, default is false
|
17
|
+
# @option options [boolean] :recursive if stop on first match, default is true
|
18
|
+
# @return [Array<Node>] matching nodes.
|
19
|
+
def query_nodes(node, options = {})
|
20
|
+
options = { including_self: true, stop_on_match: false, recursive: true }.merge(options)
|
21
|
+
matching_nodes = []
|
22
|
+
if options[:including_self] && match_node?(node)
|
23
|
+
matching_nodes.push(node)
|
24
|
+
return matching_nodes if options[:stop_on_match]
|
25
|
+
end
|
26
|
+
if options[:recursive]
|
27
|
+
NodeQuery::Helper.handle_recursive_child(node) do |child_node|
|
28
|
+
if match_node?(child_node)
|
29
|
+
matching_nodes.push(child_node)
|
30
|
+
break if options[:stop_on_match]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
else
|
34
|
+
NodeQuery.adapter.get_children(node).each do |child_node|
|
35
|
+
if match_node?(child_node)
|
36
|
+
matching_nodes.push(child_node)
|
37
|
+
break if options[:stop_on_match]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
matching_nodes
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check if the node matches the rules.
|
45
|
+
# @param node [Node] the node
|
46
|
+
# @return [Boolean]
|
47
|
+
def match_node?(node)
|
48
|
+
flat_hash(@rules).keys.all? do |multi_keys|
|
49
|
+
last_key = multi_keys.last
|
50
|
+
actual = KEYWORDS.include?(last_key) ?
|
51
|
+
NodeQuery::Helper.get_target_node(node, multi_keys[0...-1].join('.')) :
|
52
|
+
NodeQuery::Helper.get_target_node(node, multi_keys.join('.'))
|
53
|
+
expected = expected_value(@rules, multi_keys)
|
54
|
+
expected = NodeQuery::Helper.evaluate_node_value(node, expected) if expected.is_a?(String)
|
55
|
+
case last_key
|
56
|
+
when :any, :includes
|
57
|
+
actual.any? { |actual_value| match_value?(actual_value, expected) }
|
58
|
+
when :not
|
59
|
+
!match_value?(actual, expected)
|
60
|
+
when :in
|
61
|
+
expected.any? { |expected_value| match_value?(actual, expected_value) }
|
62
|
+
when :not_in
|
63
|
+
expected.all? { |expected_value| !match_value?(actual, expected_value) }
|
64
|
+
when :gt
|
65
|
+
actual > expected
|
66
|
+
when :gte
|
67
|
+
actual >= expected
|
68
|
+
when :lt
|
69
|
+
actual < expected
|
70
|
+
when :lte
|
71
|
+
actual <= expected
|
72
|
+
else
|
73
|
+
match_value?(actual, expected)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Compare actual value with expected value.
|
81
|
+
#
|
82
|
+
# @param actual [Object] actual value.
|
83
|
+
# @param expected [Object] expected value.
|
84
|
+
# @return [Boolean]
|
85
|
+
# @raise [Synvert::Core::MethodNotSupported] if expected class is not supported.
|
86
|
+
def match_value?(actual, expected)
|
87
|
+
return true if actual == expected
|
88
|
+
|
89
|
+
case expected
|
90
|
+
when Symbol
|
91
|
+
if actual.is_a?(Parser::AST::Node)
|
92
|
+
actual_source = NodeQuery.adapter.get_source(actual)
|
93
|
+
actual_source == ":#{expected}" || actual_source == expected.to_s
|
94
|
+
else
|
95
|
+
actual.to_sym == expected
|
96
|
+
end
|
97
|
+
when String
|
98
|
+
if actual.is_a?(Parser::AST::Node)
|
99
|
+
actual_source = NodeQuery.adapter.get_source(actual)
|
100
|
+
actual_source == expected || actual_source == unwrap_quote(expected) ||
|
101
|
+
unwrap_quote(actual_source) == expected || unwrap_quote(actual_source) == unwrap_quote(expected)
|
102
|
+
else
|
103
|
+
actual.to_s == expected || wrap_quote(actual.to_s) == expected
|
104
|
+
end
|
105
|
+
when Regexp
|
106
|
+
if actual.is_a?(Parser::AST::Node)
|
107
|
+
actual.to_source =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
108
|
+
else
|
109
|
+
actual.to_s =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
110
|
+
end
|
111
|
+
when Array
|
112
|
+
return false unless expected.length == actual.length
|
113
|
+
|
114
|
+
actual.zip(expected).all? { |a, e| match_value?(a, e) }
|
115
|
+
when NilClass
|
116
|
+
if actual.is_a?(Parser::AST::Node)
|
117
|
+
:nil == actual.type
|
118
|
+
else
|
119
|
+
actual.nil?
|
120
|
+
end
|
121
|
+
when Numeric
|
122
|
+
if actual.is_a?(Parser::AST::Node)
|
123
|
+
actual.children[0] == expected
|
124
|
+
else
|
125
|
+
actual == expected
|
126
|
+
end
|
127
|
+
when TrueClass
|
128
|
+
:true == actual.type
|
129
|
+
when FalseClass
|
130
|
+
:false == actual.type
|
131
|
+
when Parser::AST::Node
|
132
|
+
actual == expected
|
133
|
+
when Synvert::Core::Rewriter::AnyValue
|
134
|
+
!actual.nil?
|
135
|
+
else
|
136
|
+
raise Synvert::Core::MethodNotSupported, "#{expected.class} is not handled for match_value?"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Convert a hash to flat one.
|
141
|
+
#
|
142
|
+
# @example
|
143
|
+
# flat_hash(type: 'block', caller: {type: 'send', receiver: 'RSpec'})
|
144
|
+
# # {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
|
145
|
+
# @param h [Hash] original hash.
|
146
|
+
# @return flatten hash.
|
147
|
+
def flat_hash(h, k = [])
|
148
|
+
new_hash = {}
|
149
|
+
h.each_pair do |key, val|
|
150
|
+
if val.is_a?(Hash)
|
151
|
+
new_hash.merge!(flat_hash(val, k + [key]))
|
152
|
+
else
|
153
|
+
new_hash[k + [key]] = val
|
154
|
+
end
|
155
|
+
end
|
156
|
+
new_hash
|
157
|
+
end
|
158
|
+
|
159
|
+
# Get expected value from rules.
|
160
|
+
#
|
161
|
+
# @param rules [Hash]
|
162
|
+
# @param multi_keys [Array<Symbol>]
|
163
|
+
# @return [Object] expected value.
|
164
|
+
def expected_value(rules, multi_keys)
|
165
|
+
multi_keys.inject(rules) { |o, key| o[key] }
|
166
|
+
end
|
167
|
+
|
168
|
+
# Wrap the string with single or double quote.
|
169
|
+
# @param string [String]
|
170
|
+
# @return [String]
|
171
|
+
def wrap_quote(string)
|
172
|
+
if string.include?("'")
|
173
|
+
"\"#{string}\""
|
174
|
+
else
|
175
|
+
"'#{string}'"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Unwrap the quote from the string.
|
180
|
+
# @param string [String]
|
181
|
+
# @return [String]
|
182
|
+
def unwrap_quote(string)
|
183
|
+
if (string[0] == '"' && string[-1] == '"') || (string[0] == "'" && string[-1] == "'")
|
184
|
+
string[1...-1]
|
185
|
+
else
|
186
|
+
string
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/lib/node_query/version.rb
CHANGED
data/lib/node_query.rb
CHANGED
@@ -11,6 +11,7 @@ class NodeQuery
|
|
11
11
|
autoload :ParserAdapter, "node_query/parser_adapter"
|
12
12
|
autoload :Compiler, "node_query/compiler"
|
13
13
|
autoload :Helper, "node_query/helper"
|
14
|
+
autoload :NodeRules, "node_query/node_rules"
|
14
15
|
|
15
16
|
# Configure NodeQuery
|
16
17
|
# @param [Hash] options options to configure
|
@@ -26,15 +27,42 @@ class NodeQuery
|
|
26
27
|
end
|
27
28
|
|
28
29
|
# Initialize a NodeQuery.
|
29
|
-
# @param
|
30
|
-
def initialize(
|
31
|
-
|
30
|
+
# @param nqlOrRules [String | Hash] node query language or node rules
|
31
|
+
def initialize(nqlOrRules)
|
32
|
+
if nqlOrRules.is_a?(String)
|
33
|
+
@expression = NodeQueryParser.new.parse(nqlOrRules)
|
34
|
+
else
|
35
|
+
@rules = NodeRules.new(nqlOrRules)
|
36
|
+
end
|
32
37
|
end
|
33
38
|
|
34
|
-
#
|
39
|
+
# Query matching nodes.
|
35
40
|
# @param node [Node] ast node
|
41
|
+
# @param options [Hash] if query the current node
|
42
|
+
# @option options [boolean] :including_self if query the current node, default is ture
|
43
|
+
# @option options [boolean] :stop_on_match if stop on first match, default is false
|
44
|
+
# @option options [boolean] :recursive if stop on first match, default is true
|
36
45
|
# @return [Array<Node>] matching child nodes
|
37
|
-
def
|
38
|
-
@expression
|
46
|
+
def query_nodes(node, options = {})
|
47
|
+
if @expression
|
48
|
+
@expression.query_nodes(node, options)
|
49
|
+
elsif @rules
|
50
|
+
@rules.query_nodes(node, options)
|
51
|
+
else
|
52
|
+
[]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Check if the node matches the nql or rules.
|
57
|
+
# @param node [Node] the node
|
58
|
+
# @return [Boolean]
|
59
|
+
def match_node?(node)
|
60
|
+
if @expression
|
61
|
+
@expression.match_node?(node)
|
62
|
+
elsif @rules
|
63
|
+
@rules.match_node?(node)
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
39
67
|
end
|
40
68
|
end
|
data/lib/node_query_lexer.rex
CHANGED
@@ -7,11 +7,9 @@ macros
|
|
7
7
|
CLOSE_ARRAY /\)/
|
8
8
|
OPEN_SELECTOR /\(/
|
9
9
|
CLOSE_SELECTOR /\)/
|
10
|
-
OPEN_DYNAMIC_ATTRIBUTE /{{/
|
11
|
-
CLOSE_DYNAMIC_ATTRIBUTE /}}/
|
12
10
|
NODE_TYPE /\.[a-z]+/
|
13
|
-
IDENTIFIER /[
|
14
|
-
IDENTIFIER_VALUE /[
|
11
|
+
IDENTIFIER /[@\*\.\w]*\w/
|
12
|
+
IDENTIFIER_VALUE /[@\.\w!&:\?<>=]+/
|
15
13
|
FALSE /false/
|
16
14
|
FLOAT /\d+\.\d+/
|
17
15
|
INTEGER /\d+/
|
@@ -59,7 +57,6 @@ rules
|
|
59
57
|
:VALUE /\[\]/ { [:tIDENTIFIER_VALUE, text] }
|
60
58
|
:VALUE /:\[\]=/ { [:tSYMBOL, text[1..-1].to_sym] }
|
61
59
|
:VALUE /:\[\]/ { [:tSYMBOL, text[1..-1].to_sym] }
|
62
|
-
:VALUE /{{#{IDENTIFIER}}}/ { [:tDYNAMIC_ATTRIBUTE, text[2..-3]] }
|
63
60
|
:VALUE /#{OPEN_ARRAY}/ { @state = :ARRAY_VALUE; [:tOPEN_ARRAY, text] }
|
64
61
|
:VALUE /#{CLOSE_ATTRIBUTE}/ { @nested_count -= 1; @state = @nested_count == 0 ? nil : :VALUE; [:tCLOSE_ATTRIBUTE, text] }
|
65
62
|
:VALUE /#{NIL}\?/ { [:tIDENTIFIER_VALUE, text] }
|
data/lib/node_query_lexer.rex.rb
CHANGED
@@ -14,27 +14,25 @@ class NodeQueryLexer
|
|
14
14
|
require 'strscan'
|
15
15
|
|
16
16
|
# :stopdoc:
|
17
|
-
OPEN_ATTRIBUTE
|
18
|
-
CLOSE_ATTRIBUTE
|
19
|
-
OPEN_ARRAY
|
20
|
-
CLOSE_ARRAY
|
21
|
-
OPEN_SELECTOR
|
22
|
-
CLOSE_SELECTOR
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
SINGLE_QUOTE_STRING = /'.*?'/
|
37
|
-
DOUBLE_QUOTE_STRING = /".*?"/
|
17
|
+
OPEN_ATTRIBUTE = /\[/
|
18
|
+
CLOSE_ATTRIBUTE = /\]/
|
19
|
+
OPEN_ARRAY = /\(/
|
20
|
+
CLOSE_ARRAY = /\)/
|
21
|
+
OPEN_SELECTOR = /\(/
|
22
|
+
CLOSE_SELECTOR = /\)/
|
23
|
+
NODE_TYPE = /\.[a-z]+/
|
24
|
+
IDENTIFIER = /[@\*\.\w]*\w/
|
25
|
+
IDENTIFIER_VALUE = /[@\.\w!&:\?<>=]+/
|
26
|
+
FALSE = /false/
|
27
|
+
FLOAT = /\d+\.\d+/
|
28
|
+
INTEGER = /\d+/
|
29
|
+
NIL = /nil/
|
30
|
+
REGEXP_BODY = /(?:[^\/]|\\\/)*/
|
31
|
+
REGEXP = /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
|
32
|
+
SYMBOL = /:[\w!\?<>=]+/
|
33
|
+
TRUE = /true/
|
34
|
+
SINGLE_QUOTE_STRING = /'.*?'/
|
35
|
+
DOUBLE_QUOTE_STRING = /".*?"/
|
38
36
|
# :startdoc:
|
39
37
|
# :stopdoc:
|
40
38
|
class LexerError < StandardError ; end
|
@@ -201,8 +199,6 @@ class NodeQueryLexer
|
|
201
199
|
action { [:tSYMBOL, text[1..-1].to_sym] }
|
202
200
|
when text = ss.scan(/:\[\]/) then
|
203
201
|
action { [:tSYMBOL, text[1..-1].to_sym] }
|
204
|
-
when text = ss.scan(/{{#{IDENTIFIER}}}/) then
|
205
|
-
action { [:tDYNAMIC_ATTRIBUTE, text[2..-3]] }
|
206
202
|
when text = ss.scan(/#{OPEN_ARRAY}/) then
|
207
203
|
action { @state = :ARRAY_VALUE; [:tOPEN_ARRAY, text] }
|
208
204
|
when text = ss.scan(/#{CLOSE_ATTRIBUTE}/) then
|