synvert-core 1.0.3 → 1.1.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: 2e678636827f8ffcad275fe7c6e6bcbee9d278a954bf33a80822ff7235944469
4
- data.tar.gz: fa6c7ad36ed9fa50901f4b6f9f373190aa3ad0cbba4bdea539daf66376bc6e0c
3
+ metadata.gz: 603dafd1ddc6f40f8ef1479d359e39e1f4dbe90067bcfe51f721b15e9571c4d0
4
+ data.tar.gz: e423e49c531e6ba91f490657f34564f781f3313d82dbf9a863ee2c17985500a8
5
5
  SHA512:
6
- metadata.gz: bd53c12635ecb49e6d2c6b43d8a0513ac0fd2c2c0b6de8081bc30318512ae7714c092c2e36a721059b2ec811ec521757a1b32889d2e6ff4cb516ce83877cfef6
7
- data.tar.gz: 4a0e169b94bc54a61e944d3e44d5bd40a884783c73c2ad491dbb23a25c77c73bb2e120f0feb61494a8bf8762c234cbc3d983185e0bc2eb18a6ec8cb3d2c933ab
6
+ metadata.gz: e6177477cdf8c0cdd67d6dc533145017daebee61905b5e48b992261f6f458b8a949796aa86eb0cd8f161ffaa05b9325e1cd671b6d63fd645ece7f2101e67859c
7
+ data.tar.gz: c1daa4cca1056d9266c01037217cd6570f0b7ee0228491e408d43c7bf8bf753a52b2dbfbf20d6aa568876d7e1d7ee11727db40adbc56e17fc603ae091804475a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.1.0 (2022-04-26)
4
+
5
+ * Dynamic define Node methods by `TYPE_CHILDREN` const
6
+ * Add `Node#to_hash`
7
+ * Parse empty string in node query language
8
+ * Identifier value can contain `?`, `<`, `=`, `>` in node query language
9
+
3
10
  ## 1.0.0 (2022-04-25)
4
11
 
5
12
  * 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
@@ -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] }
@@ -66,9 +67,6 @@ rules
66
67
  :VALUE /#{DOUBLE_QUOTE_STRING}/ { [:tSTRING, text[1...-1]] }
67
68
  :VALUE /#{SINGLE_QUOTE_STRING}/ { [:tSTRING, text[1...-1]] }
68
69
  :VALUE /#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
69
- :VALUE />/ { [:tCHILD, text] }
70
- :VALUE /~/ { [:tSUBSEQUENT_SIBLING, text] }
71
- :VALUE /\+/ { [:tNEXT_SIBLING, text] }
72
70
  :VALUE /#{OPEN_ATTRIBUTE}/ { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
73
71
  :VALUE /#{IDENTIFIER_VALUE}/ { [:tIDENTIFIER_VALUE, text] }
74
72
  :DYNAMIC_ATTRIBUTE /#{CLOSE_DYNAMIC_ATTRIBUTE}/ { @state = :VALUE; [:tCLOSE_DYNAMIC_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
@@ -215,12 +217,6 @@ class Synvert::Core::NodeQuery::Lexer
215
217
  action { [:tSTRING, text[1...-1]] }
