rupkl 0.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 +7 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +123 -0
- data/lib/rupkl/exception.rb +26 -0
- data/lib/rupkl/node/boolean.rb +21 -0
- data/lib/rupkl/node/dynamic.rb +99 -0
- data/lib/rupkl/node/identifier.rb +19 -0
- data/lib/rupkl/node/member_reference.rb +46 -0
- data/lib/rupkl/node/number.rb +40 -0
- data/lib/rupkl/node/object.rb +141 -0
- data/lib/rupkl/node/operation.rb +186 -0
- data/lib/rupkl/node/pkl_class.rb +30 -0
- data/lib/rupkl/node/pkl_module.rb +47 -0
- data/lib/rupkl/node/string.rb +66 -0
- data/lib/rupkl/node/struct_common.rb +102 -0
- data/lib/rupkl/node/value_common.rb +35 -0
- data/lib/rupkl/parser/expression.rb +120 -0
- data/lib/rupkl/parser/identifier.rb +58 -0
- data/lib/rupkl/parser/literal.rb +309 -0
- data/lib/rupkl/parser/misc.rb +50 -0
- data/lib/rupkl/parser/object.rb +72 -0
- data/lib/rupkl/parser/pkl_class.rb +29 -0
- data/lib/rupkl/parser/pkl_module.rb +32 -0
- data/lib/rupkl/parser.rb +138 -0
- data/lib/rupkl/pkl_object.rb +150 -0
- data/lib/rupkl/version.rb +5 -0
- data/lib/rupkl.rb +48 -0
- metadata +101 -0
@@ -0,0 +1,309 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
class Parser
|
5
|
+
#
|
6
|
+
# Boolean literal
|
7
|
+
#
|
8
|
+
define_parser do
|
9
|
+
rule(:boolean_literal) do
|
10
|
+
kw_true.as(:true_value) | kw_false.as(:false_value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
define_transform do
|
15
|
+
rule(true_value: simple(:v)) do
|
16
|
+
Node::Boolean.new(true, node_position(v))
|
17
|
+
end
|
18
|
+
|
19
|
+
rule(false_value: simple(:v)) do
|
20
|
+
Node::Boolean.new(false, node_position(v))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Integer literal
|
26
|
+
#
|
27
|
+
define_parser do
|
28
|
+
rule(:bin_literal) do
|
29
|
+
str('0b') >> match('[01]') >> match('[_01]').repeat
|
30
|
+
end
|
31
|
+
|
32
|
+
rule(:oct_literal) do
|
33
|
+
str('0o') >> match('[0-7]') >> match('[_0-7]').repeat
|
34
|
+
end
|
35
|
+
|
36
|
+
rule(:dec_literal) do
|
37
|
+
match('[\d]') >> match('[_\d]').repeat
|
38
|
+
end
|
39
|
+
|
40
|
+
rule(:hex_literal) do
|
41
|
+
str('0x') >> match('[\h]') >> match('[_\h]').repeat
|
42
|
+
end
|
43
|
+
|
44
|
+
rule(:integer_literal) do
|
45
|
+
(
|
46
|
+
bin_literal | oct_literal | hex_literal | dec_literal
|
47
|
+
).as(:integer_literal)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
define_transform do
|
52
|
+
rule(integer_literal: simple(:v)) do
|
53
|
+
base = { 'b' => 2, 'o' => 8, 'x' => 16 }.fetch(v.to_s[1], 10)
|
54
|
+
value = v.to_s.tr('_', '').to_i(base)
|
55
|
+
Node::Integer.new(value, node_position(v))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Float literal
|
61
|
+
#
|
62
|
+
define_parser do
|
63
|
+
rule(:float_literal) do
|
64
|
+
(
|
65
|
+
(dec_literal.maybe >> str('.') >> dec_literal >> exponent.maybe) |
|
66
|
+
(dec_literal >> exponent)
|
67
|
+
).as(:float_literal)
|
68
|
+
end
|
69
|
+
|
70
|
+
rule(:exponent) do
|
71
|
+
match('[eE]') >> (match('[+-]') >> str('_').maybe).maybe >> dec_literal
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
define_transform do
|
76
|
+
rule(float_literal: simple(:f)) do
|
77
|
+
v = f.to_s.tr('_', '').to_f
|
78
|
+
Node::Float.new(v, node_position(f))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# String literal
|
84
|
+
#
|
85
|
+
ESCAPED_CHARS =
|
86
|
+
{
|
87
|
+
't' => "\t", 'n' => "\n", 'r' => "\r",
|
88
|
+
'"' => '"', '\\' => '\\'
|
89
|
+
}.freeze
|
90
|
+
|
91
|
+
define_parser do
|
92
|
+
rule(:ss_empty_literal) do
|
93
|
+
ss_bq(false).as(:ss_bq) >> ss_eq('').as(:ss_eq)
|
94
|
+
end
|
95
|
+
|
96
|
+
rule(:ss_literal) do
|
97
|
+
ss_bq(false).as(:ss_bq) >>
|
98
|
+
ss_portions('').as(:ss_portions) >> ss_eq('').as(:ss_eq)
|
99
|
+
end
|
100
|
+
|
101
|
+
rule(:ss_empty_literal_custom_delimiters) do
|
102
|
+
ss_bq(true).capture(:bq).as(:ss_bq) >>
|
103
|
+
dynamic do |_, c|
|
104
|
+
ss_eq(c.captures[:bq].to_s[0..-2]).as(:ss_eq)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
rule(:ss_literal_custom_delimiters) do
|
109
|
+
ss_bq(true).capture(:bq).as(:ss_bq) >>
|
110
|
+
dynamic do |_, c|
|
111
|
+
pounds = c.captures[:bq].to_s[0..-2]
|
112
|
+
ss_portions(pounds).as(:ss_portions) >> ss_eq(pounds).as(:ss_eq)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
rule(:ms_empty_literal) do
|
117
|
+
ms_bq(false).as(:ms_bq) >> ms_eq('', false).as(:ms_eq)
|
118
|
+
end
|
119
|
+
|
120
|
+
rule(:ms_literal) do
|
121
|
+
ms_bq(false).as(:ms_bq) >>
|
122
|
+
ms_portions('').as(:ms_portions) >> ms_eq('', false).as(:ms_eq)
|
123
|
+
end
|
124
|
+
|
125
|
+
rule(:ms_empty_literal_custom_delimiters) do
|
126
|
+
ms_bq(true).capture(:bq).as(:ms_bq) >>
|
127
|
+
dynamic do |_, c|
|
128
|
+
ms_eq(c.captures[:bq].to_s[0..-4], false).as(:ms_eq)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
rule(:ms_literal_custom_delimiters) do
|
133
|
+
ms_bq(true).capture(:bq).as(:ms_bq) >>
|
134
|
+
dynamic do |_, c|
|
135
|
+
pounds = c.captures[:bq].to_s[0..-4]
|
136
|
+
ms_portions(pounds).as(:ms_portions) >> ms_eq(pounds, false).as(:ms_eq)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
rule(:string_literal) do
|
141
|
+
[
|
142
|
+
ms_empty_literal_custom_delimiters, ms_literal_custom_delimiters,
|
143
|
+
ms_empty_literal, ms_literal,
|
144
|
+
ss_empty_literal_custom_delimiters, ss_literal_custom_delimiters,
|
145
|
+
ss_empty_literal, ss_literal
|
146
|
+
].inject(:|)
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def interplation(pounds)
|
152
|
+
str("\\#{pounds}") >> bracketed(expression, '(', ')')
|
153
|
+
end
|
154
|
+
|
155
|
+
def unicode_char(pounds)
|
156
|
+
str("\\#{pounds}u") >> str('{') >> match('[\h]').repeat(1) >> str('}')
|
157
|
+
end
|
158
|
+
|
159
|
+
def escaped_char(pounds)
|
160
|
+
str("\\#{pounds}") >> match('[^\(u]')
|
161
|
+
end
|
162
|
+
|
163
|
+
def ss_char(pounds)
|
164
|
+
(nl | str("\\#{pounds}") | ss_eq(pounds)).absent? >> any
|
165
|
+
end
|
166
|
+
|
167
|
+
def ss_string(pounds)
|
168
|
+
(unicode_char(pounds) | escaped_char(pounds) | ss_char(pounds)).repeat(1)
|
169
|
+
end
|
170
|
+
|
171
|
+
def ss_portions(pounds)
|
172
|
+
(
|
173
|
+
interplation(pounds).as(:interplation) |
|
174
|
+
ss_string(pounds).as(:ss_string)
|
175
|
+
).repeat(1)
|
176
|
+
end
|
177
|
+
|
178
|
+
def ss_bq(custom)
|
179
|
+
if custom
|
180
|
+
str('#').repeat(1) >> str('"')
|
181
|
+
else
|
182
|
+
str('"')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def ss_eq(pounds)
|
187
|
+
str("\"#{pounds}")
|
188
|
+
end
|
189
|
+
|
190
|
+
def ms_char(pounds)
|
191
|
+
(nl | str("\\#{pounds}") | ms_eq(pounds, true)).absent? >> any
|
192
|
+
end
|
193
|
+
|
194
|
+
def ms_string(pounds)
|
195
|
+
(unicode_char(pounds) | escaped_char(pounds) | ms_char(pounds)).repeat(1)
|
196
|
+
end
|
197
|
+
|
198
|
+
def ms_portion(pounds)
|
199
|
+
(
|
200
|
+
interplation(pounds).as(:interplation) |
|
201
|
+
ms_string(pounds).as(:ms_string)
|
202
|
+
).repeat(1)
|
203
|
+
end
|
204
|
+
|
205
|
+
def ms_portions(pounds)
|
206
|
+
(ms_portion(pounds).maybe >> nl.as(:ms_nl)).repeat(1)
|
207
|
+
end
|
208
|
+
|
209
|
+
def ms_bq(custom)
|
210
|
+
if custom
|
211
|
+
str('#').repeat(1) >> str('"""') >> nl.ignore
|
212
|
+
else
|
213
|
+
str('"""') >> nl.ignore
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def ms_eq(pounds, pattern_only)
|
218
|
+
if pattern_only
|
219
|
+
str('"""'"#{pounds}")
|
220
|
+
else
|
221
|
+
match('[ \t]').repeat >> str('"""'"#{pounds}")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
define_transform do
|
227
|
+
rule(ss_bq: simple(:bq), ss_eq: simple(:eq)) do
|
228
|
+
Node::String.new(nil, nil, node_position(bq))
|
229
|
+
end
|
230
|
+
|
231
|
+
rule(ss_bq: simple(:bq), ss_portions: subtree(:portions), ss_eq: simple(:eq)) do
|
232
|
+
Node::String.new(nil, process_ss_portions(portions, bq), node_position(bq))
|
233
|
+
end
|
234
|
+
|
235
|
+
rule(ms_bq: simple(:bq), ms_eq: simple(:eq)) do
|
236
|
+
Node::String.new(nil, nil, node_position(bq))
|
237
|
+
end
|
238
|
+
|
239
|
+
rule(ms_bq: simple(:bq), ms_portions: subtree(:portions), ms_eq: simple(:eq)) do
|
240
|
+
Node::String.new(nil, process_ms_portions(portions, bq, eq), node_position(bq))
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
def process_ss_portions(portions, ss_bq)
|
246
|
+
pounds = ss_bq.to_s[0..-2]
|
247
|
+
portions.map { process_sring_portion(_1.first, pounds) }
|
248
|
+
end
|
249
|
+
|
250
|
+
def process_ms_portions(portions, ms_bq, ms_eq)
|
251
|
+
pounds = ms_bq.to_s[0..-4]
|
252
|
+
portions
|
253
|
+
.flat_map(&:to_a)
|
254
|
+
.then { _1[0..-2] }
|
255
|
+
.then { trim_leading_shapes(_1, ms_eq) }
|
256
|
+
.map { process_sring_portion(_1, pounds) }
|
257
|
+
end
|
258
|
+
|
259
|
+
def trim_leading_shapes(portins, ms_eq)
|
260
|
+
prefix = ms_eq.to_s[/^[ \t]+/]
|
261
|
+
return portins unless prefix
|
262
|
+
|
263
|
+
portins.map do |t, s|
|
264
|
+
if include_bol?(s)
|
265
|
+
[t, s.to_s.delete_prefix(prefix)]
|
266
|
+
else
|
267
|
+
[t, s]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def include_bol?(string)
|
273
|
+
_, column = string.line_and_column
|
274
|
+
column == 1
|
275
|
+
end
|
276
|
+
|
277
|
+
def process_sring_portion(portion, pounds)
|
278
|
+
type, string = portion
|
279
|
+
case type
|
280
|
+
when :ss_string, :ms_string then unescape_string(string, pounds)
|
281
|
+
when :ms_nl then "\n"
|
282
|
+
when :interplation then string
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def unescape_string(string, pounds)
|
287
|
+
string
|
288
|
+
.to_s
|
289
|
+
.then { unescape_unicode(_1, pounds) }
|
290
|
+
.then { unescape_char(_1, pounds, string) }
|
291
|
+
end
|
292
|
+
|
293
|
+
def unescape_unicode(string, pounds)
|
294
|
+
re = /\\#{pounds}u\{([\h]+)\}/
|
295
|
+
string.gsub(re) { Regexp.last_match(1).to_i(16).chr(Encoding::UTF_8) }
|
296
|
+
end
|
297
|
+
|
298
|
+
def unescape_char(string, pounds, node)
|
299
|
+
string.gsub(/(\\#{pounds}.)/) do |m|
|
300
|
+
ESCAPED_CHARS[m[-1]] ||
|
301
|
+
begin
|
302
|
+
message = "invalid escape sequence is given: #{m}"
|
303
|
+
parse_error(message, node_position(node))
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
class Parser
|
5
|
+
WS_PATTERN = '[ \t\f\r\n;]'
|
6
|
+
|
7
|
+
define_parser do
|
8
|
+
rule(:line_comment) do
|
9
|
+
str('//') >> match('[^\n]').repeat >> (nl | eof)
|
10
|
+
end
|
11
|
+
|
12
|
+
rule(:block_comment) do
|
13
|
+
str('/*') >> (block_comment | (str('*/').absent? >> any)).repeat >> str('*/')
|
14
|
+
end
|
15
|
+
|
16
|
+
rule(:comment) do
|
17
|
+
line_comment | block_comment
|
18
|
+
end
|
19
|
+
|
20
|
+
rule(:nl) do
|
21
|
+
match('\n')
|
22
|
+
end
|
23
|
+
|
24
|
+
rule(:eof) do
|
25
|
+
any.absent?
|
26
|
+
end
|
27
|
+
|
28
|
+
rule(:ws) do
|
29
|
+
(match(WS_PATTERN) | comment).repeat(1).ignore
|
30
|
+
end
|
31
|
+
|
32
|
+
rule(:ws?) do
|
33
|
+
(match(WS_PATTERN) | comment).repeat.ignore
|
34
|
+
end
|
35
|
+
|
36
|
+
rule(:pure_ws?) do
|
37
|
+
(match('[ \t\f]') | comment).repeat.ignore
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def bracketed(atom, bra = '(', cket = ')')
|
43
|
+
bra_matcher, cket_matcher =
|
44
|
+
[bra, cket]
|
45
|
+
.map { _1.is_a?(String) && str(_1).ignore || _1 }
|
46
|
+
bra_matcher >> ws? >> atom >> ws? >> cket_matcher
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
class Parser
|
5
|
+
define_parser do
|
6
|
+
rule(:object) do
|
7
|
+
bracketed(
|
8
|
+
object_members.as(:members).maybe,
|
9
|
+
str('{').as(:start), '}'
|
10
|
+
).as(:object)
|
11
|
+
end
|
12
|
+
|
13
|
+
rule(:object_members) do
|
14
|
+
object_member >> (ws >> object_member).repeat
|
15
|
+
end
|
16
|
+
|
17
|
+
rule(:object_member) do
|
18
|
+
object_property | object_entry | object_element
|
19
|
+
end
|
20
|
+
|
21
|
+
rule(:object_property) do
|
22
|
+
(
|
23
|
+
id.as(:name) >> ws? >>
|
24
|
+
(
|
25
|
+
(str('=').ignore >> ws? >> expression.as(:value)) |
|
26
|
+
(object >> (ws? >> object).repeat).as(:objects)
|
27
|
+
)
|
28
|
+
).as(:object_property)
|
29
|
+
end
|
30
|
+
|
31
|
+
rule(:object_entry) do
|
32
|
+
(
|
33
|
+
bracketed(expression.as(:key), '[', ']') >> ws? >>
|
34
|
+
(
|
35
|
+
(str('=').ignore >> ws? >> expression.as(:value)) |
|
36
|
+
(object >> (ws? >> object).repeat).as(:objects)
|
37
|
+
)
|
38
|
+
).as(:object_entry)
|
39
|
+
end
|
40
|
+
|
41
|
+
rule(:object_element) do
|
42
|
+
expression
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
define_transform do
|
47
|
+
rule(object: { start: simple(:s) }) do
|
48
|
+
Node::UnresolvedObject.new(nil, node_position(s))
|
49
|
+
end
|
50
|
+
|
51
|
+
rule(object: { start: simple(:s), members: subtree(:m) }) do
|
52
|
+
Node::UnresolvedObject.new(Array(m), node_position(s))
|
53
|
+
end
|
54
|
+
|
55
|
+
rule(object_property: { name: simple(:n), value: simple(:v) }) do
|
56
|
+
Node::ObjectProperty.new(n, v, nil, n.position)
|
57
|
+
end
|
58
|
+
|
59
|
+
rule(object_property: { name: simple(:n), objects: subtree(:o) }) do
|
60
|
+
Node::ObjectProperty.new(n, nil, Array(o), n.position)
|
61
|
+
end
|
62
|
+
|
63
|
+
rule(object_entry: { key: simple(:k), value: simple(:v) }) do
|
64
|
+
Node::ObjectEntry.new(k, v, nil, k.position)
|
65
|
+
end
|
66
|
+
|
67
|
+
rule(object_entry: { key: simple(:k), objects: subtree(:o) }) do
|
68
|
+
Node::ObjectEntry.new(k, nil, Array(o), k.position)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
class Parser
|
5
|
+
define_parser do
|
6
|
+
rule(:pkl_class_property) do
|
7
|
+
(
|
8
|
+
id.as(:name) >> ws? >>
|
9
|
+
(
|
10
|
+
(str('=').ignore >> ws? >> expression.as(:value)) |
|
11
|
+
(object >> (ws? >> object).repeat).as(:objects)
|
12
|
+
)
|
13
|
+
).as(:class_property)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
define_transform do
|
18
|
+
rule(class_property: { name: simple(:n), value: simple(:v) }) do
|
19
|
+
Node::PklClassProperty.new(n, v, nil, n.position)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
define_transform do
|
24
|
+
rule(class_property: { name: simple(:n), objects: subtree(:o) }) do
|
25
|
+
Node::PklClassProperty.new(n, nil, Array(o), n.position)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
class Parser
|
5
|
+
define_parser do
|
6
|
+
rule(:pkl_module) do
|
7
|
+
(
|
8
|
+
ws? >> pkl_module_items.as(:items).maybe >> ws?
|
9
|
+
).as(:pkl_module)
|
10
|
+
end
|
11
|
+
|
12
|
+
rule(:pkl_module_items) do
|
13
|
+
pkl_module_item >> (ws >> pkl_module_item).repeat
|
14
|
+
end
|
15
|
+
|
16
|
+
rule(:pkl_module_item) do
|
17
|
+
pkl_class_property
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
define_transform do
|
22
|
+
rule(pkl_module: simple(:_)) do
|
23
|
+
Node::PklModule.new(nil, sof_position)
|
24
|
+
end
|
25
|
+
|
26
|
+
rule(pkl_module: { items: subtree(:items) }) do
|
27
|
+
Array(items)
|
28
|
+
.then { Node::PklModule.new(_1, _1.first.position) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/rupkl/parser.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
class Parser
|
5
|
+
Position = Struct.new(:filename, :line, :column)
|
6
|
+
|
7
|
+
class Parser < Parslet::Parser
|
8
|
+
def parse(io, filename: nil, root: nil)
|
9
|
+
root_parser(root).parse(io)
|
10
|
+
rescue Parslet::ParseFailed => e
|
11
|
+
raise_parse_error(e, filename)
|
12
|
+
end
|
13
|
+
|
14
|
+
def root_parser(root)
|
15
|
+
root && __send__(root) || __send__(:root)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def raise_parse_error(error, filename)
|
21
|
+
cause = error.parse_failure_cause
|
22
|
+
pos = create_error_pos(cause, filename)
|
23
|
+
message = compose_error_message(cause)
|
24
|
+
raise ParseError.new(message, pos, cause)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_error_pos(cause, filename)
|
28
|
+
Position.new(filename, *cause.source.line_and_column(cause.pos))
|
29
|
+
end
|
30
|
+
|
31
|
+
def compose_error_message(cause)
|
32
|
+
Array(cause.message)
|
33
|
+
.map { |m| m.respond_to?(:to_slice) ? m.str.inspect : m.to_s }
|
34
|
+
.join
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Context < Parslet::Context
|
39
|
+
def initialize(bindings, transform)
|
40
|
+
super(bindings)
|
41
|
+
@__transform = transform
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(method, ...)
|
45
|
+
if @__transform.respond_to?(method, true)
|
46
|
+
__define_delegator__(method)
|
47
|
+
__send__(method, ...)
|
48
|
+
else
|
49
|
+
# :nocov:
|
50
|
+
super
|
51
|
+
# :nocov:
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def respond_to_missing?(method, include_private)
|
56
|
+
# :nocov:
|
57
|
+
super || @__transform.respond_to?(method, include_private)
|
58
|
+
# :nocov:
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def __define_delegator__(method)
|
64
|
+
self.class.class_eval(<<~M, __FILE__, __LINE__ + 1)
|
65
|
+
# def foo(...)
|
66
|
+
# @__transform.__send__(:foo, ...)
|
67
|
+
# end
|
68
|
+
def #{method}(...)
|
69
|
+
@__transform.__send__(:#{method}, ...)
|
70
|
+
end
|
71
|
+
M
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Transform < Parslet::Transform
|
76
|
+
def apply(obj, context = nil, filename: nil)
|
77
|
+
@filename = filename if filename
|
78
|
+
super(obj, context)
|
79
|
+
end
|
80
|
+
|
81
|
+
def call_on_match(bindings, block)
|
82
|
+
return unless block
|
83
|
+
|
84
|
+
context = Context.new(bindings, self)
|
85
|
+
context.instance_exec(&block)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def node_position(node)
|
91
|
+
Position.new(@filename, *node.line_and_column)
|
92
|
+
end
|
93
|
+
|
94
|
+
def sof_position
|
95
|
+
Position.new(@filename, 1, 1)
|
96
|
+
end
|
97
|
+
|
98
|
+
def parse_error(message, position)
|
99
|
+
raise ParseError.new(message, position, nil)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class << self
|
104
|
+
private
|
105
|
+
|
106
|
+
def define_parser(&body)
|
107
|
+
Parser.class_eval(&body)
|
108
|
+
end
|
109
|
+
|
110
|
+
def define_transform(&body)
|
111
|
+
Transform.class_eval(&body)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse(string, filename: nil, root: nil)
|
116
|
+
tree = parse_string(string, filename, root)
|
117
|
+
transform_tree(tree, filename)
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def parse_string(string, filename, root)
|
123
|
+
parser.parse(string, filename: filename, root: root)
|
124
|
+
end
|
125
|
+
|
126
|
+
def parser
|
127
|
+
@parser ||= Parser.new
|
128
|
+
end
|
129
|
+
|
130
|
+
def transform_tree(tree, filename)
|
131
|
+
transform.apply(tree, filename: filename)
|
132
|
+
end
|
133
|
+
|
134
|
+
def transform
|
135
|
+
@transform ||= Transform.new
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|