synvert-core 0.64.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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