synvert-core 1.0.4 → 1.1.1

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: e0554ef349e1fe725c68cd277b40751ca7373b04fe118313bf70fdf307852e01
4
- data.tar.gz: 283a4ca6bf2d0fc09ba1f63bb5831524dc0634184072c340d7092a9253a93700
3
+ metadata.gz: b3972bef6a4c4b41b4211e2f45d889c6e3c71b08b1b2b039ca3511a620f79ccf
4
+ data.tar.gz: 7ddecb20188351e72e59b4a72c67a45d395638ae7a20d3c4d46f3fef0184de3c
5
5
  SHA512:
6
- metadata.gz: 9152e5eea03cbc9a842fdad557cc8ea9ecb2e7d3dc3480708b886100393c0167a804f3cd0f4fd5aca968c4992ce85b0b604d36ab6beabe9ac4023e6102e2c05e
7
- data.tar.gz: 4b35baab2ebe3c76a9151e0148dc185a8e6776d4dd6ece7abcd2cb3cd4ad21bc63b5cc68b75d6410827ef9089bf52fc6871bacb1e44c39cffce36faef07b9a8f
6
+ metadata.gz: b89480e5d9d05fe0651b5cd9edd1a4408a52364d449d0632baf062dd40251670b8c7d98370fa831f6961454498e9fbfffa67011a4fabb45f21e6031933643c36
7
+ data.tar.gz: c23844951b2938268289099f852f01d554e46a7aa80ad301011d05cc40a022864dd872a8097c71575a42e8770730dbfea2deea8d867af0412022506751cf2671
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.1.1 (2022-04-27)
4
+
5
+ * Parse empty string properly in node query language
6
+ * Parse `[]` and `[]=` properly in node query language
7
+
8
+ ## 1.1.0 (2022-04-26)
9
+
10
+ * Dynamic define Node methods by `TYPE_CHILDREN` const
11
+ * Add `Node#to_hash`
12
+ * Parse empty string in node query language
13
+ * Identifier value can contain `?`, `<`, `=`, `>` in node query language
14
+
3
15
  ## 1.0.0 (2022-04-25)
4
16
 
5
17
  * Introduce new node query language
@@ -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.
@@ -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,14 +29,14 @@ 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
36
  str = ".#{@node_type}#{@attribute_list}"
35
- return str if !@index && !@has_expression
37
+ return str if !@index && !@pseudo_class
36
38
 
37
- return "#{str}:has(#{@has_expression})" if @has_expression
39
+ return "#{str}:#{@pseudo_class}(#{@pseudo_expression})" if @pseudo_class
38
40
 
39
41
  case @index
40
42
  when 0
@@ -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
@@ -20,8 +20,8 @@ macros
20
20
  REGEXP /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
21
21
  SYMBOL /:[\w!\?<>=]+/
22
22
  TRUE /true/
23
- SINGLE_QUOTE_STRING /'(.+?)'/
24
- DOUBLE_QUOTE_STRING /"(.+?)"/
23
+ SINGLE_QUOTE_STRING /'(.*?)'/
24
+ DOUBLE_QUOTE_STRING /"(.*?)"/
25
25
 
26
26
  rules
27
27
 
@@ -31,7 +31,8 @@ rules
31
31
  /:last-child/ { [:tINDEX, -1] }
32
32
  /:nth-child\(\d+\)/ { [:tINDEX, text.sub(':nth-child(', '').to_i - 1] }
33
33
  /:nth-last-child\(\d+\)/ { [:tINDEX, -text.sub(':nth-last-child(', '').to_i] }
34
- /:has/ { [:tHAS, text[1..-1]] }
34
+ /:has/ { [:tPSEUDO_CLASS, text[1..-1]] }
35
+ /:not_has/ { [:tPSEUDO_CLASS, text[1..-1]] }
35
36
  /#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
36
37
  />/ { [:tCHILD, text] }
37
38
  /~/ { [:tSUBSEQUENT_SIBLING, text] }
@@ -53,6 +54,10 @@ rules
53
54
  :KEY /in/i { @state = :VALUE; [:tIN, text] }
54
55
  :KEY /#{IDENTIFIER}/ { [:tKEY, text] }
55
56
  :VALUE /\s+/
57
+ :VALUE /\[\]=/ { [:tIDENTIFIER_VALUE, text] }
58
+ :VALUE /\[\]/ { [:tIDENTIFIER_VALUE, text] }
59
+ :VALUE /:\[\]=/ { [:tSYMBOL, text[1..-1].to_sym] }
60
+ :VALUE /:\[\]/ { [:tSYMBOL, text[1..-1].to_sym] }
56
61
  :VALUE /#{OPEN_DYNAMIC_ATTRIBUTE}/ { @state = :DYNAMIC_ATTRIBUTE; [:tOPEN_DYNAMIC_ATTRIBUTE, text] }