216
218
  when text = ss.scan(/#{NODE_TYPE}/) then
217
219
  action { [:tNODE_TYPE, text[1..]] }
218
- when text = ss.scan(/>/) then
219
- action { [:tCHILD, text] }
220
- when text = ss.scan(/~/) then
221
- action { [:tSUBSEQUENT_SIBLING, text] }
222
- when text = ss.scan(/\+/) then
223
- action { [:tNEXT_SIBLING, text] }
224
220
  when text = ss.scan(/#{OPEN_ATTRIBUTE}/) then
225
221
  action { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
226
222
  when text = ss.scan(/#{IDENTIFIER_VALUE}/) 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.3'
5
+ VERSION = '1.1.0'
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,39 @@ 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
+ type: :class,
1042
+ parent_class: nil,
1043
+ name: {
1044
+ type: :const,
1045
+ parent_const: nil,
1046
+ name: :Synvert
1047
+ },
1048
+ body: [{
1049
+ type: :def,
1050
+ name: :foobar,
1051
+ arguments: [
1052
+ { type: :arg, name: :foo},
1053
+ { type: :arg, name: :bar}
1054
+ ],
1055
+ body: [{
1056
+ type: :send,
1057
+ receiver: { name: :foo, type: :lvar },
1058
+ message: :+,
1059
+ arguments: [{ name: :bar, type: :lvar }]
1060
+ }]
1061
+ }]
1062
+ })
1063
+ end
1064
+ end
1045
1065
  end
@@ -153,13 +153,48 @@ module Synvert::Core::NodeQuery
153
153
  end
154
154
 
155
155
  it 'identifier can contain <, >, =' do
156
- source = '.send[message=<=>]'
156
+ source = '.send[message=<]'
157
157
  expected_tokens = [
158
158
  [:tNODE_TYPE, "send"],
159
159
  [:tOPEN_ATTRIBUTE, "["],
160
160
  [:tKEY, "message"],
161
161
  [:tEQUAL, "="],
162
- [:tIDENTIFIER_VALUE, "<=>"],
162
+ [:tIDENTIFIER_VALUE, "<"],
163
+ [:tCLOSE_ATTRIBUTE, "]"]
164
+ ]
165
+ assert_tokens source, expected_tokens
166
+
167
+ source = '.send[message==]'
168
+ expected_tokens = [
169
+ [:tNODE_TYPE, "send"],
170
+ [:tOPEN_ATTRIBUTE, "["],
171
+ [:tKEY, "message"],
172
+ [:tEQUAL, "="],
173
+ [:tIDENTIFIER_VALUE, "="],
174
+ [:tCLOSE_ATTRIBUTE, "]"]
175
+ ]
176
+ assert_tokens source, expected_tokens
177
+
178
+ source = '.send[message=>]'
179
+ expected_tokens = [
180
+ [:tNODE_TYPE, "send"],
181
+ [:tOPEN_ATTRIBUTE, "["],
182
+ [:tKEY, "message"],
183
+ [:tEQUAL, "="],
184
+ [:tIDENTIFIER_VALUE, ">"],
185
+ [:tCLOSE_ATTRIBUTE, "]"]
186
+ ]
187
+ assert_tokens source, expected_tokens
188
+ end
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, ""],
163
198
  [:tCLOSE_ATTRIBUTE, "]"]
164
199
  ]
165
200
  assert_tokens source, expected_tokens
@@ -525,7 +560,22 @@ module Synvert::Core::NodeQuery
525
560
  source = '.class:has(> .def)'
526
561
  expected_tokens = [
527
562
  [:tNODE_TYPE, "class"],
528
- [:tHAS, "has"],
563
+ [:tPSEUDO_CLASS, "has"],
564
+ [:tOPEN_SELECTOR, "("],
565
+ [:tCHILD, ">"],
566
+ [:tNODE_TYPE, "def"],
567
+ [:tCLOSE_SELECTOR, ")"]
568
+ ]
569
+ assert_tokens source, expected_tokens
570
+ end
571
+ end
572
+
573
+ context ':not_has' do
574
+ it 'matches' do
575
+ source = '.class:not_has(> .def)'
576
+ expected_tokens = [
577
+ [:tNODE_TYPE, "class"],
578
+ [:tPSEUDO_CLASS, "not_has"],
529
579
  [:tOPEN_SELECTOR, "("],
530
580
  [:tCHILD, ">"],
531
581
  [: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,11 @@ 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
+
111
121
  describe '#query_nodes' do
112
122
  let(:node) {
113
123
  parse(<<~EOS)
@@ -230,6 +240,11 @@ module Synvert::Core::NodeQuery
230
240
  expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
231
241
  end
232
242
 
243
+ it 'matches not_has selector' do
244
+ expression = parser.parse('.def:not_has(> .send[receiver=FactoryBot])')
245
+ expect(expression.query_nodes(node)).to eq [node.body.last]
246
+ end
247
+
233
248
  it 'matches arguments.size' do
234
249
  expression = parser.parse('.send[arguments.size=2]')
235
250
  expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
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.3
4
+ version: 1.1.0
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-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport