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 Morph
13
14 module Enum
15
16 class Abstract < Morph::Abstract
17 def self.make(xs)
18 ASSERT.kind_of xs, ::Array
19
20 VC.make_list xs
21 end
22
23
24 define_class_method(
25 :meth_make_empty,
26 :empty, [],
27 [], VCM::List::Abstract
28 )
29 def self.meth_make_empty(loc, env, _event)
30 VC.make_nil
31 end
32
33
34 attr_reader :source
35
36 def initialize(source)
37 ASSERT.kind_of source, VC::Top
38
39 @source = source
40 end
41
42
43 def to_s
44 format "#Enum<%s>", self.source.to_s
45 end
46
47
48 define_instance_method(
49 :meth_cons,
50 :cons, [],
51 [VC::Top], VCM::List::Abstract
52 )
53 def meth_cons(loc, env, _event, value)
54 ASSERT.kind_of value, VC::Top
55
56 raise X::NotImplemented.new(
57 loc,
58 env,
59 "cons: Emnumerator morph is not be constructible"
60 )
61 end
62 end
63 Abstract.freeze
64
65
66
67 class Provider < Abstract
68 TYPE_SYM = :ProviderEnum
69
70
71 attr_reader :dest
72
73 def initialize(source, dest)
74 ASSERT.kind_of source, VC::Top
75 ASSERT.kind_of dest, ::Proc
76
77 super(source)
78
79 @dest = dest
80 end
81
82
83 def meth_dest(_loc, _env, _event)
84 val_opt = self.dest.call self.source
85 ASSERT.kind_of val_opt, VCU::Option::Abstract
86 ASSERT.kind_of val_opt.contents, VC::Top
87
88 val_opt
89 end
90 end
91 Provider.freeze
92
93
94 =begin
95 HOW TO USE UserEnum
96
97 * Same to: [1 ..5].to-list
98
99 > fun rec fn-dest = x -> (
100 * if x <= 5 then Some (x, &UserEnum.make (x + 1) fn-dest)
101 * else NONE
102 * )
103 > &UserEnum.make 1 fn-dest.to-list
104 val it : Cons = [1, 2, 3, 4, 5]
105
106 * Same to: [1 ..5].map to-s
107
108 > &UserEnum.make 1 fn-dest.map to-s
109 val it : Cons = ["1", "2", "3", "4", "5"]
110
111 * Same to: [1 ..5].select odd?
112
113 > &UserEnum.make 1 fn-dest.select odd?
114 val it : Cons = [1, 3, 5]
115
116 * Same to: [|to-s x| val x <- [1 ..5] if odd? x]
117
118 > [|to-s x| val x <- &UserEnum.make 1 fn-dest if odd? x]
119 val it : Cons = ["1", "3", "5"]
120
121 * Same to: STDIN.each-line.for-each { s -> print <| "- " ^ s }
122
123 > fun rec fn-dest' = io -> case io.gets of {
124 * | &None -> NONE
125 * | &Some s -> Some (s, &UserEnum.make io fn-dest')
126 * }
127 > &UserEnum.make STDIN fn-dest'.for-each { s -> print <| "- " ^ s }
128 aaa [Enter]
129 - aaa
130 bbb [Enter]
131 - bbb
132 [Ctrl]+[d]
133 >
134 =end
135 class User < Abstract
136 TYPE_SYM = :UserEnum
137
138
139 define_class_method(
140 :meth_make,
141 :make, [],
142 [VC::Top, VC::Fun], self
143 )
144 def self.meth_make(loc, env, event, source, fn_dest)
145 ASSERT.kind_of source, VC::Top
146 ASSERT.kind_of fn_dest, VC::Fun
147
148 VC.make_user_enumerator source, fn_dest
149 end
150
151
152 attr_reader :fn_dest
153
154 def initialize(source, fn_dest)
155 ASSERT.kind_of source, VC::Top
156 ASSERT.kind_of fn_dest, VC::Fun
157
158 super(source)
159
160 @fn_dest = fn_dest
161 end
162
163
164 def meth_dest(loc, env, event)
165 val_opt = self.fn_dest.apply self.source, [], loc, env.enter(event)
166 VC.validate_option val_opt, 'dest', loc, env
167
168 ASSERT.kind_of val_opt, VCU::Option::Abstract
169 end
170 end
171 Provider.freeze
172
173 end # Umu::Value::Core::Morph::Enum
174
175 end # Umu::Value::Core::Morph
176
177
178
179 module_function
180
181 def make_enumerator(source, dest)
182 ASSERT.kind_of source, VC::Top
183 ASSERT.kind_of dest, ::Proc
184
185 Morph::Enum::Provider.new(source, dest).freeze
186 end
187
188
189 def make_user_enumerator(source, fn_dest)
190 ASSERT.kind_of source, VC::Top
191 ASSERT.kind_of fn_dest, VC::Fun
192
193 Morph::Enum::User.new(source, fn_dest).freeze
194 end
195
196 end # Umu::Value::Core
197
198 end # Umu::Value
199
200 end # Umu