1use crate::op::*;
4use crate::program::*;
5use indexmap::IndexMap;
6use lex_ast as a;
7
8#[derive(Default)]
9struct ConstPool {
10 pool: Vec<Const>,
11 fields: IndexMap<String, u32>,
12 variants: IndexMap<String, u32>,
13 node_ids: IndexMap<String, u32>,
14 ints: IndexMap<i64, u32>,
15 bools: IndexMap<u8, u32>,
16 strs: IndexMap<String, u32>,
17}
18
19impl ConstPool {
20 fn field(&mut self, name: &str) -> u32 {
21 if let Some(i) = self.fields.get(name) { return *i; }
22 let i = self.pool.len() as u32;
23 self.pool.push(Const::FieldName(name.into()));
24 self.fields.insert(name.into(), i);
25 i
26 }
27 fn variant(&mut self, name: &str) -> u32 {
28 if let Some(i) = self.variants.get(name) { return *i; }
29 let i = self.pool.len() as u32;
30 self.pool.push(Const::VariantName(name.into()));
31 self.variants.insert(name.into(), i);
32 i
33 }
34 fn node_id(&mut self, name: &str) -> u32 {
35 if let Some(i) = self.node_ids.get(name) { return *i; }
36 let i = self.pool.len() as u32;
37 self.pool.push(Const::NodeId(name.into()));
38 self.node_ids.insert(name.into(), i);
39 i
40 }
41 fn int(&mut self, n: i64) -> u32 {
42 if let Some(i) = self.ints.get(&n) { return *i; }
43 let i = self.pool.len() as u32;
44 self.pool.push(Const::Int(n));
45 self.ints.insert(n, i);
46 i
47 }
48 fn bool(&mut self, b: bool) -> u32 {
49 let key = b as u8;
50 if let Some(i) = self.bools.get(&key) { return *i; }
51 let i = self.pool.len() as u32;
52 self.pool.push(Const::Bool(b));
53 self.bools.insert(key, i);
54 i
55 }
56 fn str(&mut self, s: &str) -> u32 {
57 if let Some(i) = self.strs.get(s) { return *i; }
58 let i = self.pool.len() as u32;
59 self.pool.push(Const::Str(s.into()));
60 self.strs.insert(s.into(), i);
61 i
62 }
63 fn float(&mut self, f: f64) -> u32 {
64 let i = self.pool.len() as u32;
66 self.pool.push(Const::Float(f));
67 i
68 }
69 fn unit(&mut self) -> u32 {
70 let i = self.pool.len() as u32;
71 self.pool.push(Const::Unit);
72 i
73 }
74}
75
76pub fn compile_program(stages: &[a::Stage]) -> Program {
77 let mut p = Program {
78 constants: Vec::new(),
79 functions: Vec::new(),
80 function_names: IndexMap::new(),
81 module_aliases: IndexMap::new(),
82 entry: None,
83 };
84
85 for s in stages {
88 if let a::Stage::Import(i) = s {
89 let module = i.reference.strip_prefix("std.").unwrap_or(&i.reference).to_string();
90 p.module_aliases.insert(i.alias.clone(), module);
91 }
92 }
93
94 for s in stages {
95 if let a::Stage::FnDecl(fd) = s {
96 let idx = p.functions.len() as u32;
97 p.function_names.insert(fd.name.clone(), idx);
98 p.functions.push(Function {
99 name: fd.name.clone(),
100 arity: fd.params.len() as u16,
101 locals_count: 0,
102 code: Vec::new(),
103 effects: fd.effects.iter().map(|e| DeclaredEffect {
104 kind: e.name.clone(),
105 arg: e.arg.as_ref().map(|a| match a {
106 a::EffectArg::Str { value } => EffectArg::Str(value.clone()),
107 a::EffectArg::Int { value } => EffectArg::Int(*value),
108 a::EffectArg::Ident { value } => EffectArg::Ident(value.clone()),
109 }),
110 }).collect(),
111 body_hash: crate::program::ZERO_BODY_HASH,
114 refinements: fd.params.iter().map(|p| match &p.ty {
118 a::TypeExpr::Refined { binding, predicate, .. } =>
119 Some(crate::program::Refinement {
120 binding: binding.clone(),
121 predicate: (**predicate).clone(),
122 }),
123 _ => None,
124 }).collect(),
125 });
126 }
127 }
128
129 let mut pool = ConstPool::default();
130 let function_names = p.function_names.clone();
131 let module_aliases = p.module_aliases.clone();
132 let mut pending_lambdas: Vec<PendingLambda> = Vec::new();
133
134 for s in stages {
135 if let a::Stage::FnDecl(_) = s {
136 let id_map = lex_ast::expr_ids(s);
139 let fd = match s { a::Stage::FnDecl(fd) => fd, _ => unreachable!() };
140 let mut fc = FnCompiler {
141 code: Vec::new(),
142 locals: IndexMap::new(),
143 next_local: 0,
144 peak_local: 0,
145 pool: &mut pool,
146 function_names: &function_names,
147 module_aliases: &module_aliases,
148 id_map: &id_map,
149 pending_lambdas: &mut pending_lambdas,
150 next_fn_id: &mut p.functions,
151 };
152 for param in &fd.params {
153 let i = fc.next_local;
154 fc.locals.insert(param.name.clone(), i);
155 fc.next_local += 1;
156 fc.peak_local = fc.next_local;
157 }
158 fc.compile_expr(&fd.body, true);
159 fc.code.push(Op::Return);
160 let code = std::mem::take(&mut fc.code);
161 let peak = fc.peak_local;
162 drop(fc);
163 let idx = function_names[&fd.name];
164 p.functions[idx as usize].code = code;
165 p.functions[idx as usize].locals_count = peak;
166 }
167 }
168
169 while let Some(pl) = pending_lambdas.pop() {
172 let id_map = std::collections::HashMap::new();
173 let mut fc = FnCompiler {
174 code: Vec::new(),
175 locals: IndexMap::new(),
176 next_local: 0,
177 peak_local: 0,
178 pool: &mut pool,
179 function_names: &function_names,
180 module_aliases: &module_aliases,
181 id_map: &id_map,
182 pending_lambdas: &mut pending_lambdas,
183 next_fn_id: &mut p.functions,
184 };
185 for name in &pl.capture_names {
186 let i = fc.next_local;
187 fc.locals.insert(name.clone(), i);
188 fc.next_local += 1;
189 fc.peak_local = fc.next_local;
190 }
191 for p in &pl.params {
192 let i = fc.next_local;
193 fc.locals.insert(p.name.clone(), i);
194 fc.next_local += 1;
195 fc.peak_local = fc.next_local;
196 }
197 fc.compile_expr(&pl.body, true);
198 fc.code.push(Op::Return);
199 let code = std::mem::take(&mut fc.code);
200 let peak = fc.peak_local;
201 drop(fc);
202 p.functions[pl.fn_id as usize].code = code;
203 p.functions[pl.fn_id as usize].locals_count = peak;
204 }
205
206 for f in p.functions.iter_mut() {
211 if f.body_hash == crate::program::ZERO_BODY_HASH {
212 f.body_hash = crate::program::compute_body_hash(
213 f.arity, f.locals_count, &f.code);
214 }
215 }
216
217 p.constants = pool.pool;
218 p
219}
220
221#[derive(Debug, Clone)]
222struct PendingLambda {
223 fn_id: u32,
224 capture_names: Vec<String>,
226 params: Vec<a::Param>,
227 body: a::CExpr,
228}
229
230struct FnCompiler<'a> {
231 code: Vec<Op>,
232 locals: IndexMap<String, u16>,
233 next_local: u16,
234 peak_local: u16,
236 pool: &'a mut ConstPool,
237 function_names: &'a IndexMap<String, u32>,
238 module_aliases: &'a IndexMap<String, String>,
239 id_map: &'a std::collections::HashMap<*const a::CExpr, lex_ast::NodeId>,
241 pending_lambdas: &'a mut Vec<PendingLambda>,
244 next_fn_id: &'a mut Vec<Function>,
247}
248
249impl<'a> FnCompiler<'a> {
250 fn alloc_local(&mut self, name: &str) -> u16 {
251 let i = self.next_local;
252 self.locals.insert(name.into(), i);
253 self.next_local += 1;
254 if self.next_local > self.peak_local { self.peak_local = self.next_local; }
255 i
256 }
257 fn emit(&mut self, op: Op) { self.code.push(op); }
258
259 fn compile_expr(&mut self, e: &a::CExpr, tail: bool) {
260 match e {
261 a::CExpr::Literal { value } => self.compile_lit(value),
262 a::CExpr::Var { name } => {
263 if let Some(slot) = self.locals.get(name) {
264 self.emit(Op::LoadLocal(*slot));
265 } else if let Some(&fn_id) = self.function_names.get(name) {
266 self.emit(Op::MakeClosure { fn_id, capture_count: 0 });
272 } else {
273 panic!("unknown var in compiler: {name}");
277 }
278 }
279 a::CExpr::Let { name, ty: _, value, body } => {
280 self.compile_expr(value, false);
281 let slot = self.alloc_local(name);
282 self.emit(Op::StoreLocal(slot));
283 self.compile_expr(body, tail);
284 }
285 a::CExpr::Block { statements, result } => {
286 for s in statements {
287 self.compile_expr(s, false);
288 self.emit(Op::Pop);
289 }
290 self.compile_expr(result, tail);
291 }
292 a::CExpr::Call { callee, args } => self.compile_call(e, callee, args, tail),
293 a::CExpr::Constructor { name, args } => {
294 for a in args { self.compile_expr(a, false); }
295 let name_idx = self.pool.variant(name);
296 self.emit(Op::MakeVariant { name_idx, arity: args.len() as u16 });
297 }
298 a::CExpr::Match { scrutinee, arms } => self.compile_match(scrutinee, arms, tail),
299 a::CExpr::RecordLit { fields } => {
300 let mut idxs = Vec::with_capacity(fields.len());
301 for f in fields {
302 self.compile_expr(&f.value, false);
303 idxs.push(self.pool.field(&f.name));
304 }
305 self.emit(Op::MakeRecord { field_name_indices: idxs });
306 }
307 a::CExpr::TupleLit { items } => {
308 for it in items { self.compile_expr(it, false); }
309 self.emit(Op::MakeTuple(items.len() as u16));
310 }
311 a::CExpr::ListLit { items } => {
312 for it in items { self.compile_expr(it, false); }
313 self.emit(Op::MakeList(items.len() as u32));
314 }
315 a::CExpr::FieldAccess { value, field } => {
316 self.compile_expr(value, false);
317 let idx = self.pool.field(field);
318 self.emit(Op::GetField(idx));
319 }
320 a::CExpr::BinOp { op, lhs, rhs } => self.compile_binop(op, lhs, rhs),
321 a::CExpr::UnaryOp { op, expr } => {
322 self.compile_expr(expr, false);
323 match op.as_str() {
324 "-" => self.emit(Op::NumNeg),
325 "not" => self.emit(Op::BoolNot),
326 other => panic!("unknown unary: {other}"),
327 }
328 }
329 a::CExpr::Lambda { params, body, .. } => self.compile_lambda(params, body),
330 a::CExpr::Return { value } => {
331 self.compile_expr(value, true);
332 self.emit(Op::Return);
333 }
334 }
335 }
336
337 fn compile_lit(&mut self, l: &a::CLit) {
338 let i = match l {
339 a::CLit::Int { value } => self.pool.int(*value),
340 a::CLit::Bool { value } => self.pool.bool(*value),
341 a::CLit::Float { value } => {
342 let f: f64 = value.parse().unwrap_or(0.0);
343 self.pool.float(f)
344 }
345 a::CLit::Str { value } => self.pool.str(value),
346 a::CLit::Bytes { value: _ } => {
347 let i = self.pool.pool.len() as u32;
349 self.pool.pool.push(Const::Bytes(Vec::new()));
350 i
351 }
352 a::CLit::Unit => self.pool.unit(),
353 };
354 self.emit(Op::PushConst(i));
355 }
356
357 fn compile_call(&mut self, call_expr: &a::CExpr, callee: &a::CExpr, args: &[a::CExpr], tail: bool) {
358 let node_id = self
359 .id_map
360 .get(&(call_expr as *const a::CExpr))
361 .map(|n| n.as_str().to_string())
362 .unwrap_or_else(|| "n_?".into());
363 let node_id_idx = self.pool.node_id(&node_id);
364
365 if let a::CExpr::FieldAccess { value, field } = callee {
370 if let a::CExpr::Var { name } = value.as_ref() {
371 if let Some(module) = self.module_aliases.get(name) {
372 if self.try_emit_higher_order(module, field, args, node_id_idx) {
373 let _ = tail;
374 return;
375 }
376 for a in args { self.compile_expr(a, false); }
377 let kind_idx = self.pool.str(module);
378 let op_idx = self.pool.str(field);
379 self.emit(Op::EffectCall {
380 kind_idx,
381 op_idx,
382 arity: args.len() as u16,
383 node_id_idx,
384 });
385 let _ = tail;
386 return;
387 }
388 }
389 }
390 match callee {
391 a::CExpr::Var { name } if self.function_names.contains_key(name) => {
392 for a in args { self.compile_expr(a, false); }
393 let fn_id = self.function_names[name];
394 if tail {
395 self.emit(Op::TailCall { fn_id, arity: args.len() as u16, node_id_idx });
396 } else {
397 self.emit(Op::Call { fn_id, arity: args.len() as u16, node_id_idx });
398 }
399 }
400 a::CExpr::Var { name } if self.locals.contains_key(name) => {
401 let slot = self.locals[name];
404 self.emit(Op::LoadLocal(slot));
405 for a in args { self.compile_expr(a, false); }
406 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
407 }
408 other => {
410 self.compile_expr(other, false);
411 for a in args { self.compile_expr(a, false); }
412 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
413 }
414 }
415 }
416
417 fn compile_binop(&mut self, op: &str, lhs: &a::CExpr, rhs: &a::CExpr) {
418 self.compile_expr(lhs, false);
419 self.compile_expr(rhs, false);
420 match op {
421 "+" => self.emit(Op::NumAdd),
422 "-" => self.emit(Op::NumSub),
423 "*" => self.emit(Op::NumMul),
424 "/" => self.emit(Op::NumDiv),
425 "%" => self.emit(Op::NumMod),
426 "==" => self.emit(Op::NumEq),
427 "!=" => { self.emit(Op::NumEq); self.emit(Op::BoolNot); }
428 "<" => self.emit(Op::NumLt),
429 "<=" => self.emit(Op::NumLe),
430 ">" => { self.emit_swap_top2(); self.emit(Op::NumLt); }
431 ">=" => { self.emit_swap_top2(); self.emit(Op::NumLe); }
432 "and" => self.emit(Op::BoolAnd),
433 "or" => self.emit(Op::BoolOr),
434 other => panic!("unknown binop: {other:?}"),
435 }
436 }
437
438 fn emit_swap_top2(&mut self) {
439 let a = self.alloc_local("__swap_a");
440 let b = self.alloc_local("__swap_b");
441 self.emit(Op::StoreLocal(b));
442 self.emit(Op::StoreLocal(a));
443 self.emit(Op::LoadLocal(b));
444 self.emit(Op::LoadLocal(a));
445 }
446
447 fn compile_match(&mut self, scrutinee: &a::CExpr, arms: &[a::Arm], tail: bool) {
448 self.compile_expr(scrutinee, false);
449 let scrut_slot = self.alloc_local("__scrut");
450 self.emit(Op::StoreLocal(scrut_slot));
451
452 let mut end_jumps: Vec<usize> = Vec::new();
453 for arm in arms {
454 let arm_start_locals = self.next_local;
455 let arm_start_locals_map = self.locals.clone();
456
457 self.emit(Op::LoadLocal(scrut_slot));
458 let mut bindings: Vec<(String, u16)> = Vec::new();
459 let fail_jumps: Vec<usize> = self.compile_pattern_test(&arm.pattern, &mut bindings);
460
461 self.compile_expr(&arm.body, tail);
462 let j_end = self.code.len();
463 self.emit(Op::Jump(0));
464 end_jumps.push(j_end);
465
466 let fail_target = self.code.len() as i32;
467 for j in fail_jumps {
468 if let Op::JumpIfNot(off) = &mut self.code[j] {
469 *off = fail_target - (j as i32 + 1);
470 }
471 }
472 self.next_local = arm_start_locals;
473 self.locals = arm_start_locals_map;
474 }
475 let panic_msg_idx = self.pool.str("non-exhaustive match");
476 self.emit(Op::Panic(panic_msg_idx));
477
478 let end_target = self.code.len() as i32;
479 for j in end_jumps {
480 if let Op::Jump(off) = &mut self.code[j] {
481 *off = end_target - (j as i32 + 1);
482 }
483 }
484 }
485
486 fn compile_pattern_test(&mut self, p: &a::Pattern, bindings: &mut Vec<(String, u16)>) -> Vec<usize> {
487 let mut fails = Vec::new();
488 match p {
489 a::Pattern::PWild => { self.emit(Op::Pop); }
490 a::Pattern::PVar { name } => {
491 let slot = self.alloc_local(name);
492 self.emit(Op::StoreLocal(slot));
493 bindings.push((name.clone(), slot));
494 }
495 a::Pattern::PLiteral { value } => {
496 self.compile_lit(value);
497 match value {
498 a::CLit::Str { .. } => self.emit(Op::StrEq),
499 a::CLit::Bytes { .. } => self.emit(Op::BytesEq),
500 _ => self.emit(Op::NumEq),
501 }
502 let j = self.code.len();
503 self.emit(Op::JumpIfNot(0));
504 fails.push(j);
505 }
506 a::Pattern::PConstructor { name, args } => {
507 let name_idx = self.pool.variant(name);
508 self.emit(Op::Dup);
509 self.emit(Op::TestVariant(name_idx));
510 let j = self.code.len();
511 self.emit(Op::JumpIfNot(0));
512 fails.push(j);
513 if args.is_empty() {
514 self.emit(Op::Pop);
515 } else if args.len() == 1 {
516 self.emit(Op::GetVariantArg(0));
517 let sub_fails = self.compile_pattern_test(&args[0], bindings);
518 fails.extend(sub_fails);
519 } else {
520 let slot = self.alloc_local("__variant");
521 self.emit(Op::StoreLocal(slot));
522 for (i, arg) in args.iter().enumerate() {
523 self.emit(Op::LoadLocal(slot));
524 self.emit(Op::GetVariantArg(i as u16));
525 let sub_fails = self.compile_pattern_test(arg, bindings);
526 fails.extend(sub_fails);
527 }
528 }
529 }
530 a::Pattern::PRecord { fields } => {
531 let slot = self.alloc_local("__record");
532 self.emit(Op::StoreLocal(slot));
533 for f in fields {
534 self.emit(Op::LoadLocal(slot));
535 let fi = self.pool.field(&f.name);
536 self.emit(Op::GetField(fi));
537 let sub_fails = self.compile_pattern_test(&f.pattern, bindings);
538 fails.extend(sub_fails);
539 }
540 }
541 a::Pattern::PTuple { items } => {
542 let slot = self.alloc_local("__tuple");
543 self.emit(Op::StoreLocal(slot));
544 for (i, item) in items.iter().enumerate() {
545 self.emit(Op::LoadLocal(slot));
546 self.emit(Op::GetElem(i as u16));
547 let sub_fails = self.compile_pattern_test(item, bindings);
548 fails.extend(sub_fails);
549 }
550 }
551 }
552 fails
553 }
554
555 fn compile_lambda(&mut self, params: &[a::Param], body: &a::CExpr) {
559 let mut bound: std::collections::HashSet<String> = params.iter().map(|p| p.name.clone()).collect();
561 let mut frees: Vec<String> = Vec::new();
562 free_vars(body, &mut bound, &mut frees);
563
564 let captures: Vec<String> = frees.into_iter()
567 .filter(|n| self.locals.contains_key(n) && !self.function_names.contains_key(n))
568 .collect();
569
570 let fn_id = self.next_fn_id.len() as u32;
572 self.next_fn_id.push(Function {
573 name: format!("__lambda_{fn_id}"),
574 arity: (captures.len() + params.len()) as u16,
575 locals_count: 0,
576 code: Vec::new(),
577 effects: Vec::new(),
578 body_hash: crate::program::ZERO_BODY_HASH,
580 refinements: Vec::new(),
585 });
586
587 for c in &captures {
589 let slot = *self.locals.get(c).expect("free var must be in scope");
590 self.emit(Op::LoadLocal(slot));
591 }
592 self.emit(Op::MakeClosure { fn_id, capture_count: captures.len() as u16 });
593
594 self.pending_lambdas.push(PendingLambda {
596 fn_id,
597 capture_names: captures,
598 params: params.to_vec(),
599 body: body.clone(),
600 });
601 }
602
603 fn try_emit_higher_order(
607 &mut self,
608 module: &str,
609 op: &str,
610 args: &[a::CExpr],
611 node_id_idx: u32,
612 ) -> bool {
613 match (module, op) {
614 ("result", "map") => self.emit_variant_map(args, "Ok", true),
615 ("result", "and_then") => self.emit_variant_map(args, "Ok", false),
616 ("result", "map_err") => self.emit_variant_map(args, "Err", true),
617 ("result", "or_else") => self.emit_variant_or_else(args, "Err", 1),
618 ("option", "map") => self.emit_variant_map(args, "Some", true),
619 ("option", "and_then") => self.emit_variant_map(args, "Some", false),
620 ("option", "or_else") => self.emit_variant_or_else(args, "None", 0),
621 ("list", "map") => self.emit_list_map(args),
622 ("list", "filter") => self.emit_list_filter(args),
623 ("list", "fold") => self.emit_list_fold(args),
624 ("map", "fold") => self.emit_map_fold(args, node_id_idx),
625 ("flow", "sequential") => self.emit_flow_sequential(args),
626 ("flow", "branch") => self.emit_flow_branch(args),
627 ("flow", "retry") => self.emit_flow_retry(args),
628 ("flow", "retry_with_backoff") => self.emit_flow_retry_with_backoff(args),
629 ("flow", "parallel") => self.emit_flow_parallel(args),
630 ("flow", "parallel_list") => self.emit_flow_parallel_list(args),
631 _ => return false,
632 }
633 true
634 }
635
636 fn emit_list_map(&mut self, args: &[a::CExpr]) {
639 self.compile_expr(&args[0], false);
641 let xs = self.alloc_local("__lm_xs");
642 self.emit(Op::StoreLocal(xs));
643 self.compile_expr(&args[1], false);
644 let f = self.alloc_local("__lm_f");
645 self.emit(Op::StoreLocal(f));
646
647 self.emit(Op::MakeList(0));
649 let out = self.alloc_local("__lm_out");
650 self.emit(Op::StoreLocal(out));
651
652 let zero = self.pool.int(0);
654 self.emit(Op::PushConst(zero));
655 let i = self.alloc_local("__lm_i");
656 self.emit(Op::StoreLocal(i));
657
658 let loop_top = self.code.len();
660 self.emit(Op::LoadLocal(i));
661 self.emit(Op::LoadLocal(xs));
662 self.emit(Op::GetListLen);
663 self.emit(Op::NumLt);
664 let j_exit = self.code.len();
665 self.emit(Op::JumpIfNot(0));
666
667 let nid = self.pool.node_id("n_list_map");
669 self.emit(Op::LoadLocal(out));
670 self.emit(Op::LoadLocal(f));
671 self.emit(Op::LoadLocal(xs));
672 self.emit(Op::LoadLocal(i));
673 self.emit(Op::GetListElemDyn);
674 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
675 self.emit(Op::ListAppend);
676 self.emit(Op::StoreLocal(out));
677
678 self.emit(Op::LoadLocal(i));
680 let one = self.pool.int(1);
681 self.emit(Op::PushConst(one));
682 self.emit(Op::NumAdd);
683 self.emit(Op::StoreLocal(i));
684
685 let jump_back = self.code.len();
687 let back = (loop_top as i32) - (jump_back as i32 + 1);
688 self.emit(Op::Jump(back));
689
690 let exit_target = self.code.len() as i32;
692 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
693 *off = exit_target - (j_exit as i32 + 1);
694 }
695 self.emit(Op::LoadLocal(out));
696 }
697
698 fn emit_list_filter(&mut self, args: &[a::CExpr]) {
700 self.compile_expr(&args[0], false);
701 let xs = self.alloc_local("__lf_xs");
702 self.emit(Op::StoreLocal(xs));
703 self.compile_expr(&args[1], false);
704 let f = self.alloc_local("__lf_f");
705 self.emit(Op::StoreLocal(f));
706
707 self.emit(Op::MakeList(0));
708 let out = self.alloc_local("__lf_out");
709 self.emit(Op::StoreLocal(out));
710
711 let zero = self.pool.int(0);
712 self.emit(Op::PushConst(zero));
713 let i = self.alloc_local("__lf_i");
714 self.emit(Op::StoreLocal(i));
715
716 let loop_top = self.code.len();
717 self.emit(Op::LoadLocal(i));
718 self.emit(Op::LoadLocal(xs));
719 self.emit(Op::GetListLen);
720 self.emit(Op::NumLt);
721 let j_exit = self.code.len();
722 self.emit(Op::JumpIfNot(0));
723
724 self.emit(Op::LoadLocal(xs));
726 self.emit(Op::LoadLocal(i));
727 self.emit(Op::GetListElemDyn);
728 let x = self.alloc_local("__lf_x");
729 self.emit(Op::StoreLocal(x));
730
731 let nid = self.pool.node_id("n_list_filter");
733 self.emit(Op::LoadLocal(f));
734 self.emit(Op::LoadLocal(x));
735 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
736 let j_skip = self.code.len();
737 self.emit(Op::JumpIfNot(0));
738
739 self.emit(Op::LoadLocal(out));
740 self.emit(Op::LoadLocal(x));
741 self.emit(Op::ListAppend);
742 self.emit(Op::StoreLocal(out));
743
744 let skip_target = self.code.len() as i32;
745 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
746 *off = skip_target - (j_skip as i32 + 1);
747 }
748
749 self.emit(Op::LoadLocal(i));
751 let one = self.pool.int(1);
752 self.emit(Op::PushConst(one));
753 self.emit(Op::NumAdd);
754 self.emit(Op::StoreLocal(i));
755
756 let jump_back = self.code.len();
757 let back = (loop_top as i32) - (jump_back as i32 + 1);
758 self.emit(Op::Jump(back));
759
760 let exit_target = self.code.len() as i32;
761 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
762 *off = exit_target - (j_exit as i32 + 1);
763 }
764 self.emit(Op::LoadLocal(out));
765 }
766
767 fn emit_list_fold(&mut self, args: &[a::CExpr]) {
769 self.compile_expr(&args[0], false);
771 let xs = self.alloc_local("__lo_xs");
772 self.emit(Op::StoreLocal(xs));
773 self.compile_expr(&args[1], false);
774 let acc = self.alloc_local("__lo_acc");
775 self.emit(Op::StoreLocal(acc));
776 self.compile_expr(&args[2], false);
777 let f = self.alloc_local("__lo_f");
778 self.emit(Op::StoreLocal(f));
779
780 let zero = self.pool.int(0);
781 self.emit(Op::PushConst(zero));
782 let i = self.alloc_local("__lo_i");
783 self.emit(Op::StoreLocal(i));
784
785 let loop_top = self.code.len();
786 self.emit(Op::LoadLocal(i));
787 self.emit(Op::LoadLocal(xs));
788 self.emit(Op::GetListLen);
789 self.emit(Op::NumLt);
790 let j_exit = self.code.len();
791 self.emit(Op::JumpIfNot(0));
792
793 let nid = self.pool.node_id("n_list_fold");
795 self.emit(Op::LoadLocal(f));
796 self.emit(Op::LoadLocal(acc));
797 self.emit(Op::LoadLocal(xs));
798 self.emit(Op::LoadLocal(i));
799 self.emit(Op::GetListElemDyn);
800 self.emit(Op::CallClosure { arity: 2, node_id_idx: nid });
801 self.emit(Op::StoreLocal(acc));
802
803 self.emit(Op::LoadLocal(i));
805 let one = self.pool.int(1);
806 self.emit(Op::PushConst(one));
807 self.emit(Op::NumAdd);
808 self.emit(Op::StoreLocal(i));
809
810 let jump_back = self.code.len();
811 let back = (loop_top as i32) - (jump_back as i32 + 1);
812 self.emit(Op::Jump(back));
813
814 let exit_target = self.code.len() as i32;
815 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
816 *off = exit_target - (j_exit as i32 + 1);
817 }
818 self.emit(Op::LoadLocal(acc));
819 }
820
821 fn emit_map_fold(&mut self, args: &[a::CExpr], node_id_idx: u32) {
827 self.compile_expr(&args[0], false);
829 let map_kind = self.pool.str("map");
830 let entries_op = self.pool.str("entries");
831 self.emit(Op::EffectCall {
832 kind_idx: map_kind,
833 op_idx: entries_op,
834 arity: 1,
835 node_id_idx,
836 });
837 let xs = self.alloc_local("__mf_xs");
838 self.emit(Op::StoreLocal(xs));
839
840 self.compile_expr(&args[1], false);
842 let acc = self.alloc_local("__mf_acc");
843 self.emit(Op::StoreLocal(acc));
844
845 self.compile_expr(&args[2], false);
847 let f = self.alloc_local("__mf_f");
848 self.emit(Op::StoreLocal(f));
849
850 let zero = self.pool.int(0);
852 self.emit(Op::PushConst(zero));
853 let i = self.alloc_local("__mf_i");
854 self.emit(Op::StoreLocal(i));
855
856 let loop_top = self.code.len();
858 self.emit(Op::LoadLocal(i));
859 self.emit(Op::LoadLocal(xs));
860 self.emit(Op::GetListLen);
861 self.emit(Op::NumLt);
862 let j_exit = self.code.len();
863 self.emit(Op::JumpIfNot(0));
864
865 self.emit(Op::LoadLocal(xs));
867 self.emit(Op::LoadLocal(i));
868 self.emit(Op::GetListElemDyn);
869 let pair = self.alloc_local("__mf_pair");
870 self.emit(Op::StoreLocal(pair));
871
872 let nid = self.pool.node_id("n_map_fold");
874 self.emit(Op::LoadLocal(f));
875 self.emit(Op::LoadLocal(acc));
876 self.emit(Op::LoadLocal(pair));
877 self.emit(Op::GetElem(0));
878 self.emit(Op::LoadLocal(pair));
879 self.emit(Op::GetElem(1));
880 self.emit(Op::CallClosure { arity: 3, node_id_idx: nid });
881 self.emit(Op::StoreLocal(acc));
882
883 self.emit(Op::LoadLocal(i));
885 let one = self.pool.int(1);
886 self.emit(Op::PushConst(one));
887 self.emit(Op::NumAdd);
888 self.emit(Op::StoreLocal(i));
889
890 let jump_back = self.code.len();
891 let back = (loop_top as i32) - (jump_back as i32 + 1);
892 self.emit(Op::Jump(back));
893
894 let exit_target = self.code.len() as i32;
895 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
896 *off = exit_target - (j_exit as i32 + 1);
897 }
898 self.emit(Op::LoadLocal(acc));
899 }
900
901 fn emit_variant_map(
907 &mut self,
908 args: &[a::CExpr],
909 wrap_with: &str,
910 wrap_result: bool,
911 ) {
912 let wrap_idx = self.pool.variant(wrap_with);
914
915 self.compile_expr(&args[0], false);
917 let val_slot = self.alloc_local("__hov");
918 self.emit(Op::StoreLocal(val_slot));
919
920 self.compile_expr(&args[1], false);
921 let f_slot = self.alloc_local("__hof");
922 self.emit(Op::StoreLocal(f_slot));
923
924 self.emit(Op::LoadLocal(val_slot));
931 self.emit(Op::Dup);
932 self.emit(Op::TestVariant(wrap_idx));
933 let j_skip = self.code.len();
934 self.emit(Op::JumpIfNot(0));
935
936 self.emit(Op::GetVariantArg(0));
938 let arg_slot = self.alloc_local("__hov_arg");
939 self.emit(Op::StoreLocal(arg_slot));
940 self.emit(Op::LoadLocal(f_slot));
941 self.emit(Op::LoadLocal(arg_slot));
942 let nid = self.pool.node_id("n_hov");
943 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
944 if wrap_result {
945 self.emit(Op::MakeVariant { name_idx: wrap_idx, arity: 1 });
946 }
947 let j_end = self.code.len();
948 self.emit(Op::Jump(0));
949
950 let skip_target = self.code.len() as i32;
952 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
953 *off = skip_target - (j_skip as i32 + 1);
954 }
955
956 let end_target = self.code.len() as i32;
957 if let Op::Jump(off) = &mut self.code[j_end] {
958 *off = end_target - (j_end as i32 + 1);
959 }
960 }
961
962 fn emit_variant_or_else(
971 &mut self,
972 args: &[a::CExpr],
973 match_on: &str,
974 closure_arity: u16,
975 ) {
976 let match_idx = self.pool.variant(match_on);
977
978 self.compile_expr(&args[0], false);
979 let val_slot = self.alloc_local("__hoe");
980 self.emit(Op::StoreLocal(val_slot));
981
982 self.compile_expr(&args[1], false);
983 let f_slot = self.alloc_local("__hoe_f");
984 self.emit(Op::StoreLocal(f_slot));
985
986 self.emit(Op::LoadLocal(val_slot));
994 self.emit(Op::Dup);
995 self.emit(Op::TestVariant(match_idx));
996 let j_skip = self.code.len();
997 self.emit(Op::JumpIfNot(0));
998
999 self.emit(Op::Pop);
1002 self.emit(Op::LoadLocal(f_slot));
1003 if closure_arity == 1 {
1004 self.emit(Op::LoadLocal(val_slot));
1005 self.emit(Op::GetVariantArg(0));
1006 }
1007 let nid = self.pool.node_id("n_hoe");
1008 self.emit(Op::CallClosure { arity: closure_arity, node_id_idx: nid });
1009
1010 let j_end = self.code.len();
1011 self.emit(Op::Jump(0));
1012
1013 let skip_target = self.code.len() as i32;
1015 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
1016 *off = skip_target - (j_skip as i32 + 1);
1017 }
1018
1019 let end_target = self.code.len() as i32;
1020 if let Op::Jump(off) = &mut self.code[j_end] {
1021 *off = end_target - (j_end as i32 + 1);
1022 }
1023 }
1024
1025 fn install_trampoline(&mut self, name: &str, arity: u16, locals_count: u16, code: Vec<Op>) -> u32 {
1042 let fn_id = self.next_fn_id.len() as u32;
1043 let body_hash = crate::program::compute_body_hash(arity, locals_count, &code);
1044 self.next_fn_id.push(Function {
1045 name: name.into(),
1046 arity,
1047 locals_count,
1048 code,
1049 effects: Vec::new(),
1050 body_hash,
1051 refinements: Vec::new(),
1054 });
1055 fn_id
1056 }
1057
1058 fn emit_flow_sequential(&mut self, args: &[a::CExpr]) {
1060 self.compile_expr(&args[0], false);
1062 self.compile_expr(&args[1], false);
1063 let nid = self.pool.node_id("n_flow_sequential");
1064 let code = vec![
1065 Op::LoadLocal(0), Op::LoadLocal(2), Op::CallClosure { arity: 1, node_id_idx: nid }, Op::StoreLocal(3), Op::LoadLocal(1), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, Op::Return,
1075 ];
1076 let fn_id = self.install_trampoline("__flow_sequential", 3, 4, code);
1077 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
1078 }
1079
1080 fn emit_flow_parallel(&mut self, args: &[a::CExpr]) {
1088 self.compile_expr(&args[0], false);
1090 self.compile_expr(&args[1], false);
1091 let nid = self.pool.node_id("n_flow_parallel");
1092 let code = vec![
1093 Op::LoadLocal(0), Op::CallClosure { arity: 0, node_id_idx: nid }, Op::LoadLocal(1), Op::CallClosure { arity: 0, node_id_idx: nid }, Op::MakeTuple(2), Op::Return,
1100 ];
1101 let fn_id = self.install_trampoline("__flow_parallel", 2, 2, code);
1102 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
1103 }
1104
1105 fn emit_flow_parallel_list(&mut self, args: &[a::CExpr]) {
1112 self.compile_expr(&args[0], false);
1114 let xs = self.alloc_local("__fpl_xs");
1115 self.emit(Op::StoreLocal(xs));
1116
1117 self.emit(Op::MakeList(0));
1119 let out = self.alloc_local("__fpl_out");
1120 self.emit(Op::StoreLocal(out));
1121
1122 let zero = self.pool.int(0);
1124 self.emit(Op::PushConst(zero));
1125 let i = self.alloc_local("__fpl_i");
1126 self.emit(Op::StoreLocal(i));
1127
1128 let loop_top = self.code.len();
1130 self.emit(Op::LoadLocal(i));
1131 self.emit(Op::LoadLocal(xs));
1132 self.emit(Op::GetListLen);
1133 self.emit(Op::NumLt);
1134 let j_exit = self.code.len();
1135 self.emit(Op::JumpIfNot(0));
1136
1137 let nid = self.pool.node_id("n_flow_parallel_list");
1139 self.emit(Op::LoadLocal(out));
1140 self.emit(Op::LoadLocal(xs));
1141 self.emit(Op::LoadLocal(i));
1142 self.emit(Op::GetListElemDyn);
1143 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
1144 self.emit(Op::ListAppend);
1145 self.emit(Op::StoreLocal(out));
1146
1147 self.emit(Op::LoadLocal(i));
1149 let one = self.pool.int(1);
1150 self.emit(Op::PushConst(one));
1151 self.emit(Op::NumAdd);
1152 self.emit(Op::StoreLocal(i));
1153
1154 let jump_back = self.code.len();
1156 let back = (loop_top as i32) - (jump_back as i32 + 1);
1157 self.emit(Op::Jump(back));
1158
1159 let exit_target = self.code.len() as i32;
1161 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
1162 *off = exit_target - (j_exit as i32 + 1);
1163 }
1164 self.emit(Op::LoadLocal(out));
1165 }
1166
1167 fn emit_flow_branch(&mut self, args: &[a::CExpr]) {
1169 self.compile_expr(&args[0], false);
1170 self.compile_expr(&args[1], false);
1171 self.compile_expr(&args[2], false);
1172 let nid = self.pool.node_id("n_flow_branch");
1173 let mut code = vec![
1174 Op::LoadLocal(0), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, ];
1179 let j_false = code.len();
1180 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(1));
1183 code.push(Op::LoadLocal(3));
1184 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
1185 code.push(Op::Return);
1186 let false_target = code.len() as i32;
1188 if let Op::JumpIfNot(off) = &mut code[j_false] {
1189 *off = false_target - (j_false as i32 + 1);
1190 }
1191 code.push(Op::LoadLocal(2));
1192 code.push(Op::LoadLocal(3));
1193 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
1194 code.push(Op::Return);
1195
1196 let fn_id = self.install_trampoline("__flow_branch", 4, 4, code);
1197 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
1198 }
1199
1200 fn emit_flow_retry(&mut self, args: &[a::CExpr]) {
1204 self.compile_expr(&args[0], false);
1205 self.compile_expr(&args[1], false);
1206 let call_nid = self.pool.node_id("n_flow_retry");
1207 let ok_idx = self.pool.variant("Ok");
1208 let zero_const = self.pool.int(0);
1209 let one_const = self.pool.int(1);
1210 let mut code = vec![
1212 Op::PushConst(zero_const),
1214 Op::StoreLocal(3),
1215 ];
1216 let loop_top = code.len() as i32;
1218 code.push(Op::LoadLocal(3));
1219 code.push(Op::LoadLocal(1));
1220 code.push(Op::NumLt);
1221 let j_done = code.len();
1222 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(0));
1226 code.push(Op::LoadLocal(2));
1227 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
1228 code.push(Op::StoreLocal(4));
1229
1230 code.push(Op::LoadLocal(4));
1232 code.push(Op::TestVariant(ok_idx));
1233 let j_was_err = code.len();
1234 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(4));
1236 code.push(Op::Return);
1237
1238 let was_err_target = code.len() as i32;
1240 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
1241 *off = was_err_target - (j_was_err as i32 + 1);
1242 }
1243 code.push(Op::LoadLocal(3));
1244 code.push(Op::PushConst(one_const));
1245 code.push(Op::NumAdd);
1246 code.push(Op::StoreLocal(3));
1247 let pc_after_jump = code.len() as i32 + 1;
1248 code.push(Op::Jump(loop_top - pc_after_jump));
1249
1250 let done_target = code.len() as i32;
1252 if let Op::JumpIfNot(off) = &mut code[j_done] {
1253 *off = done_target - (j_done as i32 + 1);
1254 }
1255 code.push(Op::LoadLocal(4));
1256 code.push(Op::Return);
1257
1258 let fn_id = self.install_trampoline("__flow_retry", 3, 5, code);
1259 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
1260 }
1261
1262 fn emit_flow_retry_with_backoff(&mut self, args: &[a::CExpr]) {
1270 self.compile_expr(&args[0], false);
1273 self.compile_expr(&args[1], false);
1274 self.compile_expr(&args[2], false);
1275 let call_nid = self.pool.node_id("n_flow_retry_backoff");
1276 let sleep_nid = self.pool.node_id("n_flow_retry_backoff_sleep");
1277 let kind_idx = self.pool.str("time");
1278 let op_idx = self.pool.str("sleep_ms");
1279 let ok_idx = self.pool.variant("Ok");
1280 let zero_const = self.pool.int(0);
1281 let one_const = self.pool.int(1);
1282 let two_const = self.pool.int(2);
1283 let mut code = vec![
1288 Op::LoadLocal(2),
1290 Op::StoreLocal(6),
1291 Op::PushConst(zero_const),
1293 Op::StoreLocal(4),
1294 ];
1295
1296 let loop_top = code.len() as i32;
1297 code.push(Op::LoadLocal(4));
1299 code.push(Op::LoadLocal(1));
1300 code.push(Op::NumLt);
1301 let j_done = code.len();
1302 code.push(Op::JumpIfNot(0)); code.push(Op::PushConst(zero_const));
1306 code.push(Op::LoadLocal(4));
1307 code.push(Op::NumLt); let j_no_sleep = code.len();
1309 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(6)); code.push(Op::EffectCall {
1313 kind_idx, op_idx, arity: 1, node_id_idx: sleep_nid,
1314 });
1315 code.push(Op::Pop); code.push(Op::LoadLocal(6));
1318 code.push(Op::PushConst(two_const));
1319 code.push(Op::NumMul);
1320 code.push(Op::StoreLocal(6));
1321 let after_sleep = code.len() as i32;
1323 if let Op::JumpIfNot(off) = &mut code[j_no_sleep] {
1324 *off = after_sleep - (j_no_sleep as i32 + 1);
1325 }
1326
1327 code.push(Op::LoadLocal(0));
1329 code.push(Op::LoadLocal(3));
1330 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
1331 code.push(Op::StoreLocal(5));
1332
1333 code.push(Op::LoadLocal(5));
1335 code.push(Op::TestVariant(ok_idx));
1336 let j_was_err = code.len();
1337 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(5));
1339 code.push(Op::Return);
1340
1341 let was_err_target = code.len() as i32;
1343 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
1344 *off = was_err_target - (j_was_err as i32 + 1);
1345 }
1346 code.push(Op::LoadLocal(4));
1347 code.push(Op::PushConst(one_const));
1348 code.push(Op::NumAdd);
1349 code.push(Op::StoreLocal(4));
1350 let pc_after_jump = code.len() as i32 + 1;
1351 code.push(Op::Jump(loop_top - pc_after_jump));
1352
1353 let done_target = code.len() as i32;
1355 if let Op::JumpIfNot(off) = &mut code[j_done] {
1356 *off = done_target - (j_done as i32 + 1);
1357 }
1358 code.push(Op::LoadLocal(5));
1359 code.push(Op::Return);
1360
1361 let fn_id = self.install_trampoline("__flow_retry_backoff", 4, 7, code);
1362 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
1363 }
1364}
1365
1366fn free_vars(e: &a::CExpr, bound: &mut std::collections::HashSet<String>, out: &mut Vec<String>) {
1371 match e {
1372 a::CExpr::Literal { .. } => {}
1373 a::CExpr::Var { name } => {
1374 if !bound.contains(name) && !out.contains(name) {
1375 out.push(name.clone());
1376 }
1377 }
1378 a::CExpr::Call { callee, args } => {
1379 free_vars(callee, bound, out);
1380 for a in args { free_vars(a, bound, out); }
1381 }
1382 a::CExpr::Let { name, value, body, .. } => {
1383 free_vars(value, bound, out);
1384 let was_bound = bound.contains(name);
1385 bound.insert(name.clone());
1386 free_vars(body, bound, out);
1387 if !was_bound { bound.remove(name); }
1388 }
1389 a::CExpr::Match { scrutinee, arms } => {
1390 free_vars(scrutinee, bound, out);
1391 for arm in arms {
1392 let mut local_bound = bound.clone();
1393 pattern_binders(&arm.pattern, &mut local_bound);
1394 free_vars(&arm.body, &mut local_bound, out);
1395 }
1396 }
1397 a::CExpr::Block { statements, result } => {
1398 let mut local_bound = bound.clone();
1399 for s in statements { free_vars(s, &mut local_bound, out); }
1400 free_vars(result, &mut local_bound, out);
1401 }
1402 a::CExpr::Constructor { args, .. } => {
1403 for a in args { free_vars(a, bound, out); }
1404 }
1405 a::CExpr::RecordLit { fields } => {
1406 for f in fields { free_vars(&f.value, bound, out); }
1407 }
1408 a::CExpr::TupleLit { items } | a::CExpr::ListLit { items } => {
1409 for it in items { free_vars(it, bound, out); }
1410 }
1411 a::CExpr::FieldAccess { value, .. } => free_vars(value, bound, out),
1412 a::CExpr::Lambda { params, body, .. } => {
1413 let mut inner = bound.clone();
1414 for p in params { inner.insert(p.name.clone()); }
1415 free_vars(body, &mut inner, out);
1416 }
1417 a::CExpr::BinOp { lhs, rhs, .. } => {
1418 free_vars(lhs, bound, out);
1419 free_vars(rhs, bound, out);
1420 }
1421 a::CExpr::UnaryOp { expr, .. } => free_vars(expr, bound, out),
1422 a::CExpr::Return { value } => free_vars(value, bound, out),
1423 }
1424}
1425
1426fn pattern_binders(p: &a::Pattern, bound: &mut std::collections::HashSet<String>) {
1427 match p {
1428 a::Pattern::PWild | a::Pattern::PLiteral { .. } => {}
1429 a::Pattern::PVar { name } => { bound.insert(name.clone()); }
1430 a::Pattern::PConstructor { args, .. } => {
1431 for a in args { pattern_binders(a, bound); }
1432 }
1433 a::Pattern::PRecord { fields } => {
1434 for f in fields { pattern_binders(&f.pattern, bound); }
1435 }
1436 a::Pattern::PTuple { items } => {
1437 for it in items { pattern_binders(it, bound); }
1438 }
1439 }
1440}