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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.gitignore +4 -0
- data/CHANGELOG.md +5 -0
- data/Guardfile +11 -2
- data/README.md +2 -2
- data/Rakefile +15 -1
- data/lib/synvert/core/array_ext.rb +41 -0
- data/lib/synvert/core/engine/erb.rb +9 -8
- data/lib/synvert/core/node_ext.rb +47 -44
- data/lib/synvert/core/node_query/compiler/array.rb +34 -0
- data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
- data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
- data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
- data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
- data/lib/synvert/core/node_query/compiler/float.rb +23 -0
- data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
- data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
- data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
- data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
- data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
- data/lib/synvert/core/node_query/compiler/string.rb +34 -0
- data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
- data/lib/synvert/core/node_query/compiler.rb +24 -0
- data/lib/synvert/core/node_query/lexer.rex +96 -0
- data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
- data/lib/synvert/core/node_query/parser.racc.rb +518 -0
- data/lib/synvert/core/node_query/parser.y +84 -0
- data/lib/synvert/core/node_query.rb +36 -0
- data/lib/synvert/core/rewriter/action/delete_action.rb +4 -2
- data/lib/synvert/core/rewriter/action/replace_action.rb +4 -2
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +2 -2
- data/lib/synvert/core/rewriter/action/wrap_action.rb +3 -2
- data/lib/synvert/core/rewriter/action.rb +2 -1
- data/lib/synvert/core/rewriter/helper.rb +5 -2
- data/lib/synvert/core/rewriter/instance.rb +25 -13
- data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
- data/lib/synvert/core/rewriter/scope/within_scope.rb +1 -1
- data/lib/synvert/core/rewriter.rb +1 -0
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +22 -5
- data/spec/synvert/core/engine/erb_spec.rb +2 -2
- data/spec/synvert/core/node_ext_spec.rb +36 -5
- data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
- data/spec/synvert/core/node_query/parser_spec.rb +270 -0
- data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
- data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
- data/spec/synvert/core/rewriter/instance_spec.rb +24 -3
- data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
- data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
- data/spec/synvert/core/rewriter_spec.rb +4 -2
- data/synvert-core-ruby.gemspec +5 -1
- 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
|