synvert-core 1.0.5 → 1.2.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: a19f84568d17d7d51bf4f4486a7f95a299261c25eacd5ff57faea07fd597bc26
4
- data.tar.gz: 8dce2d09629733e239046581aa03433a7231cf8094ba0e1aec99f1df7ec0f867
3
+ metadata.gz: e5bf36b2f9e986dd2baf0bbddf53f4671a3bf98af0db830c270960eb583b47ce
4
+ data.tar.gz: 7e8693858d73d11c09652076ee03d49fe69662386caff67cbd0a3cc37e47d0a2
5
5
  SHA512:
6
- metadata.gz: 2a731bae510193fccf6c5682ff7559e7610b7774af5e2d0a196bad1ed838f84c5700e9dfcb123d0314b2ac40034d6246cd676b9e60e01f148ed17e6c32f1bc90
7
- data.tar.gz: 15fad0af1dce17af70f2b3a8ab1af3827f57892437076ddda86acdde19f7b24691b05c5c7b064ef33985815e0fdb0eb7fc4ad29924433c955067ee715f302138
6
+ metadata.gz: ad0eb6125d82241c60f7f0e229b9735f7c253280f1e56d586dfd24e8afb9c25b2cf574ca9c21e252c485f529b1bdf5f6b26c2b59805781cdefd3bc07a76072d8
7
+ data.tar.gz: 37bd754087a17d55577a10338fef9da3c60733d8634e9c394666a826fe205dc7f895121570f1e9cca4e7e1284f0b3b11c16e532e197afe2cd0ce61424c5d1557
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.2.0 (2022-04-29)
4
+
5
+ * Remove comma in NQL array value
6
+ * Parse pseduo class without selector in NQL
7
+ * Parse multiple goto scope in NQL
8
+ * Parse `nil?` in NQL
9
+
10
+ ## 1.1.1 (2022-04-27)
11
+
12
+ * Parse empty string properly in node query language
13
+ * Parse `[]` and `[]=` properly in node query language
14
+
15
+ ## 1.1.0 (2022-04-26)
16
+
17
+ * Dynamic define Node methods by `TYPE_CHILDREN` const
18
+ * Add `Node#to_hash`
19
+ * Parse empty string in node query language
20
+ * Identifier value can contain `?`, `<`, `=`, `>` in node query language
21
+
3
22
  ## 1.0.0 (2022-04-25)
4
23
 
5
24
  * Introduce new node query language
@@ -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
@@ -27,11 +27,41 @@ module Parser::AST
27
27
  # +type: 'send', receiver: { type: 'send', receiver: { type: 'send', message: 'config' }, message: 'active_record' }, message: 'identity_map='+
28
28
  #
29
29
  # Source Code to Ast Node
30
- # {https://synvert-playground.xinminlabs.com?language=ruby}
30
+ # {https://synvert-playground.xinminlabs.com/ruby}
31
31
  class Node
32
+ TYPE_CHILDREN = {
33
+ and: %i[left_value right_value],
34
+ arg: %i[name],
35
+ begin: %i[body],
36
+ block: %i[caller arguments body],
37
+ blockarg: %i[name],
38
+ const: %i[parent_const name],
39
+ class: %i[name parent_class body],
40
+ csend: %i[receiver message arguments],
41
+ cvasgn: %i[left_value right_value],
42
+ cvar: %i[name],
43
+ def: %i[name arguments body],
44
+ definded?: %i[arguments],
45
+ defs: %i[self name arguments body],
46
+ hash: %i[pairs],
47
+ ivasgn: %i[left_value right_value],
48
+ ivar: %i[name],
49
+ lvar: %i[name],
50
+ lvasgn: %i[left_value right_value],
51
+ masgn: %i[left_value right_value],
52
+ module: %i[name body],
53
+ or: %i[left_value right_value],
54
+ or_asgn: %i[left_value right_value],
55
+ pair: %i[key value],
56
+ restarg: %i[name],
57
+ send: %i[receiver message arguments],
58
+ super: %i[arguments],
59
+ zsuper: %i[]
60
+ }
61
+
32
62
  # Initialize a Node.