57
62
  :VALUE /#{OPEN_ARRAY}/ { @state = :ARRAY_VALUE; [:tOPEN_ARRAY, text] }
58
63
  :VALUE /#{CLOSE_ATTRIBUTE}/ { @nested_count -= 1; @state = @nested_count == 0 ? nil : :VALUE; [:tCLOSE_ATTRIBUTE, text] }
@@ -33,8 +33,8 @@ class Synvert::Core::NodeQuery::Lexer
33
33
  REGEXP = /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
34
34
  SYMBOL = /:[\w!\?<>=]+/
35
35
  TRUE = /true/
36
- SINGLE_QUOTE_STRING = /'(.+?)'/
37
- DOUBLE_QUOTE_STRING = /"(.+?)"/
36
+ SINGLE_QUOTE_STRING = /'(.*?)'/
37
+ DOUBLE_QUOTE_STRING = /"(.*?)"/
38
38
  # :startdoc:
39
39
  # :stopdoc:
40
40
  class LexerError < StandardError ; end
@@ -134,7 +134,9 @@ class Synvert::Core::NodeQuery::Lexer
134
134
  when text = ss.scan(/:nth-last-child\(\d+\)/) then
135
135
  action { [:tINDEX, -text.sub(':nth-last-child(', '').to_i] }
136
136
  when text = ss.scan(/:has/) then
