1use std::collections::{BTreeMap, BTreeSet, HashMap};
4use std::sync::{Arc, Mutex};
5
6use async_recursion::async_recursion;
7use futures::future::BoxFuture;
8
9use bock_air::{
10 AIRNode, AirArg, AirInterpolationPart, AirRecordField, EnumVariantPayload, NodeKind,
11 ResultVariant,
12};
13use bock_ast::{AssignOp, BinOp, Literal, TypePath, UnaryOp};
14
15use crate::builtins::{BuiltinRegistry, CallbackInvoker, TypeTag};
16use crate::env::{EffectStack, Environment};
17use crate::error::RuntimeError;
18use crate::value::{BockString, EnumValue, FnValue, IteratorNext, OrdF64, RecordValue, Value};
19
20type NativeFn = std::sync::Arc<dyn Fn(&[Value]) -> Value + Send + Sync>;
24
25#[derive(Clone)]
27enum ClosureBody {
28 Air(Box<AIRNode>),
30 Composed { inner: u64, outer: u64 },
32 Native(NativeFn),
34}
35
36impl std::fmt::Debug for ClosureBody {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 ClosureBody::Air(node) => f.debug_tuple("Air").field(node).finish(),
40 ClosureBody::Composed { inner, outer } => f
41 .debug_struct("Composed")
42 .field("inner", inner)
43 .field("outer", outer)
44 .finish(),
45 ClosureBody::Native(_) => f.debug_tuple("Native").field(&"<fn>").finish(),
46 }
47 }
48}
49
50#[derive(Debug, Clone)]
52struct Closure {
53 params: Vec<String>,
54 body: ClosureBody,
55 captured: Environment,
56 is_toplevel: bool,
61 is_async: bool,
64}
65
66#[derive(Debug, Clone)]
68struct MethodEntry {
69 params: Vec<String>,
72 self_is_mut: bool,
76 body: AIRNode,
78}
79
80struct MethodOutcome {
82 value: Value,
84 updated_self: Option<Value>,
87}
88
89#[derive(Clone)]
97pub struct Interpreter {
98 pub env: Environment,
100 pub effect_handlers: EffectStack,
102 fn_registry: HashMap<u64, Closure>,
104 pub builtins: BuiltinRegistry,
106 method_table: HashMap<String, HashMap<String, MethodEntry>>,
108 effect_operations: HashMap<String, String>,
111}
112
113impl CallbackInvoker for Interpreter {
114 fn invoke<'a>(
115 &'a mut self,
116 callable: &'a Value,
117 args: &'a [Value],
118 ) -> BoxFuture<'a, Result<Value, RuntimeError>> {
119 Box::pin(async move { self.invoke_callback(callable, args).await })
120 }
121}
122
123impl Default for Interpreter {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl Interpreter {
130 #[must_use]
133 pub fn new() -> Self {
134 let mut builtins = BuiltinRegistry::new();
135 builtins.register_defaults();
136 let mut interp = Self {
137 env: Environment::new(),
138 effect_handlers: EffectStack::new(),
139 fn_registry: HashMap::new(),
140 builtins,
141 method_table: HashMap::new(),
142 effect_operations: HashMap::new(),
143 };
144 interp.register_prelude_constructors();
145 interp
146 }
147
148 fn register_prelude_constructors(&mut self) {
151 self.env.define("None", Value::Optional(None));
153
154 self.register_native_constructor("Some", 1, |args| {
156 Value::Optional(Some(Box::new(args[0].clone())))
157 });
158
159 self.register_native_constructor("Ok", 1, |args| {
161 Value::Result(Ok(Box::new(args[0].clone())))
162 });
163
164 self.register_native_constructor("Err", 1, |args| {
166 Value::Result(Err(Box::new(args[0].clone())))
167 });
168 }
169
170 fn register_native_constructor(
175 &mut self,
176 name: &str,
177 arity: usize,
178 build: impl Fn(&[Value]) -> Value + Send + Sync + 'static,
179 ) {
180 let fn_val = FnValue::new_named(name);
181 let id = fn_val.id;
182 let params: Vec<String> = (0..arity).map(|i| format!("__arg{i}")).collect();
183 self.fn_registry.insert(
184 id,
185 Closure {
186 params,
187 body: ClosureBody::Native(std::sync::Arc::new(build)),
188 captured: Environment::new(),
189 is_toplevel: true,
190 is_async: false,
191 },
192 );
193 self.env.define(name, Value::Function(fn_val));
194 }
195
196 pub fn register_enum(&mut self, enum_name: &str, variants: &[AIRNode]) {
202 let type_name = enum_name.to_string();
203 for variant in variants {
204 if let NodeKind::EnumVariant { name, payload } = &variant.kind {
205 let variant_name = name.name.clone();
206 match payload {
207 EnumVariantPayload::Unit => {
208 self.env.define(
209 variant_name.clone(),
210 Value::Enum(EnumValue {
211 type_name: type_name.clone(),
212 variant: variant_name,
213 payload: None,
214 }),
215 );
216 }
217 EnumVariantPayload::Tuple(fields) => {
218 let arity = fields.len();
219 let tn = type_name.clone();
220 let vn = variant_name.clone();
221 if arity == 1 {
222 self.register_native_constructor(&variant_name, arity, move |args| {
223 Value::Enum(EnumValue {
224 type_name: tn.clone(),
225 variant: vn.clone(),
226 payload: Some(Box::new(args[0].clone())),
227 })
228 });
229 } else {
230 self.register_native_constructor(&variant_name, arity, move |args| {
231 Value::Enum(EnumValue {
232 type_name: tn.clone(),
233 variant: vn.clone(),
234 payload: Some(Box::new(Value::Tuple(args.to_vec()))),
235 })
236 });
237 }
238 }
239 EnumVariantPayload::Struct(fields) => {
240 let arity = fields.len();
241 let vn = variant_name.clone();
242 let field_names: Vec<String> =
243 fields.iter().map(|f| f.name.name.clone()).collect();
244 self.register_native_constructor(&variant_name, arity, move |args| {
245 let mut field_map = std::collections::BTreeMap::new();
246 for (fname, val) in field_names.iter().zip(args.iter()) {
247 field_map.insert(fname.clone(), val.clone());
248 }
249 Value::Record(RecordValue {
250 type_name: vn.clone(),
251 fields: field_map,
252 })
253 });
254 }
255 }
256 }
257 }
258 }
259
260 pub fn register_fn(&mut self, name: &str, params: Vec<String>, body: AIRNode) {
265 self.register_fn_with_async(name, params, body, false);
266 }
267
268 pub fn register_fn_with_async(
274 &mut self,
275 name: &str,
276 params: Vec<String>,
277 body: AIRNode,
278 is_async: bool,
279 ) {
280 let fn_val = FnValue::new_named(name);
281 let id = fn_val.id;
282 self.env.define(name, Value::Function(fn_val));
283 self.fn_registry.insert(
284 id,
285 Closure {
286 params,
287 body: ClosureBody::Air(Box::new(body)),
288 captured: Environment::new(),
289 is_toplevel: true,
290 is_async,
291 },
292 );
293 }
294
295 pub fn register_impl(&mut self, target: &AIRNode, methods: &[AIRNode]) {
300 let type_name = match &target.kind {
302 NodeKind::TypeNamed { path, .. } => path
303 .segments
304 .last()
305 .map(|s| s.name.clone())
306 .unwrap_or_default(),
307 _ => return,
308 };
309
310 let type_methods = self.method_table.entry(type_name).or_default();
311 for method in methods {
312 if let NodeKind::FnDecl {
313 name, params, body, ..
314 } = &method.kind
315 {
316 let mut param_names: Vec<String> = Vec::with_capacity(params.len());
317 let mut self_is_mut = false;
318 for p in params {
319 if let NodeKind::Param { pattern, .. } = &p.kind {
320 if let NodeKind::BindPat {
321 name,
322 is_mut: bind_is_mut,
323 } = &pattern.kind
324 {
325 if param_names.is_empty() && name.name == "self" {
328 self_is_mut = *bind_is_mut;
329 }
330 param_names.push(name.name.clone());
331 }
332 }
333 }
334 type_methods.insert(
335 name.name.clone(),
336 MethodEntry {
337 params: param_names,
338 self_is_mut,
339 body: *body.clone(),
340 },
341 );
342 }
343 }
344 }
345
346 pub fn register_effect(&mut self, effect_name: &str, operations: &[AIRNode]) {
354 self.env.define(effect_name, Value::Void);
356
357 for op in operations {
358 if let NodeKind::FnDecl { name, .. } = &op.kind {
359 self.effect_operations
360 .insert(name.name.clone(), effect_name.to_string());
361 }
362 }
363 }
364
365 #[async_recursion]
369 pub async fn invoke_callback(
370 &mut self,
371 callable: &Value,
372 args: &[Value],
373 ) -> Result<Value, RuntimeError> {
374 let fn_id = match callable {
375 Value::Function(fv) => fv.id,
376 other => {
377 return Err(RuntimeError::NotCallable {
378 value: other.to_string(),
379 })
380 }
381 };
382 let closure =
383 self.fn_registry
384 .get(&fn_id)
385 .cloned()
386 .ok_or_else(|| RuntimeError::NotCallable {
387 value: format!("unregistered fn #{fn_id}"),
388 })?;
389 self.call_closure(&closure, args.to_vec()).await
390 }
391
392 #[async_recursion]
396 pub async fn eval_expr(&mut self, node: &AIRNode) -> Result<Value, RuntimeError> {
397 match &node.kind {
398 NodeKind::Literal { lit } => self.eval_literal(lit),
399
400 NodeKind::Identifier { name } => {
401 self.env
402 .get(&name.name)
403 .cloned()
404 .ok_or_else(|| RuntimeError::UndefinedVariable {
405 name: name.name.clone(),
406 })
407 }
408
409 NodeKind::BinaryOp { op, left, right } => {
410 if matches!(op, BinOp::Eq | BinOp::Ne)
422 && matches!(
423 node.metadata.get("user_eq"),
424 Some(bock_air::Value::String(kind)) if kind == "impl"
425 )
426 {
427 let l = self.eval_expr(left).await?;
428 let r = self.eval_expr(right).await?;
429 let eq = match self.try_call_impl_method(&l, "eq", vec![r.clone()]).await? {
430 Some(outcome) => matches!(outcome.value, Value::Bool(true)),
431 None => l == r,
435 };
436 return Ok(Value::Bool(if matches!(op, BinOp::Eq) { eq } else { !eq }));
437 }
438 if matches!(op, BinOp::Eq | BinOp::Ne)
447 && matches!(
448 node.metadata.get("user_eq"),
449 Some(bock_air::Value::String(kind)) if kind == "deep_custom"
450 )
451 {
452 let l = self.eval_expr(left).await?;
453 let r = self.eval_expr(right).await?;
454 let eq = self.values_equal_custom(&l, &r).await?;
455 return Ok(Value::Bool(if matches!(op, BinOp::Eq) { eq } else { !eq }));
456 }
457 self.eval_binary_op(*op, left, right).await
458 }
459
460 NodeKind::UnaryOp { op, operand } => self.eval_unary_op(*op, operand).await,
461
462 NodeKind::Assign { op, target, value } => self.eval_assign(*op, target, value).await,
463
464 NodeKind::Call { callee, args, .. } => self.eval_call(callee, args).await,
465
466 NodeKind::MethodCall {
467 receiver,
468 method,
469 args,
470 ..
471 } => {
472 self.eval_method_call(receiver, &method.name.clone(), args)
473 .await
474 }
475
476 NodeKind::FieldAccess { object, field } => {
477 self.eval_field_access(object, &field.name.clone()).await
478 }
479
480 NodeKind::Index { object, index } => self.eval_index(object, index).await,
481
482 NodeKind::Propagate { expr } => self.eval_propagate(expr).await,
483
484 NodeKind::Lambda { params, body } => self.eval_lambda(params, body),
485
486 NodeKind::Pipe { left, right } => self.eval_pipe(left, right).await,
487
488 NodeKind::Compose { left, right } => self.eval_compose(left, right).await,
489
490 NodeKind::ListLiteral { elems } => {
492 let mut values = Vec::with_capacity(elems.len());
493 for elem in elems {
494 values.push(self.eval_expr(elem).await?);
495 }
496 Ok(Value::List(values))
497 }
498
499 NodeKind::MapLiteral { entries } => {
500 let mut map = BTreeMap::new();
501 for entry in entries {
502 let k = self.eval_expr(&entry.key).await?;
503 let v = self.eval_expr(&entry.value).await?;
504 map.insert(k, v);
505 }
506 Ok(Value::Map(map))
507 }
508
509 NodeKind::SetLiteral { elems } => {
510 let mut set = BTreeSet::new();
511 for elem in elems {
512 set.insert(self.eval_expr(elem).await?);
513 }
514 Ok(Value::Set(set))
515 }
516
517 NodeKind::TupleLiteral { elems } => {
518 let mut values = Vec::with_capacity(elems.len());
519 for elem in elems {
520 values.push(self.eval_expr(elem).await?);
521 }
522 Ok(Value::Tuple(values))
523 }
524
525 NodeKind::RecordConstruct {
526 path,
527 fields,
528 spread,
529 } => {
530 self.eval_record_construct(path, fields, spread.as_deref())
531 .await
532 }
533
534 NodeKind::Interpolation { parts } => self.eval_interpolation(parts).await,
535
536 NodeKind::Range { lo, hi, inclusive } => self.eval_range(lo, hi, *inclusive).await,
537
538 NodeKind::ResultConstruct { variant, value } => {
539 let inner = match value {
540 Some(v) => self.eval_expr(v).await?,
541 None => Value::Void,
542 };
543 match variant {
544 ResultVariant::Ok => Ok(Value::Result(Ok(Box::new(inner)))),
545 ResultVariant::Err => Ok(Value::Result(Err(Box::new(inner)))),
546 }
547 }
548
549 NodeKind::Block { stmts, tail } => self.eval_block(stmts, tail.as_deref()).await,
551
552 NodeKind::If {
553 let_pattern,
554 condition,
555 then_block,
556 else_block,
557 } => {
558 self.eval_if(
559 let_pattern.as_deref(),
560 condition,
561 then_block,
562 else_block.as_deref(),
563 )
564 .await
565 }
566
567 NodeKind::Match { scrutinee, arms } => self.eval_match(scrutinee, arms).await,
568
569 NodeKind::Return { value } => {
570 let v = match value {
571 Some(e) => self.eval_expr(e).await?,
572 None => Value::Void,
573 };
574 Err(RuntimeError::Return(Box::new(v)))
575 }
576
577 NodeKind::Break { value } => {
578 let v = match value {
579 Some(e) => Some(Box::new(self.eval_expr(e).await?)),
580 None => None,
581 };
582 Err(RuntimeError::Break(v))
583 }
584
585 NodeKind::Continue => Err(RuntimeError::Continue),
586
587 NodeKind::For {
588 pattern,
589 iterable,
590 body,
591 } => self.eval_for(pattern, iterable, body).await,
592
593 NodeKind::While { condition, body } => self.eval_while(condition, body).await,
594
595 NodeKind::Loop { body } => self.eval_loop(body).await,
596
597 NodeKind::Guard {
598 let_pattern,
599 condition,
600 else_block,
601 } => {
602 self.eval_guard(let_pattern.as_deref(), condition, else_block)
603 .await
604 }
605
606 NodeKind::Unreachable => Err(RuntimeError::Unreachable),
607
608 NodeKind::Move { expr }
610 | NodeKind::Borrow { expr }
611 | NodeKind::MutableBorrow { expr } => self.eval_expr(expr).await,
612
613 NodeKind::Await { expr } => {
615 let val = self.eval_expr(expr).await?;
616 match val {
617 Value::Future(handle) => {
618 let h = handle.lock().unwrap().take();
619 match h {
620 Some(jh) => match jh.await {
621 Ok(inner) => inner,
622 Err(e) => Err(RuntimeError::TypeError(format!(
623 "async task panicked: {e}"
624 ))),
625 },
626 None => Err(RuntimeError::TypeError(
627 "future already awaited".to_string(),
628 )),
629 }
630 }
631 other => Ok(other),
632 }
633 }
634
635 NodeKind::LetBinding { pattern, value, .. } => {
637 let v = self.eval_expr(value).await?;
638 self.bind_pattern(pattern, v).await?;
639 Ok(Value::Void)
640 }
641
642 NodeKind::Placeholder => Ok(Value::Void),
644
645 NodeKind::EffectDecl { name, .. } => {
649 self.env.define(&name.name, Value::Void);
652 Ok(Value::Void)
653 }
654
655 NodeKind::ModuleHandle { effect, handler } => {
657 let handler_val = self.eval_expr(handler).await?;
658 let effect_name = self.type_path_to_name(effect);
659 self.effect_handlers
660 .set_module_handler(effect_name, handler_val);
661 Ok(Value::Void)
662 }
663
664 NodeKind::EffectOp {
666 effect,
667 operation,
668 args,
669 } => {
670 let effect_name = self.type_path_to_name(effect);
671 let handler = self.effect_handlers.resolve(&effect_name).cloned().ok_or(
672 RuntimeError::NoEffectHandler {
673 effect: effect_name,
674 },
675 )?;
676
677 let mut arg_values = Vec::with_capacity(args.len());
679 for arg in args {
680 arg_values.push(self.eval_expr(&arg.value).await?);
681 }
682
683 self.dispatch_effect_op(&handler, &operation.name, arg_values)
687 .await
688 }
689
690 NodeKind::HandlingBlock { handlers, body } => {
692 let mut frame = std::collections::HashMap::new();
693 for pair in handlers {
694 let handler_val = self.eval_expr(&pair.handler).await?;
695 let effect_name = self.type_path_to_name(&pair.effect);
696 frame.insert(effect_name, handler_val);
697 }
698 self.effect_handlers.push_handlers(frame);
699 let result = self.eval_expr(body).await;
700 self.effect_handlers.pop_handlers();
701 result
702 }
703
704 NodeKind::EffectRef { .. } => Ok(Value::Void),
706
707 other => Err(RuntimeError::NotImplemented(
708 format!("{other:?}").chars().take(60).collect(),
709 )),
710 }
711 }
712
713 fn type_path_to_name(&self, tp: &TypePath) -> String {
717 tp.segments
718 .iter()
719 .map(|s| s.name.as_str())
720 .collect::<Vec<_>>()
721 .join(".")
722 }
723
724 #[async_recursion]
735 async fn dispatch_effect_op(
736 &mut self,
737 handler: &Value,
738 operation: &str,
739 args: Vec<Value>,
740 ) -> Result<Value, RuntimeError> {
741 match handler {
742 Value::Record(rec) => {
745 if let Some(op_fn) = rec.fields.get(operation).cloned() {
746 return self.call_fn_value(&op_fn, args).await;
747 }
748 if let Some(outcome) = self.try_call_impl_method(handler, operation, args).await? {
749 return Ok(outcome.value);
752 }
753 Err(RuntimeError::FieldNotFound {
754 field: operation.to_string(),
755 type_name: rec.type_name.clone(),
756 })
757 }
758 Value::Function(_) => {
760 let handler = handler.clone();
761 self.call_fn_value(&handler, args).await
762 }
763 other => Err(RuntimeError::TypeError(format!(
764 "effect handler must be a record or function, got {other}"
765 ))),
766 }
767 }
768
769 #[async_recursion]
773 pub async fn call_fn_value(
774 &mut self,
775 val: &Value,
776 args: Vec<Value>,
777 ) -> Result<Value, RuntimeError> {
778 let fn_id = match val {
779 Value::Function(fv) => fv.id,
780 other => {
781 return Err(RuntimeError::NotCallable {
782 value: other.to_string(),
783 })
784 }
785 };
786 let closure =
787 self.fn_registry
788 .get(&fn_id)
789 .cloned()
790 .ok_or_else(|| RuntimeError::NotCallable {
791 value: format!("unregistered fn #{fn_id}"),
792 })?;
793 self.call_closure(&closure, args).await
794 }
795
796 fn eval_literal(&self, lit: &Literal) -> Result<Value, RuntimeError> {
799 match lit {
800 Literal::Int(s) => {
801 let (numeric, _) = bock_ast::strip_type_suffix(s);
803 let clean = numeric.replace('_', "");
805 let n = if clean.starts_with("0x") || clean.starts_with("0X") {
806 i64::from_str_radix(&clean[2..], 16)
807 } else if clean.starts_with("0o") || clean.starts_with("0O") {
808 i64::from_str_radix(&clean[2..], 8)
809 } else if clean.starts_with("0b") || clean.starts_with("0B") {
810 i64::from_str_radix(&clean[2..], 2)
811 } else {
812 clean.parse::<i64>()
813 };
814 n.map(Value::Int)
815 .map_err(|_| RuntimeError::IntParseFailed(s.clone()))
816 }
817 Literal::Float(s) => {
818 let (numeric, _) = bock_ast::strip_type_suffix(s);
820 numeric
821 .replace('_', "")
822 .parse::<f64>()
823 .map(|f| Value::Float(OrdF64(f)))
824 .map_err(|_| RuntimeError::FloatParseFailed(s.clone()))
825 }
826 Literal::Bool(b) => Ok(Value::Bool(*b)),
827 Literal::Char(s) => Ok(Value::Char(s.chars().next().unwrap_or('\0'))),
828 Literal::String(s) => Ok(Value::String(BockString::new(s.clone()))),
829 Literal::Unit => Ok(Value::Void),
830 }
831 }
832
833 #[async_recursion]
836 async fn eval_binary_op(
837 &mut self,
838 op: BinOp,
839 left: &AIRNode,
840 right: &AIRNode,
841 ) -> Result<Value, RuntimeError> {
842 match op {
844 BinOp::And => {
845 let l = self.eval_expr(left).await?;
846 return match l {
847 Value::Bool(false) => Ok(Value::Bool(false)),
848 Value::Bool(true) => self.eval_expr(right).await,
849 other => Err(RuntimeError::TypeError(format!(
850 "expected Bool in &&, got {other}"
851 ))),
852 };
853 }
854 BinOp::Or => {
855 let l = self.eval_expr(left).await?;
856 return match l {
857 Value::Bool(true) => Ok(Value::Bool(true)),
858 Value::Bool(false) => self.eval_expr(right).await,
859 other => Err(RuntimeError::TypeError(format!(
860 "expected Bool in ||, got {other}"
861 ))),
862 };
863 }
864 _ => {}
865 }
866
867 let l = self.eval_expr(left).await?;
868 let r = self.eval_expr(right).await?;
869
870 match (op, l, r) {
871 (BinOp::Add, Value::Int(a), Value::Int(b)) => a
873 .checked_add(b)
874 .map(Value::Int)
875 .ok_or(RuntimeError::IntOverflow),
876 (BinOp::Add, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 + b.0))),
877 (BinOp::Add, Value::String(a), Value::String(b)) => {
878 Ok(Value::String(BockString::new(format!("{a}{b}"))))
879 }
880 (BinOp::Add, Value::List(a), Value::List(b)) => {
885 let mut out = Vec::with_capacity(a.len() + b.len());
886 out.extend(a);
887 out.extend(b);
888 Ok(Value::List(out))
889 }
890
891 (BinOp::Sub, Value::Int(a), Value::Int(b)) => a
892 .checked_sub(b)
893 .map(Value::Int)
894 .ok_or(RuntimeError::IntOverflow),
895 (BinOp::Sub, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 - b.0))),
896
897 (BinOp::Mul, Value::Int(a), Value::Int(b)) => a
898 .checked_mul(b)
899 .map(Value::Int)
900 .ok_or(RuntimeError::IntOverflow),
901 (BinOp::Mul, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 * b.0))),
902
903 (BinOp::Div, Value::Int(a), Value::Int(b)) => {
904 if b == 0 {
905 Err(RuntimeError::DivisionByZero)
906 } else {
907 Ok(Value::Int(a / b))
908 }
909 }
910 (BinOp::Div, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 / b.0))),
911
912 (BinOp::Rem, Value::Int(a), Value::Int(b)) => {
913 if b == 0 {
914 Err(RuntimeError::DivisionByZero)
915 } else {
916 Ok(Value::Int(a % b))
917 }
918 }
919 (BinOp::Rem, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 % b.0))),
920
921 (BinOp::Pow, Value::Int(a), Value::Int(b)) => {
922 if b < 0 {
923 Err(RuntimeError::TypeError(
924 "negative integer exponent".to_string(),
925 ))
926 } else if b > u32::MAX as i64 {
927 Err(RuntimeError::IntOverflow)
928 } else {
929 a.checked_pow(b as u32)
930 .map(Value::Int)
931 .ok_or(RuntimeError::IntOverflow)
932 }
933 }
934 (BinOp::Pow, Value::Float(a), Value::Float(b)) => {
935 Ok(Value::Float(OrdF64(a.0.powf(b.0))))
936 }
937
938 (BinOp::Eq, l, r) => Ok(Value::Bool(l == r)),
940 (BinOp::Ne, l, r) => Ok(Value::Bool(l != r)),
941 (BinOp::Lt, l, r) => Ok(Value::Bool(l < r)),
942 (BinOp::Le, l, r) => Ok(Value::Bool(l <= r)),
943 (BinOp::Gt, l, r) => Ok(Value::Bool(l > r)),
944 (BinOp::Ge, l, r) => Ok(Value::Bool(l >= r)),
945
946 (BinOp::BitAnd, Value::Int(a), Value::Int(b)) => Ok(Value::Int(a & b)),
948 (BinOp::BitOr, Value::Int(a), Value::Int(b)) => Ok(Value::Int(a | b)),
949 (BinOp::BitXor, Value::Int(a), Value::Int(b)) => Ok(Value::Int(a ^ b)),
950 (BinOp::Compose, Value::Function(f), Value::Function(g)) => {
953 let fn_val = FnValue::new_anonymous();
954 let id = fn_val.id;
955 self.fn_registry.insert(
956 id,
957 Closure {
958 params: vec!["__x".to_string()],
959 body: ClosureBody::Composed {
960 inner: f.id,
961 outer: g.id,
962 },
963 captured: self.env.clone(),
964 is_toplevel: false,
965 is_async: false,
966 },
967 );
968 Ok(Value::Function(fn_val))
969 }
970
971 (BinOp::Is, value, Value::String(type_name)) => {
973 let tag = TypeTag::of(&value);
974 Ok(Value::Bool(tag.name() == type_name.as_str()))
975 }
976
977 (op, l, r) => {
981 let tag = TypeTag::of(&l);
982 let method = match op {
983 BinOp::Add => Some("add"),
984 BinOp::Sub => Some("sub"),
985 BinOp::Mul => Some("mul"),
986 BinOp::Div => Some("div"),
987 BinOp::Rem => Some("rem"),
988 BinOp::Pow => Some("pow"),
989 _ => None,
990 };
991 if let Some(name) = method {
992 if let Some(result) = self.builtins.call(tag, name, &[l.clone(), r.clone()]) {
993 return result;
994 }
995 }
996 Err(RuntimeError::TypeError(format!(
997 "operator {op:?} not supported for {l} and {r}"
998 )))
999 }
1000 }
1001 }
1002
1003 #[async_recursion]
1006 async fn eval_unary_op(
1007 &mut self,
1008 op: UnaryOp,
1009 operand: &AIRNode,
1010 ) -> Result<Value, RuntimeError> {
1011 let val = self.eval_expr(operand).await?;
1012 match (op, val) {
1013 (UnaryOp::Neg, Value::Int(n)) => n
1014 .checked_neg()
1015 .map(Value::Int)
1016 .ok_or(RuntimeError::IntOverflow),
1017 (UnaryOp::Neg, Value::Float(f)) => Ok(Value::Float(OrdF64(-f.0))),
1018 (UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
1019 (UnaryOp::BitNot, Value::Int(n)) => Ok(Value::Int(!n)),
1020 (op, val) => Err(RuntimeError::TypeError(format!(
1021 "unary operator {op:?} not supported for {val}"
1022 ))),
1023 }
1024 }
1025
1026 #[async_recursion]
1029 async fn eval_assign(
1030 &mut self,
1031 op: AssignOp,
1032 target: &AIRNode,
1033 value: &AIRNode,
1034 ) -> Result<Value, RuntimeError> {
1035 let rhs = self.eval_expr(value).await?;
1036 match &target.kind {
1037 NodeKind::Identifier { name } => {
1038 let name = name.name.clone();
1039 let new_val = match op {
1040 AssignOp::Assign => rhs,
1041 compound => {
1042 let current = self.env.get(&name).cloned().ok_or_else(|| {
1043 RuntimeError::UndefinedVariable { name: name.clone() }
1044 })?;
1045 self.apply_assign_op(compound, current, rhs)?
1046 }
1047 };
1048 if !self.env.assign(&name, new_val) {
1049 return Err(RuntimeError::UndefinedVariable { name });
1050 }
1051 Ok(Value::Void)
1052 }
1053 NodeKind::FieldAccess { object, field } => {
1054 let field_name = field.name.clone();
1055 let obj = self.eval_expr(object).await?;
1056 match obj {
1057 Value::Record(mut rv) => {
1058 let new_val = match op {
1059 AssignOp::Assign => rhs,
1060 compound => {
1061 let current =
1062 rv.fields.get(&field_name).cloned().ok_or_else(|| {
1063 RuntimeError::FieldNotFound {
1064 field: field_name.clone(),
1065 type_name: rv.type_name.clone(),
1066 }
1067 })?;
1068 self.apply_assign_op(compound, current, rhs)?
1069 }
1070 };
1071 rv.fields.insert(field_name, new_val);
1072 if let NodeKind::Identifier { name: obj_name } = &object.kind {
1074 let updated = Value::Record(rv);
1075 if !self.env.assign(&obj_name.name, updated) {
1076 return Err(RuntimeError::UndefinedVariable {
1077 name: obj_name.name.clone(),
1078 });
1079 }
1080 }
1081 Ok(Value::Void)
1082 }
1083 other => Err(RuntimeError::TypeError(format!(
1084 "cannot assign to field '{field_name}' on {other}"
1085 ))),
1086 }
1087 }
1088 NodeKind::Index { object, index } => {
1089 let idx = self.eval_expr(index).await?;
1090 let obj = self.eval_expr(object).await?;
1091 match (obj, idx) {
1092 (Value::List(mut items), Value::Int(i)) => {
1093 if i < 0 || i as usize >= items.len() {
1094 return Err(RuntimeError::IndexOutOfBounds {
1095 index: i,
1096 len: items.len(),
1097 });
1098 }
1099 let new_val = match op {
1100 AssignOp::Assign => rhs,
1101 compound => {
1102 let current = items[i as usize].clone();
1103 self.apply_assign_op(compound, current, rhs)?
1104 }
1105 };
1106 items[i as usize] = new_val;
1107 if let NodeKind::Identifier { name: obj_name } = &object.kind {
1108 let updated = Value::List(items);
1109 if !self.env.assign(&obj_name.name, updated) {
1110 return Err(RuntimeError::UndefinedVariable {
1111 name: obj_name.name.clone(),
1112 });
1113 }
1114 }
1115 Ok(Value::Void)
1116 }
1117 (Value::Map(mut map), key) => {
1118 let new_val = match op {
1119 AssignOp::Assign => rhs,
1120 compound => {
1121 let current = map.get(&key).cloned().ok_or_else(|| {
1122 RuntimeError::TypeError(format!("key not found: {key}"))
1123 })?;
1124 self.apply_assign_op(compound, current, rhs)?
1125 }
1126 };
1127 map.insert(key, new_val);
1128 if let NodeKind::Identifier { name: obj_name } = &object.kind {
1129 let updated = Value::Map(map);
1130 if !self.env.assign(&obj_name.name, updated) {
1131 return Err(RuntimeError::UndefinedVariable {
1132 name: obj_name.name.clone(),
1133 });
1134 }
1135 }
1136 Ok(Value::Void)
1137 }
1138 (obj, idx) => Err(RuntimeError::TypeError(format!(
1139 "cannot index-assign {obj} with {idx}"
1140 ))),
1141 }
1142 }
1143 _ => Err(RuntimeError::NotImplemented(
1144 "unsupported assignment target".to_string(),
1145 )),
1146 }
1147 }
1148
1149 fn apply_assign_op(&self, op: AssignOp, lhs: Value, rhs: Value) -> Result<Value, RuntimeError> {
1150 match (op, lhs, rhs) {
1151 (AssignOp::AddAssign, Value::Int(a), Value::Int(b)) => a
1152 .checked_add(b)
1153 .map(Value::Int)
1154 .ok_or(RuntimeError::IntOverflow),
1155 (AssignOp::SubAssign, Value::Int(a), Value::Int(b)) => a
1156 .checked_sub(b)
1157 .map(Value::Int)
1158 .ok_or(RuntimeError::IntOverflow),
1159 (AssignOp::MulAssign, Value::Int(a), Value::Int(b)) => a
1160 .checked_mul(b)
1161 .map(Value::Int)
1162 .ok_or(RuntimeError::IntOverflow),
1163 (AssignOp::DivAssign, Value::Int(a), Value::Int(b)) => {
1164 if b == 0 {
1165 Err(RuntimeError::DivisionByZero)
1166 } else {
1167 Ok(Value::Int(a / b))
1168 }
1169 }
1170 (AssignOp::RemAssign, Value::Int(a), Value::Int(b)) => {
1171 if b == 0 {
1172 Err(RuntimeError::DivisionByZero)
1173 } else {
1174 Ok(Value::Int(a % b))
1175 }
1176 }
1177 (AssignOp::AddAssign, Value::Float(a), Value::Float(b)) => {
1178 Ok(Value::Float(OrdF64(a.0 + b.0)))
1179 }
1180 (AssignOp::SubAssign, Value::Float(a), Value::Float(b)) => {
1181 Ok(Value::Float(OrdF64(a.0 - b.0)))
1182 }
1183 (AssignOp::MulAssign, Value::Float(a), Value::Float(b)) => {
1184 Ok(Value::Float(OrdF64(a.0 * b.0)))
1185 }
1186 (AssignOp::DivAssign, Value::Float(a), Value::Float(b)) => {
1187 Ok(Value::Float(OrdF64(a.0 / b.0)))
1188 }
1189 (AssignOp::AddAssign, Value::String(a), Value::String(b)) => {
1190 Ok(Value::String(BockString::new(format!("{a}{b}"))))
1191 }
1192 (op, l, r) => Err(RuntimeError::TypeError(format!(
1193 "compound assignment {op:?} not supported for {l} and {r}"
1194 ))),
1195 }
1196 }
1197
1198 #[async_recursion]
1201 async fn eval_call(
1202 &mut self,
1203 callee: &AIRNode,
1204 args: &[AirArg],
1205 ) -> Result<Value, RuntimeError> {
1206 if let NodeKind::Identifier { name } = &callee.kind {
1208 if self.builtins.has_global(&name.name) {
1209 let mut arg_values: Vec<Value> = Vec::with_capacity(args.len());
1210 for a in args {
1211 arg_values.push(self.eval_expr(&a.value).await?);
1212 }
1213 return self
1214 .builtins
1215 .call_global(&name.name, &arg_values)
1216 .expect("has_global check confirmed builtin exists");
1217 }
1218
1219 if let Some(effect_name) = self.effect_operations.get(&name.name).cloned() {
1221 let handler = self.effect_handlers.resolve(&effect_name).cloned().ok_or(
1222 RuntimeError::NoEffectHandler {
1223 effect: effect_name,
1224 },
1225 )?;
1226 let mut arg_values = Vec::with_capacity(args.len());
1227 for arg in args {
1228 arg_values.push(self.eval_expr(&arg.value).await?);
1229 }
1230 return self
1231 .dispatch_effect_op(&handler, &name.name, arg_values)
1232 .await;
1233 }
1234 }
1235
1236 if let NodeKind::FieldAccess { object, field } = &callee.kind {
1241 if let NodeKind::Identifier { name: type_name } = &object.kind {
1242 let qualified = format!("{}.{}", type_name.name, field.name);
1243 if self.builtins.has_global(&qualified) {
1244 let mut arg_values = Vec::with_capacity(args.len());
1245 for a in args {
1246 arg_values.push(self.eval_expr(&a.value).await?);
1247 }
1248 return self
1249 .builtins
1250 .call_global(&qualified, &arg_values)
1251 .expect("has_global check confirmed builtin exists");
1252 }
1253 if self.method_table.contains_key(&type_name.name)
1261 && self.env.get(&type_name.name).is_none()
1262 {
1263 let mut arg_values = Vec::with_capacity(args.len());
1264 for a in args {
1265 arg_values.push(self.eval_expr(&a.value).await?);
1266 }
1267 if let Some(result) = self
1268 .try_call_assoc_fn(&type_name.name, &field.name, arg_values)
1269 .await?
1270 {
1271 return Ok(result);
1272 }
1273 }
1274 }
1275 }
1276
1277 if let NodeKind::FieldAccess { object, field } = &callee.kind {
1281 let recv = self.eval_expr(object).await?;
1282 let type_tag = TypeTag::of(&recv);
1283 if matches!(recv, Value::Record(_))
1291 && user_impl_may_shadow_record_builtin(&field.name)
1292 && self
1293 .method_table
1294 .get(record_type_name(&recv).unwrap_or(""))
1295 .is_some_and(|m| m.contains_key(&field.name))
1296 {
1297 let mut method_args = Vec::with_capacity(args.len().saturating_sub(1));
1298 for a in args.iter().skip(1) {
1299 method_args.push(self.eval_expr(&a.value).await?);
1300 }
1301 if let Some(outcome) = self
1302 .try_call_impl_method(&recv, &field.name, method_args)
1303 .await?
1304 {
1305 if let Some(updated) = outcome.updated_self {
1306 self.write_back_receiver(object, updated);
1307 }
1308 return Ok(outcome.value);
1309 }
1310 }
1311 if let Value::List(items) = &recv {
1316 if Self::LIST_INPLACE_MUTATORS.contains(&field.name.as_str()) {
1317 let items = items.clone();
1318 let mut m_args = Vec::with_capacity(args.len().saturating_sub(1));
1319 for a in args.iter().skip(1) {
1320 m_args.push(self.eval_expr(&a.value).await?);
1321 }
1322 return self.run_list_inplace_mutator(object, items, &field.name, m_args);
1323 }
1324 }
1325 if self.builtins.has_method(type_tag, &field.name) {
1326 let mut builtin_args = Vec::with_capacity(args.len());
1327 builtin_args.push(recv);
1328 for a in args.iter().skip(1) {
1330 builtin_args.push(self.eval_expr(&a.value).await?);
1331 }
1332 if let Some(ho_func) = self.builtins.get_ho_method(type_tag, &field.name) {
1334 return ho_func(&builtin_args, self).await;
1335 }
1336 return self
1337 .builtins
1338 .call(type_tag, &field.name, &builtin_args)
1339 .expect("has_method check confirmed builtin exists");
1340 }
1341 let mut method_args = Vec::with_capacity(args.len().saturating_sub(1));
1343 for a in args.iter().skip(1) {
1344 method_args.push(self.eval_expr(&a.value).await?);
1345 }
1346 if let Some(outcome) = self
1347 .try_call_impl_method(&recv, &field.name, method_args)
1348 .await?
1349 {
1350 if let Some(updated) = outcome.updated_self {
1352 self.write_back_receiver(object, updated);
1353 }
1354 return Ok(outcome.value);
1355 }
1356 return self.eval_method_call(object, &field.name, &args[1..]).await;
1359 }
1360
1361 let fn_val = self.eval_expr(callee).await?;
1362 let fn_id = match &fn_val {
1363 Value::Function(fv) => fv.id,
1364 other => {
1365 return Err(RuntimeError::NotCallable {
1366 value: other.to_string(),
1367 })
1368 }
1369 };
1370 let mut arg_values: Vec<Value> = Vec::with_capacity(args.len());
1371 for a in args {
1372 arg_values.push(self.eval_expr(&a.value).await?);
1373 }
1374 let closure =
1375 self.fn_registry
1376 .get(&fn_id)
1377 .cloned()
1378 .ok_or_else(|| RuntimeError::NotCallable {
1379 value: format!("unregistered fn #{fn_id}"),
1380 })?;
1381 self.call_closure(&closure, arg_values).await
1382 }
1383
1384 #[async_recursion]
1385 async fn call_closure(
1386 &mut self,
1387 closure: &Closure,
1388 args: Vec<Value>,
1389 ) -> Result<Value, RuntimeError> {
1390 match &closure.body {
1391 ClosureBody::Air(body) => {
1392 if closure.params.len() != args.len() {
1393 return Err(RuntimeError::ArityMismatch {
1394 expected: closure.params.len(),
1395 got: args.len(),
1396 });
1397 }
1398 let body = *body.clone();
1399
1400 if closure.is_async {
1401 let mut sub = self.clone();
1406 if !closure.is_toplevel {
1407 sub.env = closure.captured.clone();
1408 }
1409 sub.env.push_scope();
1410 for (name, val) in closure.params.iter().zip(args) {
1411 sub.env.define(name.clone(), val);
1412 }
1413 let handle: tokio::task::JoinHandle<Result<Value, RuntimeError>> =
1414 tokio::spawn(async move {
1415 match sub.eval_expr(&body).await {
1416 Err(RuntimeError::Return(v) | RuntimeError::Propagated(v)) => {
1420 Ok(*v)
1421 }
1422 other => other,
1423 }
1424 });
1425 return Ok(Value::Future(Arc::new(Mutex::new(Some(handle)))));
1426 }
1427
1428 let saved_env = if closure.is_toplevel {
1431 self.env.clone()
1432 } else {
1433 std::mem::replace(&mut self.env, closure.captured.clone())
1434 };
1435 self.env.push_scope();
1436 for (name, val) in closure.params.iter().zip(args) {
1437 self.env.define(name.clone(), val);
1438 }
1439 let result = self.eval_expr(&body).await;
1440 self.env = saved_env;
1441 match result {
1449 Err(RuntimeError::Return(v) | RuntimeError::Propagated(v)) => Ok(*v),
1450 other => other,
1451 }
1452 }
1453 ClosureBody::Composed { inner, outer } => {
1454 let inner_id = *inner;
1455 let outer_id = *outer;
1456 let inner_closure = self.fn_registry.get(&inner_id).cloned().ok_or_else(|| {
1457 RuntimeError::NotCallable {
1458 value: format!("composed inner fn #{inner_id}"),
1459 }
1460 })?;
1461 let intermediate = self.call_closure(&inner_closure, args).await?;
1462 let outer_closure = self.fn_registry.get(&outer_id).cloned().ok_or_else(|| {
1463 RuntimeError::NotCallable {
1464 value: format!("composed outer fn #{outer_id}"),
1465 }
1466 })?;
1467 self.call_closure(&outer_closure, vec![intermediate]).await
1468 }
1469 ClosureBody::Native(build) => {
1470 if closure.params.len() != args.len() {
1471 return Err(RuntimeError::ArityMismatch {
1472 expected: closure.params.len(),
1473 got: args.len(),
1474 });
1475 }
1476 Ok(build(&args))
1477 }
1478 }
1479 }
1480
1481 fn eval_lambda(&mut self, params: &[AIRNode], body: &AIRNode) -> Result<Value, RuntimeError> {
1484 let param_names: Vec<String> = params
1485 .iter()
1486 .map(|p| match &p.kind {
1487 NodeKind::Param { pattern, .. } => match &pattern.kind {
1488 NodeKind::BindPat { name, .. } => name.name.clone(),
1489 NodeKind::WildcardPat => "_".to_string(),
1490 _ => "_".to_string(),
1491 },
1492 _ => "_".to_string(),
1493 })
1494 .collect();
1495
1496 let fn_val = FnValue::new_anonymous();
1497 let id = fn_val.id;
1498 let captured = self.env.clone();
1499 self.fn_registry.insert(
1500 id,
1501 Closure {
1502 params: param_names,
1503 body: ClosureBody::Air(Box::new(body.clone())),
1504 captured,
1505 is_toplevel: false,
1506 is_async: false,
1507 },
1508 );
1509 Ok(Value::Function(fn_val))
1510 }
1511
1512 #[async_recursion]
1515 async fn eval_pipe(&mut self, left: &AIRNode, right: &AIRNode) -> Result<Value, RuntimeError> {
1516 let lhs = self.eval_expr(left).await?;
1517 match &right.kind.clone() {
1519 NodeKind::Call { callee, args, .. } => {
1520 let callee = callee.clone();
1521 let args: Vec<AirArg> = args.clone();
1522 let fn_val = self.eval_expr(&callee).await?;
1523 let fn_id = match &fn_val {
1524 Value::Function(fv) => fv.id,
1525 other => {
1526 return Err(RuntimeError::NotCallable {
1527 value: other.to_string(),
1528 })
1529 }
1530 };
1531 let mut arg_values = vec![lhs];
1532 for a in &args {
1533 arg_values.push(self.eval_expr(&a.value).await?);
1534 }
1535 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1536 RuntimeError::NotCallable {
1537 value: format!("unregistered fn #{fn_id}"),
1538 }
1539 })?;
1540 self.call_closure(&closure, arg_values).await
1541 }
1542 _ => {
1543 let fn_val = self.eval_expr(right).await?;
1545 let fn_id = match &fn_val {
1546 Value::Function(fv) => fv.id,
1547 other => {
1548 return Err(RuntimeError::NotCallable {
1549 value: other.to_string(),
1550 })
1551 }
1552 };
1553 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1554 RuntimeError::NotCallable {
1555 value: format!("unregistered fn #{fn_id}"),
1556 }
1557 })?;
1558 self.call_closure(&closure, vec![lhs]).await
1559 }
1560 }
1561 }
1562
1563 #[async_recursion]
1566 async fn eval_compose(
1567 &mut self,
1568 left: &AIRNode,
1569 right: &AIRNode,
1570 ) -> Result<Value, RuntimeError> {
1571 let f = self.eval_expr(left).await?;
1572 let g = self.eval_expr(right).await?;
1573 let f_id = match &f {
1574 Value::Function(fv) => fv.id,
1575 other => {
1576 return Err(RuntimeError::TypeError(format!(
1577 ">> requires functions, got {other}"
1578 )))
1579 }
1580 };
1581 let g_id = match &g {
1582 Value::Function(fv) => fv.id,
1583 other => {
1584 return Err(RuntimeError::TypeError(format!(
1585 ">> requires functions, got {other}"
1586 )))
1587 }
1588 };
1589 let fn_val = FnValue::new_anonymous();
1590 let id = fn_val.id;
1591 self.fn_registry.insert(
1592 id,
1593 Closure {
1594 params: vec!["__x".to_string()],
1595 body: ClosureBody::Composed {
1596 inner: f_id,
1597 outer: g_id,
1598 },
1599 captured: self.env.clone(),
1600 is_toplevel: false,
1601 is_async: false,
1602 },
1603 );
1604 Ok(Value::Function(fn_val))
1605 }
1606
1607 #[async_recursion]
1610 async fn eval_method_call(
1611 &mut self,
1612 receiver: &AIRNode,
1613 method: &str,
1614 args: &[AirArg],
1615 ) -> Result<Value, RuntimeError> {
1616 let recv = self.eval_expr(receiver).await?;
1617 let method = method.to_string();
1618 let mut arg_values: Vec<Value> = Vec::with_capacity(args.len());
1619 for a in args {
1620 arg_values.push(self.eval_expr(&a.value).await?);
1621 }
1622
1623 if matches!(recv, Value::Record(_))
1629 && user_impl_may_shadow_record_builtin(&method)
1630 && self
1631 .method_table
1632 .get(record_type_name(&recv).unwrap_or(""))
1633 .is_some_and(|m| m.contains_key(&method))
1634 {
1635 if let Some(outcome) = self
1636 .try_call_impl_method(&recv, &method, arg_values.clone())
1637 .await?
1638 {
1639 if let Some(updated) = outcome.updated_self {
1640 self.write_back_receiver(receiver, updated);
1641 }
1642 return Ok(outcome.value);
1643 }
1644 }
1645
1646 if let Value::List(items) = &recv {
1650 if Self::LIST_INPLACE_MUTATORS.contains(&method.as_str()) {
1651 let items = items.clone();
1652 return self.run_list_inplace_mutator(receiver, items, &method, arg_values);
1653 }
1654 }
1655
1656 let type_tag = TypeTag::of(&recv);
1658 {
1659 let mut builtin_args = Vec::with_capacity(1 + arg_values.len());
1660 builtin_args.push(recv.clone());
1661 builtin_args.extend(arg_values.iter().cloned());
1662 if let Some(ho_func) = self.builtins.get_ho_method(type_tag, &method) {
1664 return ho_func(&builtin_args, self).await;
1665 }
1666 if let Some(result) = self.builtins.call(type_tag, &method, &builtin_args) {
1667 return result;
1668 }
1669 }
1670
1671 match (&recv, method.as_str()) {
1672 (_, "to_string") => Ok(Value::String(BockString::new(recv.to_string()))),
1674
1675 (recv_v, "eq") if !matches!(recv_v, Value::Function(_)) => {
1685 let other = arg_values.first().ok_or(RuntimeError::ArityMismatch {
1686 expected: 1,
1687 got: 0,
1688 })?;
1689 Ok(Value::Bool(recv == *other))
1690 }
1691
1692 (Value::List(items), "len") => Ok(Value::Int(items.len() as i64)),
1694 (Value::List(items), "is_empty") => Ok(Value::Bool(items.is_empty())),
1695 (Value::List(items), "first") => {
1696 Ok(Value::Optional(items.first().cloned().map(Box::new)))
1697 }
1698 (Value::List(items), "last") => {
1699 Ok(Value::Optional(items.last().cloned().map(Box::new)))
1700 }
1701 (Value::List(items), "get") => {
1702 let idx = arg_values.first().ok_or(RuntimeError::ArityMismatch {
1703 expected: 1,
1704 got: 0,
1705 })?;
1706 if let Value::Int(i) = idx {
1707 let i = *i;
1708 if i < 0 || i as usize >= items.len() {
1709 Ok(Value::Optional(None))
1710 } else {
1711 Ok(Value::Optional(Some(Box::new(items[i as usize].clone()))))
1712 }
1713 } else {
1714 Err(RuntimeError::TypeError(
1715 "List.get expects an Int index".to_string(),
1716 ))
1717 }
1718 }
1719 (Value::List(items), "contains") => {
1723 let v = arg_values.first().ok_or(RuntimeError::ArityMismatch {
1724 expected: 1,
1725 got: 0,
1726 })?;
1727 Ok(Value::Bool(items.contains(v)))
1728 }
1729 (Value::List(items), "map") => {
1730 let fn_val = arg_values
1731 .into_iter()
1732 .next()
1733 .ok_or(RuntimeError::ArityMismatch {
1734 expected: 1,
1735 got: 0,
1736 })?;
1737 let fn_id = match fn_val {
1738 Value::Function(ref fv) => fv.id,
1739 other => {
1740 return Err(RuntimeError::TypeError(format!(
1741 "List.map expects a function, got {other}"
1742 )))
1743 }
1744 };
1745 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1746 RuntimeError::NotCallable {
1747 value: "unregistered fn".to_string(),
1748 }
1749 })?;
1750 let items = items.clone();
1751 let mut result = Vec::with_capacity(items.len());
1752 for item in items {
1753 result.push(self.call_closure(&closure, vec![item]).await?);
1754 }
1755 Ok(Value::List(result))
1756 }
1757 (Value::List(items), "filter") => {
1758 let fn_val = arg_values
1759 .into_iter()
1760 .next()
1761 .ok_or(RuntimeError::ArityMismatch {
1762 expected: 1,
1763 got: 0,
1764 })?;
1765 let fn_id = match fn_val {
1766 Value::Function(ref fv) => fv.id,
1767 other => {
1768 return Err(RuntimeError::TypeError(format!(
1769 "List.filter expects a function, got {other}"
1770 )))
1771 }
1772 };
1773 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1774 RuntimeError::NotCallable {
1775 value: "unregistered fn".to_string(),
1776 }
1777 })?;
1778 let items = items.clone();
1779 let mut result = Vec::new();
1780 for item in items {
1781 if let Value::Bool(true) =
1782 self.call_closure(&closure, vec![item.clone()]).await?
1783 {
1784 result.push(item);
1785 }
1786 }
1787 Ok(Value::List(result))
1788 }
1789 (Value::List(items), "fold") | (Value::List(items), "reduce") => {
1790 if arg_values.len() != 2 {
1791 return Err(RuntimeError::ArityMismatch {
1792 expected: 2,
1793 got: arg_values.len(),
1794 });
1795 }
1796 let mut acc = arg_values.remove(0);
1797 let fn_val = arg_values.remove(0);
1798 let fn_id = match fn_val {
1799 Value::Function(ref fv) => fv.id,
1800 other => {
1801 return Err(RuntimeError::TypeError(format!(
1802 "List.fold expects a function, got {other}"
1803 )))
1804 }
1805 };
1806 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1807 RuntimeError::NotCallable {
1808 value: "unregistered fn".to_string(),
1809 }
1810 })?;
1811 let items = items.clone();
1812 for item in items {
1813 acc = self.call_closure(&closure, vec![acc, item]).await?;
1814 }
1815 Ok(acc)
1816 }
1817 (Value::List(items), "any") => {
1818 let fn_val = arg_values
1819 .into_iter()
1820 .next()
1821 .ok_or(RuntimeError::ArityMismatch {
1822 expected: 1,
1823 got: 0,
1824 })?;
1825 let fn_id = match fn_val {
1826 Value::Function(ref fv) => fv.id,
1827 other => {
1828 return Err(RuntimeError::TypeError(format!(
1829 "List.any expects a function, got {other}"
1830 )))
1831 }
1832 };
1833 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1834 RuntimeError::NotCallable {
1835 value: "unregistered fn".to_string(),
1836 }
1837 })?;
1838 let items = items.clone();
1839 for item in items {
1840 if let Value::Bool(true) = self.call_closure(&closure, vec![item]).await? {
1841 return Ok(Value::Bool(true));
1842 }
1843 }
1844 Ok(Value::Bool(false))
1845 }
1846 (Value::List(items), "all") => {
1847 let fn_val = arg_values
1848 .into_iter()
1849 .next()
1850 .ok_or(RuntimeError::ArityMismatch {
1851 expected: 1,
1852 got: 0,
1853 })?;
1854 let fn_id = match fn_val {
1855 Value::Function(ref fv) => fv.id,
1856 other => {
1857 return Err(RuntimeError::TypeError(format!(
1858 "List.all expects a function, got {other}"
1859 )))
1860 }
1861 };
1862 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1863 RuntimeError::NotCallable {
1864 value: "unregistered fn".to_string(),
1865 }
1866 })?;
1867 let items = items.clone();
1868 for item in items {
1869 if let Value::Bool(false) = self.call_closure(&closure, vec![item]).await? {
1870 return Ok(Value::Bool(false));
1871 }
1872 }
1873 Ok(Value::Bool(true))
1874 }
1875 (Value::List(items), "find") => {
1876 let fn_val = arg_values
1877 .into_iter()
1878 .next()
1879 .ok_or(RuntimeError::ArityMismatch {
1880 expected: 1,
1881 got: 0,
1882 })?;
1883 let fn_id = match fn_val {
1884 Value::Function(ref fv) => fv.id,
1885 other => {
1886 return Err(RuntimeError::TypeError(format!(
1887 "List.find expects a function, got {other}"
1888 )))
1889 }
1890 };
1891 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1892 RuntimeError::NotCallable {
1893 value: "unregistered fn".to_string(),
1894 }
1895 })?;
1896 let items = items.clone();
1897 for item in items {
1898 if let Value::Bool(true) =
1899 self.call_closure(&closure, vec![item.clone()]).await?
1900 {
1901 return Ok(Value::Optional(Some(Box::new(item))));
1902 }
1903 }
1904 Ok(Value::Optional(None))
1905 }
1906 (Value::List(items), "for_each") => {
1907 let fn_val = arg_values
1908 .into_iter()
1909 .next()
1910 .ok_or(RuntimeError::ArityMismatch {
1911 expected: 1,
1912 got: 0,
1913 })?;
1914 let fn_id = match fn_val {
1915 Value::Function(ref fv) => fv.id,
1916 other => {
1917 return Err(RuntimeError::TypeError(format!(
1918 "List.for_each expects a function, got {other}"
1919 )))
1920 }
1921 };
1922 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1923 RuntimeError::NotCallable {
1924 value: "unregistered fn".to_string(),
1925 }
1926 })?;
1927 let items = items.clone();
1928 for item in items {
1929 self.call_closure(&closure, vec![item]).await?;
1930 }
1931 Ok(Value::Void)
1932 }
1933 (Value::List(items), "flat_map") => {
1934 let fn_val = arg_values
1935 .into_iter()
1936 .next()
1937 .ok_or(RuntimeError::ArityMismatch {
1938 expected: 1,
1939 got: 0,
1940 })?;
1941 let fn_id = match fn_val {
1942 Value::Function(ref fv) => fv.id,
1943 other => {
1944 return Err(RuntimeError::TypeError(format!(
1945 "List.flat_map expects a function, got {other}"
1946 )))
1947 }
1948 };
1949 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1950 RuntimeError::NotCallable {
1951 value: "unregistered fn".to_string(),
1952 }
1953 })?;
1954 let items = items.clone();
1955 let mut result = Vec::new();
1956 for item in items {
1957 match self.call_closure(&closure, vec![item]).await? {
1958 Value::List(inner) => result.extend(inner),
1959 other => result.push(other),
1960 }
1961 }
1962 Ok(Value::List(result))
1963 }
1964 (Value::List(items), "sort") => {
1965 let mut v = items.clone();
1966 v.sort();
1967 Ok(Value::List(v))
1968 }
1969 (Value::List(items), "dedup") => {
1970 let mut v = items.clone();
1971 v.dedup();
1972 Ok(Value::List(v))
1973 }
1974 (Value::List(items), "flatten") => {
1975 let mut result = Vec::new();
1976 for item in items {
1977 match item {
1978 Value::List(inner) => result.extend(inner.iter().cloned()),
1979 other => result.push(other.clone()),
1980 }
1981 }
1982 Ok(Value::List(result))
1983 }
1984 (Value::List(items), "zip") => {
1985 let other = match arg_values.first() {
1986 Some(Value::List(l)) => l,
1987 _ => {
1988 return Err(RuntimeError::TypeError(
1989 "List.zip expects a List argument".to_string(),
1990 ))
1991 }
1992 };
1993 let pairs: Vec<Value> = items
1994 .iter()
1995 .zip(other.iter())
1996 .map(|(a, b)| Value::Tuple(vec![a.clone(), b.clone()]))
1997 .collect();
1998 Ok(Value::List(pairs))
1999 }
2000 (Value::List(items), "concat") => {
2001 let other = match arg_values.first() {
2002 Some(Value::List(l)) => l,
2003 _ => {
2004 return Err(RuntimeError::TypeError(
2005 "List.concat expects a List argument".to_string(),
2006 ))
2007 }
2008 };
2009 let mut new_list = items.clone();
2010 new_list.extend_from_slice(other);
2011 Ok(Value::List(new_list))
2012 }
2013 (Value::List(items), "slice") => {
2014 if arg_values.len() < 2 {
2015 return Err(RuntimeError::ArityMismatch {
2016 expected: 2,
2017 got: arg_values.len(),
2018 });
2019 }
2020 let start = match &arg_values[0] {
2021 Value::Int(i) => *i,
2022 _ => {
2023 return Err(RuntimeError::TypeError(
2024 "List.slice expects Int arguments".to_string(),
2025 ))
2026 }
2027 };
2028 let end = match &arg_values[1] {
2029 Value::Int(i) => *i,
2030 _ => {
2031 return Err(RuntimeError::TypeError(
2032 "List.slice expects Int arguments".to_string(),
2033 ))
2034 }
2035 };
2036 let len = items.len() as i64;
2037 let start = start.max(0).min(len) as usize;
2038 let end = end.max(0).min(len) as usize;
2039 if start >= end {
2040 Ok(Value::List(vec![]))
2041 } else {
2042 Ok(Value::List(items[start..end].to_vec()))
2043 }
2044 }
2045 (Value::List(items), "take") => {
2046 let n = match arg_values.first() {
2047 Some(Value::Int(i)) => *i,
2048 _ => return Err(RuntimeError::TypeError("List.take expects Int".to_string())),
2049 };
2050 let n = (n.max(0) as usize).min(items.len());
2051 Ok(Value::List(items[..n].to_vec()))
2052 }
2053 (Value::List(items), "skip") => {
2054 let n = match arg_values.first() {
2055 Some(Value::Int(i)) => *i,
2056 _ => return Err(RuntimeError::TypeError("List.skip expects Int".to_string())),
2057 };
2058 let n = (n.max(0) as usize).min(items.len());
2059 Ok(Value::List(items[n..].to_vec()))
2060 }
2061 (Value::List(items), "enumerate") => {
2062 let result: Vec<Value> = items
2063 .iter()
2064 .enumerate()
2065 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
2066 .collect();
2067 Ok(Value::List(result))
2068 }
2069 (Value::List(items), "count") => Ok(Value::Int(items.len() as i64)),
2070 (Value::List(items), "index_of") => {
2071 let needle = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2072 expected: 1,
2073 got: 0,
2074 })?;
2075 match items.iter().position(|v| v == needle) {
2076 Some(pos) => Ok(Value::Optional(Some(Box::new(Value::Int(pos as i64))))),
2077 None => Ok(Value::Optional(None)),
2078 }
2079 }
2080 (Value::List(items), "join") => {
2081 let sep = match arg_values.first() {
2082 Some(Value::String(s)) => s.as_str().to_owned(),
2083 _ => String::new(),
2084 };
2085 let parts: Vec<String> = items.iter().map(|v| v.to_string()).collect();
2086 Ok(Value::String(BockString::new(parts.join(&sep))))
2087 }
2088 (Value::List(items), "to_set") => {
2089 let set: std::collections::BTreeSet<Value> = items.iter().cloned().collect();
2090 Ok(Value::Set(set))
2091 }
2092
2093 (Value::String(s), "len") => Ok(Value::Int(s.as_str().chars().count() as i64)),
2095 (Value::String(s), "is_empty") => Ok(Value::Bool(s.as_str().is_empty())),
2096 (Value::String(s), "to_upper") => {
2097 Ok(Value::String(BockString::new(s.as_str().to_uppercase())))
2098 }
2099 (Value::String(s), "to_lower") => {
2100 Ok(Value::String(BockString::new(s.as_str().to_lowercase())))
2101 }
2102 (Value::String(s), "trim") => Ok(Value::String(BockString::new(s.as_str().trim()))),
2103 (Value::String(s), "contains") => {
2104 if let Some(Value::String(sub)) = arg_values.first() {
2105 Ok(Value::Bool(s.as_str().contains(sub.as_str())))
2106 } else {
2107 Err(RuntimeError::TypeError(
2108 "String.contains expects a String".to_string(),
2109 ))
2110 }
2111 }
2112 (Value::String(s), "starts_with") => {
2113 if let Some(Value::String(prefix)) = arg_values.first() {
2114 Ok(Value::Bool(s.as_str().starts_with(prefix.as_str())))
2115 } else {
2116 Err(RuntimeError::TypeError(
2117 "String.starts_with expects a String".to_string(),
2118 ))
2119 }
2120 }
2121 (Value::String(s), "ends_with") => {
2122 if let Some(Value::String(suffix)) = arg_values.first() {
2123 Ok(Value::Bool(s.as_str().ends_with(suffix.as_str())))
2124 } else {
2125 Err(RuntimeError::TypeError(
2126 "String.ends_with expects a String".to_string(),
2127 ))
2128 }
2129 }
2130 (Value::String(s), "split") => {
2131 let sep = match arg_values.first() {
2132 Some(Value::String(sep)) => sep.as_str().to_owned(),
2133 _ => {
2134 return Err(RuntimeError::TypeError(
2135 "String.split expects a String separator".to_string(),
2136 ))
2137 }
2138 };
2139 let parts: Vec<Value> = s
2140 .as_str()
2141 .split(&sep)
2142 .map(|p| Value::String(BockString::new(p)))
2143 .collect();
2144 Ok(Value::List(parts))
2145 }
2146 (Value::String(s), "replace") => {
2147 if arg_values.len() < 2 {
2148 return Err(RuntimeError::ArityMismatch {
2149 expected: 2,
2150 got: arg_values.len(),
2151 });
2152 }
2153 let old = match &arg_values[0] {
2154 Value::String(s) => s.as_str().to_owned(),
2155 _ => {
2156 return Err(RuntimeError::TypeError(
2157 "String.replace expects String arguments".to_string(),
2158 ))
2159 }
2160 };
2161 let new_s = match &arg_values[1] {
2162 Value::String(s) => s.as_str().to_owned(),
2163 _ => {
2164 return Err(RuntimeError::TypeError(
2165 "String.replace expects String arguments".to_string(),
2166 ))
2167 }
2168 };
2169 Ok(Value::String(BockString::new(
2170 s.as_str().replace(&old, &new_s),
2171 )))
2172 }
2173 (Value::String(s), "chars") => {
2174 let chars: Vec<Value> = s.as_str().chars().map(Value::Char).collect();
2175 Ok(Value::List(chars))
2176 }
2177 (Value::String(s), "substring") => {
2178 if arg_values.len() < 2 {
2179 return Err(RuntimeError::ArityMismatch {
2180 expected: 2,
2181 got: arg_values.len(),
2182 });
2183 }
2184 let start = match &arg_values[0] {
2185 Value::Int(i) => *i,
2186 _ => {
2187 return Err(RuntimeError::TypeError(
2188 "String.substring expects Int arguments".to_string(),
2189 ))
2190 }
2191 };
2192 let end = match &arg_values[1] {
2193 Value::Int(i) => *i,
2194 _ => {
2195 return Err(RuntimeError::TypeError(
2196 "String.substring expects Int arguments".to_string(),
2197 ))
2198 }
2199 };
2200 let chars: Vec<char> = s.as_str().chars().collect();
2201 let len = chars.len() as i64;
2202 let start = start.max(0).min(len) as usize;
2203 let end = end.max(0).min(len) as usize;
2204 if start >= end {
2205 Ok(Value::String(BockString::new("")))
2206 } else {
2207 Ok(Value::String(BockString::new(
2208 chars[start..end].iter().collect::<String>(),
2209 )))
2210 }
2211 }
2212
2213 (Value::Map(map), "len") => Ok(Value::Int(map.len() as i64)),
2215 (Value::Map(map), "is_empty") => Ok(Value::Bool(map.is_empty())),
2216 (Value::Map(map), "contains_key") => {
2217 let k = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2218 expected: 1,
2219 got: 0,
2220 })?;
2221 Ok(Value::Bool(map.contains_key(k)))
2222 }
2223 (Value::Map(map), "get") => {
2224 let k = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2225 expected: 1,
2226 got: 0,
2227 })?;
2228 Ok(Value::Optional(map.get(k).cloned().map(Box::new)))
2229 }
2230 (Value::Map(map), "set") => {
2231 if arg_values.len() < 2 {
2232 return Err(RuntimeError::ArityMismatch {
2233 expected: 2,
2234 got: arg_values.len(),
2235 });
2236 }
2237 let mut new_map = map.clone();
2238 new_map.insert(arg_values.remove(0), arg_values.remove(0));
2239 Ok(Value::Map(new_map))
2240 }
2241 (Value::Map(map), "delete") => {
2242 let k = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2243 expected: 1,
2244 got: 0,
2245 })?;
2246 let mut new_map = map.clone();
2247 new_map.remove(k);
2248 Ok(Value::Map(new_map))
2249 }
2250 (Value::Map(map), "merge") => {
2251 let other = match arg_values.first() {
2252 Some(Value::Map(m)) => m,
2253 _ => {
2254 return Err(RuntimeError::TypeError(
2255 "Map.merge expects a Map argument".to_string(),
2256 ))
2257 }
2258 };
2259 let mut new_map = map.clone();
2260 for (k, v) in other {
2261 new_map.insert(k.clone(), v.clone());
2262 }
2263 Ok(Value::Map(new_map))
2264 }
2265 (Value::Map(map), "keys") => Ok(Value::List(map.keys().cloned().collect())),
2266 (Value::Map(map), "values") => Ok(Value::List(map.values().cloned().collect())),
2267 (Value::Map(map), "entries") => {
2268 let entries: Vec<Value> = map
2269 .iter()
2270 .map(|(k, v)| Value::Tuple(vec![k.clone(), v.clone()]))
2271 .collect();
2272 Ok(Value::List(entries))
2273 }
2274
2275 (Value::Set(set), "len") => Ok(Value::Int(set.len() as i64)),
2277 (Value::Set(set), "is_empty") => Ok(Value::Bool(set.is_empty())),
2278 (Value::Set(set), "contains") => {
2279 let v = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2280 expected: 1,
2281 got: 0,
2282 })?;
2283 Ok(Value::Bool(set.contains(v)))
2284 }
2285
2286 (
2288 Value::Range {
2289 start,
2290 end,
2291 inclusive,
2292 ..
2293 },
2294 "step",
2295 ) => {
2296 let s = start;
2297 let e = end;
2298 let i = inclusive;
2299 if let Some(Value::Int(step)) = arg_values.first() {
2300 Ok(Value::Range {
2301 start: *s,
2302 end: *e,
2303 inclusive: *i,
2304 step: *step,
2305 })
2306 } else {
2307 Err(RuntimeError::TypeError(
2308 "Range.step expects an Int".to_string(),
2309 ))
2310 }
2311 }
2312 (
2313 Value::Range {
2314 start,
2315 end,
2316 inclusive,
2317 step,
2318 },
2319 "to_list",
2320 ) => Ok(Value::List(range_to_vec(*start, *end, *inclusive, *step))),
2321
2322 (Value::Optional(Some(inner)), "unwrap") => Ok(*inner.clone()),
2324 (Value::Optional(None), "unwrap") => {
2325 Err(RuntimeError::TypeError("unwrapped None".to_string()))
2326 }
2327 (Value::Optional(opt), "unwrap_or") => {
2328 let default = arg_values
2329 .into_iter()
2330 .next()
2331 .ok_or(RuntimeError::ArityMismatch {
2332 expected: 1,
2333 got: 0,
2334 })?;
2335 Ok(opt.as_deref().cloned().unwrap_or(default))
2336 }
2337 (Value::Result(Ok(inner)), "unwrap") => Ok(*inner.clone()),
2338 (Value::Result(Err(e)), "unwrap") => {
2339 Err(RuntimeError::TypeError(format!("unwrapped Err({e})")))
2340 }
2341
2342 (recv_val, _) => {
2344 if let Some(outcome) = self
2346 .try_call_impl_method(recv_val, &method, arg_values.clone())
2347 .await?
2348 {
2349 if let Some(updated) = outcome.updated_self {
2351 self.write_back_receiver(receiver, updated);
2352 }
2353 return Ok(outcome.value);
2354 }
2355 if let Some(fn_val) = self.env.get(&method).cloned() {
2356 let fn_id = match &fn_val {
2357 Value::Function(fv) => fv.id,
2358 _ => {
2359 return Err(RuntimeError::TypeError(format!(
2360 "method '{method}' not found on {type_tag}"
2361 )))
2362 }
2363 };
2364 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
2365 RuntimeError::NotCallable {
2366 value: format!("unregistered method '{method}'"),
2367 }
2368 })?;
2369 let mut all_args = vec![recv_val.clone()];
2370 all_args.extend(arg_values);
2371 self.call_closure(&closure, all_args).await
2372 } else {
2373 Err(RuntimeError::TypeError(format!(
2374 "method '{method}' not found on {type_tag}"
2375 )))
2376 }
2377 }
2378 }
2379 }
2380
2381 #[async_recursion]
2395 async fn try_call_impl_method(
2396 &mut self,
2397 receiver: &Value,
2398 method: &str,
2399 args: Vec<Value>,
2400 ) -> Result<Option<MethodOutcome>, RuntimeError> {
2401 let type_name = match receiver {
2402 Value::Record(rv) => &rv.type_name,
2403 _ => return Ok(None),
2404 };
2405
2406 let entry = self
2407 .method_table
2408 .get(type_name)
2409 .and_then(|methods| methods.get(method))
2410 .cloned();
2411
2412 let MethodEntry {
2413 params: param_names,
2414 self_is_mut,
2415 body,
2416 } = match entry {
2417 Some(e) => e,
2418 None => return Ok(None),
2419 };
2420
2421 if param_names.first().map(|s| s.as_str()) == Some("self") {
2425 let (value, updated_self) = self
2426 .run_method_body(
2427 ¶m_names,
2428 &body,
2429 Some(receiver.clone()),
2430 self_is_mut,
2431 args,
2432 )
2433 .await?;
2434 Ok(Some(MethodOutcome {
2435 value,
2436 updated_self,
2437 }))
2438 } else {
2439 let (value, _) = self
2440 .run_method_body(¶m_names, &body, None, false, args)
2441 .await?;
2442 Ok(Some(MethodOutcome {
2443 value,
2444 updated_self: None,
2445 }))
2446 }
2447 }
2448
2449 #[async_recursion]
2459 async fn values_equal_custom(&mut self, a: &Value, b: &Value) -> Result<bool, RuntimeError> {
2460 match (a, b) {
2461 (Value::Record(rv), Value::Record(_))
2466 if self
2467 .method_table
2468 .get(&rv.type_name)
2469 .is_some_and(|m| m.contains_key("eq")) =>
2470 {
2471 match self.try_call_impl_method(a, "eq", vec![b.clone()]).await? {
2472 Some(outcome) => Ok(matches!(outcome.value, Value::Bool(true))),
2473 None => Ok(a == b),
2474 }
2475 }
2476 (Value::Record(ra), Value::Record(rb)) => {
2477 if ra.type_name != rb.type_name || ra.fields.len() != rb.fields.len() {
2478 return Ok(false);
2479 }
2480 for (k, av) in &ra.fields {
2481 match rb.fields.get(k) {
2482 Some(bv) if self.values_equal_custom(av, bv).await? => {}
2483 _ => return Ok(false),
2484 }
2485 }
2486 Ok(true)
2487 }
2488 (Value::List(xs), Value::List(ys)) | (Value::Tuple(xs), Value::Tuple(ys)) => {
2489 if xs.len() != ys.len() {
2490 return Ok(false);
2491 }
2492 for (x, y) in xs.iter().zip(ys.iter()) {
2493 if !self.values_equal_custom(x, y).await? {
2494 return Ok(false);
2495 }
2496 }
2497 Ok(true)
2498 }
2499 (Value::Optional(x), Value::Optional(y)) => match (x, y) {
2500 (None, None) => Ok(true),
2501 (Some(xv), Some(yv)) => self.values_equal_custom(xv, yv).await,
2502 _ => Ok(false),
2503 },
2504 (Value::Result(x), Value::Result(y)) => match (x, y) {
2505 (Ok(xv), Ok(yv)) | (Err(xv), Err(yv)) => self.values_equal_custom(xv, yv).await,
2506 _ => Ok(false),
2507 },
2508 (Value::Map(xs), Value::Map(ys)) => {
2509 if xs.len() != ys.len() {
2510 return Ok(false);
2511 }
2512 let ys: Vec<(&Value, &Value)> = ys.iter().collect();
2514 for (xk, xv) in xs {
2515 let mut found = false;
2516 for (yk, yv) in &ys {
2517 if self.values_equal_custom(xk, yk).await?
2518 && self.values_equal_custom(xv, yv).await?
2519 {
2520 found = true;
2521 break;
2522 }
2523 }
2524 if !found {
2525 return Ok(false);
2526 }
2527 }
2528 Ok(true)
2529 }
2530 (Value::Set(xs), Value::Set(ys)) => {
2531 if xs.len() != ys.len() {
2532 return Ok(false);
2533 }
2534 let ys: Vec<&Value> = ys.iter().collect();
2535 for x in xs {
2536 let mut found = false;
2537 for y in &ys {
2538 if self.values_equal_custom(x, y).await? {
2539 found = true;
2540 break;
2541 }
2542 }
2543 if !found {
2544 return Ok(false);
2545 }
2546 }
2547 Ok(true)
2548 }
2549 (Value::Enum(ea), Value::Enum(eb)) => {
2550 if ea.type_name != eb.type_name || ea.variant != eb.variant {
2551 return Ok(false);
2552 }
2553 match (&ea.payload, &eb.payload) {
2554 (None, None) => Ok(true),
2555 (Some(x), Some(y)) => self.values_equal_custom(x, y).await,
2556 _ => Ok(false),
2557 }
2558 }
2559 _ => Ok(a == b),
2561 }
2562 }
2563
2564 #[async_recursion]
2575 async fn try_call_assoc_fn(
2576 &mut self,
2577 type_name: &str,
2578 method: &str,
2579 args: Vec<Value>,
2580 ) -> Result<Option<Value>, RuntimeError> {
2581 let entry = self
2582 .method_table
2583 .get(type_name)
2584 .and_then(|methods| methods.get(method))
2585 .cloned();
2586
2587 let MethodEntry {
2588 params: param_names,
2589 body,
2590 ..
2591 } = match entry {
2592 Some(e) if e.params.first().map(|s| s.as_str()) != Some("self") => e,
2596 _ => return Ok(None),
2597 };
2598
2599 let (value, _) = self
2600 .run_method_body(¶m_names, &body, None, false, args)
2601 .await?;
2602 Ok(Some(value))
2603 }
2604
2605 #[async_recursion]
2616 async fn run_method_body(
2617 &mut self,
2618 param_names: &[String],
2619 body: &AIRNode,
2620 receiver: Option<Value>,
2621 self_is_mut: bool,
2622 args: Vec<Value>,
2623 ) -> Result<(Value, Option<Value>), RuntimeError> {
2624 let method_env = Environment::with_globals(&self.env);
2625 let saved_env = std::mem::replace(&mut self.env, method_env);
2626 self.env.push_scope();
2627 let is_instance = receiver.is_some();
2628 if let Some(recv) = receiver {
2629 self.env.define("self", recv);
2630 }
2631 let value_params: &[String] = if is_instance {
2632 ¶m_names[1.min(param_names.len())..]
2633 } else {
2634 param_names
2635 };
2636 for (name, val) in value_params.iter().zip(args) {
2637 self.env.define(name.clone(), val);
2638 }
2639 let result = self.eval_expr(body).await;
2640 let updated_self = if self_is_mut {
2643 self.env.get("self").cloned()
2644 } else {
2645 None
2646 };
2647 self.env = saved_env;
2648 let value = match result {
2653 Err(RuntimeError::Return(v) | RuntimeError::Propagated(v)) => *v,
2654 other => other?,
2655 };
2656 Ok((value, updated_self))
2657 }
2658
2659 const LIST_INPLACE_MUTATORS: &'static [&'static str] = &[
2667 "push",
2668 "append",
2669 "pop",
2670 "remove_at",
2671 "insert",
2672 "reverse",
2673 "set",
2674 ];
2675
2676 fn run_list_inplace_mutator(
2693 &mut self,
2694 receiver: &AIRNode,
2695 mut items: Vec<Value>,
2696 method: &str,
2697 mut args: Vec<Value>,
2698 ) -> Result<Value, RuntimeError> {
2699 let arity = |expected: usize, args: &[Value]| -> Result<(), RuntimeError> {
2700 if args.len() < expected {
2701 return Err(RuntimeError::ArityMismatch {
2702 expected,
2703 got: args.len(),
2704 });
2705 }
2706 Ok(())
2707 };
2708 let int_index = |v: &Value, method: &str| -> Result<i64, RuntimeError> {
2709 match v {
2710 Value::Int(i) => Ok(*i),
2711 other => Err(RuntimeError::TypeError(format!(
2712 "List.{method} expects an Int index, got {other}"
2713 ))),
2714 }
2715 };
2716 match method {
2717 "push" | "append" => {
2718 arity(1, &args)?;
2719 items.push(args.remove(0));
2720 self.write_back_receiver(receiver, Value::List(items));
2721 Ok(Value::Void)
2722 }
2723 "pop" => match items.pop() {
2724 Some(last) => {
2725 self.write_back_receiver(receiver, Value::List(items));
2726 Ok(Value::Optional(Some(Box::new(last))))
2727 }
2728 None => Ok(Value::Optional(None)),
2729 },
2730 "remove_at" => {
2731 arity(1, &args)?;
2732 let i = int_index(&args[0], "remove_at")?;
2733 if i < 0 || i as usize >= items.len() {
2734 return Err(RuntimeError::ListIndexAbort {
2735 op: "remove_at",
2736 index: i,
2737 len: items.len(),
2738 });
2739 }
2740 let removed = items.remove(i as usize);
2741 self.write_back_receiver(receiver, Value::List(items));
2742 Ok(removed)
2743 }
2744 "insert" => {
2745 arity(2, &args)?;
2746 let i = int_index(&args[0], "insert")?;
2747 if i < 0 || i as usize > items.len() {
2749 return Err(RuntimeError::ListIndexAbort {
2750 op: "insert",
2751 index: i,
2752 len: items.len(),
2753 });
2754 }
2755 items.insert(i as usize, args.remove(1));
2756 self.write_back_receiver(receiver, Value::List(items));
2757 Ok(Value::Void)
2758 }
2759 "reverse" => {
2760 items.reverse();
2761 self.write_back_receiver(receiver, Value::List(items));
2762 Ok(Value::Void)
2763 }
2764 "set" => {
2765 arity(2, &args)?;
2766 let i = int_index(&args[0], "set")?;
2767 if i < 0 || i as usize >= items.len() {
2768 return Err(RuntimeError::ListIndexAbort {
2769 op: "set",
2770 index: i,
2771 len: items.len(),
2772 });
2773 }
2774 items[i as usize] = args.remove(1);
2775 self.write_back_receiver(receiver, Value::List(items));
2776 Ok(Value::Void)
2777 }
2778 other => Err(RuntimeError::TypeError(format!(
2779 "method '{other}' is not an in-place List mutator"
2780 ))),
2781 }
2782 }
2783
2784 fn write_back_receiver(&mut self, receiver: &AIRNode, updated: Value) {
2791 match &receiver.kind {
2792 NodeKind::Identifier { name } => {
2793 self.env.assign(&name.name, updated);
2794 }
2795 NodeKind::FieldAccess { object, field } => {
2796 if let NodeKind::Identifier { name: obj_name } = &object.kind {
2797 if let Some(Value::Record(mut rv)) = self.env.get(&obj_name.name).cloned() {
2798 rv.fields.insert(field.name.clone(), updated);
2799 self.env.assign(&obj_name.name, Value::Record(rv));
2800 }
2801 }
2802 }
2803 _ => {}
2804 }
2805 }
2806
2807 #[async_recursion]
2810 async fn eval_field_access(
2811 &mut self,
2812 object: &AIRNode,
2813 field: &str,
2814 ) -> Result<Value, RuntimeError> {
2815 let obj = self.eval_expr(object).await?;
2816 match obj {
2817 Value::Record(rv) => {
2818 rv.fields
2819 .get(field)
2820 .cloned()
2821 .ok_or_else(|| RuntimeError::FieldNotFound {
2822 field: field.to_string(),
2823 type_name: rv.type_name.clone(),
2824 })
2825 }
2826 Value::Enum(ev) => {
2827 if field == "variant" {
2828 Ok(Value::String(BockString::new(ev.variant.clone())))
2829 } else {
2830 Err(RuntimeError::FieldNotFound {
2831 field: field.to_string(),
2832 type_name: ev.type_name.clone(),
2833 })
2834 }
2835 }
2836 other => Err(RuntimeError::TypeError(format!(
2837 "cannot access field '{field}' on {other}"
2838 ))),
2839 }
2840 }
2841
2842 #[async_recursion]
2845 async fn eval_index(
2846 &mut self,
2847 object: &AIRNode,
2848 index: &AIRNode,
2849 ) -> Result<Value, RuntimeError> {
2850 let obj = self.eval_expr(object).await?;
2851 let idx = self.eval_expr(index).await?;
2852 match (obj, idx) {
2853 (Value::List(items), Value::Int(i)) => {
2854 if i < 0 || i as usize >= items.len() {
2855 Err(RuntimeError::IndexOutOfBounds {
2856 index: i,
2857 len: items.len(),
2858 })
2859 } else {
2860 Ok(items[i as usize].clone())
2861 }
2862 }
2863 (Value::Tuple(items), Value::Int(i)) => {
2864 if i < 0 || i as usize >= items.len() {
2865 Err(RuntimeError::IndexOutOfBounds {
2866 index: i,
2867 len: items.len(),
2868 })
2869 } else {
2870 Ok(items[i as usize].clone())
2871 }
2872 }
2873 (Value::Map(map), key) => map
2874 .get(&key)
2875 .cloned()
2876 .ok_or_else(|| RuntimeError::TypeError(format!("key not found: {key}"))),
2877 (Value::String(s), Value::Int(i)) => {
2878 let chars: Vec<char> = s.as_str().chars().collect();
2879 if i < 0 || i as usize >= chars.len() {
2880 Err(RuntimeError::IndexOutOfBounds {
2881 index: i,
2882 len: chars.len(),
2883 })
2884 } else {
2885 Ok(Value::Char(chars[i as usize]))
2886 }
2887 }
2888 (obj, idx) => Err(RuntimeError::TypeError(format!(
2889 "cannot index {obj} with {idx}"
2890 ))),
2891 }
2892 }
2893
2894 #[async_recursion]
2904 async fn eval_propagate(&mut self, expr: &AIRNode) -> Result<Value, RuntimeError> {
2905 let val = self.eval_expr(expr).await?;
2906 match val {
2907 Value::Optional(Some(inner)) => Ok(*inner),
2908 Value::Optional(None) => Err(RuntimeError::Propagated(Box::new(Value::Optional(None)))),
2909 Value::Result(Ok(inner)) => Ok(*inner),
2910 Value::Result(Err(e)) => Err(RuntimeError::Propagated(Box::new(Value::Result(Err(e))))),
2911 other => Err(RuntimeError::TypeError(format!(
2912 "? applied to non-Optional/Result: {other}"
2913 ))),
2914 }
2915 }
2916
2917 #[async_recursion]
2920 async fn eval_record_construct(
2921 &mut self,
2922 path: &TypePath,
2923 fields: &[AirRecordField],
2924 spread: Option<&AIRNode>,
2925 ) -> Result<Value, RuntimeError> {
2926 let type_name = path
2927 .segments
2928 .last()
2929 .map(|s| s.name.as_str())
2930 .unwrap_or("")
2931 .to_string();
2932 let mut record_fields: BTreeMap<String, Value> = BTreeMap::new();
2933
2934 if let Some(spread_expr) = spread {
2936 let spread_val = self.eval_expr(spread_expr).await?;
2937 if let Value::Record(rv) = spread_val {
2938 record_fields = rv.fields;
2939 }
2940 }
2941
2942 for field in fields {
2944 let val = match &field.value {
2945 Some(v) => self.eval_expr(v).await?,
2946 None => {
2947 self.env.get(&field.name.name).cloned().ok_or_else(|| {
2949 RuntimeError::UndefinedVariable {
2950 name: field.name.name.clone(),
2951 }
2952 })?
2953 }
2954 };
2955 record_fields.insert(field.name.name.clone(), val);
2956 }
2957
2958 Ok(Value::Record(RecordValue {
2959 type_name,
2960 fields: record_fields,
2961 }))
2962 }
2963
2964 #[async_recursion]
2967 async fn eval_interpolation(
2968 &mut self,
2969 parts: &[AirInterpolationPart],
2970 ) -> Result<Value, RuntimeError> {
2971 let mut result = String::new();
2972 for part in parts {
2973 match part {
2974 AirInterpolationPart::Literal(s) => result.push_str(s),
2975 AirInterpolationPart::Expr(expr) => {
2976 let val = self.eval_expr(expr).await?;
2977 let displayed = self.display_string(&val).await?;
2978 result.push_str(&displayed);
2979 }
2980 }
2981 }
2982 Ok(Value::String(BockString::new(result)))
2983 }
2984
2985 #[async_recursion]
2996 async fn display_string(&mut self, val: &Value) -> Result<String, RuntimeError> {
2997 if matches!(val, Value::Record(_)) {
3000 for method in ["to_string", "display"] {
3001 if let Some(outcome) = self.try_call_impl_method(val, method, Vec::new()).await? {
3002 if let Value::String(s) = outcome.value {
3003 return Ok(s.to_string());
3004 }
3005 }
3006 }
3007 }
3008 let tag = TypeTag::of(val);
3011 Ok(
3012 match self
3013 .builtins
3014 .call(tag, "display", std::slice::from_ref(val))
3015 {
3016 Some(Ok(Value::String(s))) => s.to_string(),
3017 _ => val.to_string(),
3018 },
3019 )
3020 }
3021
3022 #[async_recursion]
3025 async fn eval_range(
3026 &mut self,
3027 lo: &AIRNode,
3028 hi: &AIRNode,
3029 inclusive: bool,
3030 ) -> Result<Value, RuntimeError> {
3031 let lo_val = self.eval_expr(lo).await?;
3032 let hi_val = self.eval_expr(hi).await?;
3033 match (lo_val, hi_val) {
3034 (Value::Int(start), Value::Int(end)) => Ok(Value::Range {
3035 start,
3036 end,
3037 inclusive,
3038 step: 1,
3039 }),
3040 (lo, hi) => Err(RuntimeError::TypeError(format!(
3041 "range bounds must be Int, got {lo} and {hi}"
3042 ))),
3043 }
3044 }
3045
3046 #[async_recursion]
3050 pub async fn eval_block(
3051 &mut self,
3052 stmts: &[AIRNode],
3053 tail: Option<&AIRNode>,
3054 ) -> Result<Value, RuntimeError> {
3055 self.env.push_scope();
3056
3057 for stmt in stmts {
3058 match self.eval_expr(stmt).await {
3059 Ok(_) => {} Err(e) => {
3061 self.env.pop_scope();
3062 return Err(e);
3063 }
3064 }
3065 }
3066
3067 let result = match tail {
3068 Some(expr) => self.eval_expr(expr).await,
3069 None => Ok(Value::Void),
3070 };
3071
3072 self.env.pop_scope();
3073 result
3074 }
3075
3076 #[async_recursion]
3079 async fn eval_if(
3080 &mut self,
3081 let_pattern: Option<&AIRNode>,
3082 condition: &AIRNode,
3083 then_block: &AIRNode,
3084 else_block: Option<&AIRNode>,
3085 ) -> Result<Value, RuntimeError> {
3086 if let Some(pat) = let_pattern {
3087 let val = self.eval_expr(condition).await?;
3089 self.env.push_scope();
3090 let matched = self.try_match_pattern(pat, &val).await;
3091 if matched {
3092 let result = self.eval_expr(then_block).await;
3093 self.env.pop_scope();
3094 result
3095 } else {
3096 self.env.pop_scope();
3097 match else_block {
3098 Some(eb) => self.eval_expr(eb).await,
3099 None => Ok(Value::Void),
3100 }
3101 }
3102 } else {
3103 let cond = self.eval_expr(condition).await?;
3104 match cond {
3105 Value::Bool(true) => self.eval_expr(then_block).await,
3106 Value::Bool(false) => match else_block {
3107 Some(eb) => self.eval_expr(eb).await,
3108 None => Ok(Value::Void),
3109 },
3110 other => Err(RuntimeError::TypeError(format!(
3111 "if condition must be Bool, got {other}"
3112 ))),
3113 }
3114 }
3115 }
3116
3117 #[async_recursion]
3120 async fn eval_match(
3121 &mut self,
3122 scrutinee: &AIRNode,
3123 arms: &[AIRNode],
3124 ) -> Result<Value, RuntimeError> {
3125 let val = self.eval_expr(scrutinee).await?;
3126
3127 if matches!(&val, Value::Enum(_)) {
3131 let has_catch_all = arms.iter().any(|arm| {
3132 if let NodeKind::MatchArm { pattern, guard, .. } = &arm.kind {
3133 guard.is_none()
3134 && matches!(
3135 pattern.kind,
3136 NodeKind::WildcardPat | NodeKind::BindPat { .. }
3137 )
3138 } else {
3139 false
3140 }
3141 });
3142 if !has_catch_all {
3143 eprintln!(
3144 "warning: match on enum value may not be exhaustive (no wildcard `_` arm)"
3145 );
3146 }
3147 }
3148
3149 for arm in arms {
3150 if let NodeKind::MatchArm {
3151 pattern,
3152 guard,
3153 body,
3154 } = &arm.kind
3155 {
3156 self.env.push_scope();
3157 let matched = self.try_match_pattern(pattern, &val).await;
3158
3159 let should_exec = if matched {
3160 if let Some(guard_expr) = guard {
3161 match self.eval_expr(guard_expr).await {
3162 Ok(Value::Bool(b)) => b,
3163 Ok(_) => false,
3164 Err(_) => false,
3165 }
3166 } else {
3167 true
3168 }
3169 } else {
3170 false
3171 };
3172
3173 if should_exec {
3174 let result = self.eval_expr(body).await;
3175 self.env.pop_scope();
3176 return result;
3177 }
3178 self.env.pop_scope();
3179 }
3180 }
3181
3182 Err(RuntimeError::MatchFailed)
3183 }
3184
3185 #[async_recursion]
3190 pub async fn try_match_pattern(&mut self, pattern: &AIRNode, value: &Value) -> bool {
3191 match &pattern.kind.clone() {
3192 NodeKind::WildcardPat | NodeKind::RestPat => true,
3193
3194 NodeKind::BindPat { name, .. } => {
3195 self.env.define(name.name.clone(), value.clone());
3196 true
3197 }
3198
3199 NodeKind::LiteralPat { lit } => {
3200 matches!(self.eval_literal(lit), Ok(lit_val) if lit_val == *value)
3201 }
3202
3203 NodeKind::ConstructorPat { path, fields } => {
3204 let variant_name = path.segments.last().map(|s| s.name.as_str()).unwrap_or("");
3205 match (variant_name, value) {
3206 ("Some", Value::Optional(Some(inner))) => {
3207 fields.len() == 1 && self.try_match_pattern(&fields[0], inner).await
3208 }
3209 ("None", Value::Optional(None)) => true,
3210 ("Ok", Value::Result(Ok(inner))) => {
3211 fields.is_empty()
3212 || (fields.len() == 1
3213 && self.try_match_pattern(&fields[0], inner).await)
3214 }
3215 ("Err", Value::Result(Err(inner))) => {
3216 fields.is_empty()
3217 || (fields.len() == 1
3218 && self.try_match_pattern(&fields[0], inner).await)
3219 }
3220 (name, Value::Enum(ev)) if ev.variant == name => {
3221 match (&ev.payload, fields.len()) {
3222 (None, 0) => true,
3223 (Some(inner), 1) => self.try_match_pattern(&fields[0], inner).await,
3224 _ => false,
3225 }
3226 }
3227 _ => false,
3228 }
3229 }
3230
3231 NodeKind::RecordPat { path, fields, rest } => {
3232 if let Value::Record(rv) = value {
3233 let type_name = path.segments.last().map(|s| s.name.as_str()).unwrap_or("");
3234 if rv.type_name != type_name {
3235 return false;
3236 }
3237 if !rest && fields.len() != rv.fields.len() {
3238 return false;
3239 }
3240 for field in fields {
3241 let field_val = match rv.fields.get(&field.name.name) {
3242 Some(v) => v.clone(),
3243 None => return false,
3244 };
3245 if let Some(pat) = &field.pattern {
3246 if !self.try_match_pattern(pat, &field_val).await {
3247 return false;
3248 }
3249 } else {
3250 self.env.define(field.name.name.clone(), field_val);
3251 }
3252 }
3253 true
3254 } else {
3255 false
3256 }
3257 }
3258
3259 NodeKind::TuplePat { elems } => {
3260 if let Value::Tuple(vals) = value {
3261 if elems.len() != vals.len() {
3262 return false;
3263 }
3264 let pairs: Vec<_> = elems
3265 .iter()
3266 .zip(vals.iter())
3267 .map(|(p, v)| (p.clone(), v.clone()))
3268 .collect();
3269 for (pat, val) in pairs {
3270 if !self.try_match_pattern(&pat, &val).await {
3271 return false;
3272 }
3273 }
3274 true
3275 } else {
3276 false
3277 }
3278 }
3279
3280 NodeKind::ListPat { elems, rest } => {
3281 if let Value::List(vals) = value {
3282 if elems.len() > vals.len() {
3283 return false;
3284 }
3285 if rest.is_none() && elems.len() != vals.len() {
3286 return false;
3287 }
3288 let pairs: Vec<_> = elems
3289 .iter()
3290 .zip(vals.iter())
3291 .map(|(p, v)| (p.clone(), v.clone()))
3292 .collect();
3293 for (pat, val) in pairs {
3294 if !self.try_match_pattern(&pat, &val).await {
3295 return false;
3296 }
3297 }
3298 if let Some(rest_pat) = rest {
3299 let rest_vals = Value::List(vals[elems.len()..].to_vec());
3300 let rest_pat = rest_pat.clone();
3301 self.try_match_pattern(&rest_pat, &rest_vals).await;
3302 }
3303 true
3304 } else {
3305 false
3306 }
3307 }
3308
3309 NodeKind::OrPat { alternatives } => {
3310 let alts: Vec<_> = alternatives.to_vec();
3311 for alt in alts {
3312 if self.try_match_pattern(&alt, value).await {
3313 return true;
3314 }
3315 }
3316 false
3317 }
3318
3319 NodeKind::RangePat { lo, hi, inclusive } => {
3320 let lo = lo.clone();
3321 let hi = hi.clone();
3322 let inclusive = *inclusive;
3323 let lo_val = match self.eval_expr(&lo).await {
3324 Ok(v) => v,
3325 Err(_) => return false,
3326 };
3327 let hi_val = match self.eval_expr(&hi).await {
3328 Ok(v) => v,
3329 Err(_) => return false,
3330 };
3331 if inclusive {
3332 *value >= lo_val && *value <= hi_val
3333 } else {
3334 *value >= lo_val && *value < hi_val
3335 }
3336 }
3337
3338 _ => false,
3339 }
3340 }
3341
3342 #[async_recursion]
3344 pub async fn bind_pattern(
3345 &mut self,
3346 pattern: &AIRNode,
3347 value: Value,
3348 ) -> Result<(), RuntimeError> {
3349 match &pattern.kind.clone() {
3350 NodeKind::BindPat { name, .. } => {
3351 self.env.define(name.name.clone(), value);
3352 Ok(())
3353 }
3354 NodeKind::WildcardPat => Ok(()),
3355 NodeKind::TuplePat { elems } => {
3356 if let Value::Tuple(vals) = value {
3357 if elems.len() != vals.len() {
3358 return Err(RuntimeError::MatchFailed);
3359 }
3360 let pairs: Vec<_> = elems
3361 .iter()
3362 .zip(vals)
3363 .map(|(p, v)| (p.clone(), v))
3364 .collect();
3365 for (pat, val) in pairs {
3366 self.bind_pattern(&pat, val).await?;
3367 }
3368 Ok(())
3369 } else {
3370 Err(RuntimeError::MatchFailed)
3371 }
3372 }
3373 _ => {
3374 if self.try_match_pattern(pattern, &value).await {
3375 Ok(())
3376 } else {
3377 Err(RuntimeError::MatchFailed)
3378 }
3379 }
3380 }
3381 }
3382
3383 #[async_recursion]
3386 async fn eval_for(
3387 &mut self,
3388 pattern: &AIRNode,
3389 iterable: &AIRNode,
3390 body: &AIRNode,
3391 ) -> Result<Value, RuntimeError> {
3392 let iter_val = self.eval_expr(iterable).await?;
3393 match iter_val {
3394 Value::List(items) => self.for_loop_items(pattern, body, items).await,
3395 Value::Range {
3396 start,
3397 end,
3398 inclusive,
3399 step,
3400 } => {
3401 let items = range_to_vec(start, end, inclusive, step);
3402 self.for_loop_items(pattern, body, items).await
3403 }
3404 Value::Set(set) => {
3405 let items: Vec<Value> = set.into_iter().collect();
3406 self.for_loop_items(pattern, body, items).await
3407 }
3408 Value::Map(map) => {
3409 let items: Vec<Value> = map
3410 .into_iter()
3411 .map(|(k, v)| Value::Tuple(vec![k, v]))
3412 .collect();
3413 self.for_loop_items(pattern, body, items).await
3414 }
3415 Value::Iterator(it) => self.for_loop_iterator(pattern, body, &it).await,
3416 other => Err(RuntimeError::TypeError(format!(
3417 "cannot iterate over {other}"
3418 ))),
3419 }
3420 }
3421
3422 #[async_recursion]
3423 async fn for_loop_items(
3424 &mut self,
3425 pattern: &AIRNode,
3426 body: &AIRNode,
3427 items: Vec<Value>,
3428 ) -> Result<Value, RuntimeError> {
3429 for item in items {
3430 self.env.push_scope();
3431 let bind_result = self.bind_pattern(pattern, item).await;
3432 if let Err(e) = bind_result {
3433 self.env.pop_scope();
3434 return Err(e);
3435 }
3436 let result = self.eval_expr(body).await;
3437 self.env.pop_scope();
3438 match result {
3439 Ok(_) | Err(RuntimeError::Continue) => {}
3440 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
3441 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
3442 Err(e) => return Err(e),
3443 }
3444 }
3445 Ok(Value::Void)
3446 }
3447
3448 #[async_recursion]
3449 async fn for_loop_iterator(
3450 &mut self,
3451 pattern: &AIRNode,
3452 body: &AIRNode,
3453 it: &crate::value::IteratorValue,
3454 ) -> Result<Value, RuntimeError> {
3455 loop {
3456 let next = {
3457 let mut kind = it.kind.lock().unwrap();
3458 kind.next()
3459 };
3460 match next {
3461 IteratorNext::Some(val) => {
3462 self.env.push_scope();
3463 let bind_result = self.bind_pattern(pattern, val).await;
3464 if let Err(e) = bind_result {
3465 self.env.pop_scope();
3466 return Err(e);
3467 }
3468 let result = self.eval_expr(body).await;
3469 self.env.pop_scope();
3470 match result {
3471 Ok(_) | Err(RuntimeError::Continue) => {}
3472 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
3473 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
3474 Err(e) => return Err(e),
3475 }
3476 }
3477 IteratorNext::Done => break,
3478 IteratorNext::NeedsMapCallback { value, func } => {
3479 let fn_val = Value::Function(func);
3480 let mapped = self.invoke_callback(&fn_val, &[value]).await?;
3481 self.env.push_scope();
3482 let bind_result = self.bind_pattern(pattern, mapped).await;
3483 if let Err(e) = bind_result {
3484 self.env.pop_scope();
3485 return Err(e);
3486 }
3487 let result = self.eval_expr(body).await;
3488 self.env.pop_scope();
3489 match result {
3490 Ok(_) | Err(RuntimeError::Continue) => {}
3491 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
3492 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
3493 Err(e) => return Err(e),
3494 }
3495 }
3496 IteratorNext::NeedsFilterCallback { value, func } => {
3497 let fn_val = Value::Function(func);
3498 let keep = self
3499 .invoke_callback(&fn_val, std::slice::from_ref(&value))
3500 .await?;
3501 if keep == Value::Bool(true) {
3502 self.env.push_scope();
3503 let bind_result = self.bind_pattern(pattern, value).await;
3504 if let Err(e) = bind_result {
3505 self.env.pop_scope();
3506 return Err(e);
3507 }
3508 let result = self.eval_expr(body).await;
3509 self.env.pop_scope();
3510 match result {
3511 Ok(_) | Err(RuntimeError::Continue) => {}
3512 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
3513 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
3514 Err(e) => return Err(e),
3515 }
3516 }
3517 }
3518 }
3519 }
3520 Ok(Value::Void)
3521 }
3522
3523 #[async_recursion]
3524 async fn eval_while(
3525 &mut self,
3526 condition: &AIRNode,
3527 body: &AIRNode,
3528 ) -> Result<Value, RuntimeError> {
3529 loop {
3530 let cond = self.eval_expr(condition).await?;
3531 match cond {
3532 Value::Bool(false) => break,
3533 Value::Bool(true) => {}
3534 other => {
3535 return Err(RuntimeError::TypeError(format!(
3536 "while condition must be Bool, got {other}"
3537 )))
3538 }
3539 }
3540 match self.eval_expr(body).await {
3541 Ok(_) | Err(RuntimeError::Continue) => {}
3542 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
3543 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
3544 Err(e) => return Err(e),
3545 }
3546 }
3547 Ok(Value::Void)
3548 }
3549
3550 #[async_recursion]
3551 async fn eval_loop(&mut self, body: &AIRNode) -> Result<Value, RuntimeError> {
3552 loop {
3553 match self.eval_expr(body).await {
3554 Ok(_) | Err(RuntimeError::Continue) => {}
3555 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
3556 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
3557 Err(e) => return Err(e),
3558 }
3559 }
3560 }
3561
3562 #[async_recursion]
3563 async fn eval_guard(
3564 &mut self,
3565 let_pattern: Option<&AIRNode>,
3566 condition: &AIRNode,
3567 else_block: &AIRNode,
3568 ) -> Result<Value, RuntimeError> {
3569 if let Some(pat) = let_pattern {
3570 let val = self.eval_expr(condition).await?;
3572 let matched = self.try_match_pattern(pat, &val).await;
3573 if matched {
3574 Ok(Value::Void)
3575 } else {
3576 self.eval_expr(else_block).await?;
3577 Ok(Value::Void)
3578 }
3579 } else {
3580 let cond = self.eval_expr(condition).await?;
3581 match cond {
3582 Value::Bool(true) => Ok(Value::Void),
3583 Value::Bool(false) => {
3584 self.eval_expr(else_block).await?;
3587 Ok(Value::Void)
3588 }
3589 other => Err(RuntimeError::TypeError(format!(
3590 "guard condition must be Bool, got {other}"
3591 ))),
3592 }
3593 }
3594 }
3595
3596 #[async_recursion]
3604 pub async fn exec_stmt(&mut self, node: &AIRNode) -> Result<Option<Value>, RuntimeError> {
3605 match &node.kind {
3606 NodeKind::LetBinding { .. }
3607 | NodeKind::For { .. }
3608 | NodeKind::While { .. }
3609 | NodeKind::Guard { .. } => {
3610 self.eval_expr(node).await?;
3611 Ok(None)
3612 }
3613 _ => Ok(Some(self.eval_expr(node).await?)),
3614 }
3615 }
3616
3617 #[async_recursion]
3622 pub async fn exec_block(&mut self, block: &AIRNode) -> Result<Value, RuntimeError> {
3623 match &block.kind {
3624 NodeKind::Block { stmts, tail } => self.eval_block(stmts, tail.as_deref()).await,
3625 _ => self.eval_expr(block).await,
3626 }
3627 }
3628}
3629
3630const HARNESS_RESERVED_RECORD_METHODS: &[&str] = &[
3647 "to_equal",
3648 "to_be_ok",
3649 "to_be_err",
3650 "to_be_some",
3651 "to_be_none",
3652 "to_throw",
3653 "to_be_true",
3654 "to_be_false",
3655];
3656
3657fn user_impl_may_shadow_record_builtin(method: &str) -> bool {
3660 !HARNESS_RESERVED_RECORD_METHODS.contains(&method)
3661}
3662
3663fn record_type_name(v: &Value) -> Option<&str> {
3666 match v {
3667 Value::Record(rv) => Some(&rv.type_name),
3668 _ => None,
3669 }
3670}
3671
3672fn range_to_vec(start: i64, end: i64, inclusive: bool, step: i64) -> Vec<Value> {
3674 let effective_step = if step == 1 && start > end {
3677 -1
3678 } else if step == 0 {
3679 return Vec::new();
3680 } else {
3681 step
3682 };
3683
3684 let mut result = Vec::new();
3685 let mut i = start;
3686 if effective_step > 0 {
3687 while if inclusive { i <= end } else { i < end } {
3688 result.push(Value::Int(i));
3689 i = match i.checked_add(effective_step) {
3690 Some(next) => next,
3691 None => break,
3692 };
3693 }
3694 } else {
3695 while if inclusive { i >= end } else { i > end } {
3697 result.push(Value::Int(i));
3698 i = match i.checked_add(effective_step) {
3699 Some(next) => next,
3700 None => break,
3701 };
3702 }
3703 }
3704 result
3705}
3706
3707#[cfg(test)]
3710mod tests {
3711 use super::*;
3712 use bock_air::{AirHandlerPair, AirMapEntry, NodeId, NodeIdGen};
3713 use bock_ast::{AssignOp, BinOp, Ident, Literal, TypePath, UnaryOp};
3714 use bock_errors::{FileId, Span};
3715
3716 fn span() -> Span {
3717 Span {
3718 file: FileId(0),
3719 start: 0,
3720 end: 0,
3721 }
3722 }
3723
3724 fn ident(name: &str) -> Ident {
3725 Ident {
3726 name: name.to_string(),
3727 span: span(),
3728 }
3729 }
3730
3731 fn type_path(name: &str) -> TypePath {
3732 TypePath {
3733 segments: vec![ident(name)],
3734 span: span(),
3735 }
3736 }
3737
3738 fn gen() -> NodeIdGen {
3739 NodeIdGen::new()
3740 }
3741
3742 fn node(id: NodeId, kind: NodeKind) -> AIRNode {
3743 AIRNode::new(id, span(), kind)
3744 }
3745
3746 fn int_lit(g: &NodeIdGen, n: i64) -> AIRNode {
3747 node(
3748 g.next(),
3749 NodeKind::Literal {
3750 lit: Literal::Int(n.to_string()),
3751 },
3752 )
3753 }
3754
3755 fn float_lit(g: &NodeIdGen, f: f64) -> AIRNode {
3756 node(
3757 g.next(),
3758 NodeKind::Literal {
3759 lit: Literal::Float(f.to_string()),
3760 },
3761 )
3762 }
3763
3764 fn bool_lit(g: &NodeIdGen, b: bool) -> AIRNode {
3765 node(
3766 g.next(),
3767 NodeKind::Literal {
3768 lit: Literal::Bool(b),
3769 },
3770 )
3771 }
3772
3773 fn str_lit(g: &NodeIdGen, s: &str) -> AIRNode {
3774 node(
3775 g.next(),
3776 NodeKind::Literal {
3777 lit: Literal::String(s.to_string()),
3778 },
3779 )
3780 }
3781
3782 fn var(g: &NodeIdGen, name: &str) -> AIRNode {
3783 node(g.next(), NodeKind::Identifier { name: ident(name) })
3784 }
3785
3786 #[tokio::test]
3789 async fn eval_int_literal() {
3790 let mut interp = Interpreter::new();
3791 let g = gen();
3792 assert_eq!(interp.eval_expr(&int_lit(&g, 42)).await, Ok(Value::Int(42)));
3793 }
3794
3795 #[tokio::test]
3796 async fn eval_float_literal() {
3797 let mut interp = Interpreter::new();
3798 let g = gen();
3799 let result = interp.eval_expr(&float_lit(&g, 3.5)).await;
3800 assert!(matches!(result, Ok(Value::Float(_))));
3801 }
3802
3803 #[tokio::test]
3804 async fn eval_bool_literal() {
3805 let mut interp = Interpreter::new();
3806 let g = gen();
3807 assert_eq!(
3808 interp.eval_expr(&bool_lit(&g, true)).await,
3809 Ok(Value::Bool(true))
3810 );
3811 assert_eq!(
3812 interp.eval_expr(&bool_lit(&g, false)).await,
3813 Ok(Value::Bool(false))
3814 );
3815 }
3816
3817 #[tokio::test]
3818 async fn eval_string_literal() {
3819 let mut interp = Interpreter::new();
3820 let g = gen();
3821 assert_eq!(
3822 interp.eval_expr(&str_lit(&g, "hello")).await,
3823 Ok(Value::String(BockString::new("hello")))
3824 );
3825 }
3826
3827 #[tokio::test]
3828 async fn eval_hex_literal() {
3829 let mut interp = Interpreter::new();
3830 let g = gen();
3831 let n = node(
3832 g.next(),
3833 NodeKind::Literal {
3834 lit: Literal::Int("0xFF".to_string()),
3835 },
3836 );
3837 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(255)));
3838 }
3839
3840 #[tokio::test]
3841 async fn eval_unit_literal() {
3842 let mut interp = Interpreter::new();
3843 let g = gen();
3844 let n = node(g.next(), NodeKind::Literal { lit: Literal::Unit });
3845 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Void));
3846 }
3847
3848 #[tokio::test]
3851 async fn eval_add_ints() {
3852 let mut interp = Interpreter::new();
3853 let g = gen();
3854 let n = node(
3855 g.next(),
3856 NodeKind::BinaryOp {
3857 op: BinOp::Add,
3858 left: Box::new(int_lit(&g, 3)),
3859 right: Box::new(int_lit(&g, 4)),
3860 },
3861 );
3862 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(7)));
3863 }
3864
3865 #[tokio::test]
3866 async fn eval_add_strings() {
3867 let mut interp = Interpreter::new();
3868 let g = gen();
3869 let n = node(
3870 g.next(),
3871 NodeKind::BinaryOp {
3872 op: BinOp::Add,
3873 left: Box::new(str_lit(&g, "foo")),
3874 right: Box::new(str_lit(&g, "bar")),
3875 },
3876 );
3877 assert_eq!(
3878 interp.eval_expr(&n).await,
3879 Ok(Value::String(BockString::new("foobar")))
3880 );
3881 }
3882
3883 #[tokio::test]
3884 async fn eval_sub_ints() {
3885 let mut interp = Interpreter::new();
3886 let g = gen();
3887 let n = node(
3888 g.next(),
3889 NodeKind::BinaryOp {
3890 op: BinOp::Sub,
3891 left: Box::new(int_lit(&g, 10)),
3892 right: Box::new(int_lit(&g, 3)),
3893 },
3894 );
3895 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(7)));
3896 }
3897
3898 #[tokio::test]
3899 async fn eval_mul_ints() {
3900 let mut interp = Interpreter::new();
3901 let g = gen();
3902 let n = node(
3903 g.next(),
3904 NodeKind::BinaryOp {
3905 op: BinOp::Mul,
3906 left: Box::new(int_lit(&g, 6)),
3907 right: Box::new(int_lit(&g, 7)),
3908 },
3909 );
3910 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(42)));
3911 }
3912
3913 #[tokio::test]
3914 async fn eval_div_ints() {
3915 let mut interp = Interpreter::new();
3916 let g = gen();
3917 let n = node(
3918 g.next(),
3919 NodeKind::BinaryOp {
3920 op: BinOp::Div,
3921 left: Box::new(int_lit(&g, 10)),
3922 right: Box::new(int_lit(&g, 2)),
3923 },
3924 );
3925 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(5)));
3926 }
3927
3928 #[tokio::test]
3929 async fn eval_div_by_zero() {
3930 let mut interp = Interpreter::new();
3931 let g = gen();
3932 let n = node(
3933 g.next(),
3934 NodeKind::BinaryOp {
3935 op: BinOp::Div,
3936 left: Box::new(int_lit(&g, 1)),
3937 right: Box::new(int_lit(&g, 0)),
3938 },
3939 );
3940 assert!(matches!(
3941 interp.eval_expr(&n).await,
3942 Err(RuntimeError::DivisionByZero)
3943 ));
3944 }
3945
3946 #[tokio::test]
3947 async fn eval_pow() {
3948 let mut interp = Interpreter::new();
3949 let g = gen();
3950 let n = node(
3951 g.next(),
3952 NodeKind::BinaryOp {
3953 op: BinOp::Pow,
3954 left: Box::new(int_lit(&g, 2)),
3955 right: Box::new(int_lit(&g, 10)),
3956 },
3957 );
3958 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(1024)));
3959 }
3960
3961 #[tokio::test]
3964 async fn eval_eq() {
3965 let mut interp = Interpreter::new();
3966 let g = gen();
3967 let n = node(
3968 g.next(),
3969 NodeKind::BinaryOp {
3970 op: BinOp::Eq,
3971 left: Box::new(int_lit(&g, 5)),
3972 right: Box::new(int_lit(&g, 5)),
3973 },
3974 );
3975 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
3976 }
3977
3978 #[tokio::test]
3979 async fn eval_ne() {
3980 let mut interp = Interpreter::new();
3981 let g = gen();
3982 let n = node(
3983 g.next(),
3984 NodeKind::BinaryOp {
3985 op: BinOp::Ne,
3986 left: Box::new(int_lit(&g, 3)),
3987 right: Box::new(int_lit(&g, 5)),
3988 },
3989 );
3990 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
3991 }
3992
3993 #[tokio::test]
3994 async fn eval_lt_gt() {
3995 let mut interp = Interpreter::new();
3996 let g = gen();
3997 let lt = node(
3998 g.next(),
3999 NodeKind::BinaryOp {
4000 op: BinOp::Lt,
4001 left: Box::new(int_lit(&g, 1)),
4002 right: Box::new(int_lit(&g, 2)),
4003 },
4004 );
4005 let gt = node(
4006 g.next(),
4007 NodeKind::BinaryOp {
4008 op: BinOp::Gt,
4009 left: Box::new(int_lit(&g, 3)),
4010 right: Box::new(int_lit(&g, 2)),
4011 },
4012 );
4013 assert_eq!(interp.eval_expr(<).await, Ok(Value::Bool(true)));
4014 assert_eq!(interp.eval_expr(>).await, Ok(Value::Bool(true)));
4015 }
4016
4017 #[tokio::test]
4020 async fn eval_and_short_circuit() {
4021 let mut interp = Interpreter::new();
4022 let g = gen();
4023 let n = node(
4024 g.next(),
4025 NodeKind::BinaryOp {
4026 op: BinOp::And,
4027 left: Box::new(bool_lit(&g, false)),
4028 right: Box::new(bool_lit(&g, true)),
4029 },
4030 );
4031 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(false)));
4032 }
4033
4034 #[tokio::test]
4035 async fn eval_or_short_circuit() {
4036 let mut interp = Interpreter::new();
4037 let g = gen();
4038 let n = node(
4039 g.next(),
4040 NodeKind::BinaryOp {
4041 op: BinOp::Or,
4042 left: Box::new(bool_lit(&g, true)),
4043 right: Box::new(bool_lit(&g, false)),
4044 },
4045 );
4046 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
4047 }
4048
4049 #[tokio::test]
4052 async fn eval_neg() {
4053 let mut interp = Interpreter::new();
4054 let g = gen();
4055 let n = node(
4056 g.next(),
4057 NodeKind::UnaryOp {
4058 op: UnaryOp::Neg,
4059 operand: Box::new(int_lit(&g, 7)),
4060 },
4061 );
4062 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(-7)));
4063 }
4064
4065 #[tokio::test]
4066 async fn eval_not() {
4067 let mut interp = Interpreter::new();
4068 let g = gen();
4069 let n = node(
4070 g.next(),
4071 NodeKind::UnaryOp {
4072 op: UnaryOp::Not,
4073 operand: Box::new(bool_lit(&g, false)),
4074 },
4075 );
4076 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
4077 }
4078
4079 #[tokio::test]
4082 async fn eval_identifier() {
4083 let mut interp = Interpreter::new();
4084 let g = gen();
4085 interp.env.define("x", Value::Int(99));
4086 assert_eq!(interp.eval_expr(&var(&g, "x")).await, Ok(Value::Int(99)));
4087 }
4088
4089 #[tokio::test]
4090 async fn eval_undefined_variable() {
4091 let mut interp = Interpreter::new();
4092 let g = gen();
4093 assert!(matches!(
4094 interp.eval_expr(&var(&g, "y")).await,
4095 Err(RuntimeError::UndefinedVariable { .. })
4096 ));
4097 }
4098
4099 #[tokio::test]
4102 async fn eval_list_literal() {
4103 let mut interp = Interpreter::new();
4104 let g = gen();
4105 let n = node(
4106 g.next(),
4107 NodeKind::ListLiteral {
4108 elems: vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 3)],
4109 },
4110 );
4111 assert_eq!(
4112 interp.eval_expr(&n).await,
4113 Ok(Value::List(vec![
4114 Value::Int(1),
4115 Value::Int(2),
4116 Value::Int(3)
4117 ]))
4118 );
4119 }
4120
4121 #[tokio::test]
4122 async fn eval_tuple_literal() {
4123 let mut interp = Interpreter::new();
4124 let g = gen();
4125 let n = node(
4126 g.next(),
4127 NodeKind::TupleLiteral {
4128 elems: vec![int_lit(&g, 1), bool_lit(&g, true)],
4129 },
4130 );
4131 assert_eq!(
4132 interp.eval_expr(&n).await,
4133 Ok(Value::Tuple(vec![Value::Int(1), Value::Bool(true)]))
4134 );
4135 }
4136
4137 #[tokio::test]
4138 async fn eval_map_literal() {
4139 let mut interp = Interpreter::new();
4140 let g = gen();
4141 let n = node(
4142 g.next(),
4143 NodeKind::MapLiteral {
4144 entries: vec![AirMapEntry {
4145 key: str_lit(&g, "a"),
4146 value: int_lit(&g, 1),
4147 }],
4148 },
4149 );
4150 let result = interp.eval_expr(&n).await.unwrap();
4151 if let Value::Map(map) = result {
4152 assert_eq!(
4153 map.get(&Value::String(BockString::new("a"))),
4154 Some(&Value::Int(1))
4155 );
4156 } else {
4157 panic!("expected Map");
4158 }
4159 }
4160
4161 #[tokio::test]
4162 async fn eval_set_literal() {
4163 let mut interp = Interpreter::new();
4164 let g = gen();
4165 let n = node(
4166 g.next(),
4167 NodeKind::SetLiteral {
4168 elems: vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 1)],
4169 },
4170 );
4171 if let Ok(Value::Set(set)) = interp.eval_expr(&n).await {
4172 assert_eq!(set.len(), 2); } else {
4174 panic!("expected Set");
4175 }
4176 }
4177
4178 #[tokio::test]
4181 async fn eval_record_construct_and_field_access() {
4182 let mut interp = Interpreter::new();
4183 let g = gen();
4184
4185 let record = node(
4186 g.next(),
4187 NodeKind::RecordConstruct {
4188 path: type_path("Point"),
4189 fields: vec![
4190 AirRecordField {
4191 name: ident("x"),
4192 value: Some(Box::new(int_lit(&g, 3))),
4193 },
4194 AirRecordField {
4195 name: ident("y"),
4196 value: Some(Box::new(int_lit(&g, 4))),
4197 },
4198 ],
4199 spread: None,
4200 },
4201 );
4202
4203 let rec_val = interp.eval_expr(&record).await.unwrap();
4204 interp.env.define("p", rec_val);
4205
4206 let field_node = node(
4207 g.next(),
4208 NodeKind::FieldAccess {
4209 object: Box::new(var(&g, "p")),
4210 field: ident("x"),
4211 },
4212 );
4213 assert_eq!(interp.eval_expr(&field_node).await, Ok(Value::Int(3)));
4214 }
4215
4216 #[tokio::test]
4217 async fn eval_record_spread() {
4218 let mut interp = Interpreter::new();
4219 let g = gen();
4220
4221 let base = node(
4223 g.next(),
4224 NodeKind::RecordConstruct {
4225 path: type_path("Point"),
4226 fields: vec![
4227 AirRecordField {
4228 name: ident("x"),
4229 value: Some(Box::new(int_lit(&g, 1))),
4230 },
4231 AirRecordField {
4232 name: ident("y"),
4233 value: Some(Box::new(int_lit(&g, 2))),
4234 },
4235 ],
4236 spread: None,
4237 },
4238 );
4239 let base_val = interp.eval_expr(&base).await.unwrap();
4240 interp.env.define("base", base_val);
4241
4242 let spread_record = node(
4244 g.next(),
4245 NodeKind::RecordConstruct {
4246 path: type_path("Point"),
4247 fields: vec![AirRecordField {
4248 name: ident("y"),
4249 value: Some(Box::new(int_lit(&g, 99))),
4250 }],
4251 spread: Some(Box::new(var(&g, "base"))),
4252 },
4253 );
4254 if let Ok(Value::Record(rv)) = interp.eval_expr(&spread_record).await {
4255 assert_eq!(rv.fields["x"], Value::Int(1));
4256 assert_eq!(rv.fields["y"], Value::Int(99));
4257 } else {
4258 panic!("expected Record");
4259 }
4260 }
4261
4262 #[tokio::test]
4265 async fn eval_lambda_and_call() {
4266 let mut interp = Interpreter::new();
4267 let g = gen();
4268
4269 let param = node(
4271 g.next(),
4272 NodeKind::Param {
4273 pattern: Box::new(node(
4274 g.next(),
4275 NodeKind::BindPat {
4276 name: ident("x"),
4277 is_mut: false,
4278 },
4279 )),
4280 ty: None,
4281 default: None,
4282 },
4283 );
4284 let body = node(
4285 g.next(),
4286 NodeKind::BinaryOp {
4287 op: BinOp::Mul,
4288 left: Box::new(var(&g, "x")),
4289 right: Box::new(int_lit(&g, 2)),
4290 },
4291 );
4292 let lambda = node(
4293 g.next(),
4294 NodeKind::Lambda {
4295 params: vec![param],
4296 body: Box::new(body),
4297 },
4298 );
4299
4300 let fn_val = interp.eval_expr(&lambda).await.unwrap();
4301 interp.env.define("double", fn_val);
4302
4303 let call = node(
4304 g.next(),
4305 NodeKind::Call {
4306 callee: Box::new(var(&g, "double")),
4307 args: vec![AirArg {
4308 label: None,
4309 value: int_lit(&g, 5),
4310 }],
4311 type_args: vec![],
4312 },
4313 );
4314 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Int(10)));
4315 }
4316
4317 #[tokio::test]
4318 async fn eval_closure_captures_env() {
4319 let mut interp = Interpreter::new();
4320 let g = gen();
4321
4322 interp.env.define("factor", Value::Int(3));
4323
4324 let param = node(
4326 g.next(),
4327 NodeKind::Param {
4328 pattern: Box::new(node(
4329 g.next(),
4330 NodeKind::BindPat {
4331 name: ident("x"),
4332 is_mut: false,
4333 },
4334 )),
4335 ty: None,
4336 default: None,
4337 },
4338 );
4339 let body = node(
4340 g.next(),
4341 NodeKind::BinaryOp {
4342 op: BinOp::Mul,
4343 left: Box::new(var(&g, "x")),
4344 right: Box::new(var(&g, "factor")),
4345 },
4346 );
4347 let lambda = node(
4348 g.next(),
4349 NodeKind::Lambda {
4350 params: vec![param],
4351 body: Box::new(body),
4352 },
4353 );
4354 let fn_val = interp.eval_expr(&lambda).await.unwrap();
4355 interp.env.define("triple", fn_val);
4356
4357 let call = node(
4358 g.next(),
4359 NodeKind::Call {
4360 callee: Box::new(var(&g, "triple")),
4361 args: vec![AirArg {
4362 label: None,
4363 value: int_lit(&g, 4),
4364 }],
4365 type_args: vec![],
4366 },
4367 );
4368 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Int(12)));
4369 }
4370
4371 #[tokio::test]
4374 async fn eval_pipe_to_function() {
4375 let mut interp = Interpreter::new();
4376 let g = gen();
4377
4378 let param = node(
4380 g.next(),
4381 NodeKind::Param {
4382 pattern: Box::new(node(
4383 g.next(),
4384 NodeKind::BindPat {
4385 name: ident("x"),
4386 is_mut: false,
4387 },
4388 )),
4389 ty: None,
4390 default: None,
4391 },
4392 );
4393 let body = node(
4394 g.next(),
4395 NodeKind::BinaryOp {
4396 op: BinOp::Add,
4397 left: Box::new(var(&g, "x")),
4398 right: Box::new(int_lit(&g, 1)),
4399 },
4400 );
4401 let lambda = node(
4402 g.next(),
4403 NodeKind::Lambda {
4404 params: vec![param],
4405 body: Box::new(body),
4406 },
4407 );
4408 let fn_val = interp.eval_expr(&lambda).await.unwrap();
4409 interp.env.define("inc", fn_val);
4410
4411 let pipe = node(
4413 g.next(),
4414 NodeKind::Pipe {
4415 left: Box::new(int_lit(&g, 5)),
4416 right: Box::new(var(&g, "inc")),
4417 },
4418 );
4419 assert_eq!(interp.eval_expr(&pipe).await, Ok(Value::Int(6)));
4420 }
4421
4422 #[tokio::test]
4425 async fn eval_if_true_branch() {
4426 let mut interp = Interpreter::new();
4427 let g = gen();
4428 let n = node(
4429 g.next(),
4430 NodeKind::If {
4431 let_pattern: None,
4432 condition: Box::new(bool_lit(&g, true)),
4433 then_block: Box::new(int_lit(&g, 1)),
4434 else_block: Some(Box::new(int_lit(&g, 2))),
4435 },
4436 );
4437 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(1)));
4438 }
4439
4440 #[tokio::test]
4441 async fn eval_if_false_branch() {
4442 let mut interp = Interpreter::new();
4443 let g = gen();
4444 let n = node(
4445 g.next(),
4446 NodeKind::If {
4447 let_pattern: None,
4448 condition: Box::new(bool_lit(&g, false)),
4449 then_block: Box::new(int_lit(&g, 1)),
4450 else_block: Some(Box::new(int_lit(&g, 2))),
4451 },
4452 );
4453 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(2)));
4454 }
4455
4456 #[tokio::test]
4459 async fn eval_match_literal_pattern() {
4460 let mut interp = Interpreter::new();
4461 let g = gen();
4462
4463 let arm1 = node(
4464 g.next(),
4465 NodeKind::MatchArm {
4466 pattern: Box::new(node(
4467 g.next(),
4468 NodeKind::LiteralPat {
4469 lit: Literal::Int("1".to_string()),
4470 },
4471 )),
4472 guard: None,
4473 body: Box::new(str_lit(&g, "one")),
4474 },
4475 );
4476 let arm2 = node(
4477 g.next(),
4478 NodeKind::MatchArm {
4479 pattern: Box::new(node(g.next(), NodeKind::WildcardPat)),
4480 guard: None,
4481 body: Box::new(str_lit(&g, "other")),
4482 },
4483 );
4484 let m = node(
4485 g.next(),
4486 NodeKind::Match {
4487 scrutinee: Box::new(int_lit(&g, 1)),
4488 arms: vec![arm1, arm2],
4489 },
4490 );
4491 assert_eq!(
4492 interp.eval_expr(&m).await,
4493 Ok(Value::String(BockString::new("one")))
4494 );
4495 }
4496
4497 #[tokio::test]
4498 async fn eval_match_bind_pattern() {
4499 let mut interp = Interpreter::new();
4500 let g = gen();
4501
4502 let arm = node(
4503 g.next(),
4504 NodeKind::MatchArm {
4505 pattern: Box::new(node(
4506 g.next(),
4507 NodeKind::BindPat {
4508 name: ident("n"),
4509 is_mut: false,
4510 },
4511 )),
4512 guard: None,
4513 body: Box::new(node(
4514 g.next(),
4515 NodeKind::BinaryOp {
4516 op: BinOp::Mul,
4517 left: Box::new(var(&g, "n")),
4518 right: Box::new(int_lit(&g, 2)),
4519 },
4520 )),
4521 },
4522 );
4523 let m = node(
4524 g.next(),
4525 NodeKind::Match {
4526 scrutinee: Box::new(int_lit(&g, 5)),
4527 arms: vec![arm],
4528 },
4529 );
4530 assert_eq!(interp.eval_expr(&m).await, Ok(Value::Int(10)));
4531 }
4532
4533 #[tokio::test]
4536 async fn eval_propagate_ok() {
4537 let mut interp = Interpreter::new();
4538 let g = gen();
4539 let ok_node = node(
4541 g.next(),
4542 NodeKind::ResultConstruct {
4543 variant: ResultVariant::Ok,
4544 value: Some(Box::new(int_lit(&g, 42))),
4545 },
4546 );
4547 let prop = node(
4548 g.next(),
4549 NodeKind::Propagate {
4550 expr: Box::new(ok_node),
4551 },
4552 );
4553 assert_eq!(interp.eval_expr(&prop).await, Ok(Value::Int(42)));
4554 }
4555
4556 #[tokio::test]
4557 async fn eval_propagate_err() {
4558 let mut interp = Interpreter::new();
4559 let g = gen();
4560 let err_node = node(
4562 g.next(),
4563 NodeKind::ResultConstruct {
4564 variant: ResultVariant::Err,
4565 value: Some(Box::new(str_lit(&g, "boom"))),
4566 },
4567 );
4568 let prop = node(
4569 g.next(),
4570 NodeKind::Propagate {
4571 expr: Box::new(err_node),
4572 },
4573 );
4574 assert!(matches!(
4575 interp.eval_expr(&prop).await,
4576 Err(RuntimeError::Propagated(_))
4577 ));
4578 }
4579
4580 #[tokio::test]
4581 async fn eval_propagate_some() {
4582 let mut interp = Interpreter::new();
4583 let g = gen();
4584 interp
4586 .env
4587 .define("opt", Value::Optional(Some(Box::new(Value::Int(7)))));
4588 let prop = node(
4589 g.next(),
4590 NodeKind::Propagate {
4591 expr: Box::new(var(&g, "opt")),
4592 },
4593 );
4594 assert_eq!(interp.eval_expr(&prop).await, Ok(Value::Int(7)));
4595 }
4596
4597 #[tokio::test]
4598 async fn eval_propagate_none() {
4599 let mut interp = Interpreter::new();
4600 let g = gen();
4601 interp.env.define("opt", Value::Optional(None));
4602 let prop = node(
4603 g.next(),
4604 NodeKind::Propagate {
4605 expr: Box::new(var(&g, "opt")),
4606 },
4607 );
4608 assert!(matches!(
4609 interp.eval_expr(&prop).await,
4610 Err(RuntimeError::Propagated(_))
4611 ));
4612 }
4613
4614 #[tokio::test]
4619 async fn call_returns_propagated_err_to_caller() {
4620 let mut interp = Interpreter::new();
4621 let g = gen();
4622 let err_node = node(
4625 g.next(),
4626 NodeKind::ResultConstruct {
4627 variant: ResultVariant::Err,
4628 value: Some(Box::new(str_lit(&g, "boom"))),
4629 },
4630 );
4631 let prop = node(
4632 g.next(),
4633 NodeKind::Propagate {
4634 expr: Box::new(err_node),
4635 },
4636 );
4637 let body = node(
4638 g.next(),
4639 NodeKind::Block {
4640 stmts: vec![prop],
4641 tail: Some(Box::new(int_lit(&g, 99))),
4642 },
4643 );
4644 interp.register_fn("fails", vec![], body);
4645 let call = node(
4646 g.next(),
4647 NodeKind::Call {
4648 callee: Box::new(var(&g, "fails")),
4649 args: vec![],
4650 type_args: vec![],
4651 },
4652 );
4653 assert_eq!(
4654 interp.eval_expr(&call).await,
4655 Ok(Value::Result(Err(Box::new(Value::String(
4656 BockString::new("boom")
4657 )))))
4658 );
4659 }
4660
4661 #[tokio::test]
4664 async fn call_returns_propagated_none_to_caller() {
4665 let mut interp = Interpreter::new();
4666 let g = gen();
4667 interp.env.define("opt", Value::Optional(None));
4668 let prop = node(
4669 g.next(),
4670 NodeKind::Propagate {
4671 expr: Box::new(var(&g, "opt")),
4672 },
4673 );
4674 let body = node(
4675 g.next(),
4676 NodeKind::Block {
4677 stmts: vec![prop],
4678 tail: Some(Box::new(int_lit(&g, 99))),
4679 },
4680 );
4681 interp.register_fn("fails_opt", vec![], body);
4682 let call = node(
4683 g.next(),
4684 NodeKind::Call {
4685 callee: Box::new(var(&g, "fails_opt")),
4686 args: vec![],
4687 type_args: vec![],
4688 },
4689 );
4690 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Optional(None)));
4691 }
4692
4693 #[tokio::test]
4697 async fn call_with_ok_propagation_continues_to_tail() {
4698 let mut interp = Interpreter::new();
4699 let g = gen();
4700 let ok_node = node(
4701 g.next(),
4702 NodeKind::ResultConstruct {
4703 variant: ResultVariant::Ok,
4704 value: Some(Box::new(int_lit(&g, 42))),
4705 },
4706 );
4707 let prop = node(
4708 g.next(),
4709 NodeKind::Propagate {
4710 expr: Box::new(ok_node),
4711 },
4712 );
4713 let body = node(
4714 g.next(),
4715 NodeKind::Block {
4716 stmts: vec![prop],
4717 tail: Some(Box::new(int_lit(&g, 99))),
4718 },
4719 );
4720 interp.register_fn("succeeds", vec![], body);
4721 let call = node(
4722 g.next(),
4723 NodeKind::Call {
4724 callee: Box::new(var(&g, "succeeds")),
4725 args: vec![],
4726 type_args: vec![],
4727 },
4728 );
4729 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Int(99)));
4730 }
4731
4732 #[tokio::test]
4735 async fn eval_interpolation() {
4736 let mut interp = Interpreter::new();
4737 let g = gen();
4738 interp
4739 .env
4740 .define("name", Value::String(BockString::new("world")));
4741 let n = node(
4742 g.next(),
4743 NodeKind::Interpolation {
4744 parts: vec![
4745 AirInterpolationPart::Literal("Hello, ".to_string()),
4746 AirInterpolationPart::Expr(Box::new(var(&g, "name"))),
4747 AirInterpolationPart::Literal("!".to_string()),
4748 ],
4749 },
4750 );
4751 assert_eq!(
4752 interp.eval_expr(&n).await,
4753 Ok(Value::String(BockString::new("Hello, world!")))
4754 );
4755 }
4756
4757 #[tokio::test]
4760 async fn eval_range_exclusive() {
4761 let mut interp = Interpreter::new();
4762 let g = gen();
4763 let n = node(
4764 g.next(),
4765 NodeKind::Range {
4766 lo: Box::new(int_lit(&g, 1)),
4767 hi: Box::new(int_lit(&g, 4)),
4768 inclusive: false,
4769 },
4770 );
4771 assert_eq!(
4772 interp.eval_expr(&n).await,
4773 Ok(Value::Range {
4774 start: 1,
4775 end: 4,
4776 inclusive: false,
4777 step: 1
4778 })
4779 );
4780 }
4781
4782 #[tokio::test]
4783 async fn eval_range_inclusive() {
4784 let mut interp = Interpreter::new();
4785 let g = gen();
4786 let n = node(
4787 g.next(),
4788 NodeKind::Range {
4789 lo: Box::new(int_lit(&g, 1)),
4790 hi: Box::new(int_lit(&g, 3)),
4791 inclusive: true,
4792 },
4793 );
4794 assert_eq!(
4795 interp.eval_expr(&n).await,
4796 Ok(Value::Range {
4797 start: 1,
4798 end: 3,
4799 inclusive: true,
4800 step: 1
4801 })
4802 );
4803 }
4804
4805 #[tokio::test]
4808 async fn eval_block_with_let_binding() {
4809 let mut interp = Interpreter::new();
4810 let g = gen();
4811
4812 let let_stmt = node(
4814 g.next(),
4815 NodeKind::LetBinding {
4816 is_mut: false,
4817 pattern: Box::new(node(
4818 g.next(),
4819 NodeKind::BindPat {
4820 name: ident("x"),
4821 is_mut: false,
4822 },
4823 )),
4824 ty: None,
4825 value: Box::new(int_lit(&g, 10)),
4826 },
4827 );
4828 let tail = node(
4829 g.next(),
4830 NodeKind::BinaryOp {
4831 op: BinOp::Add,
4832 left: Box::new(var(&g, "x")),
4833 right: Box::new(int_lit(&g, 5)),
4834 },
4835 );
4836 let block = node(
4837 g.next(),
4838 NodeKind::Block {
4839 stmts: vec![let_stmt],
4840 tail: Some(Box::new(tail)),
4841 },
4842 );
4843 assert_eq!(interp.eval_expr(&block).await, Ok(Value::Int(15)));
4844 }
4845
4846 #[tokio::test]
4849 async fn eval_index_list() {
4850 let mut interp = Interpreter::new();
4851 let g = gen();
4852 interp
4853 .env
4854 .define("lst", Value::List(vec![Value::Int(10), Value::Int(20)]));
4855 let n = node(
4856 g.next(),
4857 NodeKind::Index {
4858 object: Box::new(var(&g, "lst")),
4859 index: Box::new(int_lit(&g, 1)),
4860 },
4861 );
4862 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(20)));
4863 }
4864
4865 #[tokio::test]
4868 async fn eval_result_ok() {
4869 let mut interp = Interpreter::new();
4870 let g = gen();
4871 let n = node(
4872 g.next(),
4873 NodeKind::ResultConstruct {
4874 variant: ResultVariant::Ok,
4875 value: Some(Box::new(int_lit(&g, 42))),
4876 },
4877 );
4878 assert_eq!(
4879 interp.eval_expr(&n).await,
4880 Ok(Value::Result(Ok(Box::new(Value::Int(42)))))
4881 );
4882 }
4883
4884 #[tokio::test]
4885 async fn eval_result_err() {
4886 let mut interp = Interpreter::new();
4887 let g = gen();
4888 let n = node(
4889 g.next(),
4890 NodeKind::ResultConstruct {
4891 variant: ResultVariant::Err,
4892 value: Some(Box::new(str_lit(&g, "oops"))),
4893 },
4894 );
4895 assert_eq!(
4896 interp.eval_expr(&n).await,
4897 Ok(Value::Result(Err(Box::new(Value::String(
4898 BockString::new("oops")
4899 )))))
4900 );
4901 }
4902
4903 #[tokio::test]
4906 async fn eval_compose_functions() {
4907 let mut interp = Interpreter::new();
4908 let g = gen();
4909
4910 let double_body = node(
4912 g.next(),
4913 NodeKind::BinaryOp {
4914 op: BinOp::Mul,
4915 left: Box::new(var(&g, "x")),
4916 right: Box::new(int_lit(&g, 2)),
4917 },
4918 );
4919 interp.register_fn("double", vec!["x".to_string()], double_body);
4920
4921 let inc_body = node(
4922 g.next(),
4923 NodeKind::BinaryOp {
4924 op: BinOp::Add,
4925 left: Box::new(var(&g, "x")),
4926 right: Box::new(int_lit(&g, 1)),
4927 },
4928 );
4929 interp.register_fn("inc", vec!["x".to_string()], inc_body);
4930
4931 let compose = node(
4933 g.next(),
4934 NodeKind::Compose {
4935 left: Box::new(var(&g, "double")),
4936 right: Box::new(var(&g, "inc")),
4937 },
4938 );
4939 let fn_val = interp.eval_expr(&compose).await.unwrap();
4940 interp.env.define("double_then_inc", fn_val);
4941
4942 let call = node(
4943 g.next(),
4944 NodeKind::Call {
4945 callee: Box::new(var(&g, "double_then_inc")),
4946 args: vec![AirArg {
4947 label: None,
4948 value: int_lit(&g, 5),
4949 }],
4950 type_args: vec![],
4951 },
4952 );
4953 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Int(11)));
4955 }
4956
4957 #[tokio::test]
4960 async fn eval_list_len_method() {
4961 let mut interp = Interpreter::new();
4962 let g = gen();
4963 interp.env.define(
4964 "lst",
4965 Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4966 );
4967 let n = node(
4968 g.next(),
4969 NodeKind::MethodCall {
4970 receiver: Box::new(var(&g, "lst")),
4971 method: ident("len"),
4972 type_args: vec![],
4973 args: vec![],
4974 },
4975 );
4976 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(3)));
4977 }
4978
4979 #[tokio::test]
4980 async fn eval_list_map_method() {
4981 let mut interp = Interpreter::new();
4982 let g = gen();
4983
4984 let body = node(
4986 g.next(),
4987 NodeKind::BinaryOp {
4988 op: BinOp::Mul,
4989 left: Box::new(var(&g, "x")),
4990 right: Box::new(int_lit(&g, 2)),
4991 },
4992 );
4993 interp.register_fn("double", vec!["x".to_string()], body);
4994
4995 interp.env.define(
4996 "lst",
4997 Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4998 );
4999
5000 let n = node(
5001 g.next(),
5002 NodeKind::MethodCall {
5003 receiver: Box::new(var(&g, "lst")),
5004 method: ident("map"),
5005 type_args: vec![],
5006 args: vec![AirArg {
5007 label: None,
5008 value: var(&g, "double"),
5009 }],
5010 },
5011 );
5012 assert_eq!(
5013 interp.eval_expr(&n).await,
5014 Ok(Value::List(vec![
5015 Value::Int(2),
5016 Value::Int(4),
5017 Value::Int(6)
5018 ]))
5019 );
5020 }
5021
5022 #[tokio::test]
5025 async fn eval_bitwise_and() {
5026 let mut interp = Interpreter::new();
5027 let g = gen();
5028 let n = node(
5029 g.next(),
5030 NodeKind::BinaryOp {
5031 op: BinOp::BitAnd,
5032 left: Box::new(int_lit(&g, 0b1100)),
5033 right: Box::new(int_lit(&g, 0b1010)),
5034 },
5035 );
5036 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(0b1000)));
5037 }
5038
5039 #[tokio::test]
5040 async fn eval_bitwise_or() {
5041 let mut interp = Interpreter::new();
5042 let g = gen();
5043 let n = node(
5044 g.next(),
5045 NodeKind::BinaryOp {
5046 op: BinOp::BitOr,
5047 left: Box::new(int_lit(&g, 0b1100)),
5048 right: Box::new(int_lit(&g, 0b1010)),
5049 },
5050 );
5051 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(0b1110)));
5052 }
5053
5054 fn bind_pat(g: &NodeIdGen, name: &str) -> AIRNode {
5057 node(
5058 g.next(),
5059 NodeKind::BindPat {
5060 name: ident(name),
5061 is_mut: false,
5062 },
5063 )
5064 }
5065
5066 fn let_stmt(g: &NodeIdGen, pat: AIRNode, val: AIRNode) -> AIRNode {
5067 node(
5068 g.next(),
5069 NodeKind::LetBinding {
5070 is_mut: false,
5071 pattern: Box::new(pat),
5072 ty: None,
5073 value: Box::new(val),
5074 },
5075 )
5076 }
5077
5078 fn assign_node(g: &NodeIdGen, name: &str, val: AIRNode) -> AIRNode {
5079 node(
5080 g.next(),
5081 NodeKind::Assign {
5082 op: AssignOp::Assign,
5083 target: Box::new(var(g, name)),
5084 value: Box::new(val),
5085 },
5086 )
5087 }
5088
5089 fn add(g: &NodeIdGen, left: AIRNode, right: AIRNode) -> AIRNode {
5090 node(
5091 g.next(),
5092 NodeKind::BinaryOp {
5093 op: BinOp::Add,
5094 left: Box::new(left),
5095 right: Box::new(right),
5096 },
5097 )
5098 }
5099
5100 fn lt(g: &NodeIdGen, left: AIRNode, right: AIRNode) -> AIRNode {
5101 node(
5102 g.next(),
5103 NodeKind::BinaryOp {
5104 op: BinOp::Lt,
5105 left: Box::new(left),
5106 right: Box::new(right),
5107 },
5108 )
5109 }
5110
5111 fn block(g: &NodeIdGen, stmts: Vec<AIRNode>, tail: Option<AIRNode>) -> AIRNode {
5112 node(
5113 g.next(),
5114 NodeKind::Block {
5115 stmts,
5116 tail: tail.map(Box::new),
5117 },
5118 )
5119 }
5120
5121 fn list_lit(g: &NodeIdGen, elems: Vec<AIRNode>) -> AIRNode {
5122 node(g.next(), NodeKind::ListLiteral { elems })
5123 }
5124
5125 #[tokio::test]
5128 async fn exec_stmt_let_binding_returns_none() {
5129 let mut interp = Interpreter::new();
5130 let g = gen();
5131 let stmt = let_stmt(&g, bind_pat(&g, "x"), int_lit(&g, 99));
5132 let result = interp.exec_stmt(&stmt).await.unwrap();
5133 assert_eq!(result, None);
5134 assert_eq!(interp.env.get("x"), Some(&Value::Int(99)));
5135 }
5136
5137 #[tokio::test]
5138 async fn exec_block_returns_tail_expression() {
5139 let mut interp = Interpreter::new();
5141 let g = gen();
5142 let blk = block(
5143 &g,
5144 vec![let_stmt(&g, bind_pat(&g, "a"), int_lit(&g, 3))],
5145 Some(add(&g, var(&g, "a"), int_lit(&g, 4))),
5146 );
5147 assert_eq!(interp.exec_block(&blk).await, Ok(Value::Int(7)));
5148 }
5149
5150 #[tokio::test]
5151 async fn block_scope_variables_do_not_leak() {
5152 let mut interp = Interpreter::new();
5154 let g = gen();
5155 let blk = block(
5156 &g,
5157 vec![let_stmt(&g, bind_pat(&g, "inner"), int_lit(&g, 99))],
5158 None,
5159 );
5160 interp.exec_block(&blk).await.unwrap();
5161 assert_eq!(interp.env.get("inner"), None);
5162 }
5163
5164 #[tokio::test]
5165 async fn for_loop_iterates_over_list() {
5166 let mut interp = Interpreter::new();
5168 let g = gen();
5169 interp.env.define("sum", Value::Int(0));
5170 let for_node = node(
5171 g.next(),
5172 NodeKind::For {
5173 pattern: Box::new(bind_pat(&g, "x")),
5174 iterable: Box::new(list_lit(
5175 &g,
5176 vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 3)],
5177 )),
5178 body: Box::new(assign_node(
5179 &g,
5180 "sum",
5181 add(&g, var(&g, "sum"), var(&g, "x")),
5182 )),
5183 },
5184 );
5185 assert_eq!(interp.eval_expr(&for_node).await, Ok(Value::Void));
5186 assert_eq!(interp.env.get("sum"), Some(&Value::Int(6)));
5187 }
5188
5189 #[tokio::test]
5190 async fn for_loop_break_exits_early() {
5191 let mut interp = Interpreter::new();
5193 let g = gen();
5194 let break_node = node(g.next(), NodeKind::Break { value: None });
5195 let for_node = node(
5196 g.next(),
5197 NodeKind::For {
5198 pattern: Box::new(bind_pat(&g, "x")),
5199 iterable: Box::new(list_lit(
5200 &g,
5201 vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 3)],
5202 )),
5203 body: Box::new(break_node),
5204 },
5205 );
5206 assert_eq!(interp.eval_expr(&for_node).await, Ok(Value::Void));
5207 }
5208
5209 #[tokio::test]
5210 async fn while_loop_does_not_execute_when_false() {
5211 let mut interp = Interpreter::new();
5213 let g = gen();
5214 let cond = bool_lit(&g, false);
5215 let body = block(&g, vec![], None);
5216 let while_node = node(
5217 g.next(),
5218 NodeKind::While {
5219 condition: Box::new(cond),
5220 body: Box::new(body),
5221 },
5222 );
5223 assert_eq!(interp.eval_expr(&while_node).await, Ok(Value::Void));
5224 }
5225
5226 #[tokio::test]
5227 async fn while_loop_counts_to_three() {
5228 let mut interp = Interpreter::new();
5230 let g = gen();
5231 interp.env.define("count", Value::Int(0));
5232 let cond = lt(&g, var(&g, "count"), int_lit(&g, 3));
5233 let body = assign_node(&g, "count", add(&g, var(&g, "count"), int_lit(&g, 1)));
5234 let while_node = node(
5235 g.next(),
5236 NodeKind::While {
5237 condition: Box::new(cond),
5238 body: Box::new(body),
5239 },
5240 );
5241 assert_eq!(interp.eval_expr(&while_node).await, Ok(Value::Void));
5242 assert_eq!(interp.env.get("count"), Some(&Value::Int(3)));
5243 }
5244
5245 #[tokio::test]
5246 async fn loop_break_with_value() {
5247 let mut interp = Interpreter::new();
5249 let g = gen();
5250 let break_node = node(
5251 g.next(),
5252 NodeKind::Break {
5253 value: Some(Box::new(int_lit(&g, 42))),
5254 },
5255 );
5256 let loop_node = node(
5257 g.next(),
5258 NodeKind::Loop {
5259 body: Box::new(break_node),
5260 },
5261 );
5262 assert_eq!(interp.eval_expr(&loop_node).await, Ok(Value::Int(42)));
5263 }
5264
5265 #[tokio::test]
5266 async fn loop_break_without_value() {
5267 let mut interp = Interpreter::new();
5269 let g = gen();
5270 let break_node = node(g.next(), NodeKind::Break { value: None });
5271 let loop_node = node(
5272 g.next(),
5273 NodeKind::Loop {
5274 body: Box::new(break_node),
5275 },
5276 );
5277 assert_eq!(interp.eval_expr(&loop_node).await, Ok(Value::Void));
5278 }
5279
5280 #[tokio::test]
5281 async fn guard_passes_when_condition_true() {
5282 let mut interp = Interpreter::new();
5284 let g = gen();
5285 let else_blk = node(g.next(), NodeKind::Return { value: None });
5286 let guard_node = node(
5287 g.next(),
5288 NodeKind::Guard {
5289 let_pattern: None,
5290 condition: Box::new(bool_lit(&g, true)),
5291 else_block: Box::new(else_blk),
5292 },
5293 );
5294 assert_eq!(interp.eval_expr(&guard_node).await, Ok(Value::Void));
5295 }
5296
5297 #[tokio::test]
5298 async fn guard_else_diverges_when_condition_false() {
5299 let mut interp = Interpreter::new();
5301 let g = gen();
5302 let else_blk = node(g.next(), NodeKind::Return { value: None });
5303 let guard_node = node(
5304 g.next(),
5305 NodeKind::Guard {
5306 let_pattern: None,
5307 condition: Box::new(bool_lit(&g, false)),
5308 else_block: Box::new(else_blk),
5309 },
5310 );
5311 assert_eq!(
5312 interp.eval_expr(&guard_node).await,
5313 Err(RuntimeError::Return(Box::new(Value::Void)))
5314 );
5315 }
5316
5317 #[tokio::test]
5318 async fn let_binding_with_tuple_destructuring() {
5319 let mut interp = Interpreter::new();
5321 let g = gen();
5322 let tuple_pat = node(
5323 g.next(),
5324 NodeKind::TuplePat {
5325 elems: vec![bind_pat(&g, "a"), bind_pat(&g, "b")],
5326 },
5327 );
5328 let tuple_val = node(
5329 g.next(),
5330 NodeKind::TupleLiteral {
5331 elems: vec![int_lit(&g, 1), int_lit(&g, 2)],
5332 },
5333 );
5334 let stmt = let_stmt(&g, tuple_pat, tuple_val);
5335 assert_eq!(interp.exec_stmt(&stmt).await, Ok(None));
5336 assert_eq!(interp.env.get("a"), Some(&Value::Int(1)));
5337 assert_eq!(interp.env.get("b"), Some(&Value::Int(2)));
5338 }
5339
5340 #[tokio::test]
5343 async fn effect_op_with_no_handler_errors() {
5344 let mut interp = Interpreter::new();
5345 let g = gen();
5346 let effect_op = node(
5347 g.next(),
5348 NodeKind::EffectOp {
5349 effect: type_path("Log"),
5350 operation: ident("log"),
5351 args: vec![],
5352 },
5353 );
5354 let result = interp.eval_expr(&effect_op).await;
5355 assert!(matches!(result, Err(RuntimeError::NoEffectHandler { .. })));
5356 if let Err(RuntimeError::NoEffectHandler { effect }) = result {
5357 assert_eq!(effect, "Log");
5358 }
5359 }
5360
5361 #[tokio::test]
5362 async fn effect_op_dispatches_to_single_fn_handler() {
5363 let mut interp = Interpreter::new();
5364 let g = gen();
5365
5366 let handler_body = int_lit(&g, 42);
5368 interp.register_fn("my_log", vec!["msg".to_string()], handler_body);
5369
5370 let handler_val = interp.env.get("my_log").cloned().unwrap();
5372 interp
5373 .effect_handlers
5374 .set_module_handler("Log", handler_val);
5375
5376 let effect_op = node(
5378 g.next(),
5379 NodeKind::EffectOp {
5380 effect: type_path("Log"),
5381 operation: ident("log"),
5382 args: vec![AirArg {
5383 label: None,
5384 value: str_lit(&g, "hello"),
5385 }],
5386 },
5387 );
5388 let result = interp.eval_expr(&effect_op).await;
5389 assert_eq!(result, Ok(Value::Int(42)));
5390 }
5391
5392 #[tokio::test]
5393 async fn effect_op_dispatches_to_record_handler() {
5394 let mut interp = Interpreter::new();
5395 let g = gen();
5396
5397 let op_body = int_lit(&g, 99);
5399 interp.register_fn("_log_op", vec!["msg".to_string()], op_body);
5400 let op_fn = interp.env.get("_log_op").cloned().unwrap();
5401
5402 let mut fields = BTreeMap::new();
5404 fields.insert("log".to_string(), op_fn);
5405 let handler_record = Value::Record(RecordValue {
5406 type_name: "ConsoleLog".to_string(),
5407 fields,
5408 });
5409 interp
5410 .effect_handlers
5411 .set_module_handler("Log", handler_record);
5412
5413 let effect_op = node(
5414 g.next(),
5415 NodeKind::EffectOp {
5416 effect: type_path("Log"),
5417 operation: ident("log"),
5418 args: vec![AirArg {
5419 label: None,
5420 value: str_lit(&g, "test"),
5421 }],
5422 },
5423 );
5424 let result = interp.eval_expr(&effect_op).await;
5425 assert_eq!(result, Ok(Value::Int(99)));
5426 }
5427
5428 #[tokio::test]
5429 async fn handling_block_pushes_and_pops_handler() {
5430 let mut interp = Interpreter::new();
5431 let g = gen();
5432
5433 let handler_body = int_lit(&g, 7);
5435 interp.register_fn("test_handler", vec!["msg".to_string()], handler_body);
5436
5437 let effect_op = node(
5439 g.next(),
5440 NodeKind::EffectOp {
5441 effect: type_path("Log"),
5442 operation: ident("log"),
5443 args: vec![AirArg {
5444 label: None,
5445 value: str_lit(&g, "inside"),
5446 }],
5447 },
5448 );
5449
5450 let handling = node(
5451 g.next(),
5452 NodeKind::HandlingBlock {
5453 handlers: vec![AirHandlerPair {
5454 effect: type_path("Log"),
5455 handler: Box::new(var(&g, "test_handler")),
5456 }],
5457 body: Box::new(effect_op),
5458 },
5459 );
5460
5461 let result = interp.eval_expr(&handling).await;
5463 assert_eq!(result, Ok(Value::Int(7)));
5464
5465 assert!(interp.effect_handlers.resolve("Log").is_none());
5467 }
5468
5469 #[tokio::test]
5470 async fn handling_block_pops_on_error() {
5471 let mut interp = Interpreter::new();
5472 let g = gen();
5473
5474 let bad_body = var(&g, "nonexistent");
5476 let handler_body = int_lit(&g, 1);
5477 interp.register_fn("h", vec![], handler_body);
5478
5479 let handling = node(
5480 g.next(),
5481 NodeKind::HandlingBlock {
5482 handlers: vec![AirHandlerPair {
5483 effect: type_path("Log"),
5484 handler: Box::new(var(&g, "h")),
5485 }],
5486 body: Box::new(bad_body),
5487 },
5488 );
5489
5490 let result = interp.eval_expr(&handling).await;
5491 assert!(result.is_err());
5492 assert!(interp.effect_handlers.resolve("Log").is_none());
5494 }
5495
5496 #[tokio::test]
5497 async fn nested_handling_blocks_innermost_wins() {
5498 let mut interp = Interpreter::new();
5499 let g = gen();
5500
5501 let outer_body = int_lit(&g, 1);
5503 interp.register_fn("outer_h", vec!["m".to_string()], outer_body);
5504
5505 let inner_body = int_lit(&g, 2);
5507 interp.register_fn("inner_h", vec!["m".to_string()], inner_body);
5508
5509 let effect_op = node(
5510 g.next(),
5511 NodeKind::EffectOp {
5512 effect: type_path("Log"),
5513 operation: ident("log"),
5514 args: vec![AirArg {
5515 label: None,
5516 value: str_lit(&g, "test"),
5517 }],
5518 },
5519 );
5520
5521 let inner_handling = node(
5522 g.next(),
5523 NodeKind::HandlingBlock {
5524 handlers: vec![AirHandlerPair {
5525 effect: type_path("Log"),
5526 handler: Box::new(var(&g, "inner_h")),
5527 }],
5528 body: Box::new(effect_op),
5529 },
5530 );
5531
5532 let outer_handling = node(
5533 g.next(),
5534 NodeKind::HandlingBlock {
5535 handlers: vec![AirHandlerPair {
5536 effect: type_path("Log"),
5537 handler: Box::new(var(&g, "outer_h")),
5538 }],
5539 body: Box::new(inner_handling),
5540 },
5541 );
5542
5543 let result = interp.eval_expr(&outer_handling).await;
5544 assert_eq!(result, Ok(Value::Int(2)));
5545 }
5546
5547 #[tokio::test]
5548 async fn three_layer_resolution_local_over_module_over_project() {
5549 let mut interp = Interpreter::new();
5550 let g = gen();
5551
5552 let proj_body = int_lit(&g, 1);
5554 interp.register_fn("proj_h", vec!["m".to_string()], proj_body);
5555 let proj_val = interp.env.get("proj_h").cloned().unwrap();
5556 interp.effect_handlers.set_project_handler("Log", proj_val);
5557
5558 let mod_body = int_lit(&g, 2);
5560 interp.register_fn("mod_h", vec!["m".to_string()], mod_body);
5561 let mod_val = interp.env.get("mod_h").cloned().unwrap();
5562 interp.effect_handlers.set_module_handler("Log", mod_val);
5563
5564 let local_body = int_lit(&g, 3);
5566 interp.register_fn("local_h", vec!["m".to_string()], local_body);
5567
5568 let effect_op = node(
5569 g.next(),
5570 NodeKind::EffectOp {
5571 effect: type_path("Log"),
5572 operation: ident("log"),
5573 args: vec![AirArg {
5574 label: None,
5575 value: str_lit(&g, "test"),
5576 }],
5577 },
5578 );
5579
5580 let handling = node(
5581 g.next(),
5582 NodeKind::HandlingBlock {
5583 handlers: vec![AirHandlerPair {
5584 effect: type_path("Log"),
5585 handler: Box::new(var(&g, "local_h")),
5586 }],
5587 body: Box::new(effect_op),
5588 },
5589 );
5590
5591 let result = interp.eval_expr(&handling).await;
5593 assert_eq!(result, Ok(Value::Int(3)));
5594 }
5595
5596 #[tokio::test]
5597 async fn module_handle_registers_handler() {
5598 let mut interp = Interpreter::new();
5599 let g = gen();
5600
5601 let handler_body = int_lit(&g, 55);
5603 interp.register_fn("console_log", vec!["m".to_string()], handler_body);
5604
5605 let module_handle = node(
5607 g.next(),
5608 NodeKind::ModuleHandle {
5609 effect: type_path("Log"),
5610 handler: Box::new(var(&g, "console_log")),
5611 },
5612 );
5613 let result = interp.eval_expr(&module_handle).await;
5614 assert_eq!(result, Ok(Value::Void));
5615
5616 let effect_op = node(
5618 g.next(),
5619 NodeKind::EffectOp {
5620 effect: type_path("Log"),
5621 operation: ident("log"),
5622 args: vec![AirArg {
5623 label: None,
5624 value: str_lit(&g, "test"),
5625 }],
5626 },
5627 );
5628 let result = interp.eval_expr(&effect_op).await;
5629 assert_eq!(result, Ok(Value::Int(55)));
5630 }
5631
5632 #[tokio::test]
5633 async fn effect_decl_evaluates_to_void() {
5634 let mut interp = Interpreter::new();
5635 let g = gen();
5636 let effect_decl = node(
5637 g.next(),
5638 NodeKind::EffectDecl {
5639 annotations: vec![],
5640 visibility: bock_ast::Visibility::Private,
5641 name: ident("Log"),
5642 generic_params: vec![],
5643 components: vec![],
5644 operations: vec![],
5645 },
5646 );
5647 let result = interp.eval_expr(&effect_decl).await;
5648 assert_eq!(result, Ok(Value::Void));
5649 }
5650
5651 #[tokio::test]
5652 async fn effect_ref_evaluates_to_void() {
5653 let mut interp = Interpreter::new();
5654 let g = gen();
5655 let effect_ref = node(
5656 g.next(),
5657 NodeKind::EffectRef {
5658 path: type_path("Log"),
5659 },
5660 );
5661 let result = interp.eval_expr(&effect_ref).await;
5662 assert_eq!(result, Ok(Value::Void));
5663 }
5664
5665 #[tokio::test]
5666 async fn no_handler_error_message_is_clear() {
5667 let err = RuntimeError::NoEffectHandler {
5668 effect: "Log".to_string(),
5669 };
5670 let msg = err.to_string();
5671 assert!(msg.contains("Log"));
5672 assert!(msg.contains("handling"));
5673 assert!(msg.contains("handler"));
5674 }
5675
5676 #[tokio::test]
5677 async fn register_effect_dispatches_through_call() {
5678 let mut interp = Interpreter::new();
5679 let g = gen();
5680
5681 let empty_body = node(
5683 g.next(),
5684 NodeKind::Block {
5685 stmts: vec![],
5686 tail: None,
5687 },
5688 );
5689 let log_op = node(
5690 g.next(),
5691 NodeKind::FnDecl {
5692 annotations: vec![],
5693 visibility: bock_ast::Visibility::Public,
5694 is_async: false,
5695 name: ident("log"),
5696 generic_params: vec![],
5697 params: vec![],
5698 return_type: None,
5699 effect_clause: vec![],
5700 where_clause: vec![],
5701 body: Box::new(empty_body),
5702 },
5703 );
5704 interp.register_effect("Logger", &[log_op]);
5705
5706 let handler_body = int_lit(&g, 77);
5708 interp.register_fn("my_handler", vec!["msg".to_string()], handler_body);
5709 let handler_val = interp.env.get("my_handler").cloned().unwrap();
5710 interp
5711 .effect_handlers
5712 .set_module_handler("Logger", handler_val);
5713
5714 let call_node = node(
5716 g.next(),
5717 NodeKind::Call {
5718 callee: Box::new(var(&g, "log")),
5719 args: vec![AirArg {
5720 label: None,
5721 value: str_lit(&g, "test"),
5722 }],
5723 type_args: vec![],
5724 },
5725 );
5726 let result = interp.eval_expr(&call_node).await;
5727 assert_eq!(result, Ok(Value::Int(77)));
5728 }
5729
5730 #[tokio::test]
5733 async fn is_operator_int() {
5734 let mut interp = Interpreter::new();
5735 let g = gen();
5736 let n = node(
5738 g.next(),
5739 NodeKind::BinaryOp {
5740 op: BinOp::Is,
5741 left: Box::new(int_lit(&g, 42)),
5742 right: Box::new(str_lit(&g, "Int")),
5743 },
5744 );
5745 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
5746 }
5747
5748 #[tokio::test]
5749 async fn is_operator_wrong_type() {
5750 let mut interp = Interpreter::new();
5751 let g = gen();
5752 let n = node(
5754 g.next(),
5755 NodeKind::BinaryOp {
5756 op: BinOp::Is,
5757 left: Box::new(int_lit(&g, 42)),
5758 right: Box::new(str_lit(&g, "String")),
5759 },
5760 );
5761 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(false)));
5762 }
5763
5764 #[tokio::test]
5765 async fn is_operator_string() {
5766 let mut interp = Interpreter::new();
5767 let g = gen();
5768 let n = node(
5769 g.next(),
5770 NodeKind::BinaryOp {
5771 op: BinOp::Is,
5772 left: Box::new(str_lit(&g, "hello")),
5773 right: Box::new(str_lit(&g, "String")),
5774 },
5775 );
5776 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
5777 }
5778
5779 #[tokio::test]
5780 async fn is_operator_bool() {
5781 let mut interp = Interpreter::new();
5782 let g = gen();
5783 let n = node(
5784 g.next(),
5785 NodeKind::BinaryOp {
5786 op: BinOp::Is,
5787 left: Box::new(bool_lit(&g, true)),
5788 right: Box::new(str_lit(&g, "Bool")),
5789 },
5790 );
5791 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
5792 }
5793
5794 #[tokio::test]
5795 async fn is_operator_list() {
5796 let mut interp = Interpreter::new();
5797 let g = gen();
5798 let list_node = node(
5799 g.next(),
5800 NodeKind::ListLiteral {
5801 elems: vec![int_lit(&g, 1)],
5802 },
5803 );
5804 let n = node(
5805 g.next(),
5806 NodeKind::BinaryOp {
5807 op: BinOp::Is,
5808 left: Box::new(list_node),
5809 right: Box::new(str_lit(&g, "List")),
5810 },
5811 );
5812 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
5813 }
5814
5815 #[tokio::test]
5818 async fn match_on_enum_with_wildcard_succeeds() {
5819 let mut interp = Interpreter::new();
5820 let g = gen();
5821 interp.env.define(
5823 "color",
5824 Value::Enum(crate::value::EnumValue {
5825 type_name: "Color".to_string(),
5826 variant: "Red".to_string(),
5827 payload: None,
5828 }),
5829 );
5830 let match_expr = node(
5832 g.next(),
5833 NodeKind::Match {
5834 scrutinee: Box::new(var(&g, "color")),
5835 arms: vec![node(
5836 g.next(),
5837 NodeKind::MatchArm {
5838 pattern: Box::new(node(g.next(), NodeKind::WildcardPat)),
5839 guard: None,
5840 body: Box::new(int_lit(&g, 99)),
5841 },
5842 )],
5843 },
5844 );
5845 assert_eq!(interp.eval_expr(&match_expr).await, Ok(Value::Int(99)));
5846 }
5847
5848 #[tokio::test]
5851 async fn compound_assign_field() {
5852 let mut interp = Interpreter::new();
5853 let g = gen();
5854 let mut fields = BTreeMap::new();
5856 fields.insert("x".to_string(), Value::Int(10));
5857 fields.insert("y".to_string(), Value::Int(20));
5858 interp.env.define(
5859 "obj",
5860 Value::Record(RecordValue {
5861 type_name: "Point".to_string(),
5862 fields,
5863 }),
5864 );
5865 let assign = node(
5867 g.next(),
5868 NodeKind::Assign {
5869 op: AssignOp::AddAssign,
5870 target: Box::new(node(
5871 g.next(),
5872 NodeKind::FieldAccess {
5873 object: Box::new(var(&g, "obj")),
5874 field: ident("x"),
5875 },
5876 )),
5877 value: Box::new(int_lit(&g, 5)),
5878 },
5879 );
5880 assert_eq!(interp.eval_expr(&assign).await, Ok(Value::Void));
5881 let obj = interp.env.get("obj").unwrap().clone();
5883 if let Value::Record(rv) = obj {
5884 assert_eq!(rv.fields.get("x"), Some(&Value::Int(15)));
5885 assert_eq!(rv.fields.get("y"), Some(&Value::Int(20)));
5886 } else {
5887 panic!("expected Record");
5888 }
5889 }
5890
5891 #[tokio::test]
5892 async fn compound_assign_index() {
5893 let mut interp = Interpreter::new();
5894 let g = gen();
5895 interp.env.define(
5897 "list",
5898 Value::List(vec![Value::Int(10), Value::Int(20), Value::Int(30)]),
5899 );
5900 let assign = node(
5902 g.next(),
5903 NodeKind::Assign {
5904 op: AssignOp::AddAssign,
5905 target: Box::new(node(
5906 g.next(),
5907 NodeKind::Index {
5908 object: Box::new(var(&g, "list")),
5909 index: Box::new(int_lit(&g, 1)),
5910 },
5911 )),
5912 value: Box::new(int_lit(&g, 5)),
5913 },
5914 );
5915 assert_eq!(interp.eval_expr(&assign).await, Ok(Value::Void));
5916 let list = interp.env.get("list").unwrap().clone();
5917 assert_eq!(
5918 list,
5919 Value::List(vec![Value::Int(10), Value::Int(25), Value::Int(30)])
5920 );
5921 }
5922
5923 #[tokio::test]
5924 async fn assign_field_simple() {
5925 let mut interp = Interpreter::new();
5926 let g = gen();
5927 let mut fields = BTreeMap::new();
5928 fields.insert("name".to_string(), Value::String(BockString::new("old")));
5929 interp.env.define(
5930 "obj",
5931 Value::Record(RecordValue {
5932 type_name: "Item".to_string(),
5933 fields,
5934 }),
5935 );
5936 let assign = node(
5938 g.next(),
5939 NodeKind::Assign {
5940 op: AssignOp::Assign,
5941 target: Box::new(node(
5942 g.next(),
5943 NodeKind::FieldAccess {
5944 object: Box::new(var(&g, "obj")),
5945 field: ident("name"),
5946 },
5947 )),
5948 value: Box::new(str_lit(&g, "new")),
5949 },
5950 );
5951 assert_eq!(interp.eval_expr(&assign).await, Ok(Value::Void));
5952 let obj = interp.env.get("obj").unwrap().clone();
5953 if let Value::Record(rv) = obj {
5954 assert_eq!(
5955 rv.fields.get("name"),
5956 Some(&Value::String(BockString::new("new")))
5957 );
5958 } else {
5959 panic!("expected Record");
5960 }
5961 }
5962
5963 #[tokio::test]
5966 async fn descending_range_exclusive() {
5967 let result = range_to_vec(5, 1, false, 1);
5969 assert_eq!(
5970 result,
5971 vec![Value::Int(5), Value::Int(4), Value::Int(3), Value::Int(2)]
5972 );
5973 }
5974
5975 #[tokio::test]
5976 async fn descending_range_inclusive() {
5977 let result = range_to_vec(5, 1, true, 1);
5979 assert_eq!(
5980 result,
5981 vec![
5982 Value::Int(5),
5983 Value::Int(4),
5984 Value::Int(3),
5985 Value::Int(2),
5986 Value::Int(1),
5987 ]
5988 );
5989 }
5990
5991 #[tokio::test]
5992 async fn descending_range_explicit_negative_step() {
5993 let result = range_to_vec(10, 0, false, -2);
5995 assert_eq!(
5996 result,
5997 vec![
5998 Value::Int(10),
5999 Value::Int(8),
6000 Value::Int(6),
6001 Value::Int(4),
6002 Value::Int(2),
6003 ]
6004 );
6005 }
6006
6007 #[tokio::test]
6008 async fn ascending_range_still_works() {
6009 let result = range_to_vec(1, 5, false, 1);
6010 assert_eq!(
6011 result,
6012 vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]
6013 );
6014 }
6015
6016 #[tokio::test]
6019 async fn for_in_map() {
6020 let mut interp = Interpreter::new();
6021 let g = gen();
6022 let mut map = BTreeMap::new();
6024 map.insert(Value::Int(1), Value::String(BockString::new("a")));
6025 map.insert(Value::Int(2), Value::String(BockString::new("b")));
6026 interp.env.define("m", Value::Map(map));
6027 interp.env.define("result", Value::List(vec![]));
6028
6029 let for_expr = node(
6033 g.next(),
6034 NodeKind::For {
6035 pattern: Box::new(node(
6036 g.next(),
6037 NodeKind::TuplePat {
6038 elems: vec![
6039 node(
6040 g.next(),
6041 NodeKind::BindPat {
6042 name: ident("k"),
6043 is_mut: false,
6044 },
6045 ),
6046 node(
6047 g.next(),
6048 NodeKind::BindPat {
6049 name: ident("v"),
6050 is_mut: false,
6051 },
6052 ),
6053 ],
6054 },
6055 )),
6056 iterable: Box::new(var(&g, "m")),
6057 body: Box::new(node(
6058 g.next(),
6059 NodeKind::MethodCall {
6060 receiver: Box::new(var(&g, "result")),
6061 method: ident("push"),
6062 args: vec![AirArg {
6063 label: None,
6064 value: var(&g, "k"),
6065 }],
6066 type_args: vec![],
6067 },
6068 )),
6069 },
6070 );
6071 assert_eq!(interp.eval_expr(&for_expr).await, Ok(Value::Void));
6072 let result = interp.env.get("result").unwrap().clone();
6073 assert_eq!(result, Value::List(vec![Value::Int(1), Value::Int(2)]));
6075 }
6076
6077 #[tokio::test]
6080 async fn for_in_lazy_map_iterator() {
6081 use crate::value::{IteratorKind, IteratorValue};
6082
6083 let mut interp = Interpreter::new();
6084 let g = gen();
6085
6086 let double_body = node(
6088 g.next(),
6089 NodeKind::BinaryOp {
6090 op: BinOp::Mul,
6091 left: Box::new(var(&g, "x")),
6092 right: Box::new(int_lit(&g, 2)),
6093 },
6094 );
6095 interp.register_fn("double", vec!["x".to_string()], double_body);
6096 let double_fn = match interp.env.get("double").unwrap().clone() {
6097 Value::Function(fv) => fv,
6098 _ => panic!("expected function"),
6099 };
6100
6101 let source = IteratorKind::List {
6103 items: vec![Value::Int(1), Value::Int(2), Value::Int(3)],
6104 pos: 0,
6105 };
6106 let map_iter = IteratorKind::Map {
6107 source: std::sync::Arc::new(std::sync::Mutex::new(source)),
6108 func: double_fn,
6109 };
6110 let iter_val = IteratorValue::new(map_iter);
6111 interp.env.define("it", Value::Iterator(iter_val));
6112 interp.env.define("result", Value::List(vec![]));
6113
6114 let for_expr = node(
6116 g.next(),
6117 NodeKind::For {
6118 pattern: Box::new(node(
6119 g.next(),
6120 NodeKind::BindPat {
6121 name: ident("item"),
6122 is_mut: false,
6123 },
6124 )),
6125 iterable: Box::new(var(&g, "it")),
6126 body: Box::new(node(
6127 g.next(),
6128 NodeKind::MethodCall {
6129 receiver: Box::new(var(&g, "result")),
6130 method: ident("push"),
6131 args: vec![AirArg {
6132 label: None,
6133 value: var(&g, "item"),
6134 }],
6135 type_args: vec![],
6136 },
6137 )),
6138 },
6139 );
6140 assert_eq!(interp.eval_expr(&for_expr).await, Ok(Value::Void));
6141 let result = interp.env.get("result").unwrap().clone();
6142 assert_eq!(
6143 result,
6144 Value::List(vec![Value::Int(2), Value::Int(4), Value::Int(6)])
6145 );
6146 }
6147
6148 #[tokio::test]
6149 async fn for_in_lazy_filter_iterator() {
6150 use crate::value::{IteratorKind, IteratorValue};
6151
6152 let mut interp = Interpreter::new();
6153 let g = gen();
6154
6155 let pred_body = node(
6157 g.next(),
6158 NodeKind::BinaryOp {
6159 op: BinOp::Gt,
6160 left: Box::new(var(&g, "x")),
6161 right: Box::new(int_lit(&g, 2)),
6162 },
6163 );
6164 interp.register_fn("gt2", vec!["x".to_string()], pred_body);
6165 let gt2_fn = match interp.env.get("gt2").unwrap().clone() {
6166 Value::Function(fv) => fv,
6167 _ => panic!("expected function"),
6168 };
6169
6170 let source = IteratorKind::List {
6172 items: vec![
6173 Value::Int(1),
6174 Value::Int(2),
6175 Value::Int(3),
6176 Value::Int(4),
6177 Value::Int(5),
6178 ],
6179 pos: 0,
6180 };
6181 let filter_iter = IteratorKind::Filter {
6182 source: std::sync::Arc::new(std::sync::Mutex::new(source)),
6183 pred: gt2_fn,
6184 };
6185 let iter_val = IteratorValue::new(filter_iter);
6186 interp.env.define("it", Value::Iterator(iter_val));
6187 interp.env.define("result", Value::List(vec![]));
6188
6189 let for_expr = node(
6191 g.next(),
6192 NodeKind::For {
6193 pattern: Box::new(node(
6194 g.next(),
6195 NodeKind::BindPat {
6196 name: ident("item"),
6197 is_mut: false,
6198 },
6199 )),
6200 iterable: Box::new(var(&g, "it")),
6201 body: Box::new(node(
6202 g.next(),
6203 NodeKind::MethodCall {
6204 receiver: Box::new(var(&g, "result")),
6205 method: ident("push"),
6206 args: vec![AirArg {
6207 label: None,
6208 value: var(&g, "item"),
6209 }],
6210 type_args: vec![],
6211 },
6212 )),
6213 },
6214 );
6215 assert_eq!(interp.eval_expr(&for_expr).await, Ok(Value::Void));
6216 let result = interp.env.get("result").unwrap().clone();
6217 assert_eq!(
6218 result,
6219 Value::List(vec![Value::Int(3), Value::Int(4), Value::Int(5)])
6220 );
6221 }
6222
6223 #[tokio::test]
6226 async fn method_body_sees_program_globals() {
6227 let mut interp = Interpreter::new();
6232 let g = gen();
6233
6234 let body = node(
6236 g.next(),
6237 NodeKind::Block {
6238 stmts: vec![],
6239 tail: Some(Box::new(var(&g, "None"))),
6240 },
6241 );
6242 interp.method_table.insert(
6243 "Holder".to_string(),
6244 HashMap::from([(
6245 "get_none".to_string(),
6246 MethodEntry {
6247 params: vec!["self".to_string()],
6248 self_is_mut: false,
6249 body,
6250 },
6251 )]),
6252 );
6253
6254 let receiver = Value::Record(RecordValue {
6255 type_name: "Holder".to_string(),
6256 fields: std::collections::BTreeMap::new(),
6257 });
6258
6259 let outcome = interp
6260 .try_call_impl_method(&receiver, "get_none", vec![])
6261 .await
6262 .expect("method dispatch should not error")
6263 .expect("method should be found");
6264 assert_eq!(outcome.value, Value::Optional(None));
6265 assert!(outcome.updated_self.is_none());
6266 }
6267
6268 #[tokio::test]
6269 async fn method_body_global_invisible_without_seeding() {
6270 let mut interp = Interpreter::new();
6274 let g = gen();
6275 let bare = Environment::new();
6276 interp.env = bare;
6278 assert!(interp.env.get("None").is_none());
6279 let _ = g;
6280 }
6281}