33
63
  #
34
- # It extends Parser::AST::Node and set parent for its child nodes.
64
+ # It extends {Parser::AST::Node} and set parent for its child nodes.
35
65
  def initialize(type, children = [], properties = {})
36
66
  @mutable_attributes = {}
37
67
  super
@@ -62,88 +92,32 @@ module Parser::AST
62
92
  parent.children[index + 1..]
63
93
  end
64
94
 
65
- # Get the name of node.
66
- # It supports :arg, :blockarg, :class, :const, :cvar, :def, :defs, :ivar,
67
- # :lvar, :mlhs, :module and :restarg nodes.
68
- # @example
69
- # node # => s(:class, s(:const, nil, :Synvert), nil, nil)
70
- # node.name # => s(:const, nil, :Synvert)
71
- # @return [Parser::AST::Node] name of node.
72
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
73
- def name
74
- case type
75
- when :class, :module, :def, :arg, :blockarg, :restarg, :lvar, :ivar, :cvar
76
- children[0]
77
- when :defs, :const
78
- children[1]
79
- when :mlhs
80
- self
81
- else
82
- raise Synvert::Core::MethodNotSupported, "name is not handled for #{debug_info}"
83
- end
84
- end
95
+ # Dyamically defined method
96
+ # caller, key, left_value, message, name, parent_class, parent_const, receivr, rgith_value and value.
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|
99
+ define_method(method_name) do
100
+ index = TYPE_CHILDREN[type]&.index(method_name)
101
+ return children[index] if index
85
102
 
86
- # Get parent_class of node.
87
- # It supports :class node.
88
- # @example
89
- # node # s(:class, s(:const, nil, :Post), s(:const, s(:const, nil, :ActiveRecord), :Base), nil)
90
- # node.parent_class # s(:const, s(:const, nil, :ActiveRecord), :Base)
91
- # @return [Parser::AST::Node] parent_class of node.
92
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
93
- def parent_class
94
- if :class == type
95
- children[1]
96
- else
97
- raise Synvert::Core::MethodNotSupported, "parent_class is not handled for #{debug_info}"
103
+ raise Synvert::Core::MethodNotSupported, "#{method_name} is not handled for #{debug_info}"
98
104
  end
99
105
  end
100
106
 
101
- # Get parent constant of node.
102
- # It supports :const node.
107
+ # Return the left value of node.
108
+ # It supports :and, :cvagn, :lvasgn, :masgn, :or and :or_asgn nodes.
103
109
  # @example
104
- # node # s(:const, s(:const, nil, :Synvert), :Node)
105
- # node.parent_const # s(:const, nil, :Synvert)
106
- # @return [Parser::AST::Node] parent const of node.
110
+ # node # s(:or_asgn, s(:lvasgn, :a), s(:int, 1))
111
+ # node.left_value # :a
112
+ # @return [Parser::AST::Node] left value of node.
107
113
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
108
- def parent_const
109
- if :const == type
110
- children[0]
111
- else
112
- raise Synvert::Core::MethodNotSupported, "parent_const is not handled for #{debug_info}"
113
- end
114
- end
114
+ def left_value
115
+ return children[0].children[0] if type == :or_asgn
115
116
 
116
- # Get receiver of node.
117
- # It support :csend and :send nodes.
118
- # @example
119
- # node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post))
120
- # node.receiver # s(:const, nil, :FactoryGirl)
121
- # @return [Parser::AST::Node] receiver of node.
122
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
123
- def receiver
124
- if %i[csend send].include?(type)
125
- children[0]
126
- else
127
- raise Synvert::Core::MethodNotSupported, "receiver is not handled for #{debug_info}"
128
- end
129
- end
117
+ index = TYPE_CHILDREN[type]&.index(:left_value)
118
+ return children[index] if index
130
119
 