137
- action { [:tHAS, text[1..-1]] }
137
+ action { [:tPSEUDO_CLASS, text[1..-1]] }
138
+ when text = ss.scan(/:not_has/) then
139
+ action { [:tPSEUDO_CLASS, text[1..-1]] }
138
140
  when text = ss.scan(/#{NODE_TYPE}/) then
139
141
  action { [:tNODE_TYPE, text[1..]] }
140
142
  when text = ss.scan(/>/) then
@@ -189,6 +191,14 @@ class Synvert::Core::NodeQuery::Lexer
189
191
  case
190
192
  when ss.skip(/\s+/) then
191
193
  # do nothing
194
+ when text = ss.scan(/\[\]=/) then
195
+ action { [:tIDENTIFIER_VALUE, text] }
196
+ when text = ss.scan(/\[\]/) then
197
+ action { [:tIDENTIFIER_VALUE, text] }
198
+ when text = ss.scan(/:\[\]=/) then
199
+ action { [:tSYMBOL, text[1..-1].to_sym] }
200
+ when text = ss.scan(/:\[\]/) then
201
+ action { [:tSYMBOL, text[1..-1].to_sym] }
192
202
  when text = ss.scan(/#{OPEN_DYNAMIC_ATTRIBUTE}/) then
193
203
  action { @state = :DYNAMIC_ATTRIBUTE; [:tOPEN_DYNAMIC_ATTRIBUTE, text] }
194
204
  when text = ss.scan(/#{OPEN_ARRAY}/) then
@@ -208,7 +208,7 @@ racc_token_table = {
208
208
  :tIDENTIFIER => 5,
209
209
  :tIDENTIFIER_VALUE => 6,
210
210
  :tINDEX => 7,
211
- :tHAS => 8,
211
+ :tPSEUDO_CLASS => 8,
212
212
  :tCOMMA => 9,
213
213
  :tCHILD => 10,
214
214
  :tSUBSEQUENT_SIBLING => 11,
@@ -271,7 +271,7 @@ Racc_token_to_s_table = [
271
271
  "tIDENTIFIER",
272
272
  "tIDENTIFIER_VALUE",
273
273
  "tINDEX",
274
- "tHAS",
274
+ "tPSEUDO_CLASS",
275
275
  "tCOMMA",
276
276
  "tCHILD",
277
277
  "tSUBSEQUENT_SIBLING",
@@ -363,15 +363,15 @@ def _reduce_11(val, _values)
363
363
  end
364
364
 
365
365
  def _reduce_12(val, _values)
366
- Compiler::Selector.new(node_type: val[0], attribute_list: val[1], has_expression: val[4])
366
+ Compiler::Selector.new(node_type: val[0], attribute_list: val[1], pseudo_class: val[2], pseudo_expression: val[4])
367
367
  end
368
368
 
369
369
  def _reduce_13(val, _values)
370
- Compiler::Selector.new(node_type: val[0], has_expression: val[3])
370
+ Compiler::Selector.new(node_type: val[0], pseudo_class: val[1], pseudo_expression: val[3])
371
371
  end
372
372
 
373
373
  def _reduce_14(val, _values)
374
- Compiler::Selector.new(attribute_list: val[0], has_expression: val[3])
374
+ Compiler::Selector.new(attribute_list: val[0], pseudo_class: val[1], pseudo_expression: val[3])
375
375
  end
376
376
 
377
377
  def _reduce_15(val, _values)
@@ -1,6 +1,6 @@
1
1
  class Synvert::Core::NodeQuery::Parser
2
2
  options no_result_var
3
- token tNODE_TYPE tATTRIBUTE tKEY tIDENTIFIER tIDENTIFIER_VALUE tINDEX tHAS tCOMMA
3
+ token tNODE_TYPE tATTRIBUTE tKEY tIDENTIFIER tIDENTIFIER_VALUE tINDEX tPSEUDO_CLASS tCOMMA
4
4
  tCHILD tSUBSEQUENT_SIBLING tNEXT_SIBLING
5
5
  tOPEN_ATTRIBUTE tCLOSE_ATTRIBUTE tOPEN_DYNAMIC_ATTRIBUTE tCLOSE_DYNAMIC_ATTRIBUTE tOPEN_ARRAY tCLOSE_ARRAY tOPEN_SELECTOR tCLOSE_SELECTOR
6
6
  tEQUAL tNOT_EQUAL tMATCH tNOT_MATCH tGREATER_THAN tGREATER_THAN_OR_EQUAL tLESS_THAN tLESS_THAN_OR_EQUAL tIN tNOT_IN tINCLUDES
@@ -20,9 +20,9 @@ rule
20
20
  : tNODE_TYPE attribute_list tINDEX { Compiler::Selector.new(node_type: val[0], attribute_list: val[1], index: val[2]) }
21
21
  | tNODE_TYPE tINDEX { Compiler::Selector.new(node_type: val[0], index: val[1]) }
22
22
  | attribute_list tINDEX { Compiler::Selector.new(attribute_list: val[0], index: val[1]) }
23
- | tNODE_TYPE attribute_list tHAS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], attribute_list: val[1], has_expression: val[4]) }
24
- | tNODE_TYPE tHAS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], has_expression: val[3]) }
25
- | attribute_list tHAS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(attribute_list: val[0], has_expression: val[3]) }
23
+ | tNODE_TYPE attribute_list tPSEUDO_CLASS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], attribute_list: val[1], pseudo_class: val[2], pseudo_expression: val[4]) }
24
+ | tNODE_TYPE tPSEUDO_CLASS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], pseudo_class: val[1], pseudo_expression: val[3]) }
25
+ | attribute_list tPSEUDO_CLASS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(attribute_list: val[0], pseudo_class: val[1], pseudo_expression: val[3]) }
26
26
  | tNODE_TYPE attribute_list { Compiler::Selector.new(node_type: val[0], attribute_list: val[1]) }
27
27
  | tNODE_TYPE { Compiler::Selector.new(node_type: val[0]) }
28
28
  | attribute_list { Compiler::Selector.new(attribute_list: val[0]) }
@@ -19,6 +19,7 @@
19
19
  #
20
20
  # It also supports some custom selectors:
21
21
  #
22
+ # * not_has: +.class:not_has(.def)+, it's same as +:not(:has())+ in css, just to make implementation easy.
22
23
  # * nested selector: +.send[arguments = [size = 2][first = .sym][last = .hash]]+
23
24
  # * array value: +.send[arguments = (a, b)]+
24
25
  # * IN operator: +.send[message IN (try, try!)]+
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '1.0.4'
5
+ VERSION = '1.1.1'
6
6
  end
7
7
  end
@@ -76,11 +76,6 @@ describe Parser::AST::Node do
76
76
  expect(node.name).to eq :@@foo
77
77
  end
78
78
 
79
- it 'gets for mlhs node' do
80
- node = parse('var.each { |(param1, param2)| }')
81
- expect(node.arguments.first.name).to eq node.arguments.first
82
- end
83
-
84
79
  it 'gets for restarg node' do
85
80
  node = parse('object.each { |*entry| }')
86
81
  expect(node.arguments.first.name).to eq :entry
@@ -116,16 +111,6 @@ describe Parser::AST::Node do
116
111
  node = parse('user&.update(name: name)')
117
112
  expect(node.message).to eq :update
118
113
  end
