synvert-core 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c530d34ca0e2db5d9a295cb0cb48c375d13760f09290624805d742328a49103
4
- data.tar.gz: 87cc4ef7ab9116bac2cbed78b9f71ab8d4ffa30281554d10319527a9aab94573
3
+ metadata.gz: 56b98032247ef28e548b34dfd8b2b49bad8744227cd61b4f300c7111b701ff91
4
+ data.tar.gz: ea75a1e4f442041665dbd9babdc2f7e4b55c4c78c87facef856e903f2d8c96d2
5
5
  SHA512:
6
- metadata.gz: 1b864e1e350d3467fe12d2c5e26a5c5a0b685752e21802e3773edfbbb8fca8f7dec60fb545475a630a3f00c09359c66859b228cf7cb3d19aa6bf044df4848408
7
- data.tar.gz: c8c5641b85080f03307fed835c05132488d140bb19b694c2b5b0958cb3d6377785ef1447c8b7fec59e7529f2ec610f6c78d72357b60f5fe4285d43d5071750fb
6
+ metadata.gz: 905c1f393a1702df43300432e210784790a30dff9a7bb0c798c742ec372a65ad8487b2097333f57afb1798c566f554ab9096ae92db8cd2541e5bb03769009882
7
+ data.tar.gz: bd03f1c285c5b5b42628df0b3c8777d6167d980c8c3e3b2f92badfc040b2bef5fe2bef3b848b5a6a166002a5e68f8f4f287ac6ee8fadc72a3ef6706fe191c821
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
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
+
3
9
  ## 1.2.1 (2022-05-01)
4
10
 
5
11
  * Selector always after a node type in NQL
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"
@@ -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
@@ -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
- # @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>.
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
- return find_nodes_by_goto_scope(node) if @goto_scope
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.map { |matching_node| find_nodes_by_rest(matching_node, descendant_match) }
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 << "<#{@goto_scope}>" if @goto_scope
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 find_nodes_without_relationship(node, descendant_match = true)
108
- if node.is_a?(::Array)
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
- nodes = []
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
@@ -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,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 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
63
  result = []
37
- result << ".#{@node_type}" if @node_type
38
- result << @attribute_list.to_s if @attribute_list
39
- result << ":#{@pseudo_class}(#{@pseudo_expression})" if @pseudo_class
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
@@ -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
 
@@ -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
- />/ { [:tCHILD, text] }
40
- /~/ { [:tSUBSEQUENT_SIBLING, text] }
41
- /\+/ { [:tNEXT_SIBLING, text] }
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 /!=/ { @state = :VALUE; [:tNOT_EQUAL, text] }
51
- :KEY /=~/ { @state = :VALUE; [:tMATCH, text] }
52
- :KEY /!~/ { @state = :VALUE; [:tNOT_MATCH, text] }
53
- :KEY />=/ { @state = :VALUE; [:tGREATER_THAN_OR_EQUAL, text] }
54
- :KEY /<=/ { @state = :VALUE; [:tLESS_THAN_OR_EQUAL, text] }
55
- :KEY />/ { @state = :VALUE; [:tGREATER_THAN, text] }
56
- :KEY /</ { @state = :VALUE; [:tLESS_THAN, text] }
57
- :KEY /=/ { @state = :VALUE; [:tEQUAL, text] }
58
- :KEY /includes/i { @state = :VALUE; [:tINCLUDES, text] }
59
- :KEY /not in/i { @state = :VALUE; [:tNOT_IN, text] }
60
- :KEY /in/i { @state = :VALUE; [:tIN, text] }
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 { [:tCHILD, text] }
145
+ action { [:tRELATIONSHIP, text] }
146
146
  when text = ss.scan(/~/) then
147
- action { [:tSUBSEQUENT_SIBLING, text] }
147
+ action { [:tRELATIONSHIP, text] }
148
148
  when text = ss.scan(/\+/) then
149
- action { [:tNEXT_SIBLING, text] }
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 text = ss.scan(/!=/) then
179
- action { @state = :VALUE; [:tNOT_EQUAL, text] }
180
- when text = ss.scan(/=~/) then
181
- action { @state = :VALUE; [:tMATCH, text] }
182
- when text = ss.scan(/!~/) then
183
- action { @state = :VALUE; [:tNOT_MATCH, text] }
184
- when text = ss.scan(/>=/) then
185
- action { @state = :VALUE; [:tGREATER_THAN_OR_EQUAL, text] }
186
- when text = ss.scan(/<=/) then
187
- action { @state = :VALUE; [:tLESS_THAN_OR_EQUAL, text] }
188
- when text = ss.scan(/>/) then
189
- action { @state = :VALUE; [:tGREATER_THAN, text] }
190
- when text = ss.scan(/</) then
191
- action { @state = :VALUE; [:tLESS_THAN, text] }
192
- when text = ss.scan(/=/) then
193
- action { @state = :VALUE; [:tEQUAL, text] }
194
- when text = ss.scan(/includes/i) then
195
- action { @state = :VALUE; [:tINCLUDES, text] }
196
- when text = ss.scan(/not in/i) then
197
- action { @state = :VALUE; [:tNOT_IN, text] }
198
- when text = ss.scan(/in/i) then
199
- action { @state = :VALUE; [:tIN, text] }
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