synvert-core 1.0.5 → 1.2.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: 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] }