synvert-core 0.64.0 → 1.0.1

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Guardfile +11 -2
  6. data/README.md +2 -2
  7. data/Rakefile +15 -1
  8. data/lib/synvert/core/array_ext.rb +41 -0
  9. data/lib/synvert/core/engine/erb.rb +9 -8
  10. data/lib/synvert/core/node_ext.rb +47 -44
  11. data/lib/synvert/core/node_query/compiler/array.rb +34 -0
  12. data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
  13. data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
  14. data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
  15. data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
  16. data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
  17. data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
  18. data/lib/synvert/core/node_query/compiler/float.rb +23 -0
  19. data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
  20. data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
  21. data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
  22. data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
  23. data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
  24. data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
  25. data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
  26. data/lib/synvert/core/node_query/compiler/string.rb +34 -0
  27. data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
  28. data/lib/synvert/core/node_query/compiler.rb +24 -0
  29. data/lib/synvert/core/node_query/lexer.rex +96 -0
  30. data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
  31. data/lib/synvert/core/node_query/parser.racc.rb +518 -0
  32. data/lib/synvert/core/node_query/parser.y +84 -0
  33. data/lib/synvert/core/node_query.rb +36 -0
  34. data/lib/synvert/core/rewriter/action/delete_action.rb +4 -2
  35. data/lib/synvert/core/rewriter/action/replace_action.rb +4 -2
  36. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +2 -2
  37. data/lib/synvert/core/rewriter/action/wrap_action.rb +3 -2
  38. data/lib/synvert/core/rewriter/action.rb +2 -1
  39. data/lib/synvert/core/rewriter/helper.rb +5 -2
  40. data/lib/synvert/core/rewriter/instance.rb +25 -13
  41. data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
  42. data/lib/synvert/core/rewriter/scope/within_scope.rb +1 -1
  43. data/lib/synvert/core/rewriter.rb +1 -0
  44. data/lib/synvert/core/version.rb +1 -1
  45. data/lib/synvert/core.rb +22 -5
  46. data/spec/synvert/core/engine/erb_spec.rb +2 -2
  47. data/spec/synvert/core/node_ext_spec.rb +36 -5
  48. data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
  49. data/spec/synvert/core/node_query/parser_spec.rb +270 -0
  50. data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
  51. data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
  52. data/spec/synvert/core/rewriter/instance_spec.rb +24 -3
  53. data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
  54. data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
  55. data/spec/synvert/core/rewriter_spec.rb +4 -2
  56. data/synvert-core-ruby.gemspec +5 -1
  57. metadata +75 -2
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+ # encoding: UTF-8
3
+ #--
4
+ # This file is automatically generated. Do not modify it.
5
+ # Generated by: oedipus_lex version 2.6.0.
6
+ # Source: lib/synvert/core/node_query/lexer.rex
7
+ #++
8
+
9
+
10
+ ##
11
+ # The generated lexer Synvert::Core::NodeQuery::Lexer
12
+
13
+ class Synvert::Core::NodeQuery::Lexer
14
+ require 'strscan'
15
+
16
+ # :stopdoc:
17
+ OPEN_ATTRIBUTE = /\[/
18
+ CLOSE_ATTRIBUTE = /\]/
19
+ OPEN_ARRAY = /\(/
20
+ CLOSE_ARRAY = /\)/
21
+ OPEN_SELECTOR = /\(/
22
+ CLOSE_SELECTOR = /\)/
23
+ OPEN_DYNAMIC_ATTRIBUTE = /{{/
24
+ CLOSE_DYNAMIC_ATTRIBUTE = /}}/
25
+ NODE_TYPE = /\.[a-z]+/
26
+ IDENTIFIER = /[\.\w]+/
27
+ IDENTIFIER_VALUE = /[\.\w!&:]+/
28
+ FALSE = /false/
29
+ FLOAT = /\d+\.\d+/
30
+ INTEGER = /\d+/
31
+ NIL = /nil/
32
+ REGEXP_BODY = /(?:[^\/]|\\\/)*/
33
+ REGEXP = /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
34
+ SYMBOL = /:[\w!]+/
35
+ TRUE = /true/
36
+ SINGLE_QUOTE_STRING = /'(.+?)'/
37
+ DOUBLE_QUOTE_STRING = /"(.+?)"/
38
+ # :startdoc:
39
+ # :stopdoc:
40
+ class LexerError < StandardError ; end
41
+ class ScanError < LexerError ; end
42
+ # :startdoc:
43
+
44
+ ##
45
+ # The file name / path
46
+
47
+ attr_accessor :filename
48
+
49
+ ##
50
+ # The StringScanner for this lexer.
51
+
52
+ attr_accessor :ss
53
+
54
+ ##
55
+ # The current lexical state.
56
+
57
+ attr_accessor :state
58
+
59
+ alias :match :ss
60
+
61
+ ##
62
+ # The match groups for the current scan.
63
+
64
+ def matches
65
+ m = (1..9).map { |i| ss[i] }
66
+ m.pop until m[-1] or m.empty?
67
+ m
68
+ end
69
+
70
+ ##
71
+ # Yields on the current action.
72
+
73
+ def action
74
+ yield
75
+ end
76
+
77
+
78
+ ##
79
+ # The current scanner class. Must be overridden in subclasses.
80
+
81
+ def scanner_class
82
+ StringScanner
83
+ end unless instance_methods(false).map(&:to_s).include?("scanner_class")
84
+
85
+ ##
86
+ # Parse the given string.
87
+
88
+ def parse str
89
+ self.ss = scanner_class.new str
90
+ self.state ||= nil
91
+
92
+ do_parse
93
+ end
94
+
95
+ ##
96
+ # Read in and parse the file at +path+.
97
+
98
+ def parse_file path
99
+ self.filename = path
100
+ open path do |f|
101
+ parse f.read
102
+ end
103
+ end
104
+
105
+ ##
106
+ # The current location in the parse.
107
+
108
+ def location
109
+ [
110
+ (filename || "<input>"),
111
+ ].compact.join(":")
112
+ end
113
+
114
+ ##
115
+ # Lex the next token.
116
+
117
+ def next_token
118
+
119
+ token = nil
120
+
121
+ until ss.eos? or token do
122
+ token =
123
+ case state
124
+ when nil then
125
+ case
126
+ when ss.skip(/\s+/) then
127
+ # do nothing
128
+ when ss.skip(/:first-child/) then
129
+ action { [:tINDEX, 0] }
130
+ when ss.skip(/:last-child/) then
131
+ action { [:tINDEX, -1] }
132
+ when text = ss.scan(/:nth-child\(\d+\)/) then
133
+ action { [:tINDEX, text.sub(':nth-child(', '').to_i - 1] }
134
+ when text = ss.scan(/:nth-last-child\(\d+\)/) then
135
+ action { [:tINDEX, -text.sub(':nth-last-child(', '').to_i] }
136
+ when text = ss.scan(/:has/) then
137
+ action { [:tHAS, text[1..-1]] }
138
+ when text = ss.scan(/#{NODE_TYPE}/) then
139
+ action { [:tNODE_TYPE, text[1..]] }
140
+ when text = ss.scan(/>/) then
141
+ action { [:tCHILD, text] }
142
+ when text = ss.scan(/~/) then
143
+ action { [:tSUBSEQUENT_SIBLING, text] }
144
+ when text = ss.scan(/\+/) then
145
+ action { [:tNEXT_SIBLING, text] }
146
+ when text = ss.scan(/#{OPEN_SELECTOR}/) then
147
+ action { [:tOPEN_SELECTOR, text] }
148
+ when text = ss.scan(/#{CLOSE_SELECTOR}/) then
149
+ action { [:tCLOSE_SELECTOR, text] }
150
+ when text = ss.scan(/#{OPEN_ATTRIBUTE}/) then
151
+ action { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
152
+ else
153
+ text = ss.string[ss.pos .. -1]
154
+ raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
155
+ end
156
+ when :KEY then
157
+ case
158
+ when ss.skip(/\s+/) then
159
+ # do nothing
160
+ when text = ss.scan(/!=/) then
161
+ action { @state = :VALUE; [:tNOT_EQUAL, text] }
162
+ when text = ss.scan(/=~/) then
163
+ action { @state = :VALUE; [:tMATCH, text] }
164
+ when text = ss.scan(/!~/) then
165
+ action { @state = :VALUE; [:tNOT_MATCH, text] }
166
+ when text = ss.scan(/>=/) then
167
+ action { @state = :VALUE; [:tGREATER_THAN_OR_EQUAL, text] }
168
+ when text = ss.scan(/<=/) then
169
+ action { @state = :VALUE; [:tLESS_THAN_OR_EQUAL, text] }
170
+ when text = ss.scan(/>/) then
171
+ action { @state = :VALUE; [:tGREATER_THAN, text] }
172
+ when text = ss.scan(/</) then
173
+ action { @state = :VALUE; [:tLESS_THAN, text] }
174
+ when text = ss.scan(/=/) then
175
+ action { @state = :VALUE; [:tEQUAL, text] }
176
+ when text = ss.scan(/includes/i) then
177
+ action { @state = :VALUE; [:tINCLUDES, text] }
178
+ when text = ss.scan(/not in/i) then
179
+ action { @state = :VALUE; [:tNOT_IN, text] }
180
+ when text = ss.scan(/in/i) then
181
+ action { @state = :VALUE; [:tIN, text] }
182
+ when text = ss.scan(/#{IDENTIFIER}/) then
183
+ action { [:tKEY, text] }
184
+ else
185
+ text = ss.string[ss.pos .. -1]
186
+ raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
187
+ end
188
+ when :VALUE then
189
+ case
190
+ when ss.skip(/\s+/) then
191
+ # do nothing
192
+ when text = ss.scan(/#{OPEN_DYNAMIC_ATTRIBUTE}/) then
193
+ action { @state = :DYNAMIC_ATTRIBUTE; [:tOPEN_DYNAMIC_ATTRIBUTE, text] }
194
+ when text = ss.scan(/#{OPEN_ARRAY}/) then
195
+ action { @state = :ARRAY_VALUE; [:tOPEN_ARRAY, text] }
196
+ when text = ss.scan(/#{CLOSE_ATTRIBUTE}/) then
197
+ action { @nested_count -= 1; @state = @nested_count == 0 ? nil : :VALUE; [:tCLOSE_ATTRIBUTE, text] }
198
+ when ss.skip(/#{NIL}/) then
199
+ action { [:tNIL, nil] }
200
+ when ss.skip(/#{TRUE}/) then
201
+ action { [:tBOOLEAN, true] }
202
+ when ss.skip(/#{FALSE}/) then
203
+ action { [:tBOOLEAN, false] }
204
+ when text = ss.scan(/#{SYMBOL}/) then
205
+ action { [:tSYMBOL, text[1..-1].to_sym] }
206
+ when text = ss.scan(/#{FLOAT}/) then
207
+ action { [:tFLOAT, text.to_f] }
208
+ when text = ss.scan(/#{INTEGER}/) then
209
+ action { [:tINTEGER, text.to_i] }
210
+ when text = ss.scan(/#{REGEXP}/) then
211
+ action { [:tREGEXP, eval(text)] }
212
+ when text = ss.scan(/#{DOUBLE_QUOTE_STRING}/) then
213
+ action { [:tSTRING, text[1...-1]] }
214
+ when text = ss.scan(/#{SINGLE_QUOTE_STRING}/) then
215
+ action { [:tSTRING, text[1...-1]] }
216
+ when text = ss.scan(/#{NODE_TYPE}/) then
217
+ 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
+ when text = ss.scan(/#{OPEN_ATTRIBUTE}/) then
225
+ action { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
226
+ when text = ss.scan(/#{IDENTIFIER_VALUE}/) then
227
+ action { [:tIDENTIFIER_VALUE, text] }
228
+ else
229
+ text = ss.string[ss.pos .. -1]
230
+ raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
231
+ end
232
+ when :DYNAMIC_ATTRIBUTE then
233
+ case
234
+ when text = ss.scan(/#{CLOSE_DYNAMIC_ATTRIBUTE}/) then
235
+ action { @state = :VALUE; [:tCLOSE_DYNAMIC_ATTRIBUTE, text] }
236
+ when text = ss.scan(/#{IDENTIFIER}/) then
237
+ action { [:tDYNAMIC_ATTRIBUTE, text] }
238
+ else
239
+ text = ss.string[ss.pos .. -1]
240
+ raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
241
+ end
242
+ when :ARRAY_VALUE then
243
+ case
244
+ when ss.skip(/\s+/) then
245
+ # do nothing
246
+ when text = ss.scan(/,/) then
247
+ action { [:tCOMMA, text] }
248
+ when text = ss.scan(/#{CLOSE_ARRAY}/) then
249
+ action { @state = :VALUE; [:tCLOSE_ARRAY, text] }
250
+ when ss.skip(/#{NIL}/) then
251
+ action { [:tNIL, nil] }
252
+ when ss.skip(/#{TRUE}/) then
253
+ action { [:tBOOLEAN, true] }
254
+ when ss.skip(/#{FALSE}/) then
255
+ action { [:tBOOLEAN, false] }
256
+ when text = ss.scan(/#{SYMBOL}/) then
257
+ action { [:tSYMBOL, text[1..-1].to_sym] }
258
+ when text = ss.scan(/#{FLOAT}/) then
259
+ action { [:tFLOAT, text.to_f] }
260
+ when text = ss.scan(/#{INTEGER}/) then
261
+ action { [:tINTEGER, text.to_i] }
262
+ when text = ss.scan(/#{REGEXP}/) then
263
+ action { [:tREGEXP, eval(text)] }
264
+ when text = ss.scan(/#{DOUBLE_QUOTE_STRING}/) then
265
+ action { [:tSTRING, text[1...-1]] }
266
+ when text = ss.scan(/#{SINGLE_QUOTE_STRING}/) then
267
+ action { [:tSTRING, text[1...-1]] }
268
+ when text = ss.scan(/#{IDENTIFIER_VALUE}/) then
269
+ action { [:tIDENTIFIER_VALUE, text] }
270
+ else
271
+ text = ss.string[ss.pos .. -1]
272
+ raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
273
+ end
274
+ else
275
+ raise ScanError, "undefined state at #{location}: '#{state}'"
276
+ end # token = case state
277
+
278
+ next unless token # allow functions to trigger redo w/ nil
279
+ end # while
280
+
281
+ raise LexerError, "bad lexical result at #{location}: #{token.inspect}" unless
282
+ token.nil? || (Array === token && token.size >= 2)
283
+
284
+ # auto-switch state
285
+ self.state = token.last if token && token.first == :state
286
+
287
+ token
288
+ end # def next_token
289
+ def initialize
290
+ @nested_count = 0
291
+ end
292
+ def do_parse; end
293
+ end # class