synvert-core 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3972bef6a4c4b41b4211e2f45d889c6e3c71b08b1b2b039ca3511a620f79ccf
4
- data.tar.gz: 7ddecb20188351e72e59b4a72c67a45d395638ae7a20d3c4d46f3fef0184de3c
3
+ metadata.gz: 56b98032247ef28e548b34dfd8b2b49bad8744227cd61b4f300c7111b701ff91
4
+ data.tar.gz: ea75a1e4f442041665dbd9babdc2f7e4b55c4c78c87facef856e903f2d8c96d2
5
5
  SHA512:
6
- metadata.gz: b89480e5d9d05fe0651b5cd9edd1a4408a52364d449d0632baf062dd40251670b8c7d98370fa831f6961454498e9fbfffa67011a4fabb45f21e6031933643c36
7
- data.tar.gz: c23844951b2938268289099f852f01d554e46a7aa80ad301011d05cc40a022864dd872a8097c71575a42e8770730dbfea2deea8d867af0412022506751cf2671
6
+ metadata.gz: 905c1f393a1702df43300432e210784790a30dff9a7bb0c798c742ec372a65ad8487b2097333f57afb1798c566f554ab9096ae92db8cd2541e5bb03769009882
7
+ data.tar.gz: bd03f1c285c5b5b42628df0b3c8777d6167d980c8c3e3b2f92badfc040b2bef5fe2bef3b848b5a6a166002a5e68f8f4f287ac6ee8fadc72a3ef6706fe191c821
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.3.0 (2022-05-12)
4
+
5
+ * Support `*=`, `^=` and `$=` operators
6
+ * Simplify RELATIONSHIP parser
7
+ * Rewrite compiler, let the selector to query nodes
8
+
9
+ ## 1.2.1 (2022-05-01)
10
+
11
+ * Selector always after a node type in NQL
12
+ * Define `pairs` method for `hash` ndoe
13
+
14
+ ## 1.2.0 (2022-04-29)
15
+
16
+ * Remove comma in NQL array value
17
+ * Parse pseduo class without selector in NQL
18
+ * Parse multiple goto scope in NQL
19
+ * Parse `nil?` in NQL
20
+
3
21
  ## 1.1.1 (2022-04-27)
4
22
 
5
23
  * Parse empty string properly in node query language
data/Gemfile CHANGED
@@ -4,3 +4,12 @@ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in synvert.gemspec
6
6
  gemspec
7
+
8
+ gem "guard"
9
+ gem "guard-rspec"
10
+ gem "guard-rake"
11
+ gem "oedipus_lex"
12
+ gem "racc"
13
+ gem "rake"
14
+ gem "rspec"
15
+ gem "rspec-mocks"
@@ -13,7 +13,7 @@ class Array
13
13
  return child_direct_child_node if child_direct_child_node
14
14
 
15
15
  raise Synvert::Core::MethodNotSupported,
16
- "child_node_by_name is not handled for #{map(&:debug_info).join("\n")}, child_name: #{child_name}"
16
+ "child_node_by_name is not handled for #{debug_info}, child_name: #{child_name}"
17
17
  end
18
18
 
19
19
  # Get the source range of child node.
@@ -35,7 +35,14 @@ class Array
35
35
  )
36
36
  else
37
37
  raise Synvert::Core::MethodNotSupported,
38
- "child_node_range is not handled for #{map(&:debug_info).join("\n")}, child_name: #{child_name}"
38
+ "child_node_range is not handled for #{debug_info}, child_name: #{child_name}"
39
39
  end
40
40
  end
41
+
42
+ # Return the debug info.
43
+ #
44
+ # @return [String] file, line, source and node.
45
+ def debug_info
46
+ map(&:debug_info).join("\n")
47
+ end
41
48
  end
@@ -93,9 +93,21 @@ module Parser::AST
93
93
  end
94
94
 
95
95
  # Dyamically defined method
96
- # caller, key, left_value, message, name, parent_class, parent_const, receivr, rgith_value and value.
96
+ # caller, key, left_value, message, name, pairs, parent_class, parent_const, receivr, rgith_value and value.
97
97
  # based on const TYPE_CHILDREN.
98
- %i[caller key left_value message name parent_class parent_const receiver right_value value].each do |method_name|
98
+ %i[
99
+ caller
100
+ key
101
+ left_value
102
+ message
103
+ name
104
+ pairs
105
+ parent_class
106
+ parent_const
107
+ receiver
108
+ right_value
109
+ value
110
+ ].each do |method_name|
99
111
  define_method(method_name) do
100
112
  index = TYPE_CHILDREN[type]&.index(method_name)
101
113
  return children[index] if index
@@ -28,7 +28,7 @@ module Synvert::Core::NodeQuery::Compiler
28
28
  end
29
29
 
30
30
  def to_s
31
- [@value, @rest].compact.join(', ')
31
+ [@value, @rest].compact.join(' ')
32
32
  end
33
33
  end
34
34
  end
@@ -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}!=#{@value}"
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 :not_in
30
+ when 'not_in'
43
31
  "#{@key} not in (#{@value})"
44
- when :includes
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 = [:==, :!=, :includes]
7
- NUMBER_VALID_OPERATORS = [:==, :!=, :>, :>=, :<, :<=, :includes]
8
- ARRAY_VALID_OPERATORS = [:==, :!=, :in, :not_in]
9
- REGEXP_VALID_OPERATORS = [:=~, :!~]
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>:==</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
+ # @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 :in
42
- expected_value.any? { |expected| expected.match?(node, :==) }
43
- when :not_in
44
- expected_value.all? { |expected| expected.match?(node, :!=) }
45
- when :includes
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
@@ -6,11 +6,9 @@ module Synvert::Core::NodeQuery::Compiler
6
6
  # Initialize a Expression.
7
7
  # @param selector [Synvert::Core::NodeQuery::Compiler::Selector] the selector
8
8
  # @param rest [Synvert::Core::NodeQuery::Compiler::Expression] the rest expression
9
- # @param relationship [Symbol] the relationship between the selector and rest expression, it can be <code>:descendant</code>, <code>:child</code>, <code>:next_sibling</code>, <code>:subsequent_sibling</code> or <code>nil</code>.
10
- def initialize(selector: nil, rest: nil, relationship: nil)
9
+ def initialize(selector: nil, rest: nil)
11
10
  @selector = selector
12
11
  @rest = rest
13
- @relationship = relationship
14
12
  end
15
13
 
16
14
  # Check if the node matches the expression.
@@ -20,69 +18,41 @@ module Synvert::Core::NodeQuery::Compiler
20
18
  !query_nodes(node).empty?
21
19
  end
22
20
 
23
- # Query nodes by the expression.
21
+ # Query nodes by the selector and the rest expression.
24
22
  #
25
- # * If relationship is nil, it will match in all recursive child nodes and return matching nodes.
26
- # * If relationship is :decendant, it will match in all recursive child nodes.
27
- # * If relationship is :child, it will match in direct child nodes.
28
- # * If relationship is :next_sibling, it try to match next sibling node.
29
- # * If relationship is :subsequent_sibling, it will match in all sibling nodes.
30
23
  # @param node [Parser::AST::Node] node to match
31
24
  # @param descendant_match [Boolean] whether to match in descendant node
32
25
  # @return [Array<Parser::AST::Node>] matching nodes.
33
- def query_nodes(node, descendant_match: true)
34
- matching_nodes = find_nodes_without_relationship(node, descendant_match: descendant_match)
35
- if @relationship.nil?
36
- return matching_nodes
37
- end
26
+ def query_nodes(node, descendant_match = true)
27
+ matching_nodes = find_nodes_by_selector(node, descendant_match)
28
+ return matching_nodes if @rest.nil?
38
29
 
39
- expression_nodes = matching_nodes.map do |matching_node|
40
- case @relationship
41
- when :descendant
42
- nodes = []
43
- matching_node.recursive_children { |child_node|
44
- nodes += @rest.query_nodes(child_node, descendant_match: false)
45
- }
46
- nodes
47
- when :child
48
- matching_node.children.map { |child_node| @rest.query_nodes(child_node, descendant_match: false) }
49
- .flatten
50
- when :next_sibling
51
- @rest.query_nodes(matching_node.siblings.first, descendant_match: false)
52
- when :subsequent_sibling
53
- matching_node.siblings.map { |sibling_node| @rest.query_nodes(sibling_node, descendant_match: false) }
54
- .flatten
55
- end
56
- end.flatten
30
+ matching_nodes.flat_map { |matching_node| find_nodes_by_rest(matching_node, descendant_match) }
57
31
  end