119
-
120
- it 'gets for super node' do
121
- node = parse('super(params)')
122
- expect(node.message).to eq :super
123
- end
124
-
125
- it 'gets for zuper node' do
126
- node = parse('super do; end')
127
- expect(node.caller.message).to eq :super
128
- end
129
114
  end
130
115
 
131
116
  describe '#parent_const' do
@@ -1042,4 +1027,45 @@ describe Parser::AST::Node do
1042
1027
  end
1043
1028
  end
1044
1029
  end
1030
+
1031
+ describe '#to_hash' do
1032
+ it 'gets hash' do
1033
+ node = parse(<<~EOS)
1034
+ class Synvert
1035
+ def foobar(foo, bar)
1036
+ foo + bar
1037
+ end
1038
+ end
1039
+ EOS
1040
+ expect(node.to_hash).to eq(
1041
+ {
1042
+ type: :class,
1043
+ parent_class: nil,
1044
+ name: {
1045
+ type: :const,
1046
+ parent_const: nil,
1047
+ name: :Synvert
1048
+ },
1049
+ body: [
1050
+ {
1051
+ type: :def,
1052
+ name: :foobar,
1053
+ arguments: [
1054
+ { type: :arg, name: :foo },
1055
+ { type: :arg, name: :bar }
1056
+ ],
1057
+ body: [
1058
+ {
1059
+ type: :send,
1060
+ receiver: { name: :foo, type: :lvar },
1061
+ message: :+,
1062
+ arguments: [{ name: :bar, type: :lvar }]
1063
+ }
1064
+ ]
1065
+ }
1066
+ ]
1067
+ }
1068
+ )
1069
+ end
1070
+ end
1045
1071
  end
@@ -187,6 +187,45 @@ module Synvert::Core::NodeQuery
187
187
  assert_tokens source, expected_tokens
188
188
  end
189
189
 
190
+ it 'matches empty string' do
191
+ source = ".send[arguments.first='']"
192
+ expected_tokens = [
193
+ [:tNODE_TYPE, "send"],
194
+ [:tOPEN_ATTRIBUTE, "["],
195
+ [:tKEY, "arguments.first"],
196
+ [:tEQUAL, "="],
197
+ [:tSTRING, ""],
198
+ [:tCLOSE_ATTRIBUTE, "]"]
199
+ ]
200
+ assert_tokens source, expected_tokens
201
+ end
202
+
203
+ it 'matches :[] message' do
204
+ source = ".send[message=[]]"
205
+ expected_tokens = [
206
+ [:tNODE_TYPE, "send"],
207
+ [:tOPEN_ATTRIBUTE, "["],
208
+ [:tKEY, "message"],
209
+ [:tEQUAL, "="],
210
+ [:tIDENTIFIER_VALUE, "[]"],
211
+ [:tCLOSE_ATTRIBUTE, "]"]
212
+ ]
213
+ assert_tokens source, expected_tokens
214
+ end
215
+
216
+ it 'matches :[] message' do
217
+ source = ".send[message=:[]=]"
218
+ expected_tokens = [
219
+ [:tNODE_TYPE, "send"],
220
+ [:tOPEN_ATTRIBUTE, "["],
221
+ [:tKEY, "message"],
222
+ [:tEQUAL, "="],
223
+ [:tSYMBOL, :[]=],
224
+ [:tCLOSE_ATTRIBUTE, "]"]
225
+ ]
226
+ assert_tokens source, expected_tokens
227
+ end
228
+
190
229
  it 'matches attribute value' do
191
230
  source = '.pair[key={{value}}]'