131
- # Get message of node.
132
- # It support :csend, :send, :super and :zsuper nodes.
133
- # @example
134
- # node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post))
135
- # node.message # :create
136
- # @return [Symbol] mesage of node.
137
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
138
- def message
139
- case type
140
- when :super, :zsuper
141
- :super
142
- when :send, :csend
143
- children[1]
144
- else
145
- raise Synvert::Core::MethodNotSupported, "message is not handled for #{debug_info}"
146
- end
120
+ raise Synvert::Core::MethodNotSupported, "#{left_value} is not handled for #{debug_info}"
147
121
  end
148
122
 
149
123
  # Get arguments of node.
@@ -168,21 +142,6 @@ module Parser::AST
168
142
  end
169
143
  end
170
144
 
171
- # Get caller of node.
172
- # It support :block node.
173
- # @example
174
- # node # s(:block, s(:send, s(:const, nil, :RSpec), :configure), s(:args, s(:arg, :config)), nil)
175
- # node.caller # s(:send, s(:const, nil, :RSpec), :configure)
176
- # @return [Parser::AST::Node] caller of node.
177
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
178
- def caller
179
- if :block == type
180
- children[0]
181
- else
182
- raise Synvert::Core::MethodNotSupported, "caller is not handled for #{debug_info}"
183
- end
184
- end
185
-
186
145
  # Get body of node.
187
146
  # It supports :begin, :block, :class, :def, :defs and :module node.
188
147
  # @example
@@ -281,66 +240,6 @@ module Parser::AST
281
240
  end
282
241
  end
283
242
 
284
- # Get key node of hash :pair node.
285
- # @example
286
- # node # s(:pair, s(:sym, :foo), s(:str, "bar"))
287
- # node.key # s(:sym, :foo)
288
- # @return [Parser::AST::Node] key of node.
289
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
290
- def key
291
- if :pair == type
292
- children.first
293
- else
294
- raise Synvert::Core::MethodNotSupported, "key is not handled for #{debug_info}"
295
- end
296
- end
297
-
298
- # Get value node of hash :pair node.
299
- # @example
300
- # node # s(:pair, s(:sym, :foo), s(:str, "bar"))
301
- # node.value # s(:str, "bar")
302
- # @return [Parser::AST::Node] value of node.
303
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
304
- def value
305
- if :pair == type
306
- children.last
307
- else
308
- raise Synvert::Core::MethodNotSupported, "value is not handled for #{debug_info}"
309
- end
310
- end
311
-
312
- # Return the left value of node.
313
- # It supports :and, :cvagn, :lvasgn, :masgn, :or and :or_asgn nodes.
314
- # @example
315
- # node # s(:masgn, s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b)), s(:array, s(:int, 1), s(:int, 2)))
316
- # node.left_value # s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b))
317
- # @return [Parser::AST::Node] left value of node.
318
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
319
- def left_value
320
- if %i[masgn lvasgn ivasgn cvasgn and or].include? type
321
- children[0]
322
- elsif :or_asgn == type
323
- children[0].children[0]
324
- else
325
- raise Synvert::Core::MethodNotSupported, "left_value is not handled for #{debug_info}"
326
- end
327
- end
328
-
329
- # Return the right value of node.
330
- # It supports :cvasgn, :ivasgn, :lvasgn, :masgn, :or and :or_asgn nodes.
331
- # @example
332
- # node # s(:masgn, s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b)), s(:array, s(:int, 1), s(:int, 2)))
333
- # node.right_value # s(:array, s(:int, 1), s(:int, 2))
334
- # @return [Array<Parser::AST::Node>] right value of node.
335
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
336
- def right_value
337
- if %i[masgn lvasgn ivasgn cvasgn or_asgn and or].include? type
338
- children[1]
339
- else
340
- raise Synvert::Core::MethodNotSupported, "right_value is not handled for #{debug_info}"
341
- end
342
- end
343
-
344
243
  # Return the exact value of node.
345
244
  # It supports :array, :begin, :erange, :false, :float, :irange, :int, :str, :sym and :true nodes.
346
245
  # @example
@@ -717,6 +616,28 @@ module Parser::AST
717
616
  end
718
617
  end
719
618
 
619
+ # Convert node to a hash, so that it can be converted to a json.
620
+ def to_hash
621
+ result = { type: type }
622
+ if TYPE_CHILDREN[type]
623
+ TYPE_CHILDREN[type].each do |key|
624
+ value = send(key)
625
+ result[key] =
626
+ case value
627
+ when Array
628
+ value.map { |v| v.respond_to?(:to_hash) ? v.to_hash : v }
629
+ when Parser::AST::Node
630
+ value.to_hash
631
+ else
632
+ value
633
+ end
634
+ end
635
+ else
636
+ result[:children] = children.map { |c| c.respond_to?(:to_hash) ? c.to_hash : c }
637
+ end
638
+ result
639
+ end
640
+
720
641
  private
721
642
 
722
643
  # Compare actual value with expected value.
@@ -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
@@ -5,10 +5,12 @@ 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
8
9
  # @param rest [Synvert::Core::NodeQuery::Compiler::Expression] the rest expression
9
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>.
10
- def initialize(selector: nil, rest: nil, relationship: nil)
11
+ def initialize(selector: nil, goto_scope: nil, rest: nil, relationship: nil)
11
12
  @selector = selector
13
+ @goto_scope = goto_scope
12
14
  @rest = rest
13
15
  @relationship = relationship
14
16
  end
@@ -30,49 +32,85 @@ module Synvert::Core::NodeQuery::Compiler
30
32
  # @param node [Parser::AST::Node] node to match
31
33
  # @param descendant_match [Boolean] whether to match in descendant node
32
34
  # @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
35
+ def query_nodes(node, descendant_match = true)
36
+ return find_nodes_by_goto_scope(node) if @goto_scope
38
37
 
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
38
+ return find_nodes_by_relationship(node) if @relationship
39
+
40
+ matching_nodes = find_nodes_without_relationship(node, descendant_match)
41
+ return matching_nodes if @rest.nil?
42
+
43
+ matching_nodes.map { |matching_node| find_nodes_by_rest(matching_node, descendant_match) }
44
+ .flatten
57
45
  end
58
46
 
59
47
  def to_s
60
48
  return @selector.to_s unless @rest
61
49
 
62
50
  result = []
63
- result << @selector if @selector
51
+ result << @selector.to_s if @selector
52
+ result << "<#{@goto_scope}>" if @goto_scope
64
53
  case @relationship
65
- when :child then result << '>'
66
- when :subsequent_sibling then result << '~'
67
- when :next_sibling then result << '+'
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
68
60
  end
69
- result << @rest
70
- result.map(&:to_s).join(' ')
61
+ result.join(' ')
71
62
  end
72
63
 
73
64
  private
74
65
 
75
- def find_nodes_without_relationship(node, descendant_match: true)
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
+ # Find nodes by @rest
98
+ # @param node [Parser::AST::Node] node to match
99
+ # @param descendant_match [Boolean] whether to match in descendant node
100
+ def find_nodes_by_rest(node, descendant_match = false)
101
+ @rest.query_nodes(node, descendant_match)
102
+ end
103
+
104
+ # Find nodes with nil relationship.
105
+ # @param node [Parser::AST::Node] node to match
106
+ # @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
+
76
114
  return [node] unless @selector
77
115
 
78
116
  nodes = []
@@ -7,12 +7,14 @@ module Synvert::Core::NodeQuery::Compiler
7
7
  # @param node_type [String] the node type
8
8
  # @param attribute_list [Synvert::Core::NodeQuery::Compiler::AttributeList] the attribute list
9
9
  # @param index [Integer] the index
10
- # @param has_expression [Synvert::Core::NodeQuery::Compiler::Expression] the has expression
11
- def initialize(node_type: nil, attribute_list: nil, index: nil, has_expression: nil)
10
+ # @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)
12
13
  @node_type = node_type
13
14
  @attribute_list = attribute_list
14
15
  @index = index
15
- @has_expression = has_expression
16
+ @pseudo_class = pseudo_class
17
+ @pseudo_expression = pseudo_expression
16
18
  end
17
19
 
18
20
  # Filter nodes by index.
@@ -27,25 +29,28 @@ module Synvert::Core::NodeQuery::Compiler
27
29
  def match?(node, _operator = :==)
28
30
  (!@node_type || (node.is_a?(::Parser::AST::Node) && @node_type.to_sym == node.type)) &&
29
31
  (!@attribute_list || @attribute_list.match?(node)) &&
30
- (!@has_expression || @has_expression.match?(node))
32
+ (!@pseudo_class || (@pseudo_class == 'has' && @pseudo_expression.match?(node)) || (@pseudo_class == 'not_has' && !@pseudo_expression.match?(node)))
31
33
  end
32
34
 
33
35
  def to_s
34
- str = ".#{@node_type}#{@attribute_list}"
35
- return str if !@index && !@has_expression
36
-
37
- return "#{str}:has(#{@has_expression})" if @has_expression
38
-
39
- case @index
40
- when 0
41
- str + ':first-child'
42
- when -1
43
- str + ':last-child'
44
- when (1..)
45
- str + ":nth-child(#{@index + 1})"
46
- else # ...-1
47
- str + ":nth-last-child(#{-@index})"
36
+ 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
40
+ if @index
41
+ result <<
42
+ case @index
43
+ when 0
44
+ ':first-child'
45
+ when -1
46
+ ':last-child'
47
+ when (1..)
48
+ ":nth-child(#{@index + 1})"
49
+ else # ...-1
50
+ ":nth-last-child(#{-@index})"
51
+ end
48
52
  end
53
+ result.join('')
49
54
  end
50
55
  end
51
56
  end
@@ -16,17 +16,6 @@ module Synvert::Core::NodeQuery::Compiler
16
16
  SIMPLE_VALID_OPERATORS
17
17
  end
18
18
 
19
- # Get the actual value of a node.
20
- # @param node [Parser::AST::Node] the node
21
- # @return [String] if node is a Parser::AST::Node, return the node source code, otherwise, return the string value.
22
- def actual_value(node)
23
- if node.is_a?(::Parser::AST::Node)
24
- node.to_source
25
- else
26
- node.to_s
27
- end
28
- end
29
-
30
19
  def to_s
31
20
  "\"#{@value}\""
32
21
  end
@@ -7,6 +7,8 @@ macros
7
7
  CLOSE_ARRAY /\)/
8
8
  OPEN_SELECTOR /\(/
9
9
  CLOSE_SELECTOR /\)/
10
+ OPEN_GOTO_SCOPE /</
11
+ CLOSE_GOTO_SCOPE />/
10
12
  OPEN_DYNAMIC_ATTRIBUTE /{{/
11
13
  CLOSE_DYNAMIC_ATTRIBUTE /}}/
12
14
  NODE_TYPE /\.[a-z]+/
@@ -31,14 +33,19 @@ rules
31
33
  /:last-child/ { [:tINDEX, -1] }
32
34
  /:nth-child\(\d+\)/ { [:tINDEX, text.sub(':nth-child(', '').to_i - 1] }
33
35
  /:nth-last-child\(\d+\)/ { [:tINDEX, -text.sub(':nth-last-child(', '').to_i] }
34
- /:has/ { [:tHAS, text[1..-1]] }
36
+ /:has/ { [:tPSEUDO_CLASS, text[1..-1]] }
37
+ /:not_has/ { [:tPSEUDO_CLASS, text[1..-1]] }
35
38
  /#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
36
39
  />/ { [:tCHILD, text] }
