rupkl 0.1.0 → 0.2.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 +4 -4
- data/README.md +1 -1
- data/lib/rupkl/node/amend_expression.rb +48 -0
- data/lib/rupkl/node/any.rb +99 -0
- data/lib/rupkl/node/base.rb +45 -0
- data/lib/rupkl/node/boolean.rb +12 -4
- data/lib/rupkl/node/context.rb +27 -0
- data/lib/rupkl/node/declared_type.rb +32 -0
- data/lib/rupkl/node/dynamic.rb +28 -59
- data/lib/rupkl/node/identifier.rb +16 -6
- data/lib/rupkl/node/listing.rb +61 -0
- data/lib/rupkl/node/mapping.rb +47 -0
- data/lib/rupkl/node/member_finder.rb +55 -0
- data/lib/rupkl/node/member_reference.rb +44 -21
- data/lib/rupkl/node/method_call.rb +64 -0
- data/lib/rupkl/node/method_definition.rb +143 -0
- data/lib/rupkl/node/node_common.rb +76 -0
- data/lib/rupkl/node/null.rb +27 -0
- data/lib/rupkl/node/number.rb +136 -6
- data/lib/rupkl/node/object.rb +369 -73
- data/lib/rupkl/node/operation.rb +97 -60
- data/lib/rupkl/node/pkl_module.rb +16 -28
- data/lib/rupkl/node/reference_resolver.rb +79 -0
- data/lib/rupkl/node/string.rb +388 -18
- data/lib/rupkl/node/struct_common.rb +119 -57
- data/lib/rupkl/node/this.rb +17 -0
- data/lib/rupkl/node/type_common.rb +34 -0
- data/lib/rupkl/node/value_common.rb +18 -9
- data/lib/rupkl/parser/expression.rb +158 -41
- data/lib/rupkl/parser/identifier.rb +2 -2
- data/lib/rupkl/parser/literal.rb +18 -12
- data/lib/rupkl/parser/method.rb +41 -0
- data/lib/rupkl/parser/misc.rb +4 -0
- data/lib/rupkl/parser/object.rb +57 -25
- data/lib/rupkl/parser/pkl_class.rb +28 -8
- data/lib/rupkl/parser/pkl_module.rb +5 -3
- data/lib/rupkl/parser/type.rb +28 -0
- data/lib/rupkl/parser.rb +8 -0
- data/lib/rupkl/pkl_object.rb +26 -6
- data/lib/rupkl/version.rb +1 -1
- data/lib/rupkl.rb +25 -4
- metadata +33 -3
- data/lib/rupkl/node/pkl_class.rb +0 -30
data/lib/rupkl/node/string.rb
CHANGED
@@ -2,25 +2,41 @@
|
|
2
2
|
|
3
3
|
module RuPkl
|
4
4
|
module Node
|
5
|
-
class String
|
5
|
+
class String < Any
|
6
6
|
include ValueCommon
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
uninstantiable_class
|
9
|
+
|
10
|
+
def initialize(parent, value, portions, position)
|
11
|
+
super(parent, value, position)
|
10
12
|
@portions = portions
|
13
|
+
portions&.each do |portion|
|
14
|
+
portion.is_a?(NodeCommon) && add_child(portion)
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
attr_reader :portions
|
14
19
|
|
15
|
-
def evaluate(
|
16
|
-
|
17
|
-
self
|
20
|
+
def evaluate(context = nil)
|
21
|
+
@value ||= evaluate_portions(context)
|
22
|
+
self
|
18
23
|
end
|
19
24
|
|
20
|
-
def to_pkl_string(
|
21
|
-
|
22
|
-
|
23
|
-
|
25
|
+
def to_pkl_string(context = nil)
|
26
|
+
s = to_ruby(context)
|
27
|
+
if invalid_string?(s)
|
28
|
+
s
|
29
|
+
else
|
30
|
+
"\"#{escape(s)}\""
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def copy(parent = nil)
|
35
|
+
copied_portions =
|
36
|
+
portions&.map do |portion|
|
37
|
+
portion.is_a?(NodeCommon) && portion.copy || portion
|
38
|
+
end
|
39
|
+
self.class.new(parent, nil, copied_portions, position)
|
24
40
|
end
|
25
41
|
|
26
42
|
def undefined_operator?(operator)
|
@@ -28,27 +44,308 @@ module RuPkl
|
|
28
44
|
end
|
29
45
|
|
30
46
|
def invalid_key_operand?(key)
|
31
|
-
!key.is_a?(
|
47
|
+
!key.is_a?(Int)
|
32
48
|
end
|
33
49
|
|
34
50
|
def find_by_key(key)
|
35
51
|
index = key.value
|
36
52
|
return nil unless (0...value.length).include?(index)
|
37
53
|
|
38
|
-
self.class.new(value[index], nil,
|
54
|
+
self.class.new(parent, value[index], nil, position)
|
55
|
+
end
|
56
|
+
|
57
|
+
define_builtin_property(:length) do
|
58
|
+
Int.new(self, value.length, position)
|
59
|
+
end
|
60
|
+
|
61
|
+
define_builtin_property(:lastIndex) do
|
62
|
+
Int.new(self, value.length - 1, position)
|
63
|
+
end
|
64
|
+
|
65
|
+
define_builtin_property(:isEmpty) do
|
66
|
+
Boolean.new(self, value.empty?, position)
|
67
|
+
end
|
68
|
+
|
69
|
+
define_builtin_property(:isBlank) do
|
70
|
+
result = /\A\p{White_Space}*\z/.match?(value)
|
71
|
+
Boolean.new(self, result, position)
|
72
|
+
end
|
73
|
+
|
74
|
+
define_builtin_property(:md5) do
|
75
|
+
hash = Digest::MD5.hexdigest(value)
|
76
|
+
String.new(self, hash, nil, position)
|
77
|
+
end
|
78
|
+
|
79
|
+
define_builtin_property(:sha1) do
|
80
|
+
hash = Digest::SHA1.hexdigest(value)
|
81
|
+
String.new(self, hash, nil, position)
|
82
|
+
end
|
83
|
+
|
84
|
+
define_builtin_property(:sha256) do
|
85
|
+
hash = Digest::SHA256.hexdigest(value)
|
86
|
+
String.new(self, hash, nil, position)
|
87
|
+
end
|
88
|
+
|
89
|
+
define_builtin_property(:sha256Int) do
|
90
|
+
hash = Digest::SHA256.digest(value).unpack1('q')
|
91
|
+
Int.new(self, hash, position)
|
92
|
+
end
|
93
|
+
|
94
|
+
define_builtin_property(:base64) do
|
95
|
+
result = Base64.urlsafe_encode64(value)
|
96
|
+
String.new(self, result, nil, position)
|
97
|
+
end
|
98
|
+
|
99
|
+
define_builtin_property(:base64Decoded) do
|
100
|
+
result = Base64.urlsafe_decode64(value)
|
101
|
+
String.new(self, result, nil, position)
|
102
|
+
rescue ArgumentError
|
103
|
+
message = "illegal base64: \"#{value}\""
|
104
|
+
raise EvaluationError.new(message, position)
|
105
|
+
end
|
106
|
+
|
107
|
+
define_builtin_method(:getOrNull, index: Int) do |index|
|
108
|
+
if (0...value.size).include?(index.value)
|
109
|
+
String.new(nil, value[index.value], nil, position)
|
110
|
+
else
|
111
|
+
Null.new(nil, position)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
define_builtin_method(:substring, start: Int, exclusive_end: Int) do |s, e|
|
116
|
+
check_range(s.value, 0)
|
117
|
+
check_range(e.value, s.value)
|
118
|
+
|
119
|
+
String.new(nil, value[s.value...e.value], nil, position)
|
120
|
+
end
|
121
|
+
|
122
|
+
define_builtin_method(:substringOrNull, start: Int, exclusive_end: Int) do |s, e|
|
123
|
+
if inside_range?(s.value, 0) && inside_range?(e.value, s.value)
|
124
|
+
String.new(nil, value[s.value...e.value], nil, position)
|
125
|
+
else
|
126
|
+
Null.new(nil, position)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
define_builtin_method(:repeat, count: Int) do |count|
|
131
|
+
check_positive_number(count)
|
132
|
+
|
133
|
+
result = value * count.value
|
134
|
+
String.new(nil, result, nil, position)
|
135
|
+
end
|
136
|
+
|
137
|
+
define_builtin_method(:contains, pattern: String) do |pattern|
|
138
|
+
result = value.include?(pattern.value)
|
139
|
+
Boolean.new(nil, result, position)
|
140
|
+
end
|
141
|
+
|
142
|
+
define_builtin_method(:startsWith, pattern: String) do |pattern|
|
143
|
+
result = value.start_with?(pattern.value)
|
144
|
+
Boolean.new(nil, result, position)
|
145
|
+
end
|
146
|
+
|
147
|
+
define_builtin_method(:endsWith, pattern: String) do |pattern|
|
148
|
+
result = value.end_with?(pattern.value)
|
149
|
+
Boolean.new(nil, result, position)
|
150
|
+
end
|
151
|
+
|
152
|
+
define_builtin_method(:indexOf, pattern: String) do |pattern|
|
153
|
+
index_of(:index, pattern) do
|
154
|
+
message = "\"#{pattern.value}\" does not occur in \"#{value}\""
|
155
|
+
raise EvaluationError.new(message, position)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
define_builtin_method(:indexOfOrNull, pattern: String) do |pattern|
|
160
|
+
index_of(:index, pattern) do
|
161
|
+
Null.new(nil, position)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
define_builtin_method(:lastIndexOf, pattern: String) do |pattern|
|
166
|
+
index_of(:rindex, pattern) do
|
167
|
+
message = "\"#{pattern.value}\" does not occur in \"#{value}\""
|
168
|
+
raise EvaluationError.new(message, position)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
define_builtin_method(:lastIndexOfOrNull, pattern: String) do |pattern|
|
173
|
+
index_of(:rindex, pattern) do
|
174
|
+
Null.new(nil, position)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
define_builtin_method(:take, n: Int) do |n|
|
179
|
+
check_positive_number(n)
|
180
|
+
|
181
|
+
result = value[0, n.value] || value
|
182
|
+
String.new(nil, result, nil, position)
|
183
|
+
end
|
184
|
+
|
185
|
+
define_builtin_method(:takeLast, n: Int) do |n|
|
186
|
+
check_positive_number(n)
|
187
|
+
|
188
|
+
pos = value.size - n.value
|
189
|
+
result = pos.negative? && value || value[pos..]
|
190
|
+
String.new(nil, result, nil, position)
|
191
|
+
end
|
192
|
+
|
193
|
+
define_builtin_method(:drop, n: Int) do |n|
|
194
|
+
check_positive_number(n)
|
195
|
+
|
196
|
+
result = value[n.value..] || ''
|
197
|
+
String.new(nil, result, nil, position)
|
198
|
+
end
|
199
|
+
|
200
|
+
define_builtin_method(:dropLast, n: Int) do |n|
|
201
|
+
check_positive_number(n)
|
202
|
+
|
203
|
+
length = value.size - n.value
|
204
|
+
result = length.negative? && '' || value[0, length]
|
205
|
+
String.new(nil, result, nil, position)
|
206
|
+
end
|
207
|
+
|
208
|
+
define_builtin_method(
|
209
|
+
:replaceFirst,
|
210
|
+
pattern: String, replacement: String
|
211
|
+
) do |pattern, replacement|
|
212
|
+
result = value.sub(pattern.value, replacement.value)
|
213
|
+
String.new(nil, result, nil, position)
|
214
|
+
end
|
215
|
+
|
216
|
+
define_builtin_method(
|
217
|
+
:replaceLast,
|
218
|
+
pattern: String, replacement: String
|
219
|
+
) do |pattern, replacement|
|
220
|
+
result =
|
221
|
+
if (index = value.rindex(pattern.value))
|
222
|
+
value.dup.tap { |s| s[index, replacement.value.size] = replacement.value }
|
223
|
+
else
|
224
|
+
value
|
225
|
+
end
|
226
|
+
String.new(nil, result, nil, position)
|
227
|
+
end
|
228
|
+
|
229
|
+
define_builtin_method(
|
230
|
+
:replaceAll,
|
231
|
+
pattern: String, replacement: String
|
232
|
+
) do |pattern, replacement|
|
233
|
+
result = value.gsub(pattern.value, replacement.value)
|
234
|
+
String.new(nil, result, nil, position)
|
235
|
+
end
|
236
|
+
|
237
|
+
define_builtin_method(
|
238
|
+
:replaceRange,
|
239
|
+
start: Int, exclusive_end: Int, replacement: String
|
240
|
+
) do |start, exclusive_end, replacement|
|
241
|
+
check_range(start.value, 0)
|
242
|
+
check_range(exclusive_end.value, start.value)
|
243
|
+
|
244
|
+
range = start.value...exclusive_end.value
|
245
|
+
result = value.dup.tap { |s| s[range] = replacement.value }
|
246
|
+
String.new(nil, result, nil, position)
|
247
|
+
end
|
248
|
+
|
249
|
+
define_builtin_method(:toUpperCase) do
|
250
|
+
String.new(nil, value.upcase, nil, position)
|
251
|
+
end
|
252
|
+
|
253
|
+
define_builtin_method(:toLowerCase) do
|
254
|
+
String.new(nil, value.downcase, nil, position)
|
255
|
+
end
|
256
|
+
|
257
|
+
define_builtin_method(:reverse) do
|
258
|
+
String.new(nil, value.reverse, nil, position)
|
259
|
+
end
|
260
|
+
|
261
|
+
define_builtin_method(:trim) do
|
262
|
+
pattern = /(?:\A\p{White_Space}+)|(?:\p{White_Space}+\z)/
|
263
|
+
String.new(nil, value.gsub(pattern, ''), nil, position)
|
264
|
+
end
|
265
|
+
|
266
|
+
define_builtin_method(:trimStart) do
|
267
|
+
pattern = /\A\p{White_Space}+/
|
268
|
+
String.new(nil, value.sub(pattern, ''), nil, position)
|
269
|
+
end
|
270
|
+
|
271
|
+
define_builtin_method(:trimEnd) do
|
272
|
+
pattern = /\p{White_Space}+\z/
|
273
|
+
String.new(nil, value.sub(pattern, ''), nil, position)
|
274
|
+
end
|
275
|
+
|
276
|
+
define_builtin_method(:padStart, width: Int, char: String) do |width, char|
|
277
|
+
pad(width, char, :pre)
|
278
|
+
end
|
279
|
+
|
280
|
+
define_builtin_method(:padEnd, width: Int, char: String) do |width, char|
|
281
|
+
pad(width, char, :post)
|
282
|
+
end
|
283
|
+
|
284
|
+
define_builtin_method(:capitalize) do
|
285
|
+
result =
|
286
|
+
value.empty? && value || value.dup.tap { |s| s[0] = s[0].upcase }
|
287
|
+
String.new(nil, result, nil, position)
|
288
|
+
end
|
289
|
+
|
290
|
+
define_builtin_method(:decapitalize) do
|
291
|
+
result =
|
292
|
+
value.empty? && value || value.dup.tap { |s| s[0] = s[0].downcase }
|
293
|
+
String.new(nil, result, nil, position)
|
294
|
+
end
|
295
|
+
|
296
|
+
define_builtin_method(:toInt) do
|
297
|
+
to_int do
|
298
|
+
message = "cannot parse string as Int \"#{value}\""
|
299
|
+
raise EvaluationError.new(message, position)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
define_builtin_method(:toIntOrNull) do
|
304
|
+
to_int do
|
305
|
+
Null.new(nil, position)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
define_builtin_method(:toFloat) do
|
310
|
+
to_float do
|
311
|
+
message = "cannot parse string as Float \"#{value}\""
|
312
|
+
raise EvaluationError.new(message, position)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
define_builtin_method(:toFloatOrNull) do
|
317
|
+
to_float do
|
318
|
+
Null.new(nil, position)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
define_builtin_method(:toBoolean) do
|
323
|
+
to_boolean do
|
324
|
+
message = "cannot parse string as Boolean \"#{value}\""
|
325
|
+
raise EvaluationError.new(message, position)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
define_builtin_method(:toBooleanOrNull) do
|
330
|
+
to_boolean do
|
331
|
+
Null.new(nil, position)
|
332
|
+
end
|
39
333
|
end
|
40
334
|
|
41
335
|
private
|
42
336
|
|
43
|
-
def evaluate_portions(
|
44
|
-
|
45
|
-
&.map
|
46
|
-
|
337
|
+
def evaluate_portions(context)
|
338
|
+
evaluated_portions =
|
339
|
+
portions&.map do |portion|
|
340
|
+
evaluate_portion(portion, context)
|
341
|
+
.tap { |s| return s if invalid_string?(s) }
|
342
|
+
end
|
343
|
+
evaluated_portions&.join || ''
|
47
344
|
end
|
48
345
|
|
49
|
-
def evaluate_portion(
|
346
|
+
def evaluate_portion(portion, context)
|
50
347
|
if portion.respond_to?(:to_string)
|
51
|
-
portion.to_string(
|
348
|
+
portion.to_string(context)
|
52
349
|
else
|
53
350
|
portion
|
54
351
|
end
|
@@ -61,6 +358,79 @@ module RuPkl
|
|
61
358
|
}
|
62
359
|
string.gsub(/([\t\n\r"\\])/) { replace[_1] }
|
63
360
|
end
|
361
|
+
|
362
|
+
def check_range(index, start_index)
|
363
|
+
return if inside_range?(index, start_index)
|
364
|
+
|
365
|
+
message = "index #{index} is out of range " \
|
366
|
+
"#{start_index}..#{value.size}: \"#{value}\""
|
367
|
+
raise EvaluationError.new(message, position)
|
368
|
+
end
|
369
|
+
|
370
|
+
def inside_range?(index, start_index)
|
371
|
+
(start_index..value.size).include?(index)
|
372
|
+
end
|
373
|
+
|
374
|
+
def index_of(method, pattern)
|
375
|
+
result = value.__send__(method, pattern.value)
|
376
|
+
result && Int.new(nil, result, position) || yield
|
377
|
+
end
|
378
|
+
|
379
|
+
def pad(width, char, pre_post)
|
380
|
+
unless char.value.length == 1
|
381
|
+
message = "expected a char, but got \"#{char.value}\""
|
382
|
+
raise EvaluationError.new(message, position)
|
383
|
+
end
|
384
|
+
|
385
|
+
result =
|
386
|
+
pad_prefix_postfix(width, char, pre_post)
|
387
|
+
.then { |pre, post| [pre, value, post].join }
|
388
|
+
String.new(nil, result, nil, position)
|
389
|
+
end
|
390
|
+
|
391
|
+
def pad_prefix_postfix(width, char, pre_post)
|
392
|
+
pad_width = width.value - value.length
|
393
|
+
if pad_width <= 0
|
394
|
+
[nil, nil]
|
395
|
+
elsif pre_post == :pre
|
396
|
+
[char.value * pad_width, nil]
|
397
|
+
else
|
398
|
+
[nil, char.value * pad_width]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def to_int
|
403
|
+
Int.new(nil, Integer(remove_underscore_from_number(value), 10), position)
|
404
|
+
rescue ArgumentError
|
405
|
+
yield
|
406
|
+
end
|
407
|
+
|
408
|
+
def to_float
|
409
|
+
result =
|
410
|
+
case value
|
411
|
+
when 'NaN' then ::Float::NAN
|
412
|
+
when 'Infinity' then ::Float::INFINITY
|
413
|
+
when '-Infinity' then -::Float::INFINITY
|
414
|
+
else Float(remove_underscore_from_number(value))
|
415
|
+
end
|
416
|
+
Float.new(nil, result, position)
|
417
|
+
rescue ArgumentError
|
418
|
+
yield
|
419
|
+
end
|
420
|
+
|
421
|
+
def remove_underscore_from_number(string)
|
422
|
+
string.gsub(/(?:(?<=\d)|(?<=.[eE][+-]))_+/, '')
|
423
|
+
end
|
424
|
+
|
425
|
+
def to_boolean
|
426
|
+
result =
|
427
|
+
case value
|
428
|
+
when /\Atrue\z/i then true
|
429
|
+
when /\Afalse\z/i then false
|
430
|
+
else return yield
|
431
|
+
end
|
432
|
+
Boolean.new(nil, result, position)
|
433
|
+
end
|
64
434
|
end
|
65
435
|
end
|
66
436
|
end
|
@@ -3,8 +3,68 @@
|
|
3
3
|
module RuPkl
|
4
4
|
module Node
|
5
5
|
module StructCommon
|
6
|
-
|
7
|
-
|
6
|
+
include NodeCommon
|
7
|
+
include MemberFinder
|
8
|
+
|
9
|
+
def initialize(parent, body, position)
|
10
|
+
super
|
11
|
+
@body = body
|
12
|
+
body && check_members
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :body
|
16
|
+
|
17
|
+
def evaluate(context = nil)
|
18
|
+
do_evaluation(__method__, context, 1) do |c|
|
19
|
+
@body&.evaluate(c)
|
20
|
+
end
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve_structure(context = nil)
|
25
|
+
do_evaluation(__method__, context, 1) do |c|
|
26
|
+
@body&.resolve_structure(c)
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_ruby(context = nil)
|
32
|
+
do_evaluation(__method__, context, 1, PklObject::SELF) do |c|
|
33
|
+
create_pkl_object(c)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_pkl_string(context = nil)
|
38
|
+
to_string(context)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_string(context = nil)
|
42
|
+
do_evaluation(__method__, context, 2, invalid_string) do |c|
|
43
|
+
to_string_object(c)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def current_context
|
48
|
+
super&.push_object(self) || Context.new(nil, [self])
|
49
|
+
end
|
50
|
+
|
51
|
+
def structure?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def copy(parent = nil)
|
56
|
+
self.class.new(parent, @body&.copy, position)
|
57
|
+
end
|
58
|
+
|
59
|
+
def merge!(*bodies)
|
60
|
+
return unless @body
|
61
|
+
|
62
|
+
@body.merge!(*bodies)
|
63
|
+
check_members
|
64
|
+
end
|
65
|
+
|
66
|
+
def undefined_operator?(operator)
|
67
|
+
[:[], :==, :'!='].none?(operator)
|
8
68
|
end
|
9
69
|
|
10
70
|
def coerce(_operator, r_operand)
|
@@ -13,26 +73,48 @@ module RuPkl
|
|
13
73
|
|
14
74
|
private
|
15
75
|
|
16
|
-
def
|
17
|
-
|
76
|
+
def check_members
|
77
|
+
message =
|
78
|
+
if properties_not_allowed?
|
79
|
+
"'#{self.class.basename}' cannot have a property"
|
80
|
+
elsif entries_not_allowed?
|
81
|
+
"'#{self.class.basename}' cannot have an entry"
|
82
|
+
elsif elements_not_allowed?
|
83
|
+
"'#{self.class.basename}' cannot have an element"
|
84
|
+
end
|
85
|
+
message &&
|
86
|
+
(raise EvaluationError.new(message, position))
|
18
87
|
end
|
19
88
|
|
20
|
-
def
|
21
|
-
|
22
|
-
begin
|
23
|
-
message = 'duplicate definition of member'
|
24
|
-
raise EvaluationError.new(message, member.position)
|
25
|
-
end
|
26
|
-
members << member
|
89
|
+
def properties_not_allowed?
|
90
|
+
body.properties
|
27
91
|
end
|
28
92
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
93
|
+
def entries_not_allowed?
|
94
|
+
body.entries
|
95
|
+
end
|
96
|
+
|
97
|
+
def elements_not_allowed?
|
98
|
+
body.elements
|
99
|
+
end
|
100
|
+
|
101
|
+
def do_evaluation(method, context, limit, ifreachlimit = nil)
|
102
|
+
return ifreachlimit if reach_limit?(method, limit)
|
103
|
+
|
104
|
+
call_depth[method] += 1
|
105
|
+
result = yield(context&.push_object(self) || current_context)
|
106
|
+
call_depth.delete(method)
|
107
|
+
|
108
|
+
result
|
32
109
|
end
|
33
110
|
|
34
|
-
def
|
35
|
-
|
111
|
+
def call_depth
|
112
|
+
@call_depth ||= Hash.new { |h, k| h[k] = 0 }
|
113
|
+
end
|
114
|
+
|
115
|
+
def reach_limit?(method, limit)
|
116
|
+
depth = call_depth[method]
|
117
|
+
depth >= 1 && depth >= limit
|
36
118
|
end
|
37
119
|
|
38
120
|
def match_members?(lhs, rhs, match_order)
|
@@ -44,58 +126,38 @@ module RuPkl
|
|
44
126
|
end
|
45
127
|
end
|
46
128
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
else
|
55
|
-
lhs << r
|
56
|
-
end
|
129
|
+
def create_pkl_object(context)
|
130
|
+
context.push_scope(@body).then do |c|
|
131
|
+
RuPkl::PklObject.new(
|
132
|
+
to_ruby_hash(@body.properties, c),
|
133
|
+
to_ruby_hash(@body.entries, c),
|
134
|
+
to_ruby_array(@body.elements, c)
|
135
|
+
)
|
57
136
|
end
|
58
|
-
|
59
|
-
lhs
|
60
137
|
end
|
61
138
|
|
62
|
-
def
|
63
|
-
|
139
|
+
def to_ruby_hash(members, context)
|
140
|
+
members&.to_h { _1.to_ruby(context) }
|
64
141
|
end
|
65
142
|
|
66
|
-
def
|
67
|
-
|
68
|
-
return rhs unless lhs
|
69
|
-
return lhs unless rhs
|
70
|
-
|
71
|
-
lhs.concat(rhs)
|
143
|
+
def to_ruby_array(members, context)
|
144
|
+
members&.map { _1.to_ruby(context) }
|
72
145
|
end
|
73
146
|
|
74
|
-
def
|
75
|
-
|
76
|
-
to_ruby_hash_members(scopes, properties),
|
77
|
-
to_ruby_hash_members(scopes, entries),
|
78
|
-
to_ruby_array_members(scopes, elements)
|
79
|
-
)
|
147
|
+
def to_string_object(context)
|
148
|
+
"new #{self.class.basename} #{to_string_members(context)}"
|
80
149
|
end
|
81
150
|
|
82
|
-
def
|
83
|
-
members
|
84
|
-
&.to_h { _1.to_ruby(scopes) }
|
85
|
-
end
|
86
|
-
|
87
|
-
def to_ruby_array_members(scopes, members)
|
88
|
-
members
|
89
|
-
&.map { _1.to_ruby(scopes) }
|
90
|
-
end
|
91
|
-
|
92
|
-
def to_pkl_string_object(*members)
|
151
|
+
def to_string_members(context)
|
152
|
+
members = @body.members
|
93
153
|
return '{}' if members.empty?
|
94
154
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
155
|
+
context.push_scope(@body).then do |c|
|
156
|
+
members
|
157
|
+
.map { _1.to_pkl_string(c) }
|
158
|
+
.join('; ')
|
159
|
+
.then { "{ #{_1} }" }
|
160
|
+
end
|
99
161
|
end
|
100
162
|
end
|
101
163
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
class This
|
6
|
+
include NodeCommon
|
7
|
+
|
8
|
+
def evaluate(context = nil)
|
9
|
+
resolve_reference(context)
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve_reference(context = nil)
|
13
|
+
(context || current_context).objects.last
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
module TypeCommon
|
6
|
+
include NodeCommon
|
7
|
+
|
8
|
+
def check_type(value, context, position)
|
9
|
+
klass = value.class
|
10
|
+
return if match_type?(klass, context)
|
11
|
+
|
12
|
+
message =
|
13
|
+
"expected type '#{self}', but got type '#{klass.class_name}'"
|
14
|
+
raise EvaluationError.new(message, position)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def find_type(type, context)
|
20
|
+
exec_on(context) do |c|
|
21
|
+
[Base.instance, *c&.objects]
|
22
|
+
.reverse_each do |scope|
|
23
|
+
next unless scope.respond_to?(:pkl_classes)
|
24
|
+
|
25
|
+
klass = scope.pkl_classes&.fetch(type.last.id, nil)
|
26
|
+
return klass if klass
|
27
|
+
end
|
28
|
+
|
29
|
+
raise EvaluationError.new("cannot find type '#{type.last.id}'", position)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|