58
32
 
59
33
  def to_s
60
- return @selector.to_s unless @rest
61
-
62
34
  result = []
63
- result << @selector if @selector
64
- case @relationship
65
- when :child then result << '>'
66
- when :subsequent_sibling then result << '~'
67
- when :next_sibling then result << '+'
68
- end
69
- result << @rest
70
- result.map(&:to_s).join(' ')
35
+ result << @selector.to_s if @selector
36
+ result << @rest.to_s if @rest
37
+ result.join(' ')
71
38
  end
72
39
 
73
40
  private
74
41
 
75
- def find_nodes_without_relationship(node, descendant_match: true)
76
- return [node] unless @selector
42
+ # Find nodes by @rest
43
+ # @param node [Parser::AST::Node] node to match
44
+ # @param descendant_match [Boolean] whether to match in descendant node
45
+ def find_nodes_by_rest(node, descendant_match = false)
46
+ @rest.query_nodes(node, descendant_match)
47
+ end
48
+
49
+ # Find nodes with nil relationship.
50
+ # @param node [Parser::AST::Node] node to match
51
+ # @param descendant_match [Boolean] whether to match in descendant node
52
+ def find_nodes_by_selector(node, descendant_match = true)
53
+ return Array(node) if !@selector
77
54
 
78
- nodes = []
79
- nodes << node if @selector.match?(node)
80
- if descendant_match
81
- node.recursive_children do |child_node|
82
- nodes << child_node if @selector.match?(child_node)
83
- end
84
- end
85
- @selector.filter(nodes)
55
+ @selector.query_nodes(node, descendant_match)
86
56
  end
87
57
  end
88
58
  end
@@ -31,7 +31,7 @@ module Synvert::Core::NodeQuery::Compiler
31
31
 
32
32
  # Get valid operators.
33
33
  def valid_operators
34
- SIMPLE_VALID_OPERATORS
34
+ STRING_VALID_OPERATORS
35
35
  end
36
36
 
37
37
  def to_s
@@ -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 == :=~ ? match : !match
25
+ operator == '=~' ? match : !match
26
26
  end
27
27
 
28
28
  # Get valid operators.
@@ -4,50 +4,135 @@ 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 node_type [String] the node type
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 pseudo_expression [Synvert::Core::NodeQuery::Compiler::Expression] the pseudo expression
12
- def initialize(node_type: nil, attribute_list: nil, index: nil, pseudo_class: nil, pseudo_expression: nil)
13
- @node_type = node_type
14
- @attribute_list = attribute_list
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
- @pseudo_expression = pseudo_expression
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, _operator = :==)
30
- (!@node_type || (node.is_a?(::Parser::AST::Node) && @node_type.to_sym == node.type)) &&
31
- (!@attribute_list || @attribute_list.match?(node)) &&
32
- (!@pseudo_class || (@pseudo_class == 'has' && @pseudo_expression.match?(node)) || (@pseudo_class == 'not_has' && !@pseudo_expression.match?(node)))
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
- str = ".#{@node_type}#{@attribute_list}"
37
- return str if !@index && !@pseudo_class
38
-
39
- return "#{str}:#{@pseudo_class}(#{@pseudo_expression})" if @pseudo_class
40
-
41
- case @index
42
- when 0
43
- str + ':first-child'
44
- when -1
45
- str + ':last-child'
46
- when (1..)
47
- str + ":nth-child(#{@index + 1})"
48
- else # ...-1
49
- str + ":nth-last-child(#{-@index})"
63
+ 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
69
+ if @index
70
+ result <<
71
+ case @index
72
+ when 0
73
+ ':first-child'
74
+ when -1
75
+ ':last-child'
76
+ when (1..)
77
+ ":nth-child(#{@index + 1})"
78
+ else # ...-1
79
+ ":nth-last-child(#{-@index})"
80
+ end
81
+ end
82
+ result.join('')
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
50
128
  end
51
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
52
137
  end
53
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
@@ -13,7 +13,7 @@ module Synvert::Core::NodeQuery::Compiler
13
13
 
14
14
  # Get valid operators.
15
15
  def valid_operators
16
- SIMPLE_VALID_OPERATORS
16
+ STRING_VALID_OPERATORS
17
17
  end
18
18
 
19
19
  def to_s
@@ -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