192
231
  expected_tokens = [
@@ -547,7 +586,22 @@ module Synvert::Core::NodeQuery
547
586
  source = '.class:has(> .def)'
548
587
  expected_tokens = [
549
588
  [:tNODE_TYPE, "class"],
550
- [:tHAS, "has"],
589
+ [:tPSEUDO_CLASS, "has"],
590
+ [:tOPEN_SELECTOR, "("],
591
+ [:tCHILD, ">"],
592
+ [:tNODE_TYPE, "def"],
593
+ [:tCLOSE_SELECTOR, ")"]
594
+ ]
595
+ assert_tokens source, expected_tokens
596
+ end
597
+ end
598
+
599
+ context ':not_has' do
600
+ it 'matches' do
601
+ source = '.class:not_has(> .def)'
602
+ expected_tokens = [
603
+ [:tNODE_TYPE, "class"],
604
+ [:tPSEUDO_CLASS, "not_has"],
551
605
  [:tOPEN_SELECTOR, "("],
552
606
  [:tCHILD, ">"],
553
607
  [:tNODE_TYPE, "def"],
@@ -53,6 +53,11 @@ module Synvert::Core::NodeQuery
53
53
  assert_parser(source)
54
54
  end
55
55
 
56
+ it 'parses :not_has selector' do
57
+ source = '.class:not_has(> .def)'
58
+ assert_parser(source)
59
+ end
60
+
56
61
  it 'parses multiple attributes' do
57
62
  source = '.send[receiver=nil][message=:create]'
58
63
  assert_parser(source)
@@ -108,6 +113,21 @@ module Synvert::Core::NodeQuery
108
113
  assert_parser(source)
109
114
  end
110
115
 
116
+ it 'parses empty string' do
117
+ source = '.send[arguments.first=""]'
118
+ assert_parser(source)
119
+ end
120
+
121
+ it 'parses []=' do
122
+ source = '.send[message=[]=]'
123
+ assert_parser(source)
124
+ end
125
+
126
+ it 'parses :[]' do
127
+ source = '.send[message=:[]]'
128
+ assert_parser(source)
129
+ end
130
+
111
131
  describe '#query_nodes' do
112
132
  let(:node) {
113
133
  parse(<<~EOS)
@@ -124,6 +144,8 @@ module Synvert::Core::NodeQuery
124
144
  { a: a, b: b }
125
145
  foo.merge(bar)
126
146
  arr[index]
147
+ arr[index] = value
148
+ call('')
127
149
  end
128
150
  end
129
151
  EOS
@@ -202,17 +224,17 @@ module Synvert::Core::NodeQuery
202
224
 
203
225
  it 'matches descendant node' do
204
226
  expression = parser.parse('.class .send[message=:create]')
205
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
227
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
206
228
  end
207
229
 
208
230
  it 'matches three level descendant node' do
209
231
  expression = parser.parse('.class .def .send[message=:create]')
210
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
232
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
211
233
  end
212
234
 
213
235
  it 'matches child node' do
214
236
  expression = parser.parse('.def > .send[message=:create]')
215
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
237
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
216
238
  end
217
239
 
218
240
  it 'matches next sibling node' do
@@ -230,18 +252,31 @@ module Synvert::Core::NodeQuery
230
252
  expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
231
253
  end
232
254
 
255
+ it 'matches not_has selector' do
256
+ expression = parser.parse('.def:not_has(> .send[receiver=FactoryBot])')
257
+ expect(expression.query_nodes(node)).to eq [node.body.last]
258
+ end
259
+
233
260
  it 'matches arguments.size' do
234
- expression = parser.parse('.send[arguments.size=2]')
235
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
236
- expression = parser.parse('.send[arguments.size>2]')
261
+ expression = parser.parse('.def .send[arguments.size=2]')
262
+ expect(expression.query_nodes(node)).to eq [
263
+ node.body.first.body.last,
264
+ node.body.second.body.last,
265
+ node.body.third.body.fourth
266
+ ]
267
+ expression = parser.parse('.def .send[arguments.size>2]')
237
268
  expect(expression.query_nodes(node)).to eq []
238
- expression = parser.parse('.send[arguments.size>=2]')
239
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
269
+ expression = parser.parse('.def .send[arguments.size>=2]')
270
+ expect(expression.query_nodes(node)).to eq [
271
+ node.body.first.body.last,
272
+ node.body.second.body.last,
273
+ node.body.third.body.fourth
274
+ ]
240
275
  end
241
276
 
242
277
  it 'matches arguments' do
243
278
  expression = parser.parse('.send[arguments=[size=2][first=.sym][last=.hash]]')
244
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
279
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
245
280
  end
246
281
 
247
282
  it 'matches regexp value' do
@@ -262,9 +297,19 @@ module Synvert::Core::NodeQuery
262
297
  end
263
298
 
264
299
  it 'matches []' do
265
- expression = parser.parse('.send[message="[]"]')
300
+ expression = parser.parse('.send[message=[]]')
266
301
  expect(expression.query_nodes(node)).to eq [node.body.last.body.third]
267
302
  end
303
+
304
+ it 'matches []=' do
305
+ expression = parser.parse('.send[message=:[]=]')
306
+ expect(expression.query_nodes(node)).to eq [node.body.last.body.fourth]
307
+ end
308
+
309
+ it 'matches empty string' do
310
+ expression = parser.parse('.send[message=call][arguments.first=""]')
311
+ expect(expression.query_nodes(node)).to eq [node.body.last.body.last]
312
+ end
268
313
  end
269
314
  end
270
315
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synvert-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-25 00:00:00.000000000 Z
11
+ date: 2022-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport