rupkl 0.2.0 → 0.3.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/lib/rupkl/node/amend_expression.rb +2 -2
- data/lib/rupkl/node/any.rb +15 -4
- data/lib/rupkl/node/base.rb +32 -2
- data/lib/rupkl/node/boolean.rb +26 -9
- data/lib/rupkl/node/collection.rb +176 -0
- data/lib/rupkl/node/data_size.rb +254 -0
- data/lib/rupkl/node/duration.rb +266 -0
- data/lib/rupkl/node/dynamic.rb +19 -15
- data/lib/rupkl/node/identifier.rb +5 -1
- data/lib/rupkl/node/if_expression.rb +45 -0
- data/lib/rupkl/node/intseq.rb +84 -0
- data/lib/rupkl/node/listing.rb +11 -4
- data/lib/rupkl/node/map.rb +120 -0
- data/lib/rupkl/node/mapping.rb +13 -6
- data/lib/rupkl/node/member_finder.rb +4 -10
- data/lib/rupkl/node/member_reference.rb +5 -3
- data/lib/rupkl/node/method_call.rb +2 -3
- data/lib/rupkl/node/method_definition.rb +74 -18
- data/lib/rupkl/node/node_common.rb +1 -1
- data/lib/rupkl/node/null.rb +1 -4
- data/lib/rupkl/node/number.rb +133 -45
- data/lib/rupkl/node/object.rb +322 -66
- data/lib/rupkl/node/operation.rb +123 -100
- data/lib/rupkl/node/pair.rb +58 -0
- data/lib/rupkl/node/pkl_module.rb +4 -4
- data/lib/rupkl/node/regex.rb +196 -0
- data/lib/rupkl/node/string.rb +155 -133
- data/lib/rupkl/node/struct_common.rb +70 -35
- data/lib/rupkl/node/value_common.rb +1 -5
- data/lib/rupkl/parser/expression.rb +43 -6
- data/lib/rupkl/parser/misc.rb +20 -0
- data/lib/rupkl/parser/object.rb +96 -13
- data/lib/rupkl/parser/pkl_class.rb +12 -5
- data/lib/rupkl/pkl_object.rb +2 -18
- data/lib/rupkl/version.rb +1 -1
- data/lib/rupkl.rb +10 -2
- metadata +17 -9
@@ -0,0 +1,266 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
class Duration < Any
|
6
|
+
include Operatable
|
7
|
+
|
8
|
+
uninstantiable_class
|
9
|
+
|
10
|
+
def initialize(parent, value, unit, position)
|
11
|
+
super(parent, value, position)
|
12
|
+
@unit = unit
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :unit
|
16
|
+
|
17
|
+
def value
|
18
|
+
@children[0]
|
19
|
+
end
|
20
|
+
|
21
|
+
def evaluate(_context = nil)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_ruby(context = nil)
|
26
|
+
value.to_ruby(context) * UNIT_FACTOR[unit]
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_pkl_string(context = nil)
|
30
|
+
to_string(context)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_string(context = nil)
|
34
|
+
"#{value.to_pkl_string(context)}.#{unit}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def u_op_minus(position)
|
38
|
+
Duration.new(nil, value.u_op_minus(position), unit, position)
|
39
|
+
end
|
40
|
+
|
41
|
+
def ==(other)
|
42
|
+
other.is_a?(Duration) &&
|
43
|
+
begin
|
44
|
+
l = calc_second(self)
|
45
|
+
r = calc_second(other)
|
46
|
+
l.value == r.value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
[:b_op_lt, :b_op_gt, :b_op_le, :b_op_ge].each do |method_name|
|
51
|
+
class_eval(<<~M, __FILE__, __LINE__ + 1)
|
52
|
+
# def b_op_lt(r_operand, position)
|
53
|
+
# l = calc_second(self)
|
54
|
+
# r = calc_second(r_operand)
|
55
|
+
# l.b_op_lt(r, position)
|
56
|
+
# end
|
57
|
+
def #{method_name}(r_operand, position)
|
58
|
+
l = calc_second(self)
|
59
|
+
r = calc_second(r_operand)
|
60
|
+
l.#{method_name}(r, position)
|
61
|
+
end
|
62
|
+
M
|
63
|
+
end
|
64
|
+
|
65
|
+
[:b_op_add, :b_op_sub].each do |method_name|
|
66
|
+
class_eval(<<~M, __FILE__, __LINE__ + 1)
|
67
|
+
# def b_op_add(r_operand, position)
|
68
|
+
# l, r, u = align_unit(self, r_operand)
|
69
|
+
# result = l.b_op_add(r, position)
|
70
|
+
# Duration.new(nil, result, u, position)
|
71
|
+
# end
|
72
|
+
def #{method_name}(r_operand, position)
|
73
|
+
l, r, u = align_unit(self, r_operand)
|
74
|
+
result = l.#{method_name}(r, position)
|
75
|
+
Duration.new(nil, result, u, position)
|
76
|
+
end
|
77
|
+
M
|
78
|
+
end
|
79
|
+
|
80
|
+
[:b_op_mul, :b_op_rem, :b_op_exp].each do |method_name|
|
81
|
+
class_eval(<<~M, __FILE__, __LINE__ + 1)
|
82
|
+
# def b_op_mul(r_operand, position)
|
83
|
+
# result = value.b_op_mul(r_operand, position)
|
84
|
+
# Duration.new(nil, result, unit, position)
|
85
|
+
# end
|
86
|
+
def #{method_name}(r_operand, position)
|
87
|
+
result = value.#{method_name}(r_operand, position)
|
88
|
+
Duration.new(nil, result, unit, position)
|
89
|
+
end
|
90
|
+
M
|
91
|
+
end
|
92
|
+
|
93
|
+
[:b_op_div, :b_op_truncating_div].each do |method_name|
|
94
|
+
class_eval(<<~M, __FILE__, __LINE__ + 1)
|
95
|
+
# def b_op_div(r_operand, position)
|
96
|
+
# if r_operand.is_a?(Duration)
|
97
|
+
# l = calc_second(self)
|
98
|
+
# r = calc_second(r_operand)
|
99
|
+
# l.b_op_div(r, position)
|
100
|
+
# else
|
101
|
+
# result = value.b_op_div(r_operand, position)
|
102
|
+
# Duration.new(nil, result, unit, position)
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
def #{method_name}(r_operand, position)
|
106
|
+
if r_operand.is_a?(Duration)
|
107
|
+
l = calc_second(self)
|
108
|
+
r = calc_second(r_operand)
|
109
|
+
l.#{method_name}(r, position)
|
110
|
+
else
|
111
|
+
result = value.#{method_name}(r_operand, position)
|
112
|
+
Duration.new(nil, result, unit, position)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
M
|
116
|
+
end
|
117
|
+
|
118
|
+
define_builtin_property(:value) do
|
119
|
+
value
|
120
|
+
end
|
121
|
+
|
122
|
+
define_builtin_property(:unit) do
|
123
|
+
String.new(self, unit.to_s, nil, position)
|
124
|
+
end
|
125
|
+
|
126
|
+
define_builtin_property(:isPositive) do
|
127
|
+
result = value.value.zero? || value.value.positive?
|
128
|
+
Boolean.new(self, result, position)
|
129
|
+
end
|
130
|
+
|
131
|
+
define_builtin_property(:isoString) do
|
132
|
+
unless value.value.finite?
|
133
|
+
message = "cannot convert duration '#{to_string(nil)}' to ISO 8601 duration"
|
134
|
+
raise EvaluationError.new(message, position)
|
135
|
+
end
|
136
|
+
|
137
|
+
String.new(self, iso8601_string, nil, position)
|
138
|
+
end
|
139
|
+
|
140
|
+
define_builtin_method(
|
141
|
+
:isBetween, first: Duration, last: Duration
|
142
|
+
) do |args, parent, position|
|
143
|
+
r = calc_second(self)
|
144
|
+
f = calc_second(args[:first])
|
145
|
+
l = calc_second(args[:last])
|
146
|
+
r.execute_builtin_method(:isBetween, { first: f, last: l }, parent, position)
|
147
|
+
end
|
148
|
+
|
149
|
+
define_builtin_method(:toUnit, unit: String) do |args, parent, position|
|
150
|
+
unit = unit_symbol(args[:unit], position)
|
151
|
+
value = convert_unit(self, unit, position)
|
152
|
+
Duration.new(parent, value, unit, position)
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
UNIT_FACTOR = {
|
158
|
+
ns: 10.0**-9, us: 10.0**-6, ms: 10.0**-3,
|
159
|
+
s: 1, min: 60, h: 60 * 60, d: 24 * 60 * 60
|
160
|
+
}.freeze
|
161
|
+
|
162
|
+
def unit_factor(unit)
|
163
|
+
factor = UNIT_FACTOR[unit]
|
164
|
+
if factor >= 1
|
165
|
+
Int.new(nil, factor, nil)
|
166
|
+
else
|
167
|
+
Float.new(nil, factor, nil)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def defined_operator?(operator)
|
172
|
+
[:[], :'!@', :'&&', :'||'].none?(operator)
|
173
|
+
end
|
174
|
+
|
175
|
+
def valid_r_operand?(operator, operand)
|
176
|
+
case operator
|
177
|
+
when :*, :%, :** then operand in Number
|
178
|
+
when :/, :'~/' then operand in Number | Duration
|
179
|
+
else super
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def calc_second(duration)
|
184
|
+
factor = unit_factor(duration.unit)
|
185
|
+
duration.value.b_op_mul(factor, nil)
|
186
|
+
end
|
187
|
+
|
188
|
+
def align_unit(l_operand, r_operand)
|
189
|
+
unit =
|
190
|
+
if UNIT_FACTOR[l_operand.unit] >= UNIT_FACTOR[r_operand.unit]
|
191
|
+
l_operand.unit
|
192
|
+
else
|
193
|
+
r_operand.unit
|
194
|
+
end
|
195
|
+
|
196
|
+
[
|
197
|
+
convert_unit(l_operand, unit, position),
|
198
|
+
convert_unit(r_operand, unit, position),
|
199
|
+
unit
|
200
|
+
]
|
201
|
+
end
|
202
|
+
|
203
|
+
def convert_unit(duration, unit, position)
|
204
|
+
return duration.value if duration.unit == unit
|
205
|
+
|
206
|
+
second = calc_second(duration)
|
207
|
+
result = second.b_op_div(unit_factor(unit), position)
|
208
|
+
if result.value.to_i == result.value
|
209
|
+
Int.new(nil, result.value, position)
|
210
|
+
else
|
211
|
+
result
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def iso8601_string
|
216
|
+
return 'PT0S' if value.value.zero?
|
217
|
+
|
218
|
+
parts = value.value.negative? && ['-PT'] || ['PT']
|
219
|
+
iso8601_elements.each do |unit, n|
|
220
|
+
parts << format_iso8601_element(unit, n)
|
221
|
+
end
|
222
|
+
|
223
|
+
parts.join
|
224
|
+
end
|
225
|
+
|
226
|
+
def iso8601_elements
|
227
|
+
result, _ =
|
228
|
+
[:h, :min, :s]
|
229
|
+
.inject([{}, calc_second(self).value.abs]) do |(elements, sec), unit|
|
230
|
+
q, r =
|
231
|
+
if unit == :s
|
232
|
+
sec
|
233
|
+
else
|
234
|
+
sec.divmod(UNIT_FACTOR[unit])
|
235
|
+
end
|
236
|
+
elements[unit] = q if q.positive?
|
237
|
+
|
238
|
+
[elements, r]
|
239
|
+
end
|
240
|
+
|
241
|
+
result
|
242
|
+
end
|
243
|
+
|
244
|
+
def format_iso8601_element(unit, value)
|
245
|
+
s =
|
246
|
+
if unit != :s || value.to_i == value
|
247
|
+
value
|
248
|
+
else
|
249
|
+
format('%.12f', value).sub(/0+\Z/, '')
|
250
|
+
end
|
251
|
+
"#{s}#{unit[0].upcase}"
|
252
|
+
end
|
253
|
+
|
254
|
+
def unit_symbol(string, position)
|
255
|
+
symbol = string.value.to_sym
|
256
|
+
return symbol if UNIT_FACTOR.key?(symbol)
|
257
|
+
|
258
|
+
message =
|
259
|
+
'expected value of type ' \
|
260
|
+
'"ns"|"us"|"ms"|"s"|"min"|"h"|"d", ' \
|
261
|
+
"but got #{string.to_pkl_string(nil)}"
|
262
|
+
raise EvaluationError.new(message, position)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
data/lib/rupkl/node/dynamic.rb
CHANGED
@@ -3,12 +3,9 @@
|
|
3
3
|
module RuPkl
|
4
4
|
module Node
|
5
5
|
class Dynamic < Any
|
6
|
+
include Operatable
|
6
7
|
include StructCommon
|
7
8
|
|
8
|
-
def properties
|
9
|
-
@body&.properties(visibility: :object)
|
10
|
-
end
|
11
|
-
|
12
9
|
def entries
|
13
10
|
@body&.entries
|
14
11
|
end
|
@@ -17,6 +14,10 @@ module RuPkl
|
|
17
14
|
@body&.elements
|
18
15
|
end
|
19
16
|
|
17
|
+
def to_ruby(context = nil)
|
18
|
+
to_pkl_object(context)
|
19
|
+
end
|
20
|
+
|
20
21
|
def ==(other)
|
21
22
|
other.instance_of?(self.class) &&
|
22
23
|
match_members?(properties, other.properties, false) &&
|
@@ -25,29 +26,32 @@ module RuPkl
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def find_by_key(key)
|
28
|
-
find_entry(key) || find_element(key)
|
29
|
+
(find_entry(key) || find_element(key))&.value
|
29
30
|
end
|
30
31
|
|
31
|
-
define_builtin_method(:length) do
|
32
|
+
define_builtin_method(:length) do |_, parent, position|
|
32
33
|
result = elements&.size || 0
|
33
|
-
Int.new(
|
34
|
+
Int.new(parent, result, position)
|
34
35
|
end
|
35
36
|
|
36
|
-
define_builtin_method(:hasProperty, name: String) do |
|
37
|
-
|
38
|
-
|
37
|
+
define_builtin_method(:hasProperty, name: String) do |args, parent, position|
|
38
|
+
name = args[:name].value.to_sym
|
39
|
+
result = find_property(name) && true || false
|
40
|
+
Boolean.new(parent, result, position)
|
39
41
|
end
|
40
42
|
|
41
|
-
define_builtin_method(:getProperty, name: String) do |
|
42
|
-
|
43
|
+
define_builtin_method(:getProperty, name: String) do |args, _, position|
|
44
|
+
name = args[:name].value.to_sym
|
45
|
+
find_property(name)&.value ||
|
43
46
|
begin
|
44
|
-
m = "cannot find property '#{name
|
47
|
+
m = "cannot find property '#{name}'"
|
45
48
|
raise EvaluationError.new(m, position)
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
49
|
-
define_builtin_method(:getPropertyOrNull, name: String) do |
|
50
|
-
|
52
|
+
define_builtin_method(:getPropertyOrNull, name: String) do |args, parent, position|
|
53
|
+
name = args[:name].value.to_sym
|
54
|
+
find_property(name)&.value || Null.new(parent, position)
|
51
55
|
end
|
52
56
|
|
53
57
|
private
|
@@ -12,7 +12,7 @@ module RuPkl
|
|
12
12
|
|
13
13
|
attr_reader :id
|
14
14
|
|
15
|
-
def copy(parent = nil)
|
15
|
+
def copy(parent = nil, position = @position)
|
16
16
|
self.class.new(parent, id, position)
|
17
17
|
end
|
18
18
|
|
@@ -24,6 +24,10 @@ module RuPkl
|
|
24
24
|
other
|
25
25
|
end
|
26
26
|
end
|
27
|
+
|
28
|
+
def to_sym
|
29
|
+
id.to_sym
|
30
|
+
end
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
class IfExpression
|
6
|
+
include NodeCommon
|
7
|
+
|
8
|
+
def condition
|
9
|
+
@children[0]
|
10
|
+
end
|
11
|
+
|
12
|
+
def if_expression
|
13
|
+
@children[1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def else_expression
|
17
|
+
@children[2]
|
18
|
+
end
|
19
|
+
|
20
|
+
def evaluate(context = nil)
|
21
|
+
resolve_structure(context).evaluate(context)
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve_structure(context = nil)
|
25
|
+
@result ||=
|
26
|
+
if evaluate_condition(context)
|
27
|
+
if_expression
|
28
|
+
else
|
29
|
+
else_expression
|
30
|
+
end
|
31
|
+
@result.resolve_structure(context)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def evaluate_condition(context)
|
37
|
+
evaluated = condition.evaluate(context)
|
38
|
+
return evaluated.value if evaluated.is_a?(Boolean)
|
39
|
+
|
40
|
+
message = "expected type 'Boolean', but got type '#{evaluated.class_name}'"
|
41
|
+
raise EvaluationError.new(message, position)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
class IntSeq < Any
|
6
|
+
include Operatable
|
7
|
+
|
8
|
+
uninstantiable_class
|
9
|
+
|
10
|
+
def initialize(parent, start, last, step, position)
|
11
|
+
super(parent, *[start, last, step].compact, position)
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
children[0]
|
16
|
+
end
|
17
|
+
|
18
|
+
def last
|
19
|
+
children[1]
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :end, :last
|
23
|
+
|
24
|
+
def step
|
25
|
+
children[2]
|
26
|
+
end
|
27
|
+
|
28
|
+
def evaluate(_context = nil)
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_ruby(_context = nil)
|
33
|
+
to_array(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_pkl_string(context = nil)
|
37
|
+
to_string(context)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_string(context = nil)
|
41
|
+
start_value, last_value, step_value = children.map { _1.to_pkl_string(context) }
|
42
|
+
if step && step.value != 1
|
43
|
+
"IntSeq(#{start_value}, #{last_value}).step(#{step_value})"
|
44
|
+
else
|
45
|
+
"IntSeq(#{start_value}, #{last_value})"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(other)
|
50
|
+
other.is_a?(self.class) &&
|
51
|
+
to_array(self) == to_array(other)
|
52
|
+
end
|
53
|
+
|
54
|
+
define_builtin_property(:start) do
|
55
|
+
Int.new(self, start.value, position)
|
56
|
+
end
|
57
|
+
|
58
|
+
define_builtin_property(:end) do
|
59
|
+
Int.new(self, last.value, position)
|
60
|
+
end
|
61
|
+
|
62
|
+
define_builtin_property(:step) do
|
63
|
+
Int.new(self, step&.value || 1, position)
|
64
|
+
end
|
65
|
+
|
66
|
+
define_builtin_method(:step, step: Int) do |args, parent, position|
|
67
|
+
step = args[:step]
|
68
|
+
if step.value.zero?
|
69
|
+
message = "expected a non zero number, but got '#{step.value}'"
|
70
|
+
raise EvaluationError.new(message, position)
|
71
|
+
end
|
72
|
+
|
73
|
+
IntSeq.new(parent, start, last, step, position)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def to_array(intseq)
|
79
|
+
start_value, last_value, step_value = intseq.children.map(&:value)
|
80
|
+
Range.new(start_value, last_value).step(step_value || 1).to_a
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/rupkl/node/listing.rb
CHANGED
@@ -3,19 +3,26 @@
|
|
3
3
|
module RuPkl
|
4
4
|
module Node
|
5
5
|
class Listing < Any
|
6
|
+
include Operatable
|
6
7
|
include StructCommon
|
7
8
|
|
8
9
|
def elements
|
9
10
|
@body&.elements
|
10
11
|
end
|
11
12
|
|
13
|
+
def to_ruby(context = nil)
|
14
|
+
to_ruby_object(context) do |_properties, _entries, elements|
|
15
|
+
replace_self_array(elements, elements) || []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
12
19
|
def ==(other)
|
13
20
|
other.instance_of?(self.class) &&
|
14
21
|
match_members?(elements, other.elements, true)
|
15
22
|
end
|
16
23
|
|
17
24
|
def find_by_key(key)
|
18
|
-
find_element(key)
|
25
|
+
find_element(key)&.value
|
19
26
|
end
|
20
27
|
|
21
28
|
define_builtin_property(:isEmpty) do
|
@@ -43,12 +50,12 @@ module RuPkl
|
|
43
50
|
Listing.new(self, body, position)
|
44
51
|
end
|
45
52
|
|
46
|
-
define_builtin_method(:join, separator: String) do |
|
53
|
+
define_builtin_method(:join, separator: String) do |args, parent, position|
|
47
54
|
result =
|
48
55
|
elements
|
49
56
|
&.map { _1.value.to_string }
|
50
|
-
&.join(separator.value)
|
51
|
-
String.new(
|
57
|
+
&.join(args[:separator].value)
|
58
|
+
String.new(parent, result || '', nil, position)
|
52
59
|
end
|
53
60
|
|
54
61
|
private
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuPkl
|
4
|
+
module Node
|
5
|
+
class Map < Any
|
6
|
+
MapEntry = Struct.new(:key, :value) do
|
7
|
+
def match_key?(other_key)
|
8
|
+
key == other_key
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
include Operatable
|
13
|
+
include MemberFinder
|
14
|
+
undef_method :pkl_method
|
15
|
+
|
16
|
+
uninstantiable_class
|
17
|
+
|
18
|
+
def initialize(parent, entries, position)
|
19
|
+
@entries = compose_entries(entries, position)
|
20
|
+
super(parent, *@entries&.flat_map(&:to_a), position)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :entries
|
24
|
+
|
25
|
+
def evaluate(_context = nil)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_ruby(context = nil)
|
30
|
+
entries&.to_h { |e| e.map { _1.to_ruby(context) } } || {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_pkl_string(context = nil)
|
34
|
+
to_string(context)
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_string(context = nil)
|
38
|
+
entry_string =
|
39
|
+
entries
|
40
|
+
&.flat_map { |entry| entry.map { _1.to_pkl_string(context) } }
|
41
|
+
&.join(', ')
|
42
|
+
"Map(#{entry_string})"
|
43
|
+
end
|
44
|
+
|
45
|
+
def b_op_add(r_operand, position)
|
46
|
+
result =
|
47
|
+
if entries && r_operand.entries
|
48
|
+
entries + r_operand.entries
|
49
|
+
else
|
50
|
+
entries || r_operand.entries
|
51
|
+
end
|
52
|
+
self.class.new(nil, result&.flat_map(&:to_a), position)
|
53
|
+
end
|
54
|
+
|
55
|
+
def ==(other)
|
56
|
+
return false unless other.is_a?(self.class)
|
57
|
+
return false unless entries&.size == other.entries&.size
|
58
|
+
return true unless entries
|
59
|
+
|
60
|
+
entries
|
61
|
+
.all? { |entry| entry.value == other.find_by_key(entry.key) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_by_key(key)
|
65
|
+
find_entry(key)&.value
|
66
|
+
end
|
67
|
+
|
68
|
+
define_builtin_property(:isEmpty) do
|
69
|
+
result = entries.nil? || entries.empty?
|
70
|
+
Boolean.new(self, result, position)
|
71
|
+
end
|
72
|
+
|
73
|
+
define_builtin_property(:length) do
|
74
|
+
result = entries&.length || 0
|
75
|
+
Int.new(self, result, position)
|
76
|
+
end
|
77
|
+
|
78
|
+
define_builtin_property(:keys) do
|
79
|
+
keys = entries&.map(&:key)
|
80
|
+
Set.new(self, keys, position)
|
81
|
+
end
|
82
|
+
|
83
|
+
define_builtin_property(:values) do
|
84
|
+
values = entries&.map(&:value)
|
85
|
+
List.new(self, values, position)
|
86
|
+
end
|
87
|
+
|
88
|
+
define_builtin_property(:entries) do
|
89
|
+
key_value_pairs =
|
90
|
+
entries&.map { |e| Pair.new(nil, e.key, e.value, position) }
|
91
|
+
List.new(self, key_value_pairs, position)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def compose_entries(entries, position)
|
97
|
+
return if entries.nil? || entries.empty?
|
98
|
+
|
99
|
+
check_arity(entries, position)
|
100
|
+
entries.each_slice(2)&.with_object([]) do |(k, v), result|
|
101
|
+
index =
|
102
|
+
result.find_index { _1.match_key?(k) } ||
|
103
|
+
result.size
|
104
|
+
result[index] = MapEntry.new(k, v)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def check_arity(entries, position)
|
109
|
+
return if ((entries&.size || 0) % 2).zero?
|
110
|
+
|
111
|
+
m = 'number of arguments must be a multiple of two'
|
112
|
+
raise EvaluationError.new(m, position)
|
113
|
+
end
|
114
|
+
|
115
|
+
def defined_operator?(operator)
|
116
|
+
[:[], :+].any?(operator)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/rupkl/node/mapping.rb
CHANGED
@@ -3,19 +3,26 @@
|
|
3
3
|
module RuPkl
|
4
4
|
module Node
|
5
5
|
class Mapping < Any
|
6
|
+
include Operatable
|
6
7
|
include StructCommon
|
7
8
|
|
8
9
|
def entries
|
9
10
|
@body&.entries
|
10
11
|
end
|
11
12
|
|
13
|
+
def to_ruby(context = nil)
|
14
|
+
to_ruby_object(context) do |_properties, entries, _elements|
|
15
|
+
replace_self_hash(entries, entries) || {}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
12
19
|
def ==(other)
|
13
20
|
other.instance_of?(self.class) &&
|
14
21
|
match_members?(entries, other.entries, false)
|
15
22
|
end
|
16
23
|
|
17
24
|
def find_by_key(key)
|
18
|
-
find_entry(key)
|
25
|
+
find_entry(key)&.value
|
19
26
|
end
|
20
27
|
|
21
28
|
define_builtin_property(:isEmpty) do
|
@@ -28,13 +35,13 @@ module RuPkl
|
|
28
35
|
Int.new(self, result, position)
|
29
36
|
end
|
30
37
|
|
31
|
-
define_builtin_method(:containsKey, key: Any) do |
|
32
|
-
result = find_entry(key) && true || false
|
33
|
-
Boolean.new(
|
38
|
+
define_builtin_method(:containsKey, key: Any) do |args, parent, position|
|
39
|
+
result = find_entry(args[:key]) && true || false
|
40
|
+
Boolean.new(parent, result, position)
|
34
41
|
end
|
35
42
|
|
36
|
-
define_builtin_method(:getOrNull, key: Any) do |
|
37
|
-
find_entry(key) || Null.new(
|
43
|
+
define_builtin_method(:getOrNull, key: Any) do |args, parent, position|
|
44
|
+
find_entry(args[:key])&.value || Null.new(parent, position)
|
38
45
|
end
|
39
46
|
|
40
47
|
private
|