1use crate::dispatch::{DispatchEffect, EffectContext};
2use crate::error::EffectError;
3use tidepool_eval::heap::Heap;
4use tidepool_eval::value::Value;
5use tidepool_repr::{CoreExpr, DataConId, DataConTable};
6
7pub struct EffectMachine<'a> {
8 table: &'a DataConTable,
9 heap: &'a mut dyn Heap,
10 val_id: DataConId,
11 e_id: DataConId,
12 leaf_id: DataConId,
13 node_id: DataConId,
14 union_id: DataConId,
15}
16
17impl<'a> EffectMachine<'a> {
18 pub fn new(table: &'a DataConTable, heap: &'a mut dyn Heap) -> Result<Self, EffectError> {
19 let val_id = table
20 .get_by_name("Val")
21 .ok_or(EffectError::MissingConstructor { name: "Val" })?;
22 let e_id = table
23 .get_by_name("E")
24 .ok_or(EffectError::MissingConstructor { name: "E" })?;
25 let leaf_id = table
26 .get_by_name("Leaf")
27 .ok_or(EffectError::MissingConstructor { name: "Leaf" })?;
28 let node_id = table
29 .get_by_name("Node")
30 .ok_or(EffectError::MissingConstructor { name: "Node" })?;
31 let union_id = table
32 .get_by_name("Union")
33 .ok_or(EffectError::MissingConstructor { name: "Union" })?;
34 Ok(Self {
35 table,
36 heap,
37 val_id,
38 e_id,
39 leaf_id,
40 node_id,
41 union_id,
42 })
43 }
44
45 pub fn run<H: DispatchEffect>(
48 &mut self,
49 expr: &CoreExpr,
50 handlers: &mut H,
51 ) -> Result<Value, EffectError> {
52 self.run_with_user(expr, handlers, &())
53 }
54
55 pub fn run_with_user<U, H: DispatchEffect<U>>(
57 &mut self,
58 expr: &CoreExpr,
59 handlers: &mut H,
60 user: &U,
61 ) -> Result<Value, EffectError> {
62 let env = tidepool_eval::eval::env_from_datacon_table(self.table);
63 let mut current = tidepool_eval::eval::eval(expr, &env, self.heap)?;
64
65 loop {
66 let forced = tidepool_eval::eval::force(current, self.heap)?;
67 match forced {
68 Value::Con(id, ref fields) if id == self.val_id => {
69 let result = fields
71 .first()
72 .cloned()
73 .unwrap_or(Value::Lit(tidepool_repr::Literal::LitInt(0)));
74 return Ok(tidepool_eval::eval::deep_force(result, self.heap)?);
75 }
76 Value::Con(id, ref fields) if id == self.e_id => {
77 if fields.len() != 2 {
79 return Err(EffectError::FieldCountMismatch {
80 constructor: "E",
81 expected: 2,
82 got: fields.len(),
83 });
84 }
85 let union_val = tidepool_eval::eval::deep_force(fields[0].clone(), self.heap)?;
86 let k = tidepool_eval::eval::force(fields[1].clone(), self.heap)?;
87
88 let (tag, request) = match union_val {
90 Value::Con(uid, ref ufields) if uid == self.union_id => {
91 if ufields.len() != 2 {
92 return Err(EffectError::FieldCountMismatch {
93 constructor: "Union",
94 expected: 2,
95 got: ufields.len(),
96 });
97 }
98 let tag = match &ufields[0] {
99 Value::Lit(tidepool_repr::Literal::LitWord(w)) => *w,
100 Value::Lit(tidepool_repr::Literal::LitInt(i)) => *i as u64,
101 other => {
102 return Err(EffectError::UnexpectedValue {
103 context: "Union tag (Word#/Int#)",
104 got: format!("{:?}", other),
105 })
106 }
107 };
108 let req = ufields[1].clone();
110 (tag, req)
111 }
112 other => {
113 return Err(EffectError::UnexpectedValue {
114 context: "Union constructor",
115 got: format!("{:?}", other),
116 })
117 }
118 };
119
120 let cx = EffectContext::with_user(self.table, user);
122 let response = handlers.dispatch(tag, &request, &cx)?;
123
124 current = self.apply_cont(k, response)?;
126 }
127 other => {
128 return Err(EffectError::UnexpectedValue {
129 context: "Val or E constructor",
130 got: format!("{:?}", other),
131 });
132 }
133 }
134 }
135 }
136
137 fn apply_cont(&mut self, k: Value, arg: Value) -> Result<Value, EffectError> {
139 let k = tidepool_eval::eval::force(k, self.heap)?;
140 match k {
141 Value::Con(id, ref fields) if id == self.leaf_id => {
142 if fields.len() != 1 {
144 return Err(EffectError::FieldCountMismatch {
145 constructor: "Leaf",
146 expected: 1,
147 got: fields.len(),
148 });
149 }
150 let f = tidepool_eval::eval::force(fields[0].clone(), self.heap)?;
151 Ok(self.apply_closure(f, arg)?)
152 }
153 Value::Con(id, ref fields) if id == self.node_id => {
154 if fields.len() != 2 {
156 return Err(EffectError::FieldCountMismatch {
157 constructor: "Node",
158 expected: 2,
159 got: fields.len(),
160 });
161 }
162 let k1 = fields[0].clone();
163 let k2 = fields[1].clone();
164 let result = self.apply_cont(k1, arg)?;
165 let forced = tidepool_eval::eval::force(result, self.heap)?;
166
167 match forced {
168 Value::Con(vid, ref vfields) if vid == self.val_id => {
169 let y = vfields
171 .first()
172 .cloned()
173 .unwrap_or(Value::Lit(tidepool_repr::Literal::LitInt(0)));
174 self.apply_cont(k2, y)
175 }
176 Value::Con(eid, ref efields) if eid == self.e_id => {
177 if efields.len() != 2 {
179 return Err(EffectError::FieldCountMismatch {
180 constructor: "E (continuation)",
181 expected: 2,
182 got: efields.len(),
183 });
184 }
185 let union_val = efields[0].clone();
186 let k_prime = efields[1].clone();
187 let new_k = Value::Con(self.node_id, vec![k_prime, k2]);
188 Ok(Value::Con(self.e_id, vec![union_val, new_k]))
189 }
190 other => Err(EffectError::UnexpectedValue {
191 context: "Val or E after applying k1",
192 got: format!("{:?}", other),
193 }),
194 }
195 }
196 Value::Closure(..) => {
197 Ok(self.apply_closure(k, arg)?)
199 }
200 other => Err(EffectError::UnexpectedValue {
201 context: "Leaf or Node continuation",
202 got: format!("{:?}", other),
203 }),
204 }
205 }
206
207 fn apply_closure(&mut self, closure: Value, arg: Value) -> Result<Value, EffectError> {
209 match closure {
210 Value::Closure(env, binder, body) => {
211 let new_env = env.update(binder, arg);
212 Ok(tidepool_eval::eval::eval(&body, &new_env, self.heap)?)
213 }
214 Value::ConFun(tag, arity, mut args) => {
215 args.push(arg);
216 if args.len() == arity {
217 Ok(Value::Con(tag, args))
218 } else {
219 Ok(Value::ConFun(tag, arity, args))
220 }
221 }
222 other => Err(EffectError::UnexpectedValue {
223 context: "closure",
224 got: format!("{:?}", other),
225 }),
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use tidepool_eval::heap::VecHeap;
234 use tidepool_repr::datacon::DataCon;
235 use tidepool_repr::datacon_table::DataConTable;
236 use tidepool_repr::types::{DataConId, Literal, VarId};
237 use tidepool_repr::{CoreExpr, CoreFrame, RecursiveTree};
238
239 fn make_test_table() -> DataConTable {
240 let mut table = DataConTable::new();
241 table.insert(DataCon {
242 id: DataConId(1),
243 name: "Val".to_string(),
244 tag: 1,
245 rep_arity: 1,
246 field_bangs: vec![],
247 qualified_name: None,
248 });
249 table.insert(DataCon {
250 id: DataConId(2),
251 name: "E".to_string(),
252 tag: 2,
253 rep_arity: 2,
254 field_bangs: vec![],
255 qualified_name: None,
256 });
257 table.insert(DataCon {
258 id: DataConId(3),
259 name: "Leaf".to_string(),
260 tag: 1,
261 rep_arity: 1,
262 field_bangs: vec![],
263 qualified_name: None,
264 });
265 table.insert(DataCon {
266 id: DataConId(4),
267 name: "Node".to_string(),
268 tag: 2,
269 rep_arity: 2,
270 field_bangs: vec![],
271 qualified_name: None,
272 });
273 table.insert(DataCon {
274 id: DataConId(5),
275 name: "Union".to_string(),
276 tag: 1,
277 rep_arity: 2,
278 field_bangs: vec![],
279 qualified_name: None,
280 });
281 table
282 }
283
284 #[test]
285 fn test_effect_machine_pure_val() {
286 let table = make_test_table();
288 let mut heap = VecHeap::new();
289
290 let expr: CoreExpr = RecursiveTree {
292 nodes: vec![
293 CoreFrame::Lit(Literal::LitInt(42)),
294 CoreFrame::Con {
295 tag: DataConId(1), fields: vec![0],
297 },
298 ],
299 };
300
301 let mut handlers = frunk::HNil;
302 let mut machine = EffectMachine::new(&table, &mut heap).unwrap();
303 let result = machine.run(&expr, &mut handlers).unwrap();
304
305 match result {
306 Value::Lit(Literal::LitInt(n)) => assert_eq!(n, 42),
307 other => panic!("Expected Lit(42), got {:?}", other),
308 }
309 }
310
311 #[test]
312 fn test_effect_machine_single_effect() {
313 let table = make_test_table();
316 let mut heap = VecHeap::new();
317
318 let expr: CoreExpr = RecursiveTree {
319 nodes: vec![
320 CoreFrame::Var(VarId(100)),
322 CoreFrame::Con {
324 tag: DataConId(1), fields: vec![0],
326 },
327 CoreFrame::Lam {
329 binder: VarId(100),
330 body: 1,
331 },
332 CoreFrame::Con {
334 tag: DataConId(3), fields: vec![2],
336 },
337 CoreFrame::Lit(Literal::LitInt(99)),
339 CoreFrame::Lit(Literal::LitWord(0)),
341 CoreFrame::Con {
343 tag: DataConId(5), fields: vec![5, 4],
345 },
346 CoreFrame::Con {
348 tag: DataConId(2), fields: vec![6, 3],
350 },
351 ],
352 };
353
354 use crate::dispatch::{EffectContext, EffectHandler};
356 use tidepool_bridge::FromCore;
357
358 struct TestReq(i64);
359 impl FromCore for TestReq {
360 fn from_value(
361 value: &Value,
362 _table: &DataConTable,
363 ) -> Result<Self, tidepool_bridge::BridgeError> {
364 match value {
365 Value::Lit(Literal::LitInt(n)) => Ok(TestReq(*n)),
366 _ => Err(tidepool_bridge::BridgeError::TypeMismatch {
367 expected: "LitInt".into(),
368 got: format!("{:?}", value),
369 }),
370 }
371 }
372 }
373
374 struct TestHandler;
375 impl EffectHandler for TestHandler {
376 type Request = TestReq;
377 fn handle(&mut self, req: TestReq, _cx: &EffectContext) -> Result<Value, EffectError> {
378 Ok(Value::Lit(Literal::LitInt(req.0 + 1)))
380 }
381 }
382
383 let mut handlers = frunk::hlist![TestHandler];
384 let mut machine = EffectMachine::new(&table, &mut heap).unwrap();
385 let result = machine.run(&expr, &mut handlers).unwrap();
386
387 match result {
388 Value::Lit(Literal::LitInt(n)) => assert_eq!(n, 100),
389 other => panic!("Expected Lit(100), got {:?}", other),
390 }
391 }
392
393 #[test]
394 fn test_run_with_user_data() {
395 let table = make_test_table();
397 let mut heap = VecHeap::new();
398
399 let expr: CoreExpr = RecursiveTree {
400 nodes: vec![
401 CoreFrame::Var(VarId(100)),
402 CoreFrame::Con {
403 tag: DataConId(1),
404 fields: vec![0],
405 },
406 CoreFrame::Lam {
407 binder: VarId(100),
408 body: 1,
409 },
410 CoreFrame::Con {
411 tag: DataConId(3),
412 fields: vec![2],
413 },
414 CoreFrame::Lit(Literal::LitInt(10)),
415 CoreFrame::Lit(Literal::LitWord(0)),
416 CoreFrame::Con {
417 tag: DataConId(5),
418 fields: vec![5, 4],
419 },
420 CoreFrame::Con {
421 tag: DataConId(2),
422 fields: vec![6, 3],
423 },
424 ],
425 };
426
427 use crate::dispatch::{EffectContext, EffectHandler};
428 use tidepool_bridge::FromCore;
429
430 struct TestReq(i64);
431 impl FromCore for TestReq {
432 fn from_value(
433 value: &Value,
434 _table: &DataConTable,
435 ) -> Result<Self, tidepool_bridge::BridgeError> {
436 match value {
437 Value::Lit(Literal::LitInt(n)) => Ok(TestReq(*n)),
438 _ => Err(tidepool_bridge::BridgeError::TypeMismatch {
439 expected: "LitInt".into(),
440 got: format!("{:?}", value),
441 }),
442 }
443 }
444 }
445
446 struct UserData {
447 multiplier: i64,
448 }
449
450 struct UserHandler;
451 impl EffectHandler<UserData> for UserHandler {
452 type Request = TestReq;
453 fn handle(
454 &mut self,
455 req: TestReq,
456 cx: &EffectContext<'_, UserData>,
457 ) -> Result<Value, EffectError> {
458 Ok(Value::Lit(Literal::LitInt(req.0 * cx.user().multiplier)))
459 }
460 }
461
462 let user = UserData { multiplier: 5 };
463 let mut handlers = frunk::hlist![UserHandler];
464 let mut machine = EffectMachine::new(&table, &mut heap).unwrap();
465 let result = machine.run_with_user(&expr, &mut handlers, &user).unwrap();
466
467 match result {
468 Value::Lit(Literal::LitInt(n)) => assert_eq!(n, 50), other => panic!("Expected Lit(50), got {:?}", other),
470 }
471 }
472}