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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/rupkl/node/amend_expression.rb +48 -0
  4. data/lib/rupkl/node/any.rb +99 -0
  5. data/lib/rupkl/node/base.rb +45 -0
  6. data/lib/rupkl/node/boolean.rb +12 -4
  7. data/lib/rupkl/node/context.rb +27 -0
  8. data/lib/rupkl/node/declared_type.rb +32 -0
  9. data/lib/rupkl/node/dynamic.rb +28 -59
  10. data/lib/rupkl/node/identifier.rb +16 -6
  11. data/lib/rupkl/node/listing.rb +61 -0
  12. data/lib/rupkl/node/mapping.rb +47 -0
  13. data/lib/rupkl/node/member_finder.rb +55 -0
  14. data/lib/rupkl/node/member_reference.rb +44 -21
  15. data/lib/rupkl/node/method_call.rb +64 -0
  16. data/lib/rupkl/node/method_definition.rb +143 -0
  17. data/lib/rupkl/node/node_common.rb +76 -0
  18. data/lib/rupkl/node/null.rb +27 -0
  19. data/lib/rupkl/node/number.rb +136 -6
  20. data/lib/rupkl/node/object.rb +369 -73
  21. data/lib/rupkl/node/operation.rb +97 -60
  22. data/lib/rupkl/node/pkl_module.rb +16 -28
  23. data/lib/rupkl/node/reference_resolver.rb +79 -0
  24. data/lib/rupkl/node/string.rb +388 -18
  25. data/lib/rupkl/node/struct_common.rb +119 -57
  26. data/lib/rupkl/node/this.rb +17 -0
  27. data/lib/rupkl/node/type_common.rb +34 -0
  28. data/lib/rupkl/node/value_common.rb +18 -9
  29. data/lib/rupkl/parser/expression.rb +158 -41
  30. data/lib/rupkl/parser/identifier.rb +2 -2
  31. data/lib/rupkl/parser/literal.rb +18 -12
  32. data/lib/rupkl/parser/method.rb +41 -0
  33. data/lib/rupkl/parser/misc.rb +4 -0
  34. data/lib/rupkl/parser/object.rb +57 -25
  35. data/lib/rupkl/parser/pkl_class.rb +28 -8
  36. data/lib/rupkl/parser/pkl_module.rb +5 -3
  37. data/lib/rupkl/parser/type.rb +28 -0
  38. data/lib/rupkl/parser.rb +8 -0
  39. data/lib/rupkl/pkl_object.rb +26 -6
  40. data/lib/rupkl/version.rb +1 -1
  41. data/lib/rupkl.rb +25 -4
  42. metadata +33 -3
  43. data/lib/rupkl/node/pkl_class.rb +0 -30
@@ -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
- def initialize(value, portions, position)
9
- super(value, position)
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(scopes)
16
- s = value || evaluate_portions(scopes) || ''
17
- self.class.new(s, nil, position)
20
+ def evaluate(context = nil)
21
+ @value ||= evaluate_portions(context)
22
+ self
18
23
  end
19
24
 
20
- def to_pkl_string(scopes)
21
- super(scopes)
22
- .then { |s| escape(s) }
23
- .then { |s| "\"#{s}\"" }
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?(Integer)
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, portions)
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(scopes)
44
- portions
45
- &.map { evaluate_portion(scopes, _1) }
46
- &.join
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(scopes, portion)
346
+ def evaluate_portion(portion, context)
50
347
  if portion.respond_to?(:to_string)
51
- portion.to_string(scopes)
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
- def to_string(_scopes)
7
- "new #{self.class.basename} #{to_pkl_string(nil)}"
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 push_scope(scopes)
17
- yield([*scopes, self])
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 add_hash_member(members, member, accessor)
21
- duplicate_member?(members, member, accessor) &&
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 duplicate_member?(members, member, accessor)
30
- members
31
- .any? { _1.__send__(accessor) == member.__send__(accessor) }
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 add_array_member(members, member)
35
- members << member
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 merge_hash_members(lhs, rhs, accessor)
48
- return nil unless lhs || rhs
49
- return rhs unless lhs
50
-
51
- rhs&.each do |r|
52
- if (index = find_index(lhs, r, accessor))
53
- lhs[index] = r
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 find_index(lhs, rhs, accessor)
63
- lhs.find_index { _1.__send__(accessor) == rhs.__send__(accessor) }
139
+ def to_ruby_hash(members, context)
140
+ members&.to_h { _1.to_ruby(context) }
64
141
  end
65
142
 
66
- def merge_array_members(lhs, rhs)
67
- return nil unless lhs || rhs
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 create_pkl_object(scopes, properties, entries, elements)
75
- RuPkl::PklObject.new(
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 to_ruby_hash_members(scopes, members)
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
- members
96
- .map { _1.to_pkl_string(nil) }
97
- .join('; ')
98
- .then { "{ #{_1} }" }
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