1 # coding: utf-8
2 # frozen_string_literal: true
3
4
5
6 module Umu
7
8 module Value
9
10 module Core
11
12 module Product
13
14 class Named < Abstract
15 alias values objs
16 attr_reader :index_by_label
17
18
19 def initialize(fst_value, snd_value, tail_values, index_by_label)
20 ASSERT.kind_of fst_value, ::Object
21 ASSERT.kind_of snd_value, ::Object
22 ASSERT.kind_of tail_values, ::Array
23 ASSERT.kind_of index_by_label, ::Hash
24 ASSERT.assert index_by_label.size == 2 + tail_values.size
25
26 super(fst_value, snd_value, tail_values)
27
28 @index_by_label = index_by_label.freeze
29 end
30
31
32 def labels
33 self.index_by_label.keys
34 end
35
36
37 def each
38 self.index_by_label.each do |label, index|
39 ASSERT.kind_of label, ::Symbol
40 ASSERT.kind_of index, ::Integer
41
42 yield label, self.values[index]
43 end
44 end
45
46
47 def to_s
48 format("(%s)",
49 self.map { |label, value|
50 format "%s:%s", label.to_s, value.to_s
51 }.join(' ')
52 )
53 end
54
55
56 def pretty_print(q)
57 PRT.group_for_enum q, self, bb:'(', eb:')', join:' ' do
58 |label, value|
59
60 q.text label.to_s
61 q.text ':'
62 q.pp value
63 end
64 end
65
66
67 def select_by_label(sel_lab, loc, env)
68 ASSERT.kind_of sel_lab, ::Symbol
69 ASSERT.kind_of loc, LOC::Entry
70
71 opt_index = self.index_by_label[sel_lab]
72 unless opt_index
73 raise X::SelectionError.new(
74 loc,
75 env,
76 "Unknown selector label: '%s'", sel_lab.to_s
77 )
78 end
79 index = opt_index
80
81 ASSERT.kind_of self.values[index], VC::Top
82 end
83
84
85 def modify(value_by_label, loc, env)
86 ASSERT.kind_of value_by_label, ::Hash
87 ASSERT.kind_of loc, LOC::Entry
88
89 mut_values = self.values.dup
90 value_by_label.each_key do |label, expr|
91 index = self.index_by_label[label]
92 unless index
93 raise X::SelectionError.new(
94 loc,
95 env,
96 "Unknown modifier label: '%s'", label.to_s
97 )
98 end
99
100 mut_values[index] = value_by_label[label]
101 end
102
103 VC.make_named_tuple self.index_by_label, *mut_values
104 end
105
106
107 define_instance_method(
108 :meth_to_string,
109 :'to-s', [],
110 [], VCA::String
111 )
112 def meth_to_string(loc, env, event)
113 VC.make_string(
114 format("(%s)",
115 self.map { |label, value|
116 format("%s:%s",
117 label.to_s,
118 value.meth_to_string(loc, env, event).val
119 )
120 }.join(' ')
121 )
122 )
123 end
124
125
126 define_instance_method(
127 :meth_is_equal,
128 :'==', [],
129 [VC::Top], VCA::Bool
130 )
131 def meth_is_equal(loc, env, event, other)
132 ASSERT.kind_of other, VC::Top
133
134 unless other.kind_of?(self.class) && self.arity == other.arity
135 return VC.make_false
136 end
137
138 VC.make_bool(
139 self.values.zip(other.values).all? {
140 |self_value, other_value|
141
142 other_value.kind_of?(self_value.class) &&
143 self_value.meth_is_equal(loc, env, event, other_value).true?
144 }
145 )
146 end
147
148
149 define_instance_method(
150 :meth_is_less_than,
151 :'<', [],
152 [self], VCA::Bool
153 )
154 def meth_is_less_than(loc, env, event, other)
155 ASSERT.kind_of other, VCP::Named
156
157 unless other.kind_of?(self.class) && self.arity == other.arity
158 raise X::TypeError.new(
159 loc,
160 env,
161 "Expected a named tuple of %d element, but %d: %s",
162 self.arity, other.arity, other.to_s
163 )
164 end
165
166 result, _index = self.index_by_label.map { |label, index|
167 [label, self.values[index]]
168 }.zip(
169 other.index_by_label.map { |label, index|
170 [label, other.values[index]]
171 }
172 ).inject([VC.make_false, 0]) {
173 |
174 (res, index),
175 ((self_label, self_value), (other_label, other_value))
176 |
177 ASSERT.kind_of res, VCA::Bool
178 ASSERT.kind_of index, ::Integer
179 ASSERT.kind_of self_label, ::Symbol
180 ASSERT.kind_of self_value, VC::Top
181 ASSERT.kind_of other_value, VC::Top
182 ASSERT.kind_of other_label, ::Symbol
183
184 =begin
185 pp({index: index,
186 self_label: self_label, self_value: self_value,
187 other_label: other_label, other_value: other_value
188 })
189 =end
190
191 unless self_label == other_label
192 raise X::TypeError.new(
193 loc,
194 env,
195 "Expected '%s:' " +
196 "as label for #%d named tuple element, " +
197 "but '%s:'",
198 self_label.to_s, index + 1, other_label
199 )
200 end
201
202 unless other_value.kind_of?(self_value.class)
203 raise X::TypeError.new(
204 loc,
205 env,
206 "In %d's element of tuple, " +
207 "expected a %s, but %s : %s",
208 index + 1,
209 self_value.type_sym,
210 other_value.to_s,
211 other_value.type_sym
212 )
213 end
214
215 if self_value.meth_is_less_than( # self < other
216 loc, env, event, other_value
217 ).true?
218 break VC.make_true
219 elsif self_value.meth_is_equal( # self = other
220 loc, env, event, other_value
221 ).true?
222 [res, index + 1]
223 elsif other_value.meth_is_less_than( # self > other
224 loc, env, event, self_value
225 ).true?
226 break VC.make_false
227 else
228 ASSERT.abort 'No case'
229 end
230 }
231
232 ASSERT.kind_of result, VCA::Bool
233 end
234 end
235 Named.freeze
236
237 end # Umu::Value::Core::Product
238
239
240 module_function
241
242 def make_named_tuple(index_by_label, fst_value, snd_value, *tail_values)
243 ASSERT.kind_of index_by_label, ::Hash
244 ASSERT.kind_of fst_value, ::Object
245 ASSERT.kind_of snd_value, ::Object
246 ASSERT.kind_of tail_values, ::Array
247
248 Product::Named.new(
249 fst_value, snd_value, tail_values.freeze, index_by_label.freeze
250 ).freeze
251 end
252
253 end # Umu::Value::Core
254
255 end # Umu::Value
256
257 end # Umu