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 match &mut self.code[j] {
474 Op::JumpIfNot(off) => *off = fail_target - (j as i32 + 1),
475 Op::Jump(off) => *off = fail_target - (j as i32 + 1),
476 _ => {}
477 }
478 }
479 self.next_local = arm_start_locals;
480 self.locals = arm_start_locals_map;
481 }
482 let panic_msg_idx = self.pool.str("non-exhaustive match");
483 self.emit(Op::Panic(panic_msg_idx));
484
485 let end_target = self.code.len() as i32;
486 for j in end_jumps {
487 if let Op::Jump(off) = &mut self.code[j] {
488 *off = end_target - (j as i32 + 1);
489 }
490 }
491 }
492
493 fn compile_pattern_test(&mut self, p: &a::Pattern, bindings: &mut Vec<(String, u16)>) -> Vec<usize> {
494 let mut fails = Vec::new();
495 match p {
496 a::Pattern::PWild => { self.emit(Op::Pop); }
497 a::Pattern::PVar { name } => {
498 let slot = self.alloc_local(name);
499 self.emit(Op::StoreLocal(slot));
500 bindings.push((name.clone(), slot));
501 }
502 a::Pattern::PLiteral { value } => {
503 self.compile_lit(value);
504 match value {
505 a::CLit::Str { .. } => self.emit(Op::StrEq),
506 a::CLit::Bytes { .. } => self.emit(Op::BytesEq),
507 _ => self.emit(Op::NumEq),
508 }
509 let j = self.code.len();
510 self.emit(Op::JumpIfNot(0));
511 fails.push(j);
512 }
513 a::Pattern::PConstructor { name, args } => {
514 let name_idx = self.pool.variant(name);
515 self.emit(Op::Dup); self.emit(Op::TestVariant(name_idx)); let j_success = self.code.len();
532 self.emit(Op::JumpIf(0)); self.emit(Op::Pop); let j_fail = self.code.len();
535 self.emit(Op::Jump(0)); fails.push(j_fail);
537 let success_target = self.code.len() as i32;
538 if let Op::JumpIf(off) = &mut self.code[j_success] {
539 *off = success_target - (j_success as i32 + 1);
540 }
541 if args.is_empty() {
542 self.emit(Op::Pop);
543 } else if args.len() == 1 {
544 self.emit(Op::GetVariantArg(0));
545 let sub_fails = self.compile_pattern_test(&args[0], bindings);
546 fails.extend(sub_fails);
547 } else {
548 let slot = self.alloc_local("__variant");
549 self.emit(Op::StoreLocal(slot));
550 for (i, arg) in args.iter().enumerate() {
551 self.emit(Op::LoadLocal(slot));
552 self.emit(Op::GetVariantArg(i as u16));
553 let sub_fails = self.compile_pattern_test(arg, bindings);
554 fails.extend(sub_fails);
555 }
556 }
557 }
558 a::Pattern::PRecord { fields } => {
559 let slot = self.alloc_local("__record");
560 self.emit(Op::StoreLocal(slot));
561 for f in fields {
562 self.emit(Op::LoadLocal(slot));
563 let fi = self.pool.field(&f.name);
564 self.emit(Op::GetField(fi));
565 let sub_fails = self.compile_pattern_test(&f.pattern, bindings);
566 fails.extend(sub_fails);
567 }
568 }
569 a::Pattern::PTuple { items } => {
570 let slot = self.alloc_local("__tuple");
571 self.emit(Op::StoreLocal(slot));
572 for (i, item) in items.iter().enumerate() {
573 self.emit(Op::LoadLocal(slot));
574 self.emit(Op::GetElem(i as u16));
575 let sub_fails = self.compile_pattern_test(item, bindings);
576 fails.extend(sub_fails);
577 }
578 }
579 }
580 fails
581 }
582
583 fn compile_lambda(&mut self, params: &[a::Param], body: &a::CExpr) {
587 let mut bound: std::collections::HashSet<String> = params.iter().map(|p| p.name.clone()).collect();
589 let mut frees: Vec<String> = Vec::new();
590 free_vars(body, &mut bound, &mut frees);
591
592 let captures: Vec<String> = frees.into_iter()
601 .filter(|n| self.locals.contains_key(n))
602 .collect();
603
604 let fn_id = self.next_fn_id.len() as u32;
606 self.next_fn_id.push(Function {
607 name: format!("__lambda_{fn_id}"),
608 arity: (captures.len() + params.len()) as u16,
609 locals_count: 0,
610 code: Vec::new(),
611 effects: Vec::new(),
612 body_hash: crate::program::ZERO_BODY_HASH,
614 refinements: Vec::new(),
619 });
620
621 for c in &captures {
623 let slot = *self.locals.get(c).expect("free var must be in scope");
624 self.emit(Op::LoadLocal(slot));
625 }
626 self.emit(Op::MakeClosure { fn_id, capture_count: captures.len() as u16 });
627
628 self.pending_lambdas.push(PendingLambda {
630 fn_id,
631 capture_names: captures,
632 params: params.to_vec(),
633 body: body.clone(),
634 });
635 }
636
637 fn try_emit_higher_order(
641 &mut self,
642 module: &str,
643 op: &str,
644 args: &[a::CExpr],
645 node_id_idx: u32,
646 ) -> bool {
647 match (module, op) {
648 ("result", "map") => self.emit_variant_map(args, "Ok", true),
649 ("result", "and_then") => self.emit_variant_map(args, "Ok", false),
650 ("result", "map_err") => self.emit_variant_map(args, "Err", true),
651 ("result", "or_else") => self.emit_variant_or_else(args, "Err", 1),
652 ("option", "map") => self.emit_variant_map(args, "Some", true),
653 ("option", "and_then") => self.emit_variant_map(args, "Some", false),
654 ("option", "or_else") => self.emit_variant_or_else(args, "None", 0),
655 ("option", "unwrap_or_else") => self.emit_option_unwrap_or_else(args),
656 ("list", "map") => self.emit_list_map(args),
657 ("list", "par_map") => self.emit_list_par_map(args),
658 ("list", "sort_by") => self.emit_list_sort_by(args),
659 ("list", "filter") => self.emit_list_filter(args),
660 ("list", "fold") => self.emit_list_fold(args),
661 ("map", "fold") => self.emit_map_fold(args, node_id_idx),
662 ("flow", "sequential") => self.emit_flow_sequential(args),
663 ("flow", "branch") => self.emit_flow_branch(args),
664 ("flow", "retry") => self.emit_flow_retry(args),
665 ("flow", "retry_with_backoff") => self.emit_flow_retry_with_backoff(args),
666 ("flow", "parallel") => self.emit_flow_parallel(args),
667 ("flow", "parallel_list") => self.emit_flow_parallel_list(args),
668 _ => return false,
669 }
670 true
671 }
672
673 fn emit_list_map(&mut self, args: &[a::CExpr]) {
676 self.compile_expr(&args[0], false);
678 let xs = self.alloc_local("__lm_xs");
679 self.emit(Op::StoreLocal(xs));
680 self.compile_expr(&args[1], false);
681 let f = self.alloc_local("__lm_f");
682 self.emit(Op::StoreLocal(f));
683
684 self.emit(Op::MakeList(0));
686 let out = self.alloc_local("__lm_out");
687 self.emit(Op::StoreLocal(out));
688
689 let zero = self.pool.int(0);
691 self.emit(Op::PushConst(zero));
692 let i = self.alloc_local("__lm_i");
693 self.emit(Op::StoreLocal(i));
694
695 let loop_top = self.code.len();
697 self.emit(Op::LoadLocal(i));
698 self.emit(Op::LoadLocal(xs));
699 self.emit(Op::GetListLen);
700 self.emit(Op::NumLt);
701 let j_exit = self.code.len();
702 self.emit(Op::JumpIfNot(0));
703
704 let nid = self.pool.node_id("n_list_map");
706 self.emit(Op::LoadLocal(out));
707 self.emit(Op::LoadLocal(f));
708 self.emit(Op::LoadLocal(xs));
709 self.emit(Op::LoadLocal(i));
710 self.emit(Op::GetListElemDyn);
711 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
712 self.emit(Op::ListAppend);
713 self.emit(Op::StoreLocal(out));
714
715 self.emit(Op::LoadLocal(i));
717 let one = self.pool.int(1);
718 self.emit(Op::PushConst(one));
719 self.emit(Op::NumAdd);
720 self.emit(Op::StoreLocal(i));
721
722 let jump_back = self.code.len();
724 let back = (loop_top as i32) - (jump_back as i32 + 1);
725 self.emit(Op::Jump(back));
726
727 let exit_target = self.code.len() as i32;
729 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
730 *off = exit_target - (j_exit as i32 + 1);
731 }
732 self.emit(Op::LoadLocal(out));
733 }
734
735 fn emit_list_par_map(&mut self, args: &[a::CExpr]) {
741 self.compile_expr(&args[0], false);
742 self.compile_expr(&args[1], false);
743 let nid = self.pool.node_id("n_list_par_map");
744 self.emit(Op::ParallelMap { node_id_idx: nid });
745 }
746
747 fn emit_list_sort_by(&mut self, args: &[a::CExpr]) {
755 self.compile_expr(&args[0], false);
756 self.compile_expr(&args[1], false);
757 let nid = self.pool.node_id("n_list_sort_by");
758 self.emit(Op::SortByKey { node_id_idx: nid });
759 }
760
761 fn emit_list_filter(&mut self, args: &[a::CExpr]) {
763 self.compile_expr(&args[0], false);
764 let xs = self.alloc_local("__lf_xs");
765 self.emit(Op::StoreLocal(xs));
766 self.compile_expr(&args[1], false);
767 let f = self.alloc_local("__lf_f");
768 self.emit(Op::StoreLocal(f));
769
770 self.emit(Op::MakeList(0));
771 let out = self.alloc_local("__lf_out");
772 self.emit(Op::StoreLocal(out));
773
774 let zero = self.pool.int(0);
775 self.emit(Op::PushConst(zero));
776 let i = self.alloc_local("__lf_i");
777 self.emit(Op::StoreLocal(i));
778
779 let loop_top = self.code.len();
780 self.emit(Op::LoadLocal(i));
781 self.emit(Op::LoadLocal(xs));
782 self.emit(Op::GetListLen);
783 self.emit(Op::NumLt);
784 let j_exit = self.code.len();
785 self.emit(Op::JumpIfNot(0));
786
787 self.emit(Op::LoadLocal(xs));
789 self.emit(Op::LoadLocal(i));
790 self.emit(Op::GetListElemDyn);
791 let x = self.alloc_local("__lf_x");
792 self.emit(Op::StoreLocal(x));
793
794 let nid = self.pool.node_id("n_list_filter");
796 self.emit(Op::LoadLocal(f));
797 self.emit(Op::LoadLocal(x));
798 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
799 let j_skip = self.code.len();
800 self.emit(Op::JumpIfNot(0));
801
802 self.emit(Op::LoadLocal(out));
803 self.emit(Op::LoadLocal(x));
804 self.emit(Op::ListAppend);
805 self.emit(Op::StoreLocal(out));
806
807 let skip_target = self.code.len() as i32;
808 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
809 *off = skip_target - (j_skip as i32 + 1);
810 }
811
812 self.emit(Op::LoadLocal(i));
814 let one = self.pool.int(1);
815 self.emit(Op::PushConst(one));
816 self.emit(Op::NumAdd);
817 self.emit(Op::StoreLocal(i));
818
819 let jump_back = self.code.len();
820 let back = (loop_top as i32) - (jump_back as i32 + 1);
821 self.emit(Op::Jump(back));
822
823 let exit_target = self.code.len() as i32;
824 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
825 *off = exit_target - (j_exit as i32 + 1);
826 }
827 self.emit(Op::LoadLocal(out));
828 }
829
830 fn emit_list_fold(&mut self, args: &[a::CExpr]) {
832 self.compile_expr(&args[0], false);
834 let xs = self.alloc_local("__lo_xs");
835 self.emit(Op::StoreLocal(xs));
836 self.compile_expr(&args[1], false);
837 let acc = self.alloc_local("__lo_acc");
838 self.emit(Op::StoreLocal(acc));
839 self.compile_expr(&args[2], false);
840 let f = self.alloc_local("__lo_f");
841 self.emit(Op::StoreLocal(f));
842
843 let zero = self.pool.int(0);
844 self.emit(Op::PushConst(zero));
845 let i = self.alloc_local("__lo_i");
846 self.emit(Op::StoreLocal(i));
847
848 let loop_top = self.code.len();
849 self.emit(Op::LoadLocal(i));
850 self.emit(Op::LoadLocal(xs));
851 self.emit(Op::GetListLen);
852 self.emit(Op::NumLt);
853 let j_exit = self.code.len();
854 self.emit(Op::JumpIfNot(0));
855
856 let nid = self.pool.node_id("n_list_fold");
858 self.emit(Op::LoadLocal(f));
859 self.emit(Op::LoadLocal(acc));
860 self.emit(Op::LoadLocal(xs));
861 self.emit(Op::LoadLocal(i));
862 self.emit(Op::GetListElemDyn);
863 self.emit(Op::CallClosure { arity: 2, node_id_idx: nid });
864 self.emit(Op::StoreLocal(acc));
865
866 self.emit(Op::LoadLocal(i));
868 let one = self.pool.int(1);
869 self.emit(Op::PushConst(one));
870 self.emit(Op::NumAdd);
871 self.emit(Op::StoreLocal(i));
872
873 let jump_back = self.code.len();
874 let back = (loop_top as i32) - (jump_back as i32 + 1);
875 self.emit(Op::Jump(back));
876
877 let exit_target = self.code.len() as i32;
878 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
879 *off = exit_target - (j_exit as i32 + 1);
880 }
881 self.emit(Op::LoadLocal(acc));
882 }
883
884 fn emit_map_fold(&mut self, args: &[a::CExpr], node_id_idx: u32) {
890 self.compile_expr(&args[0], false);
892 let map_kind = self.pool.str("map");
893 let entries_op = self.pool.str("entries");
894 self.emit(Op::EffectCall {
895 kind_idx: map_kind,
896 op_idx: entries_op,
897 arity: 1,
898 node_id_idx,
899 });
900 let xs = self.alloc_local("__mf_xs");
901 self.emit(Op::StoreLocal(xs));
902
903 self.compile_expr(&args[1], false);
905 let acc = self.alloc_local("__mf_acc");
906 self.emit(Op::StoreLocal(acc));
907
908 self.compile_expr(&args[2], false);
910 let f = self.alloc_local("__mf_f");
911 self.emit(Op::StoreLocal(f));
912
913 let zero = self.pool.int(0);
915 self.emit(Op::PushConst(zero));
916 let i = self.alloc_local("__mf_i");
917 self.emit(Op::StoreLocal(i));
918
919 let loop_top = self.code.len();
921 self.emit(Op::LoadLocal(i));
922 self.emit(Op::LoadLocal(xs));
923 self.emit(Op::GetListLen);
924 self.emit(Op::NumLt);
925 let j_exit = self.code.len();
926 self.emit(Op::JumpIfNot(0));
927
928 self.emit(Op::LoadLocal(xs));
930 self.emit(Op::LoadLocal(i));
931 self.emit(Op::GetListElemDyn);
932 let pair = self.alloc_local("__mf_pair");
933 self.emit(Op::StoreLocal(pair));
934
935 let nid = self.pool.node_id("n_map_fold");
937 self.emit(Op::LoadLocal(f));
938 self.emit(Op::LoadLocal(acc));
939 self.emit(Op::LoadLocal(pair));
940 self.emit(Op::GetElem(0));
941 self.emit(Op::LoadLocal(pair));
942 self.emit(Op::GetElem(1));
943 self.emit(Op::CallClosure { arity: 3, node_id_idx: nid });
944 self.emit(Op::StoreLocal(acc));
945
946 self.emit(Op::LoadLocal(i));
948 let one = self.pool.int(1);
949 self.emit(Op::PushConst(one));
950 self.emit(Op::NumAdd);
951 self.emit(Op::StoreLocal(i));
952
953 let jump_back = self.code.len();
954 let back = (loop_top as i32) - (jump_back as i32 + 1);
955 self.emit(Op::Jump(back));
956
957 let exit_target = self.code.len() as i32;
958 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
959 *off = exit_target - (j_exit as i32 + 1);
960 }
961 self.emit(Op::LoadLocal(acc));
962 }
963
964 fn emit_variant_map(
970 &mut self,
971 args: &[a::CExpr],
972 wrap_with: &str,
973 wrap_result: bool,
974 ) {
975 let wrap_idx = self.pool.variant(wrap_with);
977
978 self.compile_expr(&args[0], false);
980 let val_slot = self.alloc_local("__hov");
981 self.emit(Op::StoreLocal(val_slot));
982
983 self.compile_expr(&args[1], false);
984 let f_slot = self.alloc_local("__hof");
985 self.emit(Op::StoreLocal(f_slot));
986
987 self.emit(Op::LoadLocal(val_slot));
994 self.emit(Op::Dup);
995 self.emit(Op::TestVariant(wrap_idx));
996 let j_skip = self.code.len();
997 self.emit(Op::JumpIfNot(0));
998
999 self.emit(Op::GetVariantArg(0));
1001 let arg_slot = self.alloc_local("__hov_arg");
1002 self.emit(Op::StoreLocal(arg_slot));
1003 self.emit(Op::LoadLocal(f_slot));
1004 self.emit(Op::LoadLocal(arg_slot));
1005 let nid = self.pool.node_id("n_hov");
1006 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
1007 if wrap_result {
1008 self.emit(Op::MakeVariant { name_idx: wrap_idx, arity: 1 });
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 emit_variant_or_else(
1034 &mut self,
1035 args: &[a::CExpr],
1036 match_on: &str,
1037 closure_arity: u16,
1038 ) {
1039 let match_idx = self.pool.variant(match_on);
1040
1041 self.compile_expr(&args[0], false);
1042 let val_slot = self.alloc_local("__hoe");
1043 self.emit(Op::StoreLocal(val_slot));
1044
1045 self.compile_expr(&args[1], false);
1046 let f_slot = self.alloc_local("__hoe_f");
1047 self.emit(Op::StoreLocal(f_slot));
1048
1049 self.emit(Op::LoadLocal(val_slot));
1057 self.emit(Op::Dup);
1058 self.emit(Op::TestVariant(match_idx));
1059 let j_skip = self.code.len();
1060 self.emit(Op::JumpIfNot(0));
1061
1062 self.emit(Op::Pop);
1065 self.emit(Op::LoadLocal(f_slot));
1066 if closure_arity == 1 {
1067 self.emit(Op::LoadLocal(val_slot));
1068 self.emit(Op::GetVariantArg(0));
1069 }
1070 let nid = self.pool.node_id("n_hoe");
1071 self.emit(Op::CallClosure { arity: closure_arity, node_id_idx: nid });
1072
1073 let j_end = self.code.len();
1074 self.emit(Op::Jump(0));
1075
1076 let skip_target = self.code.len() as i32;
1078 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
1079 *off = skip_target - (j_skip as i32 + 1);
1080 }
1081
1082 let end_target = self.code.len() as i32;
1083 if let Op::Jump(off) = &mut self.code[j_end] {
1084 *off = end_target - (j_end as i32 + 1);
1085 }
1086 }
1087
1088 fn emit_option_unwrap_or_else(&mut self, args: &[a::CExpr]) {
1092 let some_idx = self.pool.variant("Some");
1093
1094 self.compile_expr(&args[0], false);
1096 let val_slot = self.alloc_local("__uoe_val");
1097 self.emit(Op::StoreLocal(val_slot));
1098
1099 self.compile_expr(&args[1], false);
1100 let f_slot = self.alloc_local("__uoe_f");
1101 self.emit(Op::StoreLocal(f_slot));
1102
1103 self.emit(Op::LoadLocal(val_slot));
1109 self.emit(Op::Dup);
1110 self.emit(Op::TestVariant(some_idx));
1111 let j_none = self.code.len();
1112 self.emit(Op::JumpIfNot(0));
1113
1114 self.emit(Op::GetVariantArg(0));
1116 let j_end = self.code.len();
1117 self.emit(Op::Jump(0));
1118
1119 let none_target = self.code.len() as i32;
1121 if let Op::JumpIfNot(off) = &mut self.code[j_none] {
1122 *off = none_target - (j_none as i32 + 1);
1123 }
1124 self.emit(Op::Pop);
1125 self.emit(Op::LoadLocal(f_slot));
1126 let nid = self.pool.node_id("n_uoe");
1127 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
1128
1129 let end_target = self.code.len() as i32;
1131 if let Op::Jump(off) = &mut self.code[j_end] {
1132 *off = end_target - (j_end as i32 + 1);
1133 }
1134 }
1135
1136 fn install_trampoline(&mut self, name: &str, arity: u16, locals_count: u16, code: Vec<Op>) -> u32 {
1153 let fn_id = self.next_fn_id.len() as u32;
1154 let body_hash = crate::program::compute_body_hash(arity, locals_count, &code);
1155 self.next_fn_id.push(Function {
1156 name: name.into(),
1157 arity,
1158 locals_count,
1159 code,
1160 effects: Vec::new(),
1161 body_hash,
1162 refinements: Vec::new(),
1165 });
1166 fn_id
1167 }
1168
1169 fn emit_flow_sequential(&mut self, args: &[a::CExpr]) {
1171 self.compile_expr(&args[0], false);
1173 self.compile_expr(&args[1], false);
1174 let nid = self.pool.node_id("n_flow_sequential");
1175 let code = vec![
1176 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,
1186 ];
1187 let fn_id = self.install_trampoline("__flow_sequential", 3, 4, code);
1188 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
1189 }
1190
1191 fn emit_flow_parallel(&mut self, args: &[a::CExpr]) {
1199 self.compile_expr(&args[0], false);
1201 self.compile_expr(&args[1], false);
1202 let nid = self.pool.node_id("n_flow_parallel");
1203 let code = vec![
1204 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,
1211 ];
1212 let fn_id = self.install_trampoline("__flow_parallel", 2, 2, code);
1213 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
1214 }
1215
1216 fn emit_flow_parallel_list(&mut self, args: &[a::CExpr]) {
1223 self.compile_expr(&args[0], false);
1225 let xs = self.alloc_local("__fpl_xs");
1226 self.emit(Op::StoreLocal(xs));
1227
1228 self.emit(Op::MakeList(0));
1230 let out = self.alloc_local("__fpl_out");
1231 self.emit(Op::StoreLocal(out));
1232
1233 let zero = self.pool.int(0);
1235 self.emit(Op::PushConst(zero));
1236 let i = self.alloc_local("__fpl_i");
1237 self.emit(Op::StoreLocal(i));
1238
1239 let loop_top = self.code.len();
1241 self.emit(Op::LoadLocal(i));
1242 self.emit(Op::LoadLocal(xs));
1243 self.emit(Op::GetListLen);
1244 self.emit(Op::NumLt);
1245 let j_exit = self.code.len();
1246 self.emit(Op::JumpIfNot(0));
1247
1248 let nid = self.pool.node_id("n_flow_parallel_list");
1250 self.emit(Op::LoadLocal(out));
1251 self.emit(Op::LoadLocal(xs));
1252 self.emit(Op::LoadLocal(i));
1253 self.emit(Op::GetListElemDyn);
1254 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
1255 self.emit(Op::ListAppend);
1256 self.emit(Op::StoreLocal(out));
1257
1258 self.emit(Op::LoadLocal(i));
1260 let one = self.pool.int(1);
1261 self.emit(Op::PushConst(one));
1262 self.emit(Op::NumAdd);
1263 self.emit(Op::StoreLocal(i));
1264
1265 let jump_back = self.code.len();
1267 let back = (loop_top as i32) - (jump_back as i32 + 1);
1268 self.emit(Op::Jump(back));
1269
1270 let exit_target = self.code.len() as i32;
1272 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
1273 *off = exit_target - (j_exit as i32 + 1);
1274 }
1275 self.emit(Op::LoadLocal(out));
1276 }
1277
1278 fn emit_flow_branch(&mut self, args: &[a::CExpr]) {
1280 self.compile_expr(&args[0], false);
1281 self.compile_expr(&args[1], false);
1282 self.compile_expr(&args[2], false);
1283 let nid = self.pool.node_id("n_flow_branch");
1284 let mut code = vec![
1285 Op::LoadLocal(0), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, ];
1290 let j_false = code.len();
1291 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(1));
1294 code.push(Op::LoadLocal(3));
1295 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
1296 code.push(Op::Return);
1297 let false_target = code.len() as i32;
1299 if let Op::JumpIfNot(off) = &mut code[j_false] {
1300 *off = false_target - (j_false as i32 + 1);
1301 }
1302 code.push(Op::LoadLocal(2));
1303 code.push(Op::LoadLocal(3));
1304 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
1305 code.push(Op::Return);
1306
1307 let fn_id = self.install_trampoline("__flow_branch", 4, 4, code);
1308 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
1309 }
1310
1311 fn emit_flow_retry(&mut self, args: &[a::CExpr]) {
1315 self.compile_expr(&args[0], false);
1316 self.compile_expr(&args[1], false);
1317 let call_nid = self.pool.node_id("n_flow_retry");
1318 let ok_idx = self.pool.variant("Ok");
1319 let zero_const = self.pool.int(0);
1320 let one_const = self.pool.int(1);
1321 let mut code = vec![
1323 Op::PushConst(zero_const),
1325 Op::StoreLocal(3),
1326 ];
1327 let loop_top = code.len() as i32;
1329 code.push(Op::LoadLocal(3));
1330 code.push(Op::LoadLocal(1));
1331 code.push(Op::NumLt);
1332 let j_done = code.len();
1333 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(0));
1337 code.push(Op::LoadLocal(2));
1338 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
1339 code.push(Op::StoreLocal(4));
1340
1341 code.push(Op::LoadLocal(4));
1343 code.push(Op::TestVariant(ok_idx));
1344 let j_was_err = code.len();
1345 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(4));
1347 code.push(Op::Return);
1348
1349 let was_err_target = code.len() as i32;
1351 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
1352 *off = was_err_target - (j_was_err as i32 + 1);
1353 }
1354 code.push(Op::LoadLocal(3));
1355 code.push(Op::PushConst(one_const));
1356 code.push(Op::NumAdd);
1357 code.push(Op::StoreLocal(3));
1358 let pc_after_jump = code.len() as i32 + 1;
1359 code.push(Op::Jump(loop_top - pc_after_jump));
1360
1361 let done_target = code.len() as i32;
1363 if let Op::JumpIfNot(off) = &mut code[j_done] {
1364 *off = done_target - (j_done as i32 + 1);
1365 }
1366 code.push(Op::LoadLocal(4));
1367 code.push(Op::Return);
1368
1369 let fn_id = self.install_trampoline("__flow_retry", 3, 5, code);
1370 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
1371 }
1372
1373 fn emit_flow_retry_with_backoff(&mut self, args: &[a::CExpr]) {
1381 self.compile_expr(&args[0], false);
1384 self.compile_expr(&args[1], false);
1385 self.compile_expr(&args[2], false);
1386 let call_nid = self.pool.node_id("n_flow_retry_backoff");
1387 let sleep_nid = self.pool.node_id("n_flow_retry_backoff_sleep");
1388 let kind_idx = self.pool.str("time");
1389 let op_idx = self.pool.str("sleep_ms");
1390 let ok_idx = self.pool.variant("Ok");
1391 let zero_const = self.pool.int(0);
1392 let one_const = self.pool.int(1);
1393 let two_const = self.pool.int(2);
1394 let mut code = vec![
1399 Op::LoadLocal(2),
1401 Op::StoreLocal(6),
1402 Op::PushConst(zero_const),
1404 Op::StoreLocal(4),
1405 ];
1406
1407 let loop_top = code.len() as i32;
1408 code.push(Op::LoadLocal(4));
1410 code.push(Op::LoadLocal(1));
1411 code.push(Op::NumLt);
1412 let j_done = code.len();
1413 code.push(Op::JumpIfNot(0)); code.push(Op::PushConst(zero_const));
1417 code.push(Op::LoadLocal(4));
1418 code.push(Op::NumLt); let j_no_sleep = code.len();
1420 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(6)); code.push(Op::EffectCall {
1424 kind_idx, op_idx, arity: 1, node_id_idx: sleep_nid,
1425 });
1426 code.push(Op::Pop); code.push(Op::LoadLocal(6));
1429 code.push(Op::PushConst(two_const));
1430 code.push(Op::NumMul);
1431 code.push(Op::StoreLocal(6));
1432 let after_sleep = code.len() as i32;
1434 if let Op::JumpIfNot(off) = &mut code[j_no_sleep] {
1435 *off = after_sleep - (j_no_sleep as i32 + 1);
1436 }
1437
1438 code.push(Op::LoadLocal(0));
1440 code.push(Op::LoadLocal(3));
1441 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
1442 code.push(Op::StoreLocal(5));
1443
1444 code.push(Op::LoadLocal(5));
1446 code.push(Op::TestVariant(ok_idx));
1447 let j_was_err = code.len();
1448 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(5));
1450 code.push(Op::Return);
1451
1452 let was_err_target = code.len() as i32;
1454 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
1455 *off = was_err_target - (j_was_err as i32 + 1);
1456 }
1457 code.push(Op::LoadLocal(4));
1458 code.push(Op::PushConst(one_const));
1459 code.push(Op::NumAdd);
1460 code.push(Op::StoreLocal(4));
1461 let pc_after_jump = code.len() as i32 + 1;
1462 code.push(Op::Jump(loop_top - pc_after_jump));
1463
1464 let done_target = code.len() as i32;
1466 if let Op::JumpIfNot(off) = &mut code[j_done] {
1467 *off = done_target - (j_done as i32 + 1);
1468 }
1469 code.push(Op::LoadLocal(5));
1470 code.push(Op::Return);
1471
1472 let fn_id = self.install_trampoline("__flow_retry_backoff", 4, 7, code);
1473 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
1474 }
1475}
1476
1477fn free_vars(e: &a::CExpr, bound: &mut std::collections::HashSet<String>, out: &mut Vec<String>) {
1482 match e {
1483 a::CExpr::Literal { .. } => {}
1484 a::CExpr::Var { name } => {
1485 if !bound.contains(name) && !out.contains(name) {
1486 out.push(name.clone());
1487 }
1488 }
1489 a::CExpr::Call { callee, args } => {
1490 free_vars(callee, bound, out);
1491 for a in args { free_vars(a, bound, out); }
1492 }
1493 a::CExpr::Let { name, value, body, .. } => {
1494 free_vars(value, bound, out);
1495 let was_bound = bound.contains(name);
1496 bound.insert(name.clone());
1497 free_vars(body, bound, out);
1498 if !was_bound { bound.remove(name); }
1499 }
1500 a::CExpr::Match { scrutinee, arms } => {
1501 free_vars(scrutinee, bound, out);
1502 for arm in arms {
1503 let mut local_bound = bound.clone();
1504 pattern_binders(&arm.pattern, &mut local_bound);
1505 free_vars(&arm.body, &mut local_bound, out);
1506 }
1507 }
1508 a::CExpr::Block { statements, result } => {
1509 let mut local_bound = bound.clone();
1510 for s in statements { free_vars(s, &mut local_bound, out); }
1511 free_vars(result, &mut local_bound, out);
1512 }
1513 a::CExpr::Constructor { args, .. } => {
1514 for a in args { free_vars(a, bound, out); }
1515 }
1516 a::CExpr::RecordLit { fields } => {
1517 for f in fields { free_vars(&f.value, bound, out); }
1518 }
1519 a::CExpr::TupleLit { items } | a::CExpr::ListLit { items } => {
1520 for it in items { free_vars(it, bound, out); }
1521 }
1522 a::CExpr::FieldAccess { value, .. } => free_vars(value, bound, out),
1523 a::CExpr::Lambda { params, body, .. } => {
1524 let mut inner = bound.clone();
1525 for p in params { inner.insert(p.name.clone()); }
1526 free_vars(body, &mut inner, out);
1527 }
1528 a::CExpr::BinOp { lhs, rhs, .. } => {
1529 free_vars(lhs, bound, out);
1530 free_vars(rhs, bound, out);
1531 }
1532 a::CExpr::UnaryOp { expr, .. } => free_vars(expr, bound, out),
1533 a::CExpr::Return { value } => free_vars(value, bound, out),
1534 }
1535}
1536
1537fn pattern_binders(p: &a::Pattern, bound: &mut std::collections::HashSet<String>) {
1538 match p {
1539 a::Pattern::PWild | a::Pattern::PLiteral { .. } => {}
1540 a::Pattern::PVar { name } => { bound.insert(name.clone()); }
1541 a::Pattern::PConstructor { args, .. } => {
1542 for a in args { pattern_binders(a, bound); }
1543 }
1544 a::Pattern::PRecord { fields } => {
1545 for f in fields { pattern_binders(&f.pattern, bound); }
1546 }
1547 a::Pattern::PTuple { items } => {
1548 for it in items { pattern_binders(it, bound); }
1549 }
1550 }
1551}