37
40
  /~/ { [:tSUBSEQUENT_SIBLING, text] }
38
41
  /\+/ { [:tNEXT_SIBLING, text] }
39
42
  /#{OPEN_SELECTOR}/ { [:tOPEN_SELECTOR, text] }
40
43
  /#{CLOSE_SELECTOR}/ { [:tCLOSE_SELECTOR, text] }
44
+ /#{OPEN_GOTO_SCOPE}/ { @state = :GOTO_SCOPE; [:tOPEN_GOTO_SCOPE, text] }
41
45
  /#{OPEN_ATTRIBUTE}/ { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
46
+ :GOTO_SCOPE /\s+/
47
+ :GOTO_SCOPE /#{IDENTIFIER}/ { [:tIDENTIFIER, text] }
48
+ :GOTO_SCOPE /#{CLOSE_GOTO_SCOPE}/ { @state = nil; [:tCLOSE_GOTO_SCOPE, text] }
42
49
  :KEY /\s+/
43
50
  :KEY /!=/ { @state = :VALUE; [:tNOT_EQUAL, text] }
44
51
  :KEY /=~/ { @state = :VALUE; [:tMATCH, text] }
@@ -53,9 +60,14 @@ rules
53
60
  :KEY /in/i { @state = :VALUE; [:tIN, text] }
54
61
  :KEY /#{IDENTIFIER}/ { [:tKEY, text] }
55
62
  :VALUE /\s+/
63
+ :VALUE /\[\]=/ { [:tIDENTIFIER_VALUE, text] }
64
+ :VALUE /\[\]/ { [:tIDENTIFIER_VALUE, text] }
65
+ :VALUE /:\[\]=/ { [:tSYMBOL, text[1..-1].to_sym] }
66
+ :VALUE /:\[\]/ { [:tSYMBOL, text[1..-1].to_sym] }
56
67
  :VALUE /#{OPEN_DYNAMIC_ATTRIBUTE}/ { @state = :DYNAMIC_ATTRIBUTE; [:tOPEN_DYNAMIC_ATTRIBUTE, text] }
57
68
  :VALUE /#{OPEN_ARRAY}/ { @state = :ARRAY_VALUE; [:tOPEN_ARRAY, text] }
58
69
  :VALUE /#{CLOSE_ATTRIBUTE}/ { @nested_count -= 1; @state = @nested_count == 0 ? nil : :VALUE; [:tCLOSE_ATTRIBUTE, text] }
70
+ :VALUE /#{NIL}\?/ { [:tIDENTIFIER_VALUE, text] }
59
71
  :VALUE /#{NIL}/ { [:tNIL, nil] }
60
72
  :VALUE /#{TRUE}/ { [:tBOOLEAN, true] }
61
73
  :VALUE /#{FALSE}/ { [:tBOOLEAN, false] }
@@ -71,8 +83,8 @@ rules
71
83
  :DYNAMIC_ATTRIBUTE /#{CLOSE_DYNAMIC_ATTRIBUTE}/ { @state = :VALUE; [:tCLOSE_DYNAMIC_ATTRIBUTE, text] }
72
84
  :DYNAMIC_ATTRIBUTE /#{IDENTIFIER}/ { [:tDYNAMIC_ATTRIBUTE, text] }
73
85
  :ARRAY_VALUE /\s+/
74
- :ARRAY_VALUE /,/ { [:tCOMMA, text] }
75
86
  :ARRAY_VALUE /#{CLOSE_ARRAY}/ { @state = :VALUE; [:tCLOSE_ARRAY, text] }
87
+ :ARRAY_VALUE /#{NIL}\?/ { [:tIDENTIFIER_VALUE, text] }
76
88
  :ARRAY_VALUE /#{NIL}/ { [:tNIL, nil] }
77
89
  :ARRAY_VALUE /#{TRUE}/ { [:tBOOLEAN, true] }
78
90
  :ARRAY_VALUE /#{FALSE}/ { [:tBOOLEAN, false] }