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(Clone)]
74pub struct Interpreter {
75 pub env: Environment,
77 pub effect_handlers: EffectStack,
79 fn_registry: HashMap<u64, Closure>,
81 pub builtins: BuiltinRegistry,
83 method_table: HashMap<String, HashMap<String, (Vec<String>, AIRNode)>>,
85 effect_operations: HashMap<String, String>,
88}
89
90impl CallbackInvoker for Interpreter {
91 fn invoke<'a>(
92 &'a mut self,
93 callable: &'a Value,
94 args: &'a [Value],
95 ) -> BoxFuture<'a, Result<Value, RuntimeError>> {
96 Box::pin(async move { self.invoke_callback(callable, args).await })
97 }
98}
99
100impl Default for Interpreter {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106impl Interpreter {
107 #[must_use]
110 pub fn new() -> Self {
111 let mut builtins = BuiltinRegistry::new();
112 builtins.register_defaults();
113 let mut interp = Self {
114 env: Environment::new(),
115 effect_handlers: EffectStack::new(),
116 fn_registry: HashMap::new(),
117 builtins,
118 method_table: HashMap::new(),
119 effect_operations: HashMap::new(),
120 };
121 interp.register_prelude_constructors();
122 interp
123 }
124
125 fn register_prelude_constructors(&mut self) {
128 self.env.define("None", Value::Optional(None));
130
131 self.register_native_constructor("Some", 1, |args| {
133 Value::Optional(Some(Box::new(args[0].clone())))
134 });
135
136 self.register_native_constructor("Ok", 1, |args| {
138 Value::Result(Ok(Box::new(args[0].clone())))
139 });
140
141 self.register_native_constructor("Err", 1, |args| {
143 Value::Result(Err(Box::new(args[0].clone())))
144 });
145 }
146
147 fn register_native_constructor(
152 &mut self,
153 name: &str,
154 arity: usize,
155 build: impl Fn(&[Value]) -> Value + Send + Sync + 'static,
156 ) {
157 let fn_val = FnValue::new_named(name);
158 let id = fn_val.id;
159 let params: Vec<String> = (0..arity).map(|i| format!("__arg{i}")).collect();
160 self.fn_registry.insert(
161 id,
162 Closure {
163 params,
164 body: ClosureBody::Native(std::sync::Arc::new(build)),
165 captured: Environment::new(),
166 is_toplevel: true,
167 is_async: false,
168 },
169 );
170 self.env.define(name, Value::Function(fn_val));
171 }
172
173 pub fn register_enum(&mut self, enum_name: &str, variants: &[AIRNode]) {
179 let type_name = enum_name.to_string();
180 for variant in variants {
181 if let NodeKind::EnumVariant { name, payload } = &variant.kind {
182 let variant_name = name.name.clone();
183 match payload {
184 EnumVariantPayload::Unit => {
185 self.env.define(
186 variant_name.clone(),
187 Value::Enum(EnumValue {
188 type_name: type_name.clone(),
189 variant: variant_name,
190 payload: None,
191 }),
192 );
193 }
194 EnumVariantPayload::Tuple(fields) => {
195 let arity = fields.len();
196 let tn = type_name.clone();
197 let vn = variant_name.clone();
198 if arity == 1 {
199 self.register_native_constructor(&variant_name, arity, move |args| {
200 Value::Enum(EnumValue {
201 type_name: tn.clone(),
202 variant: vn.clone(),
203 payload: Some(Box::new(args[0].clone())),
204 })
205 });
206 } else {
207 self.register_native_constructor(&variant_name, arity, move |args| {
208 Value::Enum(EnumValue {
209 type_name: tn.clone(),
210 variant: vn.clone(),
211 payload: Some(Box::new(Value::Tuple(args.to_vec()))),
212 })
213 });
214 }
215 }
216 EnumVariantPayload::Struct(fields) => {
217 let arity = fields.len();
218 let vn = variant_name.clone();
219 let field_names: Vec<String> =
220 fields.iter().map(|f| f.name.name.clone()).collect();
221 self.register_native_constructor(&variant_name, arity, move |args| {
222 let mut field_map = std::collections::BTreeMap::new();
223 for (fname, val) in field_names.iter().zip(args.iter()) {
224 field_map.insert(fname.clone(), val.clone());
225 }
226 Value::Record(RecordValue {
227 type_name: vn.clone(),
228 fields: field_map,
229 })
230 });
231 }
232 }
233 }
234 }
235 }
236
237 pub fn register_fn(&mut self, name: &str, params: Vec<String>, body: AIRNode) {
242 self.register_fn_with_async(name, params, body, false);
243 }
244
245 pub fn register_fn_with_async(
251 &mut self,
252 name: &str,
253 params: Vec<String>,
254 body: AIRNode,
255 is_async: bool,
256 ) {
257 let fn_val = FnValue::new_named(name);
258 let id = fn_val.id;
259 self.env.define(name, Value::Function(fn_val));
260 self.fn_registry.insert(
261 id,
262 Closure {
263 params,
264 body: ClosureBody::Air(Box::new(body)),
265 captured: Environment::new(),
266 is_toplevel: true,
267 is_async,
268 },
269 );
270 }
271
272 pub fn register_impl(&mut self, target: &AIRNode, methods: &[AIRNode]) {
277 let type_name = match &target.kind {
279 NodeKind::TypeNamed { path, .. } => path
280 .segments
281 .last()
282 .map(|s| s.name.clone())
283 .unwrap_or_default(),
284 _ => return,
285 };
286
287 let type_methods = self.method_table.entry(type_name).or_default();
288 for method in methods {
289 if let NodeKind::FnDecl {
290 name, params, body, ..
291 } = &method.kind
292 {
293 let param_names: Vec<String> = params
294 .iter()
295 .filter_map(|p| {
296 if let NodeKind::Param { pattern, .. } = &p.kind {
297 if let NodeKind::BindPat { name, .. } = &pattern.kind {
298 return Some(name.name.clone());
299 }
300 }
301 None
302 })
303 .collect();
304 type_methods.insert(name.name.clone(), (param_names, *body.clone()));
305 }
306 }
307 }
308
309 pub fn register_effect(&mut self, effect_name: &str, operations: &[AIRNode]) {
317 self.env.define(effect_name, Value::Void);
319
320 for op in operations {
321 if let NodeKind::FnDecl { name, .. } = &op.kind {
322 self.effect_operations
323 .insert(name.name.clone(), effect_name.to_string());
324 }
325 }
326 }
327
328 #[async_recursion]
332 pub async fn invoke_callback(
333 &mut self,
334 callable: &Value,
335 args: &[Value],
336 ) -> Result<Value, RuntimeError> {
337 let fn_id = match callable {
338 Value::Function(fv) => fv.id,
339 other => {
340 return Err(RuntimeError::NotCallable {
341 value: other.to_string(),
342 })
343 }
344 };
345 let closure =
346 self.fn_registry
347 .get(&fn_id)
348 .cloned()
349 .ok_or_else(|| RuntimeError::NotCallable {
350 value: format!("unregistered fn #{fn_id}"),
351 })?;
352 self.call_closure(&closure, args.to_vec()).await
353 }
354
355 #[async_recursion]
359 pub async fn eval_expr(&mut self, node: &AIRNode) -> Result<Value, RuntimeError> {
360 match &node.kind {
361 NodeKind::Literal { lit } => self.eval_literal(lit),
362
363 NodeKind::Identifier { name } => {
364 self.env
365 .get(&name.name)
366 .cloned()
367 .ok_or_else(|| RuntimeError::UndefinedVariable {
368 name: name.name.clone(),
369 })
370 }
371
372 NodeKind::BinaryOp { op, left, right } => self.eval_binary_op(*op, left, right).await,
373
374 NodeKind::UnaryOp { op, operand } => self.eval_unary_op(*op, operand).await,
375
376 NodeKind::Assign { op, target, value } => self.eval_assign(*op, target, value).await,
377
378 NodeKind::Call { callee, args, .. } => self.eval_call(callee, args).await,
379
380 NodeKind::MethodCall {
381 receiver,
382 method,
383 args,
384 ..
385 } => self.eval_method_call(receiver, &method.name.clone(), args).await,
386
387 NodeKind::FieldAccess { object, field } => {
388 self.eval_field_access(object, &field.name.clone()).await
389 }
390
391 NodeKind::Index { object, index } => self.eval_index(object, index).await,
392
393 NodeKind::Propagate { expr } => self.eval_propagate(expr).await,
394
395 NodeKind::Lambda { params, body } => self.eval_lambda(params, body),
396
397 NodeKind::Pipe { left, right } => self.eval_pipe(left, right).await,
398
399 NodeKind::Compose { left, right } => self.eval_compose(left, right).await,
400
401 NodeKind::ListLiteral { elems } => {
403 let mut values = Vec::with_capacity(elems.len());
404 for elem in elems {
405 values.push(self.eval_expr(elem).await?);
406 }
407 Ok(Value::List(values))
408 }
409
410 NodeKind::MapLiteral { entries } => {
411 let mut map = BTreeMap::new();
412 for entry in entries {
413 let k = self.eval_expr(&entry.key).await?;
414 let v = self.eval_expr(&entry.value).await?;
415 map.insert(k, v);
416 }
417 Ok(Value::Map(map))
418 }
419
420 NodeKind::SetLiteral { elems } => {
421 let mut set = BTreeSet::new();
422 for elem in elems {
423 set.insert(self.eval_expr(elem).await?);
424 }
425 Ok(Value::Set(set))
426 }
427
428 NodeKind::TupleLiteral { elems } => {
429 let mut values = Vec::with_capacity(elems.len());
430 for elem in elems {
431 values.push(self.eval_expr(elem).await?);
432 }
433 Ok(Value::Tuple(values))
434 }
435
436 NodeKind::RecordConstruct {
437 path,
438 fields,
439 spread,
440 } => self.eval_record_construct(path, fields, spread.as_deref()).await,
441
442 NodeKind::Interpolation { parts } => self.eval_interpolation(parts).await,
443
444 NodeKind::Range { lo, hi, inclusive } => self.eval_range(lo, hi, *inclusive).await,
445
446 NodeKind::ResultConstruct { variant, value } => {
447 let inner = match value {
448 Some(v) => self.eval_expr(v).await?,
449 None => Value::Void,
450 };
451 match variant {
452 ResultVariant::Ok => Ok(Value::Result(Ok(Box::new(inner)))),
453 ResultVariant::Err => Ok(Value::Result(Err(Box::new(inner)))),
454 }
455 }
456
457 NodeKind::Block { stmts, tail } => self.eval_block(stmts, tail.as_deref()).await,
459
460 NodeKind::If {
461 let_pattern,
462 condition,
463 then_block,
464 else_block,
465 } => self.eval_if(
466 let_pattern.as_deref(),
467 condition,
468 then_block,
469 else_block.as_deref(),
470 ).await,
471
472 NodeKind::Match { scrutinee, arms } => self.eval_match(scrutinee, arms).await,
473
474 NodeKind::Return { value } => {
475 let v = match value {
476 Some(e) => self.eval_expr(e).await?,
477 None => Value::Void,
478 };
479 Err(RuntimeError::Return(Box::new(v)))
480 }
481
482 NodeKind::Break { value } => {
483 let v = match value {
484 Some(e) => Some(Box::new(self.eval_expr(e).await?)),
485 None => None,
486 };
487 Err(RuntimeError::Break(v))
488 }
489
490 NodeKind::Continue => Err(RuntimeError::Continue),
491
492 NodeKind::For {
493 pattern,
494 iterable,
495 body,
496 } => self.eval_for(pattern, iterable, body).await,
497
498 NodeKind::While { condition, body } => self.eval_while(condition, body).await,
499
500 NodeKind::Loop { body } => self.eval_loop(body).await,
501
502 NodeKind::Guard {
503 let_pattern,
504 condition,
505 else_block,
506 } => self.eval_guard(let_pattern.as_deref(), condition, else_block).await,
507
508 NodeKind::Unreachable => Err(RuntimeError::Unreachable),
509
510 NodeKind::Move { expr }
512 | NodeKind::Borrow { expr }
513 | NodeKind::MutableBorrow { expr } => self.eval_expr(expr).await,
514
515 NodeKind::Await { expr } => {
517 let val = self.eval_expr(expr).await?;
518 match val {
519 Value::Future(handle) => {
520 let h = handle.lock().unwrap().take();
521 match h {
522 Some(jh) => match jh.await {
523 Ok(inner) => inner,
524 Err(e) => Err(RuntimeError::TypeError(format!(
525 "async task panicked: {e}"
526 ))),
527 },
528 None => Err(RuntimeError::TypeError(
529 "future already awaited".to_string(),
530 )),
531 }
532 }
533 other => Ok(other),
534 }
535 }
536
537 NodeKind::LetBinding { pattern, value, .. } => {
539 let v = self.eval_expr(value).await?;
540 self.bind_pattern(pattern, v).await?;
541 Ok(Value::Void)
542 }
543
544 NodeKind::Placeholder => Ok(Value::Void),
546
547 NodeKind::EffectDecl { name, .. } => {
551 self.env.define(&name.name, Value::Void);
554 Ok(Value::Void)
555 }
556
557 NodeKind::ModuleHandle { effect, handler } => {
559 let handler_val = self.eval_expr(handler).await?;
560 let effect_name = self.type_path_to_name(effect);
561 self.effect_handlers
562 .set_module_handler(effect_name, handler_val);
563 Ok(Value::Void)
564 }
565
566 NodeKind::EffectOp {
568 effect,
569 operation,
570 args,
571 } => {
572 let effect_name = self.type_path_to_name(effect);
573 let handler = self.effect_handlers.resolve(&effect_name).cloned().ok_or(
574 RuntimeError::NoEffectHandler {
575 effect: effect_name,
576 },
577 )?;
578
579 let mut arg_values = Vec::with_capacity(args.len());
581 for arg in args {
582 arg_values.push(self.eval_expr(&arg.value).await?);
583 }
584
585 self.dispatch_effect_op(&handler, &operation.name, arg_values).await
589 }
590
591 NodeKind::HandlingBlock { handlers, body } => {
593 let mut frame = std::collections::HashMap::new();
594 for pair in handlers {
595 let handler_val = self.eval_expr(&pair.handler).await?;
596 let effect_name = self.type_path_to_name(&pair.effect);
597 frame.insert(effect_name, handler_val);
598 }
599 self.effect_handlers.push_handlers(frame);
600 let result = self.eval_expr(body).await;
601 self.effect_handlers.pop_handlers();
602 result
603 }
604
605 NodeKind::EffectRef { .. } => Ok(Value::Void),
607
608 other => Err(RuntimeError::NotImplemented(
609 format!("{other:?}").chars().take(60).collect(),
610 )),
611 }
612 }
613
614 fn type_path_to_name(&self, tp: &TypePath) -> String {
618 tp.segments
619 .iter()
620 .map(|s| s.name.as_str())
621 .collect::<Vec<_>>()
622 .join(".")
623 }
624
625 #[async_recursion]
636 async fn dispatch_effect_op(
637 &mut self,
638 handler: &Value,
639 operation: &str,
640 args: Vec<Value>,
641 ) -> Result<Value, RuntimeError> {
642 match handler {
643 Value::Record(rec) => {
646 if let Some(op_fn) = rec.fields.get(operation).cloned() {
647 return self.call_fn_value(&op_fn, args).await;
648 }
649 if let Some(result) = self
650 .try_call_impl_method(handler, operation, args)
651 .await?
652 {
653 return Ok(result);
654 }
655 Err(RuntimeError::FieldNotFound {
656 field: operation.to_string(),
657 type_name: rec.type_name.clone(),
658 })
659 }
660 Value::Function(_) => {
662 let handler = handler.clone();
663 self.call_fn_value(&handler, args).await
664 }
665 other => Err(RuntimeError::TypeError(format!(
666 "effect handler must be a record or function, got {other}"
667 ))),
668 }
669 }
670
671 #[async_recursion]
675 pub async fn call_fn_value(&mut self, val: &Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
676 let fn_id = match val {
677 Value::Function(fv) => fv.id,
678 other => {
679 return Err(RuntimeError::NotCallable {
680 value: other.to_string(),
681 })
682 }
683 };
684 let closure =
685 self.fn_registry
686 .get(&fn_id)
687 .cloned()
688 .ok_or_else(|| RuntimeError::NotCallable {
689 value: format!("unregistered fn #{fn_id}"),
690 })?;
691 self.call_closure(&closure, args).await
692 }
693
694 fn eval_literal(&self, lit: &Literal) -> Result<Value, RuntimeError> {
697 match lit {
698 Literal::Int(s) => {
699 let (numeric, _) = bock_ast::strip_type_suffix(s);
701 let clean = numeric.replace('_', "");
703 let n = if clean.starts_with("0x") || clean.starts_with("0X") {
704 i64::from_str_radix(&clean[2..], 16)
705 } else if clean.starts_with("0o") || clean.starts_with("0O") {
706 i64::from_str_radix(&clean[2..], 8)
707 } else if clean.starts_with("0b") || clean.starts_with("0B") {
708 i64::from_str_radix(&clean[2..], 2)
709 } else {
710 clean.parse::<i64>()
711 };
712 n.map(Value::Int)
713 .map_err(|_| RuntimeError::IntParseFailed(s.clone()))
714 }
715 Literal::Float(s) => {
716 let (numeric, _) = bock_ast::strip_type_suffix(s);
718 numeric
719 .replace('_', "")
720 .parse::<f64>()
721 .map(|f| Value::Float(OrdF64(f)))
722 .map_err(|_| RuntimeError::FloatParseFailed(s.clone()))
723 }
724 Literal::Bool(b) => Ok(Value::Bool(*b)),
725 Literal::Char(s) => Ok(Value::Char(s.chars().next().unwrap_or('\0'))),
726 Literal::String(s) => Ok(Value::String(BockString::new(s.clone()))),
727 Literal::Unit => Ok(Value::Void),
728 }
729 }
730
731 #[async_recursion]
734 async fn eval_binary_op(
735 &mut self,
736 op: BinOp,
737 left: &AIRNode,
738 right: &AIRNode,
739 ) -> Result<Value, RuntimeError> {
740 match op {
742 BinOp::And => {
743 let l = self.eval_expr(left).await?;
744 return match l {
745 Value::Bool(false) => Ok(Value::Bool(false)),
746 Value::Bool(true) => self.eval_expr(right).await,
747 other => Err(RuntimeError::TypeError(format!(
748 "expected Bool in &&, got {other}"
749 ))),
750 };
751 }
752 BinOp::Or => {
753 let l = self.eval_expr(left).await?;
754 return match l {
755 Value::Bool(true) => Ok(Value::Bool(true)),
756 Value::Bool(false) => self.eval_expr(right).await,
757 other => Err(RuntimeError::TypeError(format!(
758 "expected Bool in ||, got {other}"
759 ))),
760 };
761 }
762 _ => {}
763 }
764
765 let l = self.eval_expr(left).await?;
766 let r = self.eval_expr(right).await?;
767
768 match (op, l, r) {
769 (BinOp::Add, Value::Int(a), Value::Int(b)) => a
771 .checked_add(b)
772 .map(Value::Int)
773 .ok_or(RuntimeError::IntOverflow),
774 (BinOp::Add, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 + b.0))),
775 (BinOp::Add, Value::String(a), Value::String(b)) => {
776 Ok(Value::String(BockString::new(format!("{a}{b}"))))
777 }
778
779 (BinOp::Sub, Value::Int(a), Value::Int(b)) => a
780 .checked_sub(b)
781 .map(Value::Int)
782 .ok_or(RuntimeError::IntOverflow),
783 (BinOp::Sub, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 - b.0))),
784
785 (BinOp::Mul, Value::Int(a), Value::Int(b)) => a
786 .checked_mul(b)
787 .map(Value::Int)
788 .ok_or(RuntimeError::IntOverflow),
789 (BinOp::Mul, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 * b.0))),
790
791 (BinOp::Div, Value::Int(a), Value::Int(b)) => {
792 if b == 0 {
793 Err(RuntimeError::DivisionByZero)
794 } else {
795 Ok(Value::Int(a / b))
796 }
797 }
798 (BinOp::Div, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 / b.0))),
799
800 (BinOp::Rem, Value::Int(a), Value::Int(b)) => {
801 if b == 0 {
802 Err(RuntimeError::DivisionByZero)
803 } else {
804 Ok(Value::Int(a % b))
805 }
806 }
807 (BinOp::Rem, Value::Float(a), Value::Float(b)) => Ok(Value::Float(OrdF64(a.0 % b.0))),
808
809 (BinOp::Pow, Value::Int(a), Value::Int(b)) => {
810 if b < 0 {
811 Err(RuntimeError::TypeError(
812 "negative integer exponent".to_string(),
813 ))
814 } else if b > u32::MAX as i64 {
815 Err(RuntimeError::IntOverflow)
816 } else {
817 a.checked_pow(b as u32)
818 .map(Value::Int)
819 .ok_or(RuntimeError::IntOverflow)
820 }
821 }
822 (BinOp::Pow, Value::Float(a), Value::Float(b)) => {
823 Ok(Value::Float(OrdF64(a.0.powf(b.0))))
824 }
825
826 (BinOp::Eq, l, r) => Ok(Value::Bool(l == r)),
828 (BinOp::Ne, l, r) => Ok(Value::Bool(l != r)),
829 (BinOp::Lt, l, r) => Ok(Value::Bool(l < r)),
830 (BinOp::Le, l, r) => Ok(Value::Bool(l <= r)),
831 (BinOp::Gt, l, r) => Ok(Value::Bool(l > r)),
832 (BinOp::Ge, l, r) => Ok(Value::Bool(l >= r)),
833
834 (BinOp::BitAnd, Value::Int(a), Value::Int(b)) => Ok(Value::Int(a & b)),
836 (BinOp::BitOr, Value::Int(a), Value::Int(b)) => Ok(Value::Int(a | b)),
837 (BinOp::BitXor, Value::Int(a), Value::Int(b)) => Ok(Value::Int(a ^ b)),
838 (BinOp::Compose, Value::Function(f), Value::Function(g)) => {
841 let fn_val = FnValue::new_anonymous();
842 let id = fn_val.id;
843 self.fn_registry.insert(
844 id,
845 Closure {
846 params: vec!["__x".to_string()],
847 body: ClosureBody::Composed {
848 inner: f.id,
849 outer: g.id,
850 },
851 captured: self.env.clone(),
852 is_toplevel: false,
853 is_async: false,
854 },
855 );
856 Ok(Value::Function(fn_val))
857 }
858
859 (BinOp::Is, value, Value::String(type_name)) => {
861 let tag = TypeTag::of(&value);
862 Ok(Value::Bool(tag.name() == type_name.as_str()))
863 }
864
865 (op, l, r) => {
869 let tag = TypeTag::of(&l);
870 let method = match op {
871 BinOp::Add => Some("add"),
872 BinOp::Sub => Some("sub"),
873 BinOp::Mul => Some("mul"),
874 BinOp::Div => Some("div"),
875 BinOp::Rem => Some("rem"),
876 BinOp::Pow => Some("pow"),
877 _ => None,
878 };
879 if let Some(name) = method {
880 if let Some(result) = self.builtins.call(tag, name, &[l.clone(), r.clone()]) {
881 return result;
882 }
883 }
884 Err(RuntimeError::TypeError(format!(
885 "operator {op:?} not supported for {l} and {r}"
886 )))
887 }
888 }
889 }
890
891 #[async_recursion]
894 async fn eval_unary_op(&mut self, op: UnaryOp, operand: &AIRNode) -> Result<Value, RuntimeError> {
895 let val = self.eval_expr(operand).await?;
896 match (op, val) {
897 (UnaryOp::Neg, Value::Int(n)) => n
898 .checked_neg()
899 .map(Value::Int)
900 .ok_or(RuntimeError::IntOverflow),
901 (UnaryOp::Neg, Value::Float(f)) => Ok(Value::Float(OrdF64(-f.0))),
902 (UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
903 (UnaryOp::BitNot, Value::Int(n)) => Ok(Value::Int(!n)),
904 (op, val) => Err(RuntimeError::TypeError(format!(
905 "unary operator {op:?} not supported for {val}"
906 ))),
907 }
908 }
909
910 #[async_recursion]
913 async fn eval_assign(
914 &mut self,
915 op: AssignOp,
916 target: &AIRNode,
917 value: &AIRNode,
918 ) -> Result<Value, RuntimeError> {
919 let rhs = self.eval_expr(value).await?;
920 match &target.kind {
921 NodeKind::Identifier { name } => {
922 let name = name.name.clone();
923 let new_val = match op {
924 AssignOp::Assign => rhs,
925 compound => {
926 let current = self.env.get(&name).cloned().ok_or_else(|| {
927 RuntimeError::UndefinedVariable { name: name.clone() }
928 })?;
929 self.apply_assign_op(compound, current, rhs)?
930 }
931 };
932 if !self.env.assign(&name, new_val) {
933 return Err(RuntimeError::UndefinedVariable { name });
934 }
935 Ok(Value::Void)
936 }
937 NodeKind::FieldAccess { object, field } => {
938 let field_name = field.name.clone();
939 let obj = self.eval_expr(object).await?;
940 match obj {
941 Value::Record(mut rv) => {
942 let new_val = match op {
943 AssignOp::Assign => rhs,
944 compound => {
945 let current =
946 rv.fields.get(&field_name).cloned().ok_or_else(|| {
947 RuntimeError::FieldNotFound {
948 field: field_name.clone(),
949 type_name: rv.type_name.clone(),
950 }
951 })?;
952 self.apply_assign_op(compound, current, rhs)?
953 }
954 };
955 rv.fields.insert(field_name, new_val);
956 if let NodeKind::Identifier { name: obj_name } = &object.kind {
958 let updated = Value::Record(rv);
959 if !self.env.assign(&obj_name.name, updated) {
960 return Err(RuntimeError::UndefinedVariable {
961 name: obj_name.name.clone(),
962 });
963 }
964 }
965 Ok(Value::Void)
966 }
967 other => Err(RuntimeError::TypeError(format!(
968 "cannot assign to field '{field_name}' on {other}"
969 ))),
970 }
971 }
972 NodeKind::Index { object, index } => {
973 let idx = self.eval_expr(index).await?;
974 let obj = self.eval_expr(object).await?;
975 match (obj, idx) {
976 (Value::List(mut items), Value::Int(i)) => {
977 if i < 0 || i as usize >= items.len() {
978 return Err(RuntimeError::IndexOutOfBounds {
979 index: i,
980 len: items.len(),
981 });
982 }
983 let new_val = match op {
984 AssignOp::Assign => rhs,
985 compound => {
986 let current = items[i as usize].clone();
987 self.apply_assign_op(compound, current, rhs)?
988 }
989 };
990 items[i as usize] = new_val;
991 if let NodeKind::Identifier { name: obj_name } = &object.kind {
992 let updated = Value::List(items);
993 if !self.env.assign(&obj_name.name, updated) {
994 return Err(RuntimeError::UndefinedVariable {
995 name: obj_name.name.clone(),
996 });
997 }
998 }
999 Ok(Value::Void)
1000 }
1001 (Value::Map(mut map), key) => {
1002 let new_val = match op {
1003 AssignOp::Assign => rhs,
1004 compound => {
1005 let current = map.get(&key).cloned().ok_or_else(|| {
1006 RuntimeError::TypeError(format!("key not found: {key}"))
1007 })?;
1008 self.apply_assign_op(compound, current, rhs)?
1009 }
1010 };
1011 map.insert(key, new_val);
1012 if let NodeKind::Identifier { name: obj_name } = &object.kind {
1013 let updated = Value::Map(map);
1014 if !self.env.assign(&obj_name.name, updated) {
1015 return Err(RuntimeError::UndefinedVariable {
1016 name: obj_name.name.clone(),
1017 });
1018 }
1019 }
1020 Ok(Value::Void)
1021 }
1022 (obj, idx) => Err(RuntimeError::TypeError(format!(
1023 "cannot index-assign {obj} with {idx}"
1024 ))),
1025 }
1026 }
1027 _ => Err(RuntimeError::NotImplemented(
1028 "unsupported assignment target".to_string(),
1029 )),
1030 }
1031 }
1032
1033 fn apply_assign_op(&self, op: AssignOp, lhs: Value, rhs: Value) -> Result<Value, RuntimeError> {
1034 match (op, lhs, rhs) {
1035 (AssignOp::AddAssign, Value::Int(a), Value::Int(b)) => a
1036 .checked_add(b)
1037 .map(Value::Int)
1038 .ok_or(RuntimeError::IntOverflow),
1039 (AssignOp::SubAssign, Value::Int(a), Value::Int(b)) => a
1040 .checked_sub(b)
1041 .map(Value::Int)
1042 .ok_or(RuntimeError::IntOverflow),
1043 (AssignOp::MulAssign, Value::Int(a), Value::Int(b)) => a
1044 .checked_mul(b)
1045 .map(Value::Int)
1046 .ok_or(RuntimeError::IntOverflow),
1047 (AssignOp::DivAssign, Value::Int(a), Value::Int(b)) => {
1048 if b == 0 {
1049 Err(RuntimeError::DivisionByZero)
1050 } else {
1051 Ok(Value::Int(a / b))
1052 }
1053 }
1054 (AssignOp::RemAssign, Value::Int(a), Value::Int(b)) => {
1055 if b == 0 {
1056 Err(RuntimeError::DivisionByZero)
1057 } else {
1058 Ok(Value::Int(a % b))
1059 }
1060 }
1061 (AssignOp::AddAssign, Value::Float(a), Value::Float(b)) => {
1062 Ok(Value::Float(OrdF64(a.0 + b.0)))
1063 }
1064 (AssignOp::SubAssign, Value::Float(a), Value::Float(b)) => {
1065 Ok(Value::Float(OrdF64(a.0 - b.0)))
1066 }
1067 (AssignOp::MulAssign, Value::Float(a), Value::Float(b)) => {
1068 Ok(Value::Float(OrdF64(a.0 * b.0)))
1069 }
1070 (AssignOp::DivAssign, Value::Float(a), Value::Float(b)) => {
1071 Ok(Value::Float(OrdF64(a.0 / b.0)))
1072 }
1073 (AssignOp::AddAssign, Value::String(a), Value::String(b)) => {
1074 Ok(Value::String(BockString::new(format!("{a}{b}"))))
1075 }
1076 (op, l, r) => Err(RuntimeError::TypeError(format!(
1077 "compound assignment {op:?} not supported for {l} and {r}"
1078 ))),
1079 }
1080 }
1081
1082 #[async_recursion]
1085 async fn eval_call(&mut self, callee: &AIRNode, args: &[AirArg]) -> Result<Value, RuntimeError> {
1086 if let NodeKind::Identifier { name } = &callee.kind {
1088 if self.builtins.has_global(&name.name) {
1089 let mut arg_values: Vec<Value> = Vec::with_capacity(args.len());
1090 for a in args {
1091 arg_values.push(self.eval_expr(&a.value).await?);
1092 }
1093 return self
1094 .builtins
1095 .call_global(&name.name, &arg_values)
1096 .expect("has_global check confirmed builtin exists");
1097 }
1098
1099 if let Some(effect_name) = self.effect_operations.get(&name.name).cloned() {
1101 let handler =
1102 self.effect_handlers
1103 .resolve(&effect_name)
1104 .cloned()
1105 .ok_or(RuntimeError::NoEffectHandler {
1106 effect: effect_name,
1107 })?;
1108 let mut arg_values = Vec::with_capacity(args.len());
1109 for arg in args {
1110 arg_values.push(self.eval_expr(&arg.value).await?);
1111 }
1112 return self.dispatch_effect_op(&handler, &name.name, arg_values).await;
1113 }
1114 }
1115
1116 if let NodeKind::FieldAccess { object, field } = &callee.kind {
1121 if let NodeKind::Identifier { name: type_name } = &object.kind {
1122 let qualified = format!("{}.{}", type_name.name, field.name);
1123 if self.builtins.has_global(&qualified) {
1124 let mut arg_values = Vec::with_capacity(args.len());
1125 for a in args {
1126 arg_values.push(self.eval_expr(&a.value).await?);
1127 }
1128 return self
1129 .builtins
1130 .call_global(&qualified, &arg_values)
1131 .expect("has_global check confirmed builtin exists");
1132 }
1133 }
1134 }
1135
1136 if let NodeKind::FieldAccess { object, field } = &callee.kind {
1140 let recv = self.eval_expr(object).await?;
1141 let type_tag = TypeTag::of(&recv);
1142 if self.builtins.has_method(type_tag, &field.name) {
1143 let mut builtin_args = Vec::with_capacity(args.len());
1144 builtin_args.push(recv);
1145 for a in args.iter().skip(1) {
1147 builtin_args.push(self.eval_expr(&a.value).await?);
1148 }
1149 if let Some(ho_func) = self.builtins.get_ho_method(type_tag, &field.name) {
1151 return ho_func(&builtin_args, self).await;
1152 }
1153 return self
1154 .builtins
1155 .call(type_tag, &field.name, &builtin_args)
1156 .expect("has_method check confirmed builtin exists");
1157 }
1158 let mut method_args = Vec::with_capacity(args.len().saturating_sub(1));
1160 for a in args.iter().skip(1) {
1161 method_args.push(self.eval_expr(&a.value).await?);
1162 }
1163 if let Some(result) =
1164 self.try_call_impl_method(&recv, &field.name, method_args).await?
1165 {
1166 return Ok(result);
1167 }
1168 return self.eval_method_call(object, &field.name, &args[1..]).await;
1171 }
1172
1173 let fn_val = self.eval_expr(callee).await?;
1174 let fn_id = match &fn_val {
1175 Value::Function(fv) => fv.id,
1176 other => {
1177 return Err(RuntimeError::NotCallable {
1178 value: other.to_string(),
1179 })
1180 }
1181 };
1182 let mut arg_values: Vec<Value> = Vec::with_capacity(args.len());
1183 for a in args {
1184 arg_values.push(self.eval_expr(&a.value).await?);
1185 }
1186 let closure =
1187 self.fn_registry
1188 .get(&fn_id)
1189 .cloned()
1190 .ok_or_else(|| RuntimeError::NotCallable {
1191 value: format!("unregistered fn #{fn_id}"),
1192 })?;
1193 self.call_closure(&closure, arg_values).await
1194 }
1195
1196 #[async_recursion]
1197 async fn call_closure(&mut self, closure: &Closure, args: Vec<Value>) -> Result<Value, RuntimeError> {
1198 match &closure.body {
1199 ClosureBody::Air(body) => {
1200 if closure.params.len() != args.len() {
1201 return Err(RuntimeError::ArityMismatch {
1202 expected: closure.params.len(),
1203 got: args.len(),
1204 });
1205 }
1206 let body = *body.clone();
1207
1208 if closure.is_async {
1209 let mut sub = self.clone();
1214 if !closure.is_toplevel {
1215 sub.env = closure.captured.clone();
1216 }
1217 sub.env.push_scope();
1218 for (name, val) in closure.params.iter().zip(args) {
1219 sub.env.define(name.clone(), val);
1220 }
1221 let handle: tokio::task::JoinHandle<Result<Value, RuntimeError>> =
1222 tokio::spawn(async move {
1223 match sub.eval_expr(&body).await {
1224 Err(RuntimeError::Return(v)) => Ok(*v),
1225 other => other,
1226 }
1227 });
1228 return Ok(Value::Future(Arc::new(Mutex::new(Some(handle)))));
1229 }
1230
1231 let saved_env = if closure.is_toplevel {
1234 self.env.clone()
1235 } else {
1236 std::mem::replace(&mut self.env, closure.captured.clone())
1237 };
1238 self.env.push_scope();
1239 for (name, val) in closure.params.iter().zip(args) {
1240 self.env.define(name.clone(), val);
1241 }
1242 let result = self.eval_expr(&body).await;
1243 self.env = saved_env;
1244 match result {
1246 Err(RuntimeError::Return(v)) => Ok(*v),
1247 other => other,
1248 }
1249 }
1250 ClosureBody::Composed { inner, outer } => {
1251 let inner_id = *inner;
1252 let outer_id = *outer;
1253 let inner_closure = self.fn_registry.get(&inner_id).cloned().ok_or_else(|| {
1254 RuntimeError::NotCallable {
1255 value: format!("composed inner fn #{inner_id}"),
1256 }
1257 })?;
1258 let intermediate = self.call_closure(&inner_closure, args).await?;
1259 let outer_closure = self.fn_registry.get(&outer_id).cloned().ok_or_else(|| {
1260 RuntimeError::NotCallable {
1261 value: format!("composed outer fn #{outer_id}"),
1262 }
1263 })?;
1264 self.call_closure(&outer_closure, vec![intermediate]).await
1265 }
1266 ClosureBody::Native(build) => {
1267 if closure.params.len() != args.len() {
1268 return Err(RuntimeError::ArityMismatch {
1269 expected: closure.params.len(),
1270 got: args.len(),
1271 });
1272 }
1273 Ok(build(&args))
1274 }
1275 }
1276 }
1277
1278 fn eval_lambda(&mut self, params: &[AIRNode], body: &AIRNode) -> Result<Value, RuntimeError> {
1281 let param_names: Vec<String> = params
1282 .iter()
1283 .map(|p| match &p.kind {
1284 NodeKind::Param { pattern, .. } => match &pattern.kind {
1285 NodeKind::BindPat { name, .. } => name.name.clone(),
1286 NodeKind::WildcardPat => "_".to_string(),
1287 _ => "_".to_string(),
1288 },
1289 _ => "_".to_string(),
1290 })
1291 .collect();
1292
1293 let fn_val = FnValue::new_anonymous();
1294 let id = fn_val.id;
1295 let captured = self.env.clone();
1296 self.fn_registry.insert(
1297 id,
1298 Closure {
1299 params: param_names,
1300 body: ClosureBody::Air(Box::new(body.clone())),
1301 captured,
1302 is_toplevel: false,
1303 is_async: false,
1304 },
1305 );
1306 Ok(Value::Function(fn_val))
1307 }
1308
1309 #[async_recursion]
1312 async fn eval_pipe(&mut self, left: &AIRNode, right: &AIRNode) -> Result<Value, RuntimeError> {
1313 let lhs = self.eval_expr(left).await?;
1314 match &right.kind.clone() {
1316 NodeKind::Call { callee, args, .. } => {
1317 let callee = callee.clone();
1318 let args: Vec<AirArg> = args.clone();
1319 let fn_val = self.eval_expr(&callee).await?;
1320 let fn_id = match &fn_val {
1321 Value::Function(fv) => fv.id,
1322 other => {
1323 return Err(RuntimeError::NotCallable {
1324 value: other.to_string(),
1325 })
1326 }
1327 };
1328 let mut arg_values = vec![lhs];
1329 for a in &args {
1330 arg_values.push(self.eval_expr(&a.value).await?);
1331 }
1332 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1333 RuntimeError::NotCallable {
1334 value: format!("unregistered fn #{fn_id}"),
1335 }
1336 })?;
1337 self.call_closure(&closure, arg_values).await
1338 }
1339 _ => {
1340 let fn_val = self.eval_expr(right).await?;
1342 let fn_id = match &fn_val {
1343 Value::Function(fv) => fv.id,
1344 other => {
1345 return Err(RuntimeError::NotCallable {
1346 value: other.to_string(),
1347 })
1348 }
1349 };
1350 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1351 RuntimeError::NotCallable {
1352 value: format!("unregistered fn #{fn_id}"),
1353 }
1354 })?;
1355 self.call_closure(&closure, vec![lhs]).await
1356 }
1357 }
1358 }
1359
1360 #[async_recursion]
1363 async fn eval_compose(&mut self, left: &AIRNode, right: &AIRNode) -> Result<Value, RuntimeError> {
1364 let f = self.eval_expr(left).await?;
1365 let g = self.eval_expr(right).await?;
1366 let f_id = match &f {
1367 Value::Function(fv) => fv.id,
1368 other => {
1369 return Err(RuntimeError::TypeError(format!(
1370 ">> requires functions, got {other}"
1371 )))
1372 }
1373 };
1374 let g_id = match &g {
1375 Value::Function(fv) => fv.id,
1376 other => {
1377 return Err(RuntimeError::TypeError(format!(
1378 ">> requires functions, got {other}"
1379 )))
1380 }
1381 };
1382 let fn_val = FnValue::new_anonymous();
1383 let id = fn_val.id;
1384 self.fn_registry.insert(
1385 id,
1386 Closure {
1387 params: vec!["__x".to_string()],
1388 body: ClosureBody::Composed {
1389 inner: f_id,
1390 outer: g_id,
1391 },
1392 captured: self.env.clone(),
1393 is_toplevel: false,
1394 is_async: false,
1395 },
1396 );
1397 Ok(Value::Function(fn_val))
1398 }
1399
1400 #[async_recursion]
1403 async fn eval_method_call(
1404 &mut self,
1405 receiver: &AIRNode,
1406 method: &str,
1407 args: &[AirArg],
1408 ) -> Result<Value, RuntimeError> {
1409 let recv = self.eval_expr(receiver).await?;
1410 let method = method.to_string();
1411 let mut arg_values: Vec<Value> = Vec::with_capacity(args.len());
1412 for a in args {
1413 arg_values.push(self.eval_expr(&a.value).await?);
1414 }
1415
1416 let type_tag = TypeTag::of(&recv);
1418 {
1419 let mut builtin_args = Vec::with_capacity(1 + arg_values.len());
1420 builtin_args.push(recv.clone());
1421 builtin_args.extend(arg_values.iter().cloned());
1422 if let Some(ho_func) = self.builtins.get_ho_method(type_tag, &method) {
1424 return ho_func(&builtin_args, self).await;
1425 }
1426 if let Some(result) = self.builtins.call(type_tag, &method, &builtin_args) {
1427 return result;
1428 }
1429 }
1430
1431 match (&recv, method.as_str()) {
1432 (_, "to_string") => Ok(Value::String(BockString::new(recv.to_string()))),
1434
1435 (Value::List(items), "len") => Ok(Value::Int(items.len() as i64)),
1437 (Value::List(items), "is_empty") => Ok(Value::Bool(items.is_empty())),
1438 (Value::List(items), "first") => {
1439 Ok(Value::Optional(items.first().cloned().map(Box::new)))
1440 }
1441 (Value::List(items), "last") => {
1442 Ok(Value::Optional(items.last().cloned().map(Box::new)))
1443 }
1444 (Value::List(items), "get") => {
1445 let idx = arg_values.first().ok_or(RuntimeError::ArityMismatch {
1446 expected: 1,
1447 got: 0,
1448 })?;
1449 if let Value::Int(i) = idx {
1450 let i = *i;
1451 if i < 0 || i as usize >= items.len() {
1452 Ok(Value::Optional(None))
1453 } else {
1454 Ok(Value::Optional(Some(Box::new(items[i as usize].clone()))))
1455 }
1456 } else {
1457 Err(RuntimeError::TypeError(
1458 "List.get expects an Int index".to_string(),
1459 ))
1460 }
1461 }
1462 (Value::List(items), "push") => {
1463 let v = arg_values
1464 .into_iter()
1465 .next()
1466 .ok_or(RuntimeError::ArityMismatch {
1467 expected: 1,
1468 got: 0,
1469 })?;
1470 let mut new_list = items.clone();
1471 new_list.push(v);
1472 Ok(Value::List(new_list))
1473 }
1474 (Value::List(items), "contains") => {
1475 let v = arg_values.first().ok_or(RuntimeError::ArityMismatch {
1476 expected: 1,
1477 got: 0,
1478 })?;
1479 Ok(Value::Bool(items.contains(v)))
1480 }
1481 (Value::List(items), "reverse") => {
1482 let mut v = items.clone();
1483 v.reverse();
1484 Ok(Value::List(v))
1485 }
1486 (Value::List(items), "map") => {
1487 let fn_val = arg_values
1488 .into_iter()
1489 .next()
1490 .ok_or(RuntimeError::ArityMismatch {
1491 expected: 1,
1492 got: 0,
1493 })?;
1494 let fn_id = match fn_val {
1495 Value::Function(ref fv) => fv.id,
1496 other => {
1497 return Err(RuntimeError::TypeError(format!(
1498 "List.map expects a function, got {other}"
1499 )))
1500 }
1501 };
1502 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1503 RuntimeError::NotCallable {
1504 value: "unregistered fn".to_string(),
1505 }
1506 })?;
1507 let items = items.clone();
1508 let mut result = Vec::with_capacity(items.len());
1509 for item in items {
1510 result.push(self.call_closure(&closure, vec![item]).await?);
1511 }
1512 Ok(Value::List(result))
1513 }
1514 (Value::List(items), "filter") => {
1515 let fn_val = arg_values
1516 .into_iter()
1517 .next()
1518 .ok_or(RuntimeError::ArityMismatch {
1519 expected: 1,
1520 got: 0,
1521 })?;
1522 let fn_id = match fn_val {
1523 Value::Function(ref fv) => fv.id,
1524 other => {
1525 return Err(RuntimeError::TypeError(format!(
1526 "List.filter expects a function, got {other}"
1527 )))
1528 }
1529 };
1530 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1531 RuntimeError::NotCallable {
1532 value: "unregistered fn".to_string(),
1533 }
1534 })?;
1535 let items = items.clone();
1536 let mut result = Vec::new();
1537 for item in items {
1538 if let Value::Bool(true) = self.call_closure(&closure, vec![item.clone()]).await? {
1539 result.push(item);
1540 }
1541 }
1542 Ok(Value::List(result))
1543 }
1544 (Value::List(items), "fold") | (Value::List(items), "reduce") => {
1545 if arg_values.len() != 2 {
1546 return Err(RuntimeError::ArityMismatch {
1547 expected: 2,
1548 got: arg_values.len(),
1549 });
1550 }
1551 let mut acc = arg_values.remove(0);
1552 let fn_val = arg_values.remove(0);
1553 let fn_id = match fn_val {
1554 Value::Function(ref fv) => fv.id,
1555 other => {
1556 return Err(RuntimeError::TypeError(format!(
1557 "List.fold expects a function, got {other}"
1558 )))
1559 }
1560 };
1561 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1562 RuntimeError::NotCallable {
1563 value: "unregistered fn".to_string(),
1564 }
1565 })?;
1566 let items = items.clone();
1567 for item in items {
1568 acc = self.call_closure(&closure, vec![acc, item]).await?;
1569 }
1570 Ok(acc)
1571 }
1572 (Value::List(items), "any") => {
1573 let fn_val = arg_values
1574 .into_iter()
1575 .next()
1576 .ok_or(RuntimeError::ArityMismatch {
1577 expected: 1,
1578 got: 0,
1579 })?;
1580 let fn_id = match fn_val {
1581 Value::Function(ref fv) => fv.id,
1582 other => {
1583 return Err(RuntimeError::TypeError(format!(
1584 "List.any expects a function, got {other}"
1585 )))
1586 }
1587 };
1588 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1589 RuntimeError::NotCallable {
1590 value: "unregistered fn".to_string(),
1591 }
1592 })?;
1593 let items = items.clone();
1594 for item in items {
1595 if let Value::Bool(true) = self.call_closure(&closure, vec![item]).await? {
1596 return Ok(Value::Bool(true));
1597 }
1598 }
1599 Ok(Value::Bool(false))
1600 }
1601 (Value::List(items), "all") => {
1602 let fn_val = arg_values
1603 .into_iter()
1604 .next()
1605 .ok_or(RuntimeError::ArityMismatch {
1606 expected: 1,
1607 got: 0,
1608 })?;
1609 let fn_id = match fn_val {
1610 Value::Function(ref fv) => fv.id,
1611 other => {
1612 return Err(RuntimeError::TypeError(format!(
1613 "List.all expects a function, got {other}"
1614 )))
1615 }
1616 };
1617 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1618 RuntimeError::NotCallable {
1619 value: "unregistered fn".to_string(),
1620 }
1621 })?;
1622 let items = items.clone();
1623 for item in items {
1624 if let Value::Bool(false) = self.call_closure(&closure, vec![item]).await? {
1625 return Ok(Value::Bool(false));
1626 }
1627 }
1628 Ok(Value::Bool(true))
1629 }
1630 (Value::List(items), "find") => {
1631 let fn_val = arg_values
1632 .into_iter()
1633 .next()
1634 .ok_or(RuntimeError::ArityMismatch {
1635 expected: 1,
1636 got: 0,
1637 })?;
1638 let fn_id = match fn_val {
1639 Value::Function(ref fv) => fv.id,
1640 other => {
1641 return Err(RuntimeError::TypeError(format!(
1642 "List.find expects a function, got {other}"
1643 )))
1644 }
1645 };
1646 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1647 RuntimeError::NotCallable {
1648 value: "unregistered fn".to_string(),
1649 }
1650 })?;
1651 let items = items.clone();
1652 for item in items {
1653 if let Value::Bool(true) =
1654 self.call_closure(&closure, vec![item.clone()]).await?
1655 {
1656 return Ok(Value::Optional(Some(Box::new(item))));
1657 }
1658 }
1659 Ok(Value::Optional(None))
1660 }
1661 (Value::List(items), "for_each") => {
1662 let fn_val = arg_values
1663 .into_iter()
1664 .next()
1665 .ok_or(RuntimeError::ArityMismatch {
1666 expected: 1,
1667 got: 0,
1668 })?;
1669 let fn_id = match fn_val {
1670 Value::Function(ref fv) => fv.id,
1671 other => {
1672 return Err(RuntimeError::TypeError(format!(
1673 "List.for_each expects a function, got {other}"
1674 )))
1675 }
1676 };
1677 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1678 RuntimeError::NotCallable {
1679 value: "unregistered fn".to_string(),
1680 }
1681 })?;
1682 let items = items.clone();
1683 for item in items {
1684 self.call_closure(&closure, vec![item]).await?;
1685 }
1686 Ok(Value::Void)
1687 }
1688 (Value::List(items), "flat_map") => {
1689 let fn_val = arg_values
1690 .into_iter()
1691 .next()
1692 .ok_or(RuntimeError::ArityMismatch {
1693 expected: 1,
1694 got: 0,
1695 })?;
1696 let fn_id = match fn_val {
1697 Value::Function(ref fv) => fv.id,
1698 other => {
1699 return Err(RuntimeError::TypeError(format!(
1700 "List.flat_map expects a function, got {other}"
1701 )))
1702 }
1703 };
1704 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
1705 RuntimeError::NotCallable {
1706 value: "unregistered fn".to_string(),
1707 }
1708 })?;
1709 let items = items.clone();
1710 let mut result = Vec::new();
1711 for item in items {
1712 match self.call_closure(&closure, vec![item]).await? {
1713 Value::List(inner) => result.extend(inner),
1714 other => result.push(other),
1715 }
1716 }
1717 Ok(Value::List(result))
1718 }
1719 (Value::List(items), "sort") => {
1720 let mut v = items.clone();
1721 v.sort();
1722 Ok(Value::List(v))
1723 }
1724 (Value::List(items), "dedup") => {
1725 let mut v = items.clone();
1726 v.dedup();
1727 Ok(Value::List(v))
1728 }
1729 (Value::List(items), "flatten") => {
1730 let mut result = Vec::new();
1731 for item in items {
1732 match item {
1733 Value::List(inner) => result.extend(inner.iter().cloned()),
1734 other => result.push(other.clone()),
1735 }
1736 }
1737 Ok(Value::List(result))
1738 }
1739 (Value::List(items), "zip") => {
1740 let other = match arg_values.first() {
1741 Some(Value::List(l)) => l,
1742 _ => {
1743 return Err(RuntimeError::TypeError(
1744 "List.zip expects a List argument".to_string(),
1745 ))
1746 }
1747 };
1748 let pairs: Vec<Value> = items
1749 .iter()
1750 .zip(other.iter())
1751 .map(|(a, b)| Value::Tuple(vec![a.clone(), b.clone()]))
1752 .collect();
1753 Ok(Value::List(pairs))
1754 }
1755 (Value::List(items), "concat") => {
1756 let other = match arg_values.first() {
1757 Some(Value::List(l)) => l,
1758 _ => {
1759 return Err(RuntimeError::TypeError(
1760 "List.concat expects a List argument".to_string(),
1761 ))
1762 }
1763 };
1764 let mut new_list = items.clone();
1765 new_list.extend_from_slice(other);
1766 Ok(Value::List(new_list))
1767 }
1768 (Value::List(items), "slice") => {
1769 if arg_values.len() < 2 {
1770 return Err(RuntimeError::ArityMismatch {
1771 expected: 2,
1772 got: arg_values.len(),
1773 });
1774 }
1775 let start = match &arg_values[0] {
1776 Value::Int(i) => *i,
1777 _ => return Err(RuntimeError::TypeError("List.slice expects Int arguments".to_string())),
1778 };
1779 let end = match &arg_values[1] {
1780 Value::Int(i) => *i,
1781 _ => return Err(RuntimeError::TypeError("List.slice expects Int arguments".to_string())),
1782 };
1783 let len = items.len() as i64;
1784 let start = start.max(0).min(len) as usize;
1785 let end = end.max(0).min(len) as usize;
1786 if start >= end {
1787 Ok(Value::List(vec![]))
1788 } else {
1789 Ok(Value::List(items[start..end].to_vec()))
1790 }
1791 }
1792 (Value::List(items), "take") => {
1793 let n = match arg_values.first() {
1794 Some(Value::Int(i)) => *i,
1795 _ => return Err(RuntimeError::TypeError("List.take expects Int".to_string())),
1796 };
1797 let n = (n.max(0) as usize).min(items.len());
1798 Ok(Value::List(items[..n].to_vec()))
1799 }
1800 (Value::List(items), "skip") => {
1801 let n = match arg_values.first() {
1802 Some(Value::Int(i)) => *i,
1803 _ => return Err(RuntimeError::TypeError("List.skip expects Int".to_string())),
1804 };
1805 let n = (n.max(0) as usize).min(items.len());
1806 Ok(Value::List(items[n..].to_vec()))
1807 }
1808 (Value::List(items), "enumerate") => {
1809 let result: Vec<Value> = items
1810 .iter()
1811 .enumerate()
1812 .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
1813 .collect();
1814 Ok(Value::List(result))
1815 }
1816 (Value::List(items), "count") => Ok(Value::Int(items.len() as i64)),
1817 (Value::List(items), "pop") => {
1818 if items.is_empty() {
1819 Ok(Value::List(vec![]))
1820 } else {
1821 Ok(Value::List(items[..items.len() - 1].to_vec()))
1822 }
1823 }
1824 (Value::List(items), "insert") => {
1825 if arg_values.len() < 2 {
1826 return Err(RuntimeError::ArityMismatch {
1827 expected: 2,
1828 got: arg_values.len(),
1829 });
1830 }
1831 let idx = match &arg_values[0] {
1832 Value::Int(i) => *i as usize,
1833 _ => return Err(RuntimeError::TypeError("List.insert expects Int index".to_string())),
1834 };
1835 if idx > items.len() {
1836 return Err(RuntimeError::IndexOutOfBounds {
1837 index: idx as i64,
1838 len: items.len(),
1839 });
1840 }
1841 let mut new_list = items.clone();
1842 new_list.insert(idx, arg_values[1].clone());
1843 Ok(Value::List(new_list))
1844 }
1845 (Value::List(items), "remove") => {
1846 let idx = match arg_values.first() {
1847 Some(Value::Int(i)) => *i,
1848 _ => return Err(RuntimeError::TypeError("List.remove expects Int index".to_string())),
1849 };
1850 if idx < 0 || idx as usize >= items.len() {
1851 return Err(RuntimeError::IndexOutOfBounds {
1852 index: idx,
1853 len: items.len(),
1854 });
1855 }
1856 let mut new_list = items.clone();
1857 new_list.remove(idx as usize);
1858 Ok(Value::List(new_list))
1859 }
1860 (Value::List(items), "index_of") => {
1861 let needle = arg_values.first().ok_or(RuntimeError::ArityMismatch {
1862 expected: 1,
1863 got: 0,
1864 })?;
1865 match items.iter().position(|v| v == needle) {
1866 Some(pos) => Ok(Value::Optional(Some(Box::new(Value::Int(pos as i64))))),
1867 None => Ok(Value::Optional(None)),
1868 }
1869 }
1870 (Value::List(items), "join") => {
1871 let sep = match arg_values.first() {
1872 Some(Value::String(s)) => s.as_str().to_owned(),
1873 _ => String::new(),
1874 };
1875 let parts: Vec<String> = items.iter().map(|v| v.to_string()).collect();
1876 Ok(Value::String(BockString::new(parts.join(&sep))))
1877 }
1878 (Value::List(items), "to_set") => {
1879 let set: std::collections::BTreeSet<Value> = items.iter().cloned().collect();
1880 Ok(Value::Set(set))
1881 }
1882
1883 (Value::String(s), "len") => Ok(Value::Int(s.as_str().chars().count() as i64)),
1885 (Value::String(s), "is_empty") => Ok(Value::Bool(s.as_str().is_empty())),
1886 (Value::String(s), "to_upper") => {
1887 Ok(Value::String(BockString::new(s.as_str().to_uppercase())))
1888 }
1889 (Value::String(s), "to_lower") => {
1890 Ok(Value::String(BockString::new(s.as_str().to_lowercase())))
1891 }
1892 (Value::String(s), "trim") => Ok(Value::String(BockString::new(s.as_str().trim()))),
1893 (Value::String(s), "contains") => {
1894 if let Some(Value::String(sub)) = arg_values.first() {
1895 Ok(Value::Bool(s.as_str().contains(sub.as_str())))
1896 } else {
1897 Err(RuntimeError::TypeError(
1898 "String.contains expects a String".to_string(),
1899 ))
1900 }
1901 }
1902 (Value::String(s), "starts_with") => {
1903 if let Some(Value::String(prefix)) = arg_values.first() {
1904 Ok(Value::Bool(s.as_str().starts_with(prefix.as_str())))
1905 } else {
1906 Err(RuntimeError::TypeError(
1907 "String.starts_with expects a String".to_string(),
1908 ))
1909 }
1910 }
1911 (Value::String(s), "ends_with") => {
1912 if let Some(Value::String(suffix)) = arg_values.first() {
1913 Ok(Value::Bool(s.as_str().ends_with(suffix.as_str())))
1914 } else {
1915 Err(RuntimeError::TypeError(
1916 "String.ends_with expects a String".to_string(),
1917 ))
1918 }
1919 }
1920 (Value::String(s), "split") => {
1921 let sep = match arg_values.first() {
1922 Some(Value::String(sep)) => sep.as_str().to_owned(),
1923 _ => {
1924 return Err(RuntimeError::TypeError(
1925 "String.split expects a String separator".to_string(),
1926 ))
1927 }
1928 };
1929 let parts: Vec<Value> = s
1930 .as_str()
1931 .split(&sep)
1932 .map(|p| Value::String(BockString::new(p)))
1933 .collect();
1934 Ok(Value::List(parts))
1935 }
1936 (Value::String(s), "replace") => {
1937 if arg_values.len() < 2 {
1938 return Err(RuntimeError::ArityMismatch {
1939 expected: 2,
1940 got: arg_values.len(),
1941 });
1942 }
1943 let old = match &arg_values[0] {
1944 Value::String(s) => s.as_str().to_owned(),
1945 _ => {
1946 return Err(RuntimeError::TypeError(
1947 "String.replace expects String arguments".to_string(),
1948 ))
1949 }
1950 };
1951 let new_s = match &arg_values[1] {
1952 Value::String(s) => s.as_str().to_owned(),
1953 _ => {
1954 return Err(RuntimeError::TypeError(
1955 "String.replace expects String arguments".to_string(),
1956 ))
1957 }
1958 };
1959 Ok(Value::String(BockString::new(
1960 s.as_str().replace(&old, &new_s),
1961 )))
1962 }
1963 (Value::String(s), "chars") => {
1964 let chars: Vec<Value> = s.as_str().chars().map(Value::Char).collect();
1965 Ok(Value::List(chars))
1966 }
1967 (Value::String(s), "substring") => {
1968 if arg_values.len() < 2 {
1969 return Err(RuntimeError::ArityMismatch {
1970 expected: 2,
1971 got: arg_values.len(),
1972 });
1973 }
1974 let start = match &arg_values[0] {
1975 Value::Int(i) => *i,
1976 _ => {
1977 return Err(RuntimeError::TypeError(
1978 "String.substring expects Int arguments".to_string(),
1979 ))
1980 }
1981 };
1982 let end = match &arg_values[1] {
1983 Value::Int(i) => *i,
1984 _ => {
1985 return Err(RuntimeError::TypeError(
1986 "String.substring expects Int arguments".to_string(),
1987 ))
1988 }
1989 };
1990 let chars: Vec<char> = s.as_str().chars().collect();
1991 let len = chars.len() as i64;
1992 let start = start.max(0).min(len) as usize;
1993 let end = end.max(0).min(len) as usize;
1994 if start >= end {
1995 Ok(Value::String(BockString::new("")))
1996 } else {
1997 Ok(Value::String(BockString::new(
1998 chars[start..end].iter().collect::<String>(),
1999 )))
2000 }
2001 }
2002
2003 (Value::Map(map), "len") => Ok(Value::Int(map.len() as i64)),
2005 (Value::Map(map), "is_empty") => Ok(Value::Bool(map.is_empty())),
2006 (Value::Map(map), "contains_key") => {
2007 let k = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2008 expected: 1,
2009 got: 0,
2010 })?;
2011 Ok(Value::Bool(map.contains_key(k)))
2012 }
2013 (Value::Map(map), "get") => {
2014 let k = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2015 expected: 1,
2016 got: 0,
2017 })?;
2018 Ok(Value::Optional(map.get(k).cloned().map(Box::new)))
2019 }
2020 (Value::Map(map), "set") => {
2021 if arg_values.len() < 2 {
2022 return Err(RuntimeError::ArityMismatch {
2023 expected: 2,
2024 got: arg_values.len(),
2025 });
2026 }
2027 let mut new_map = map.clone();
2028 new_map.insert(arg_values.remove(0), arg_values.remove(0));
2029 Ok(Value::Map(new_map))
2030 }
2031 (Value::Map(map), "delete") => {
2032 let k = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2033 expected: 1,
2034 got: 0,
2035 })?;
2036 let mut new_map = map.clone();
2037 new_map.remove(k);
2038 Ok(Value::Map(new_map))
2039 }
2040 (Value::Map(map), "merge") => {
2041 let other = match arg_values.first() {
2042 Some(Value::Map(m)) => m,
2043 _ => {
2044 return Err(RuntimeError::TypeError(
2045 "Map.merge expects a Map argument".to_string(),
2046 ))
2047 }
2048 };
2049 let mut new_map = map.clone();
2050 for (k, v) in other {
2051 new_map.insert(k.clone(), v.clone());
2052 }
2053 Ok(Value::Map(new_map))
2054 }
2055 (Value::Map(map), "keys") => Ok(Value::List(map.keys().cloned().collect())),
2056 (Value::Map(map), "values") => Ok(Value::List(map.values().cloned().collect())),
2057 (Value::Map(map), "entries") => {
2058 let entries: Vec<Value> = map
2059 .iter()
2060 .map(|(k, v)| Value::Tuple(vec![k.clone(), v.clone()]))
2061 .collect();
2062 Ok(Value::List(entries))
2063 }
2064
2065 (Value::Set(set), "len") => Ok(Value::Int(set.len() as i64)),
2067 (Value::Set(set), "is_empty") => Ok(Value::Bool(set.is_empty())),
2068 (Value::Set(set), "contains") => {
2069 let v = arg_values.first().ok_or(RuntimeError::ArityMismatch {
2070 expected: 1,
2071 got: 0,
2072 })?;
2073 Ok(Value::Bool(set.contains(v)))
2074 }
2075
2076 (
2078 Value::Range {
2079 start,
2080 end,
2081 inclusive,
2082 ..
2083 },
2084 "step",
2085 ) => {
2086 let s = start;
2087 let e = end;
2088 let i = inclusive;
2089 if let Some(Value::Int(step)) = arg_values.first() {
2090 Ok(Value::Range {
2091 start: *s,
2092 end: *e,
2093 inclusive: *i,
2094 step: *step,
2095 })
2096 } else {
2097 Err(RuntimeError::TypeError(
2098 "Range.step expects an Int".to_string(),
2099 ))
2100 }
2101 }
2102 (
2103 Value::Range {
2104 start,
2105 end,
2106 inclusive,
2107 step,
2108 },
2109 "to_list",
2110 ) => Ok(Value::List(range_to_vec(*start, *end, *inclusive, *step))),
2111
2112 (Value::Optional(Some(inner)), "unwrap") => Ok(*inner.clone()),
2114 (Value::Optional(None), "unwrap") => {
2115 Err(RuntimeError::TypeError("unwrapped None".to_string()))
2116 }
2117 (Value::Optional(opt), "unwrap_or") => {
2118 let default = arg_values
2119 .into_iter()
2120 .next()
2121 .ok_or(RuntimeError::ArityMismatch {
2122 expected: 1,
2123 got: 0,
2124 })?;
2125 Ok(opt.as_deref().cloned().unwrap_or(default))
2126 }
2127 (Value::Result(Ok(inner)), "unwrap") => Ok(*inner.clone()),
2128 (Value::Result(Err(e)), "unwrap") => {
2129 Err(RuntimeError::TypeError(format!("unwrapped Err({e})")))
2130 }
2131
2132 (recv_val, _) => {
2134 if let Some(result) =
2136 self.try_call_impl_method(recv_val, &method, arg_values.clone()).await?
2137 {
2138 return Ok(result);
2139 }
2140 if let Some(fn_val) = self.env.get(&method).cloned() {
2141 let fn_id = match &fn_val {
2142 Value::Function(fv) => fv.id,
2143 _ => {
2144 return Err(RuntimeError::TypeError(format!(
2145 "method '{method}' not found on {type_tag}"
2146 )))
2147 }
2148 };
2149 let closure = self.fn_registry.get(&fn_id).cloned().ok_or_else(|| {
2150 RuntimeError::NotCallable {
2151 value: format!("unregistered method '{method}'"),
2152 }
2153 })?;
2154 let mut all_args = vec![recv_val.clone()];
2155 all_args.extend(arg_values);
2156 self.call_closure(&closure, all_args).await
2157 } else {
2158 Err(RuntimeError::TypeError(format!(
2159 "method '{method}' not found on {type_tag}"
2160 )))
2161 }
2162 }
2163 }
2164 }
2165
2166 #[async_recursion]
2173 async fn try_call_impl_method(
2174 &mut self,
2175 receiver: &Value,
2176 method: &str,
2177 args: Vec<Value>,
2178 ) -> Result<Option<Value>, RuntimeError> {
2179 let type_name = match receiver {
2180 Value::Record(rv) => &rv.type_name,
2181 _ => return Ok(None),
2182 };
2183
2184 let entry = self
2185 .method_table
2186 .get(type_name)
2187 .and_then(|methods| methods.get(method))
2188 .cloned();
2189
2190 let (param_names, body) = match entry {
2191 Some(e) => e,
2192 None => return Ok(None),
2193 };
2194
2195 if param_names.first().map(|s| s.as_str()) == Some("self") {
2197 let saved_env = std::mem::replace(&mut self.env, Environment::new());
2198 self.env.push_scope();
2199 self.env.define("self", receiver.clone());
2200 for (name, val) in param_names.iter().skip(1).zip(args) {
2201 self.env.define(name.clone(), val);
2202 }
2203 let result = self.eval_expr(&body).await;
2204 self.env = saved_env;
2205 match result {
2206 Err(RuntimeError::Return(v)) => Ok(Some(*v)),
2207 other => other.map(Some),
2208 }
2209 } else {
2210 let saved_env = std::mem::replace(&mut self.env, Environment::new());
2212 self.env.push_scope();
2213 for (name, val) in param_names.iter().zip(args) {
2214 self.env.define(name.clone(), val);
2215 }
2216 let result = self.eval_expr(&body).await;
2217 self.env = saved_env;
2218 match result {
2219 Err(RuntimeError::Return(v)) => Ok(Some(*v)),
2220 other => other.map(Some),
2221 }
2222 }
2223 }
2224
2225 #[async_recursion]
2228 async fn eval_field_access(&mut self, object: &AIRNode, field: &str) -> Result<Value, RuntimeError> {
2229 let obj = self.eval_expr(object).await?;
2230 match obj {
2231 Value::Record(rv) => {
2232 rv.fields
2233 .get(field)
2234 .cloned()
2235 .ok_or_else(|| RuntimeError::FieldNotFound {
2236 field: field.to_string(),
2237 type_name: rv.type_name.clone(),
2238 })
2239 }
2240 Value::Enum(ev) => {
2241 if field == "variant" {
2242 Ok(Value::String(BockString::new(ev.variant.clone())))
2243 } else {
2244 Err(RuntimeError::FieldNotFound {
2245 field: field.to_string(),
2246 type_name: ev.type_name.clone(),
2247 })
2248 }
2249 }
2250 other => Err(RuntimeError::TypeError(format!(
2251 "cannot access field '{field}' on {other}"
2252 ))),
2253 }
2254 }
2255
2256 #[async_recursion]
2259 async fn eval_index(&mut self, object: &AIRNode, index: &AIRNode) -> Result<Value, RuntimeError> {
2260 let obj = self.eval_expr(object).await?;
2261 let idx = self.eval_expr(index).await?;
2262 match (obj, idx) {
2263 (Value::List(items), Value::Int(i)) => {
2264 if i < 0 || i as usize >= items.len() {
2265 Err(RuntimeError::IndexOutOfBounds {
2266 index: i,
2267 len: items.len(),
2268 })
2269 } else {
2270 Ok(items[i as usize].clone())
2271 }
2272 }
2273 (Value::Tuple(items), Value::Int(i)) => {
2274 if i < 0 || i as usize >= items.len() {
2275 Err(RuntimeError::IndexOutOfBounds {
2276 index: i,
2277 len: items.len(),
2278 })
2279 } else {
2280 Ok(items[i as usize].clone())
2281 }
2282 }
2283 (Value::Map(map), key) => map
2284 .get(&key)
2285 .cloned()
2286 .ok_or_else(|| RuntimeError::TypeError(format!("key not found: {key}"))),
2287 (Value::String(s), Value::Int(i)) => {
2288 let chars: Vec<char> = s.as_str().chars().collect();
2289 if i < 0 || i as usize >= chars.len() {
2290 Err(RuntimeError::IndexOutOfBounds {
2291 index: i,
2292 len: chars.len(),
2293 })
2294 } else {
2295 Ok(Value::Char(chars[i as usize]))
2296 }
2297 }
2298 (obj, idx) => Err(RuntimeError::TypeError(format!(
2299 "cannot index {obj} with {idx}"
2300 ))),
2301 }
2302 }
2303
2304 #[async_recursion]
2307 async fn eval_propagate(&mut self, expr: &AIRNode) -> Result<Value, RuntimeError> {
2308 let val = self.eval_expr(expr).await?;
2309 match val {
2310 Value::Optional(Some(inner)) => Ok(*inner),
2311 Value::Optional(None) => Err(RuntimeError::Propagated(Box::new(Value::Optional(None)))),
2312 Value::Result(Ok(inner)) => Ok(*inner),
2313 Value::Result(Err(e)) => Err(RuntimeError::Propagated(e)),
2314 other => Err(RuntimeError::TypeError(format!(
2315 "? applied to non-Optional/Result: {other}"
2316 ))),
2317 }
2318 }
2319
2320 #[async_recursion]
2323 async fn eval_record_construct(
2324 &mut self,
2325 path: &TypePath,
2326 fields: &[AirRecordField],
2327 spread: Option<&AIRNode>,
2328 ) -> Result<Value, RuntimeError> {
2329 let type_name = path
2330 .segments
2331 .last()
2332 .map(|s| s.name.as_str())
2333 .unwrap_or("")
2334 .to_string();
2335 let mut record_fields: BTreeMap<String, Value> = BTreeMap::new();
2336
2337 if let Some(spread_expr) = spread {
2339 let spread_val = self.eval_expr(spread_expr).await?;
2340 if let Value::Record(rv) = spread_val {
2341 record_fields = rv.fields;
2342 }
2343 }
2344
2345 for field in fields {
2347 let val = match &field.value {
2348 Some(v) => self.eval_expr(v).await?,
2349 None => {
2350 self.env.get(&field.name.name).cloned().ok_or_else(|| {
2352 RuntimeError::UndefinedVariable {
2353 name: field.name.name.clone(),
2354 }
2355 })?
2356 }
2357 };
2358 record_fields.insert(field.name.name.clone(), val);
2359 }
2360
2361 Ok(Value::Record(RecordValue {
2362 type_name,
2363 fields: record_fields,
2364 }))
2365 }
2366
2367 #[async_recursion]
2370 async fn eval_interpolation(
2371 &mut self,
2372 parts: &[AirInterpolationPart],
2373 ) -> Result<Value, RuntimeError> {
2374 let mut result = String::new();
2375 for part in parts {
2376 match part {
2377 AirInterpolationPart::Literal(s) => result.push_str(s),
2378 AirInterpolationPart::Expr(expr) => {
2379 let val = self.eval_expr(expr).await?;
2380 let tag = TypeTag::of(&val);
2383 let displayed =
2384 match self
2385 .builtins
2386 .call(tag, "display", std::slice::from_ref(&val))
2387 {
2388 Some(Ok(Value::String(s))) => s.to_string(),
2389 _ => val.to_string(),
2390 };
2391 result.push_str(&displayed);
2392 }
2393 }
2394 }
2395 Ok(Value::String(BockString::new(result)))
2396 }
2397
2398 #[async_recursion]
2401 async fn eval_range(
2402 &mut self,
2403 lo: &AIRNode,
2404 hi: &AIRNode,
2405 inclusive: bool,
2406 ) -> Result<Value, RuntimeError> {
2407 let lo_val = self.eval_expr(lo).await?;
2408 let hi_val = self.eval_expr(hi).await?;
2409 match (lo_val, hi_val) {
2410 (Value::Int(start), Value::Int(end)) => Ok(Value::Range {
2411 start,
2412 end,
2413 inclusive,
2414 step: 1,
2415 }),
2416 (lo, hi) => Err(RuntimeError::TypeError(format!(
2417 "range bounds must be Int, got {lo} and {hi}"
2418 ))),
2419 }
2420 }
2421
2422 #[async_recursion]
2426 pub async fn eval_block(
2427 &mut self,
2428 stmts: &[AIRNode],
2429 tail: Option<&AIRNode>,
2430 ) -> Result<Value, RuntimeError> {
2431 self.env.push_scope();
2432
2433 for stmt in stmts {
2434 match self.eval_expr(stmt).await {
2435 Ok(_) => {} Err(e) => {
2437 self.env.pop_scope();
2438 return Err(e);
2439 }
2440 }
2441 }
2442
2443 let result = match tail {
2444 Some(expr) => self.eval_expr(expr).await,
2445 None => Ok(Value::Void),
2446 };
2447
2448 self.env.pop_scope();
2449 result
2450 }
2451
2452 #[async_recursion]
2455 async fn eval_if(
2456 &mut self,
2457 let_pattern: Option<&AIRNode>,
2458 condition: &AIRNode,
2459 then_block: &AIRNode,
2460 else_block: Option<&AIRNode>,
2461 ) -> Result<Value, RuntimeError> {
2462 if let Some(pat) = let_pattern {
2463 let val = self.eval_expr(condition).await?;
2465 self.env.push_scope();
2466 let matched = self.try_match_pattern(pat, &val).await;
2467 if matched {
2468 let result = self.eval_expr(then_block).await;
2469 self.env.pop_scope();
2470 result
2471 } else {
2472 self.env.pop_scope();
2473 match else_block {
2474 Some(eb) => self.eval_expr(eb).await,
2475 None => Ok(Value::Void),
2476 }
2477 }
2478 } else {
2479 let cond = self.eval_expr(condition).await?;
2480 match cond {
2481 Value::Bool(true) => self.eval_expr(then_block).await,
2482 Value::Bool(false) => match else_block {
2483 Some(eb) => self.eval_expr(eb).await,
2484 None => Ok(Value::Void),
2485 },
2486 other => Err(RuntimeError::TypeError(format!(
2487 "if condition must be Bool, got {other}"
2488 ))),
2489 }
2490 }
2491 }
2492
2493 #[async_recursion]
2496 async fn eval_match(&mut self, scrutinee: &AIRNode, arms: &[AIRNode]) -> Result<Value, RuntimeError> {
2497 let val = self.eval_expr(scrutinee).await?;
2498
2499 if matches!(&val, Value::Enum(_)) {
2503 let has_catch_all = arms.iter().any(|arm| {
2504 if let NodeKind::MatchArm { pattern, guard, .. } = &arm.kind {
2505 guard.is_none()
2506 && matches!(
2507 pattern.kind,
2508 NodeKind::WildcardPat | NodeKind::BindPat { .. }
2509 )
2510 } else {
2511 false
2512 }
2513 });
2514 if !has_catch_all {
2515 eprintln!(
2516 "warning: match on enum value may not be exhaustive (no wildcard `_` arm)"
2517 );
2518 }
2519 }
2520
2521 for arm in arms {
2522 if let NodeKind::MatchArm {
2523 pattern,
2524 guard,
2525 body,
2526 } = &arm.kind
2527 {
2528 self.env.push_scope();
2529 let matched = self.try_match_pattern(pattern, &val).await;
2530
2531 let should_exec = if matched {
2532 if let Some(guard_expr) = guard {
2533 match self.eval_expr(guard_expr).await {
2534 Ok(Value::Bool(b)) => b,
2535 Ok(_) => false,
2536 Err(_) => false,
2537 }
2538 } else {
2539 true
2540 }
2541 } else {
2542 false
2543 };
2544
2545 if should_exec {
2546 let result = self.eval_expr(body).await;
2547 self.env.pop_scope();
2548 return result;
2549 }
2550 self.env.pop_scope();
2551 }
2552 }
2553
2554 Err(RuntimeError::MatchFailed)
2555 }
2556
2557 #[async_recursion]
2562 pub async fn try_match_pattern(&mut self, pattern: &AIRNode, value: &Value) -> bool {
2563 match &pattern.kind.clone() {
2564 NodeKind::WildcardPat | NodeKind::RestPat => true,
2565
2566 NodeKind::BindPat { name, .. } => {
2567 self.env.define(name.name.clone(), value.clone());
2568 true
2569 }
2570
2571 NodeKind::LiteralPat { lit } => {
2572 matches!(self.eval_literal(lit), Ok(lit_val) if lit_val == *value)
2573 }
2574
2575 NodeKind::ConstructorPat { path, fields } => {
2576 let variant_name = path.segments.last().map(|s| s.name.as_str()).unwrap_or("");
2577 match (variant_name, value) {
2578 ("Some", Value::Optional(Some(inner))) => {
2579 fields.len() == 1 && self.try_match_pattern(&fields[0], inner).await
2580 }
2581 ("None", Value::Optional(None)) => true,
2582 ("Ok", Value::Result(Ok(inner))) => {
2583 fields.is_empty()
2584 || (fields.len() == 1 && self.try_match_pattern(&fields[0], inner).await)
2585 }
2586 ("Err", Value::Result(Err(inner))) => {
2587 fields.is_empty()
2588 || (fields.len() == 1 && self.try_match_pattern(&fields[0], inner).await)
2589 }
2590 (name, Value::Enum(ev)) if ev.variant == name => {
2591 match (&ev.payload, fields.len()) {
2592 (None, 0) => true,
2593 (Some(inner), 1) => self.try_match_pattern(&fields[0], inner).await,
2594 _ => false,
2595 }
2596 }
2597 _ => false,
2598 }
2599 }
2600
2601 NodeKind::RecordPat { path, fields, rest } => {
2602 if let Value::Record(rv) = value {
2603 let type_name = path.segments.last().map(|s| s.name.as_str()).unwrap_or("");
2604 if rv.type_name != type_name {
2605 return false;
2606 }
2607 if !rest && fields.len() != rv.fields.len() {
2608 return false;
2609 }
2610 for field in fields {
2611 let field_val = match rv.fields.get(&field.name.name) {
2612 Some(v) => v.clone(),
2613 None => return false,
2614 };
2615 if let Some(pat) = &field.pattern {
2616 if !self.try_match_pattern(pat, &field_val).await {
2617 return false;
2618 }
2619 } else {
2620 self.env.define(field.name.name.clone(), field_val);
2621 }
2622 }
2623 true
2624 } else {
2625 false
2626 }
2627 }
2628
2629 NodeKind::TuplePat { elems } => {
2630 if let Value::Tuple(vals) = value {
2631 if elems.len() != vals.len() {
2632 return false;
2633 }
2634 let pairs: Vec<_> = elems
2635 .iter()
2636 .zip(vals.iter())
2637 .map(|(p, v)| (p.clone(), v.clone()))
2638 .collect();
2639 for (pat, val) in pairs {
2640 if !self.try_match_pattern(&pat, &val).await {
2641 return false;
2642 }
2643 }
2644 true
2645 } else {
2646 false
2647 }
2648 }
2649
2650 NodeKind::ListPat { elems, rest } => {
2651 if let Value::List(vals) = value {
2652 if elems.len() > vals.len() {
2653 return false;
2654 }
2655 if rest.is_none() && elems.len() != vals.len() {
2656 return false;
2657 }
2658 let pairs: Vec<_> = elems
2659 .iter()
2660 .zip(vals.iter())
2661 .map(|(p, v)| (p.clone(), v.clone()))
2662 .collect();
2663 for (pat, val) in pairs {
2664 if !self.try_match_pattern(&pat, &val).await {
2665 return false;
2666 }
2667 }
2668 if let Some(rest_pat) = rest {
2669 let rest_vals = Value::List(vals[elems.len()..].to_vec());
2670 let rest_pat = rest_pat.clone();
2671 self.try_match_pattern(&rest_pat, &rest_vals).await;
2672 }
2673 true
2674 } else {
2675 false
2676 }
2677 }
2678
2679 NodeKind::OrPat { alternatives } => {
2680 let alts: Vec<_> = alternatives.to_vec();
2681 for alt in alts {
2682 if self.try_match_pattern(&alt, value).await {
2683 return true;
2684 }
2685 }
2686 false
2687 }
2688
2689 NodeKind::RangePat { lo, hi, inclusive } => {
2690 let lo = lo.clone();
2691 let hi = hi.clone();
2692 let inclusive = *inclusive;
2693 let lo_val = match self.eval_expr(&lo).await {
2694 Ok(v) => v,
2695 Err(_) => return false,
2696 };
2697 let hi_val = match self.eval_expr(&hi).await {
2698 Ok(v) => v,
2699 Err(_) => return false,
2700 };
2701 if inclusive {
2702 *value >= lo_val && *value <= hi_val
2703 } else {
2704 *value >= lo_val && *value < hi_val
2705 }
2706 }
2707
2708 _ => false,
2709 }
2710 }
2711
2712 #[async_recursion]
2714 pub async fn bind_pattern(&mut self, pattern: &AIRNode, value: Value) -> Result<(), RuntimeError> {
2715 match &pattern.kind.clone() {
2716 NodeKind::BindPat { name, .. } => {
2717 self.env.define(name.name.clone(), value);
2718 Ok(())
2719 }
2720 NodeKind::WildcardPat => Ok(()),
2721 NodeKind::TuplePat { elems } => {
2722 if let Value::Tuple(vals) = value {
2723 if elems.len() != vals.len() {
2724 return Err(RuntimeError::MatchFailed);
2725 }
2726 let pairs: Vec<_> = elems
2727 .iter()
2728 .zip(vals)
2729 .map(|(p, v)| (p.clone(), v))
2730 .collect();
2731 for (pat, val) in pairs {
2732 self.bind_pattern(&pat, val).await?;
2733 }
2734 Ok(())
2735 } else {
2736 Err(RuntimeError::MatchFailed)
2737 }
2738 }
2739 _ => {
2740 if self.try_match_pattern(pattern, &value).await {
2741 Ok(())
2742 } else {
2743 Err(RuntimeError::MatchFailed)
2744 }
2745 }
2746 }
2747 }
2748
2749 #[async_recursion]
2752 async fn eval_for(
2753 &mut self,
2754 pattern: &AIRNode,
2755 iterable: &AIRNode,
2756 body: &AIRNode,
2757 ) -> Result<Value, RuntimeError> {
2758 let iter_val = self.eval_expr(iterable).await?;
2759 match iter_val {
2760 Value::List(items) => self.for_loop_items(pattern, body, items).await,
2761 Value::Range {
2762 start,
2763 end,
2764 inclusive,
2765 step,
2766 } => {
2767 let items = range_to_vec(start, end, inclusive, step);
2768 self.for_loop_items(pattern, body, items).await
2769 }
2770 Value::Set(set) => {
2771 let items: Vec<Value> = set.into_iter().collect();
2772 self.for_loop_items(pattern, body, items).await
2773 }
2774 Value::Map(map) => {
2775 let items: Vec<Value> = map
2776 .into_iter()
2777 .map(|(k, v)| Value::Tuple(vec![k, v]))
2778 .collect();
2779 self.for_loop_items(pattern, body, items).await
2780 }
2781 Value::Iterator(it) => self.for_loop_iterator(pattern, body, &it).await,
2782 other => Err(RuntimeError::TypeError(format!(
2783 "cannot iterate over {other}"
2784 ))),
2785 }
2786 }
2787
2788 #[async_recursion]
2789 async fn for_loop_items(
2790 &mut self,
2791 pattern: &AIRNode,
2792 body: &AIRNode,
2793 items: Vec<Value>,
2794 ) -> Result<Value, RuntimeError> {
2795 for item in items {
2796 self.env.push_scope();
2797 let bind_result = self.bind_pattern(pattern, item).await;
2798 if let Err(e) = bind_result {
2799 self.env.pop_scope();
2800 return Err(e);
2801 }
2802 let result = self.eval_expr(body).await;
2803 self.env.pop_scope();
2804 match result {
2805 Ok(_) | Err(RuntimeError::Continue) => {}
2806 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
2807 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
2808 Err(e) => return Err(e),
2809 }
2810 }
2811 Ok(Value::Void)
2812 }
2813
2814 #[async_recursion]
2815 async fn for_loop_iterator(
2816 &mut self,
2817 pattern: &AIRNode,
2818 body: &AIRNode,
2819 it: &crate::value::IteratorValue,
2820 ) -> Result<Value, RuntimeError> {
2821 loop {
2822 let next = {
2823 let mut kind = it.kind.lock().unwrap();
2824 kind.next()
2825 };
2826 match next {
2827 IteratorNext::Some(val) => {
2828 self.env.push_scope();
2829 let bind_result = self.bind_pattern(pattern, val).await;
2830 if let Err(e) = bind_result {
2831 self.env.pop_scope();
2832 return Err(e);
2833 }
2834 let result = self.eval_expr(body).await;
2835 self.env.pop_scope();
2836 match result {
2837 Ok(_) | Err(RuntimeError::Continue) => {}
2838 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
2839 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
2840 Err(e) => return Err(e),
2841 }
2842 }
2843 IteratorNext::Done => break,
2844 IteratorNext::NeedsMapCallback { value, func } => {
2845 let fn_val = Value::Function(func);
2846 let mapped = self.invoke_callback(&fn_val, &[value]).await?;
2847 self.env.push_scope();
2848 let bind_result = self.bind_pattern(pattern, mapped).await;
2849 if let Err(e) = bind_result {
2850 self.env.pop_scope();
2851 return Err(e);
2852 }
2853 let result = self.eval_expr(body).await;
2854 self.env.pop_scope();
2855 match result {
2856 Ok(_) | Err(RuntimeError::Continue) => {}
2857 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
2858 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
2859 Err(e) => return Err(e),
2860 }
2861 }
2862 IteratorNext::NeedsFilterCallback { value, func } => {
2863 let fn_val = Value::Function(func);
2864 let keep = self.invoke_callback(&fn_val, std::slice::from_ref(&value)).await?;
2865 if keep == Value::Bool(true) {
2866 self.env.push_scope();
2867 let bind_result = self.bind_pattern(pattern, value).await;
2868 if let Err(e) = bind_result {
2869 self.env.pop_scope();
2870 return Err(e);
2871 }
2872 let result = self.eval_expr(body).await;
2873 self.env.pop_scope();
2874 match result {
2875 Ok(_) | Err(RuntimeError::Continue) => {}
2876 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
2877 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
2878 Err(e) => return Err(e),
2879 }
2880 }
2881 }
2882 }
2883 }
2884 Ok(Value::Void)
2885 }
2886
2887 #[async_recursion]
2888 async fn eval_while(&mut self, condition: &AIRNode, body: &AIRNode) -> Result<Value, RuntimeError> {
2889 loop {
2890 let cond = self.eval_expr(condition).await?;
2891 match cond {
2892 Value::Bool(false) => break,
2893 Value::Bool(true) => {}
2894 other => {
2895 return Err(RuntimeError::TypeError(format!(
2896 "while condition must be Bool, got {other}"
2897 )))
2898 }
2899 }
2900 match self.eval_expr(body).await {
2901 Ok(_) | Err(RuntimeError::Continue) => {}
2902 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
2903 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
2904 Err(e) => return Err(e),
2905 }
2906 }
2907 Ok(Value::Void)
2908 }
2909
2910 #[async_recursion]
2911 async fn eval_loop(&mut self, body: &AIRNode) -> Result<Value, RuntimeError> {
2912 loop {
2913 match self.eval_expr(body).await {
2914 Ok(_) | Err(RuntimeError::Continue) => {}
2915 Err(RuntimeError::Break(None)) => return Ok(Value::Void),
2916 Err(RuntimeError::Break(Some(v))) => return Ok(*v),
2917 Err(e) => return Err(e),
2918 }
2919 }
2920 }
2921
2922 #[async_recursion]
2923 async fn eval_guard(
2924 &mut self,
2925 let_pattern: Option<&AIRNode>,
2926 condition: &AIRNode,
2927 else_block: &AIRNode,
2928 ) -> Result<Value, RuntimeError> {
2929 if let Some(pat) = let_pattern {
2930 let val = self.eval_expr(condition).await?;
2932 let matched = self.try_match_pattern(pat, &val).await;
2933 if matched {
2934 Ok(Value::Void)
2935 } else {
2936 self.eval_expr(else_block).await?;
2937 Ok(Value::Void)
2938 }
2939 } else {
2940 let cond = self.eval_expr(condition).await?;
2941 match cond {
2942 Value::Bool(true) => Ok(Value::Void),
2943 Value::Bool(false) => {
2944 self.eval_expr(else_block).await?;
2947 Ok(Value::Void)
2948 }
2949 other => Err(RuntimeError::TypeError(format!(
2950 "guard condition must be Bool, got {other}"
2951 ))),
2952 }
2953 }
2954 }
2955
2956 #[async_recursion]
2964 pub async fn exec_stmt(&mut self, node: &AIRNode) -> Result<Option<Value>, RuntimeError> {
2965 match &node.kind {
2966 NodeKind::LetBinding { .. }
2967 | NodeKind::For { .. }
2968 | NodeKind::While { .. }
2969 | NodeKind::Guard { .. } => {
2970 self.eval_expr(node).await?;
2971 Ok(None)
2972 }
2973 _ => Ok(Some(self.eval_expr(node).await?)),
2974 }
2975 }
2976
2977 #[async_recursion]
2982 pub async fn exec_block(&mut self, block: &AIRNode) -> Result<Value, RuntimeError> {
2983 match &block.kind {
2984 NodeKind::Block { stmts, tail } => self.eval_block(stmts, tail.as_deref()).await,
2985 _ => self.eval_expr(block).await,
2986 }
2987 }
2988}
2989
2990fn range_to_vec(start: i64, end: i64, inclusive: bool, step: i64) -> Vec<Value> {
2994 let effective_step = if step == 1 && start > end {
2997 -1
2998 } else if step == 0 {
2999 return Vec::new();
3000 } else {
3001 step
3002 };
3003
3004 let mut result = Vec::new();
3005 let mut i = start;
3006 if effective_step > 0 {
3007 while if inclusive { i <= end } else { i < end } {
3008 result.push(Value::Int(i));
3009 i = match i.checked_add(effective_step) {
3010 Some(next) => next,
3011 None => break,
3012 };
3013 }
3014 } else {
3015 while if inclusive { i >= end } else { i > end } {
3017 result.push(Value::Int(i));
3018 i = match i.checked_add(effective_step) {
3019 Some(next) => next,
3020 None => break,
3021 };
3022 }
3023 }
3024 result
3025}
3026
3027#[cfg(test)]
3030mod tests {
3031 use super::*;
3032 use bock_air::{AirHandlerPair, AirMapEntry, NodeId, NodeIdGen};
3033 use bock_ast::{AssignOp, BinOp, Ident, Literal, TypePath, UnaryOp};
3034 use bock_errors::{FileId, Span};
3035
3036 fn span() -> Span {
3037 Span {
3038 file: FileId(0),
3039 start: 0,
3040 end: 0,
3041 }
3042 }
3043
3044 fn ident(name: &str) -> Ident {
3045 Ident {
3046 name: name.to_string(),
3047 span: span(),
3048 }
3049 }
3050
3051 fn type_path(name: &str) -> TypePath {
3052 TypePath {
3053 segments: vec![ident(name)],
3054 span: span(),
3055 }
3056 }
3057
3058 fn gen() -> NodeIdGen {
3059 NodeIdGen::new()
3060 }
3061
3062 fn node(id: NodeId, kind: NodeKind) -> AIRNode {
3063 AIRNode::new(id, span(), kind)
3064 }
3065
3066 fn int_lit(g: &NodeIdGen, n: i64) -> AIRNode {
3067 node(
3068 g.next(),
3069 NodeKind::Literal {
3070 lit: Literal::Int(n.to_string()),
3071 },
3072 )
3073 }
3074
3075 fn float_lit(g: &NodeIdGen, f: f64) -> AIRNode {
3076 node(
3077 g.next(),
3078 NodeKind::Literal {
3079 lit: Literal::Float(f.to_string()),
3080 },
3081 )
3082 }
3083
3084 fn bool_lit(g: &NodeIdGen, b: bool) -> AIRNode {
3085 node(
3086 g.next(),
3087 NodeKind::Literal {
3088 lit: Literal::Bool(b),
3089 },
3090 )
3091 }
3092
3093 fn str_lit(g: &NodeIdGen, s: &str) -> AIRNode {
3094 node(
3095 g.next(),
3096 NodeKind::Literal {
3097 lit: Literal::String(s.to_string()),
3098 },
3099 )
3100 }
3101
3102 fn var(g: &NodeIdGen, name: &str) -> AIRNode {
3103 node(g.next(), NodeKind::Identifier { name: ident(name) })
3104 }
3105
3106 #[tokio::test]
3109 async fn eval_int_literal() {
3110 let mut interp = Interpreter::new();
3111 let g = gen();
3112 assert_eq!(interp.eval_expr(&int_lit(&g, 42)).await, Ok(Value::Int(42)));
3113 }
3114
3115 #[tokio::test]
3116 async fn eval_float_literal() {
3117 let mut interp = Interpreter::new();
3118 let g = gen();
3119 let result = interp.eval_expr(&float_lit(&g, 3.14)).await;
3120 assert!(matches!(result, Ok(Value::Float(_))));
3121 }
3122
3123 #[tokio::test]
3124 async fn eval_bool_literal() {
3125 let mut interp = Interpreter::new();
3126 let g = gen();
3127 assert_eq!(interp.eval_expr(&bool_lit(&g, true)).await, Ok(Value::Bool(true)));
3128 assert_eq!(
3129 interp.eval_expr(&bool_lit(&g, false)).await,
3130 Ok(Value::Bool(false))
3131 );
3132 }
3133
3134 #[tokio::test]
3135 async fn eval_string_literal() {
3136 let mut interp = Interpreter::new();
3137 let g = gen();
3138 assert_eq!(
3139 interp.eval_expr(&str_lit(&g, "hello")).await,
3140 Ok(Value::String(BockString::new("hello")))
3141 );
3142 }
3143
3144 #[tokio::test]
3145 async fn eval_hex_literal() {
3146 let mut interp = Interpreter::new();
3147 let g = gen();
3148 let n = node(
3149 g.next(),
3150 NodeKind::Literal {
3151 lit: Literal::Int("0xFF".to_string()),
3152 },
3153 );
3154 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(255)));
3155 }
3156
3157 #[tokio::test]
3158 async fn eval_unit_literal() {
3159 let mut interp = Interpreter::new();
3160 let g = gen();
3161 let n = node(g.next(), NodeKind::Literal { lit: Literal::Unit });
3162 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Void));
3163 }
3164
3165 #[tokio::test]
3168 async fn eval_add_ints() {
3169 let mut interp = Interpreter::new();
3170 let g = gen();
3171 let n = node(
3172 g.next(),
3173 NodeKind::BinaryOp {
3174 op: BinOp::Add,
3175 left: Box::new(int_lit(&g, 3)),
3176 right: Box::new(int_lit(&g, 4)),
3177 },
3178 );
3179 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(7)));
3180 }
3181
3182 #[tokio::test]
3183 async fn eval_add_strings() {
3184 let mut interp = Interpreter::new();
3185 let g = gen();
3186 let n = node(
3187 g.next(),
3188 NodeKind::BinaryOp {
3189 op: BinOp::Add,
3190 left: Box::new(str_lit(&g, "foo")),
3191 right: Box::new(str_lit(&g, "bar")),
3192 },
3193 );
3194 assert_eq!(
3195 interp.eval_expr(&n).await,
3196 Ok(Value::String(BockString::new("foobar")))
3197 );
3198 }
3199
3200 #[tokio::test]
3201 async fn eval_sub_ints() {
3202 let mut interp = Interpreter::new();
3203 let g = gen();
3204 let n = node(
3205 g.next(),
3206 NodeKind::BinaryOp {
3207 op: BinOp::Sub,
3208 left: Box::new(int_lit(&g, 10)),
3209 right: Box::new(int_lit(&g, 3)),
3210 },
3211 );
3212 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(7)));
3213 }
3214
3215 #[tokio::test]
3216 async fn eval_mul_ints() {
3217 let mut interp = Interpreter::new();
3218 let g = gen();
3219 let n = node(
3220 g.next(),
3221 NodeKind::BinaryOp {
3222 op: BinOp::Mul,
3223 left: Box::new(int_lit(&g, 6)),
3224 right: Box::new(int_lit(&g, 7)),
3225 },
3226 );
3227 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(42)));
3228 }
3229
3230 #[tokio::test]
3231 async fn eval_div_ints() {
3232 let mut interp = Interpreter::new();
3233 let g = gen();
3234 let n = node(
3235 g.next(),
3236 NodeKind::BinaryOp {
3237 op: BinOp::Div,
3238 left: Box::new(int_lit(&g, 10)),
3239 right: Box::new(int_lit(&g, 2)),
3240 },
3241 );
3242 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(5)));
3243 }
3244
3245 #[tokio::test]
3246 async fn eval_div_by_zero() {
3247 let mut interp = Interpreter::new();
3248 let g = gen();
3249 let n = node(
3250 g.next(),
3251 NodeKind::BinaryOp {
3252 op: BinOp::Div,
3253 left: Box::new(int_lit(&g, 1)),
3254 right: Box::new(int_lit(&g, 0)),
3255 },
3256 );
3257 assert!(matches!(
3258 interp.eval_expr(&n).await,
3259 Err(RuntimeError::DivisionByZero)
3260 ));
3261 }
3262
3263 #[tokio::test]
3264 async fn eval_pow() {
3265 let mut interp = Interpreter::new();
3266 let g = gen();
3267 let n = node(
3268 g.next(),
3269 NodeKind::BinaryOp {
3270 op: BinOp::Pow,
3271 left: Box::new(int_lit(&g, 2)),
3272 right: Box::new(int_lit(&g, 10)),
3273 },
3274 );
3275 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(1024)));
3276 }
3277
3278 #[tokio::test]
3281 async fn eval_eq() {
3282 let mut interp = Interpreter::new();
3283 let g = gen();
3284 let n = node(
3285 g.next(),
3286 NodeKind::BinaryOp {
3287 op: BinOp::Eq,
3288 left: Box::new(int_lit(&g, 5)),
3289 right: Box::new(int_lit(&g, 5)),
3290 },
3291 );
3292 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
3293 }
3294
3295 #[tokio::test]
3296 async fn eval_ne() {
3297 let mut interp = Interpreter::new();
3298 let g = gen();
3299 let n = node(
3300 g.next(),
3301 NodeKind::BinaryOp {
3302 op: BinOp::Ne,
3303 left: Box::new(int_lit(&g, 3)),
3304 right: Box::new(int_lit(&g, 5)),
3305 },
3306 );
3307 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
3308 }
3309
3310 #[tokio::test]
3311 async fn eval_lt_gt() {
3312 let mut interp = Interpreter::new();
3313 let g = gen();
3314 let lt = node(
3315 g.next(),
3316 NodeKind::BinaryOp {
3317 op: BinOp::Lt,
3318 left: Box::new(int_lit(&g, 1)),
3319 right: Box::new(int_lit(&g, 2)),
3320 },
3321 );
3322 let gt = node(
3323 g.next(),
3324 NodeKind::BinaryOp {
3325 op: BinOp::Gt,
3326 left: Box::new(int_lit(&g, 3)),
3327 right: Box::new(int_lit(&g, 2)),
3328 },
3329 );
3330 assert_eq!(interp.eval_expr(<).await, Ok(Value::Bool(true)));
3331 assert_eq!(interp.eval_expr(>).await, Ok(Value::Bool(true)));
3332 }
3333
3334 #[tokio::test]
3337 async fn eval_and_short_circuit() {
3338 let mut interp = Interpreter::new();
3339 let g = gen();
3340 let n = node(
3341 g.next(),
3342 NodeKind::BinaryOp {
3343 op: BinOp::And,
3344 left: Box::new(bool_lit(&g, false)),
3345 right: Box::new(bool_lit(&g, true)),
3346 },
3347 );
3348 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(false)));
3349 }
3350
3351 #[tokio::test]
3352 async fn eval_or_short_circuit() {
3353 let mut interp = Interpreter::new();
3354 let g = gen();
3355 let n = node(
3356 g.next(),
3357 NodeKind::BinaryOp {
3358 op: BinOp::Or,
3359 left: Box::new(bool_lit(&g, true)),
3360 right: Box::new(bool_lit(&g, false)),
3361 },
3362 );
3363 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
3364 }
3365
3366 #[tokio::test]
3369 async fn eval_neg() {
3370 let mut interp = Interpreter::new();
3371 let g = gen();
3372 let n = node(
3373 g.next(),
3374 NodeKind::UnaryOp {
3375 op: UnaryOp::Neg,
3376 operand: Box::new(int_lit(&g, 7)),
3377 },
3378 );
3379 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(-7)));
3380 }
3381
3382 #[tokio::test]
3383 async fn eval_not() {
3384 let mut interp = Interpreter::new();
3385 let g = gen();
3386 let n = node(
3387 g.next(),
3388 NodeKind::UnaryOp {
3389 op: UnaryOp::Not,
3390 operand: Box::new(bool_lit(&g, false)),
3391 },
3392 );
3393 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
3394 }
3395
3396 #[tokio::test]
3399 async fn eval_identifier() {
3400 let mut interp = Interpreter::new();
3401 let g = gen();
3402 interp.env.define("x", Value::Int(99));
3403 assert_eq!(interp.eval_expr(&var(&g, "x")).await, Ok(Value::Int(99)));
3404 }
3405
3406 #[tokio::test]
3407 async fn eval_undefined_variable() {
3408 let mut interp = Interpreter::new();
3409 let g = gen();
3410 assert!(matches!(
3411 interp.eval_expr(&var(&g, "y")).await,
3412 Err(RuntimeError::UndefinedVariable { .. })
3413 ));
3414 }
3415
3416 #[tokio::test]
3419 async fn eval_list_literal() {
3420 let mut interp = Interpreter::new();
3421 let g = gen();
3422 let n = node(
3423 g.next(),
3424 NodeKind::ListLiteral {
3425 elems: vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 3)],
3426 },
3427 );
3428 assert_eq!(
3429 interp.eval_expr(&n).await,
3430 Ok(Value::List(vec![
3431 Value::Int(1),
3432 Value::Int(2),
3433 Value::Int(3)
3434 ]))
3435 );
3436 }
3437
3438 #[tokio::test]
3439 async fn eval_tuple_literal() {
3440 let mut interp = Interpreter::new();
3441 let g = gen();
3442 let n = node(
3443 g.next(),
3444 NodeKind::TupleLiteral {
3445 elems: vec![int_lit(&g, 1), bool_lit(&g, true)],
3446 },
3447 );
3448 assert_eq!(
3449 interp.eval_expr(&n).await,
3450 Ok(Value::Tuple(vec![Value::Int(1), Value::Bool(true)]))
3451 );
3452 }
3453
3454 #[tokio::test]
3455 async fn eval_map_literal() {
3456 let mut interp = Interpreter::new();
3457 let g = gen();
3458 let n = node(
3459 g.next(),
3460 NodeKind::MapLiteral {
3461 entries: vec![AirMapEntry {
3462 key: str_lit(&g, "a"),
3463 value: int_lit(&g, 1),
3464 }],
3465 },
3466 );
3467 let result = interp.eval_expr(&n).await.unwrap();
3468 if let Value::Map(map) = result {
3469 assert_eq!(
3470 map.get(&Value::String(BockString::new("a"))),
3471 Some(&Value::Int(1))
3472 );
3473 } else {
3474 panic!("expected Map");
3475 }
3476 }
3477
3478 #[tokio::test]
3479 async fn eval_set_literal() {
3480 let mut interp = Interpreter::new();
3481 let g = gen();
3482 let n = node(
3483 g.next(),
3484 NodeKind::SetLiteral {
3485 elems: vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 1)],
3486 },
3487 );
3488 if let Ok(Value::Set(set)) = interp.eval_expr(&n).await {
3489 assert_eq!(set.len(), 2); } else {
3491 panic!("expected Set");
3492 }
3493 }
3494
3495 #[tokio::test]
3498 async fn eval_record_construct_and_field_access() {
3499 let mut interp = Interpreter::new();
3500 let g = gen();
3501
3502 let record = node(
3503 g.next(),
3504 NodeKind::RecordConstruct {
3505 path: type_path("Point"),
3506 fields: vec![
3507 AirRecordField {
3508 name: ident("x"),
3509 value: Some(Box::new(int_lit(&g, 3))),
3510 },
3511 AirRecordField {
3512 name: ident("y"),
3513 value: Some(Box::new(int_lit(&g, 4))),
3514 },
3515 ],
3516 spread: None,
3517 },
3518 );
3519
3520 let rec_val = interp.eval_expr(&record).await.unwrap();
3521 interp.env.define("p", rec_val);
3522
3523 let field_node = node(
3524 g.next(),
3525 NodeKind::FieldAccess {
3526 object: Box::new(var(&g, "p")),
3527 field: ident("x"),
3528 },
3529 );
3530 assert_eq!(interp.eval_expr(&field_node).await, Ok(Value::Int(3)));
3531 }
3532
3533 #[tokio::test]
3534 async fn eval_record_spread() {
3535 let mut interp = Interpreter::new();
3536 let g = gen();
3537
3538 let base = node(
3540 g.next(),
3541 NodeKind::RecordConstruct {
3542 path: type_path("Point"),
3543 fields: vec![
3544 AirRecordField {
3545 name: ident("x"),
3546 value: Some(Box::new(int_lit(&g, 1))),
3547 },
3548 AirRecordField {
3549 name: ident("y"),
3550 value: Some(Box::new(int_lit(&g, 2))),
3551 },
3552 ],
3553 spread: None,
3554 },
3555 );
3556 let base_val = interp.eval_expr(&base).await.unwrap();
3557 interp.env.define("base", base_val);
3558
3559 let spread_record = node(
3561 g.next(),
3562 NodeKind::RecordConstruct {
3563 path: type_path("Point"),
3564 fields: vec![AirRecordField {
3565 name: ident("y"),
3566 value: Some(Box::new(int_lit(&g, 99))),
3567 }],
3568 spread: Some(Box::new(var(&g, "base"))),
3569 },
3570 );
3571 if let Ok(Value::Record(rv)) = interp.eval_expr(&spread_record).await {
3572 assert_eq!(rv.fields["x"], Value::Int(1));
3573 assert_eq!(rv.fields["y"], Value::Int(99));
3574 } else {
3575 panic!("expected Record");
3576 }
3577 }
3578
3579 #[tokio::test]
3582 async fn eval_lambda_and_call() {
3583 let mut interp = Interpreter::new();
3584 let g = gen();
3585
3586 let param = node(
3588 g.next(),
3589 NodeKind::Param {
3590 pattern: Box::new(node(
3591 g.next(),
3592 NodeKind::BindPat {
3593 name: ident("x"),
3594 is_mut: false,
3595 },
3596 )),
3597 ty: None,
3598 default: None,
3599 },
3600 );
3601 let body = node(
3602 g.next(),
3603 NodeKind::BinaryOp {
3604 op: BinOp::Mul,
3605 left: Box::new(var(&g, "x")),
3606 right: Box::new(int_lit(&g, 2)),
3607 },
3608 );
3609 let lambda = node(
3610 g.next(),
3611 NodeKind::Lambda {
3612 params: vec![param],
3613 body: Box::new(body),
3614 },
3615 );
3616
3617 let fn_val = interp.eval_expr(&lambda).await.unwrap();
3618 interp.env.define("double", fn_val);
3619
3620 let call = node(
3621 g.next(),
3622 NodeKind::Call {
3623 callee: Box::new(var(&g, "double")),
3624 args: vec![AirArg {
3625 label: None,
3626 value: int_lit(&g, 5),
3627 }],
3628 type_args: vec![],
3629 },
3630 );
3631 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Int(10)));
3632 }
3633
3634 #[tokio::test]
3635 async fn eval_closure_captures_env() {
3636 let mut interp = Interpreter::new();
3637 let g = gen();
3638
3639 interp.env.define("factor", Value::Int(3));
3640
3641 let param = node(
3643 g.next(),
3644 NodeKind::Param {
3645 pattern: Box::new(node(
3646 g.next(),
3647 NodeKind::BindPat {
3648 name: ident("x"),
3649 is_mut: false,
3650 },
3651 )),
3652 ty: None,
3653 default: None,
3654 },
3655 );
3656 let body = node(
3657 g.next(),
3658 NodeKind::BinaryOp {
3659 op: BinOp::Mul,
3660 left: Box::new(var(&g, "x")),
3661 right: Box::new(var(&g, "factor")),
3662 },
3663 );
3664 let lambda = node(
3665 g.next(),
3666 NodeKind::Lambda {
3667 params: vec![param],
3668 body: Box::new(body),
3669 },
3670 );
3671 let fn_val = interp.eval_expr(&lambda).await.unwrap();
3672 interp.env.define("triple", fn_val);
3673
3674 let call = node(
3675 g.next(),
3676 NodeKind::Call {
3677 callee: Box::new(var(&g, "triple")),
3678 args: vec![AirArg {
3679 label: None,
3680 value: int_lit(&g, 4),
3681 }],
3682 type_args: vec![],
3683 },
3684 );
3685 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Int(12)));
3686 }
3687
3688 #[tokio::test]
3691 async fn eval_pipe_to_function() {
3692 let mut interp = Interpreter::new();
3693 let g = gen();
3694
3695 let param = node(
3697 g.next(),
3698 NodeKind::Param {
3699 pattern: Box::new(node(
3700 g.next(),
3701 NodeKind::BindPat {
3702 name: ident("x"),
3703 is_mut: false,
3704 },
3705 )),
3706 ty: None,
3707 default: None,
3708 },
3709 );
3710 let body = node(
3711 g.next(),
3712 NodeKind::BinaryOp {
3713 op: BinOp::Add,
3714 left: Box::new(var(&g, "x")),
3715 right: Box::new(int_lit(&g, 1)),
3716 },
3717 );
3718 let lambda = node(
3719 g.next(),
3720 NodeKind::Lambda {
3721 params: vec![param],
3722 body: Box::new(body),
3723 },
3724 );
3725 let fn_val = interp.eval_expr(&lambda).await.unwrap();
3726 interp.env.define("inc", fn_val);
3727
3728 let pipe = node(
3730 g.next(),
3731 NodeKind::Pipe {
3732 left: Box::new(int_lit(&g, 5)),
3733 right: Box::new(var(&g, "inc")),
3734 },
3735 );
3736 assert_eq!(interp.eval_expr(&pipe).await, Ok(Value::Int(6)));
3737 }
3738
3739 #[tokio::test]
3742 async fn eval_if_true_branch() {
3743 let mut interp = Interpreter::new();
3744 let g = gen();
3745 let n = node(
3746 g.next(),
3747 NodeKind::If {
3748 let_pattern: None,
3749 condition: Box::new(bool_lit(&g, true)),
3750 then_block: Box::new(int_lit(&g, 1)),
3751 else_block: Some(Box::new(int_lit(&g, 2))),
3752 },
3753 );
3754 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(1)));
3755 }
3756
3757 #[tokio::test]
3758 async fn eval_if_false_branch() {
3759 let mut interp = Interpreter::new();
3760 let g = gen();
3761 let n = node(
3762 g.next(),
3763 NodeKind::If {
3764 let_pattern: None,
3765 condition: Box::new(bool_lit(&g, false)),
3766 then_block: Box::new(int_lit(&g, 1)),
3767 else_block: Some(Box::new(int_lit(&g, 2))),
3768 },
3769 );
3770 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(2)));
3771 }
3772
3773 #[tokio::test]
3776 async fn eval_match_literal_pattern() {
3777 let mut interp = Interpreter::new();
3778 let g = gen();
3779
3780 let arm1 = node(
3781 g.next(),
3782 NodeKind::MatchArm {
3783 pattern: Box::new(node(
3784 g.next(),
3785 NodeKind::LiteralPat {
3786 lit: Literal::Int("1".to_string()),
3787 },
3788 )),
3789 guard: None,
3790 body: Box::new(str_lit(&g, "one")),
3791 },
3792 );
3793 let arm2 = node(
3794 g.next(),
3795 NodeKind::MatchArm {
3796 pattern: Box::new(node(g.next(), NodeKind::WildcardPat)),
3797 guard: None,
3798 body: Box::new(str_lit(&g, "other")),
3799 },
3800 );
3801 let m = node(
3802 g.next(),
3803 NodeKind::Match {
3804 scrutinee: Box::new(int_lit(&g, 1)),
3805 arms: vec![arm1, arm2],
3806 },
3807 );
3808 assert_eq!(
3809 interp.eval_expr(&m).await,
3810 Ok(Value::String(BockString::new("one")))
3811 );
3812 }
3813
3814 #[tokio::test]
3815 async fn eval_match_bind_pattern() {
3816 let mut interp = Interpreter::new();
3817 let g = gen();
3818
3819 let arm = node(
3820 g.next(),
3821 NodeKind::MatchArm {
3822 pattern: Box::new(node(
3823 g.next(),
3824 NodeKind::BindPat {
3825 name: ident("n"),
3826 is_mut: false,
3827 },
3828 )),
3829 guard: None,
3830 body: Box::new(node(
3831 g.next(),
3832 NodeKind::BinaryOp {
3833 op: BinOp::Mul,
3834 left: Box::new(var(&g, "n")),
3835 right: Box::new(int_lit(&g, 2)),
3836 },
3837 )),
3838 },
3839 );
3840 let m = node(
3841 g.next(),
3842 NodeKind::Match {
3843 scrutinee: Box::new(int_lit(&g, 5)),
3844 arms: vec![arm],
3845 },
3846 );
3847 assert_eq!(interp.eval_expr(&m).await, Ok(Value::Int(10)));
3848 }
3849
3850 #[tokio::test]
3853 async fn eval_propagate_ok() {
3854 let mut interp = Interpreter::new();
3855 let g = gen();
3856 let ok_node = node(
3858 g.next(),
3859 NodeKind::ResultConstruct {
3860 variant: ResultVariant::Ok,
3861 value: Some(Box::new(int_lit(&g, 42))),
3862 },
3863 );
3864 let prop = node(
3865 g.next(),
3866 NodeKind::Propagate {
3867 expr: Box::new(ok_node),
3868 },
3869 );
3870 assert_eq!(interp.eval_expr(&prop).await, Ok(Value::Int(42)));
3871 }
3872
3873 #[tokio::test]
3874 async fn eval_propagate_err() {
3875 let mut interp = Interpreter::new();
3876 let g = gen();
3877 let err_node = node(
3879 g.next(),
3880 NodeKind::ResultConstruct {
3881 variant: ResultVariant::Err,
3882 value: Some(Box::new(str_lit(&g, "boom"))),
3883 },
3884 );
3885 let prop = node(
3886 g.next(),
3887 NodeKind::Propagate {
3888 expr: Box::new(err_node),
3889 },
3890 );
3891 assert!(matches!(
3892 interp.eval_expr(&prop).await,
3893 Err(RuntimeError::Propagated(_))
3894 ));
3895 }
3896
3897 #[tokio::test]
3898 async fn eval_propagate_some() {
3899 let mut interp = Interpreter::new();
3900 let g = gen();
3901 interp
3903 .env
3904 .define("opt", Value::Optional(Some(Box::new(Value::Int(7)))));
3905 let prop = node(
3906 g.next(),
3907 NodeKind::Propagate {
3908 expr: Box::new(var(&g, "opt")),
3909 },
3910 );
3911 assert_eq!(interp.eval_expr(&prop).await, Ok(Value::Int(7)));
3912 }
3913
3914 #[tokio::test]
3915 async fn eval_propagate_none() {
3916 let mut interp = Interpreter::new();
3917 let g = gen();
3918 interp.env.define("opt", Value::Optional(None));
3919 let prop = node(
3920 g.next(),
3921 NodeKind::Propagate {
3922 expr: Box::new(var(&g, "opt")),
3923 },
3924 );
3925 assert!(matches!(
3926 interp.eval_expr(&prop).await,
3927 Err(RuntimeError::Propagated(_))
3928 ));
3929 }
3930
3931 #[tokio::test]
3934 async fn eval_interpolation() {
3935 let mut interp = Interpreter::new();
3936 let g = gen();
3937 interp
3938 .env
3939 .define("name", Value::String(BockString::new("world")));
3940 let n = node(
3941 g.next(),
3942 NodeKind::Interpolation {
3943 parts: vec![
3944 AirInterpolationPart::Literal("Hello, ".to_string()),
3945 AirInterpolationPart::Expr(Box::new(var(&g, "name"))),
3946 AirInterpolationPart::Literal("!".to_string()),
3947 ],
3948 },
3949 );
3950 assert_eq!(
3951 interp.eval_expr(&n).await,
3952 Ok(Value::String(BockString::new("Hello, world!")))
3953 );
3954 }
3955
3956 #[tokio::test]
3959 async fn eval_range_exclusive() {
3960 let mut interp = Interpreter::new();
3961 let g = gen();
3962 let n = node(
3963 g.next(),
3964 NodeKind::Range {
3965 lo: Box::new(int_lit(&g, 1)),
3966 hi: Box::new(int_lit(&g, 4)),
3967 inclusive: false,
3968 },
3969 );
3970 assert_eq!(
3971 interp.eval_expr(&n).await,
3972 Ok(Value::Range {
3973 start: 1,
3974 end: 4,
3975 inclusive: false,
3976 step: 1
3977 })
3978 );
3979 }
3980
3981 #[tokio::test]
3982 async fn eval_range_inclusive() {
3983 let mut interp = Interpreter::new();
3984 let g = gen();
3985 let n = node(
3986 g.next(),
3987 NodeKind::Range {
3988 lo: Box::new(int_lit(&g, 1)),
3989 hi: Box::new(int_lit(&g, 3)),
3990 inclusive: true,
3991 },
3992 );
3993 assert_eq!(
3994 interp.eval_expr(&n).await,
3995 Ok(Value::Range {
3996 start: 1,
3997 end: 3,
3998 inclusive: true,
3999 step: 1
4000 })
4001 );
4002 }
4003
4004 #[tokio::test]
4007 async fn eval_block_with_let_binding() {
4008 let mut interp = Interpreter::new();
4009 let g = gen();
4010
4011 let let_stmt = node(
4013 g.next(),
4014 NodeKind::LetBinding {
4015 is_mut: false,
4016 pattern: Box::new(node(
4017 g.next(),
4018 NodeKind::BindPat {
4019 name: ident("x"),
4020 is_mut: false,
4021 },
4022 )),
4023 ty: None,
4024 value: Box::new(int_lit(&g, 10)),
4025 },
4026 );
4027 let tail = node(
4028 g.next(),
4029 NodeKind::BinaryOp {
4030 op: BinOp::Add,
4031 left: Box::new(var(&g, "x")),
4032 right: Box::new(int_lit(&g, 5)),
4033 },
4034 );
4035 let block = node(
4036 g.next(),
4037 NodeKind::Block {
4038 stmts: vec![let_stmt],
4039 tail: Some(Box::new(tail)),
4040 },
4041 );
4042 assert_eq!(interp.eval_expr(&block).await, Ok(Value::Int(15)));
4043 }
4044
4045 #[tokio::test]
4048 async fn eval_index_list() {
4049 let mut interp = Interpreter::new();
4050 let g = gen();
4051 interp
4052 .env
4053 .define("lst", Value::List(vec![Value::Int(10), Value::Int(20)]));
4054 let n = node(
4055 g.next(),
4056 NodeKind::Index {
4057 object: Box::new(var(&g, "lst")),
4058 index: Box::new(int_lit(&g, 1)),
4059 },
4060 );
4061 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(20)));
4062 }
4063
4064 #[tokio::test]
4067 async fn eval_result_ok() {
4068 let mut interp = Interpreter::new();
4069 let g = gen();
4070 let n = node(
4071 g.next(),
4072 NodeKind::ResultConstruct {
4073 variant: ResultVariant::Ok,
4074 value: Some(Box::new(int_lit(&g, 42))),
4075 },
4076 );
4077 assert_eq!(
4078 interp.eval_expr(&n).await,
4079 Ok(Value::Result(Ok(Box::new(Value::Int(42)))))
4080 );
4081 }
4082
4083 #[tokio::test]
4084 async fn eval_result_err() {
4085 let mut interp = Interpreter::new();
4086 let g = gen();
4087 let n = node(
4088 g.next(),
4089 NodeKind::ResultConstruct {
4090 variant: ResultVariant::Err,
4091 value: Some(Box::new(str_lit(&g, "oops"))),
4092 },
4093 );
4094 assert_eq!(
4095 interp.eval_expr(&n).await,
4096 Ok(Value::Result(Err(Box::new(Value::String(
4097 BockString::new("oops")
4098 )))))
4099 );
4100 }
4101
4102 #[tokio::test]
4105 async fn eval_compose_functions() {
4106 let mut interp = Interpreter::new();
4107 let g = gen();
4108
4109 let double_body = node(
4111 g.next(),
4112 NodeKind::BinaryOp {
4113 op: BinOp::Mul,
4114 left: Box::new(var(&g, "x")),
4115 right: Box::new(int_lit(&g, 2)),
4116 },
4117 );
4118 interp.register_fn("double", vec!["x".to_string()], double_body);
4119
4120 let inc_body = node(
4121 g.next(),
4122 NodeKind::BinaryOp {
4123 op: BinOp::Add,
4124 left: Box::new(var(&g, "x")),
4125 right: Box::new(int_lit(&g, 1)),
4126 },
4127 );
4128 interp.register_fn("inc", vec!["x".to_string()], inc_body);
4129
4130 let compose = node(
4132 g.next(),
4133 NodeKind::Compose {
4134 left: Box::new(var(&g, "double")),
4135 right: Box::new(var(&g, "inc")),
4136 },
4137 );
4138 let fn_val = interp.eval_expr(&compose).await.unwrap();
4139 interp.env.define("double_then_inc", fn_val);
4140
4141 let call = node(
4142 g.next(),
4143 NodeKind::Call {
4144 callee: Box::new(var(&g, "double_then_inc")),
4145 args: vec![AirArg {
4146 label: None,
4147 value: int_lit(&g, 5),
4148 }],
4149 type_args: vec![],
4150 },
4151 );
4152 assert_eq!(interp.eval_expr(&call).await, Ok(Value::Int(11)));
4154 }
4155
4156 #[tokio::test]
4159 async fn eval_list_len_method() {
4160 let mut interp = Interpreter::new();
4161 let g = gen();
4162 interp.env.define(
4163 "lst",
4164 Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4165 );
4166 let n = node(
4167 g.next(),
4168 NodeKind::MethodCall {
4169 receiver: Box::new(var(&g, "lst")),
4170 method: ident("len"),
4171 type_args: vec![],
4172 args: vec![],
4173 },
4174 );
4175 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(3)));
4176 }
4177
4178 #[tokio::test]
4179 async fn eval_list_map_method() {
4180 let mut interp = Interpreter::new();
4181 let g = gen();
4182
4183 let body = node(
4185 g.next(),
4186 NodeKind::BinaryOp {
4187 op: BinOp::Mul,
4188 left: Box::new(var(&g, "x")),
4189 right: Box::new(int_lit(&g, 2)),
4190 },
4191 );
4192 interp.register_fn("double", vec!["x".to_string()], body);
4193
4194 interp.env.define(
4195 "lst",
4196 Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4197 );
4198
4199 let n = node(
4200 g.next(),
4201 NodeKind::MethodCall {
4202 receiver: Box::new(var(&g, "lst")),
4203 method: ident("map"),
4204 type_args: vec![],
4205 args: vec![AirArg {
4206 label: None,
4207 value: var(&g, "double"),
4208 }],
4209 },
4210 );
4211 assert_eq!(
4212 interp.eval_expr(&n).await,
4213 Ok(Value::List(vec![
4214 Value::Int(2),
4215 Value::Int(4),
4216 Value::Int(6)
4217 ]))
4218 );
4219 }
4220
4221 #[tokio::test]
4224 async fn eval_bitwise_and() {
4225 let mut interp = Interpreter::new();
4226 let g = gen();
4227 let n = node(
4228 g.next(),
4229 NodeKind::BinaryOp {
4230 op: BinOp::BitAnd,
4231 left: Box::new(int_lit(&g, 0b1100)),
4232 right: Box::new(int_lit(&g, 0b1010)),
4233 },
4234 );
4235 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(0b1000)));
4236 }
4237
4238 #[tokio::test]
4239 async fn eval_bitwise_or() {
4240 let mut interp = Interpreter::new();
4241 let g = gen();
4242 let n = node(
4243 g.next(),
4244 NodeKind::BinaryOp {
4245 op: BinOp::BitOr,
4246 left: Box::new(int_lit(&g, 0b1100)),
4247 right: Box::new(int_lit(&g, 0b1010)),
4248 },
4249 );
4250 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Int(0b1110)));
4251 }
4252
4253 fn bind_pat(g: &NodeIdGen, name: &str) -> AIRNode {
4256 node(
4257 g.next(),
4258 NodeKind::BindPat {
4259 name: ident(name),
4260 is_mut: false,
4261 },
4262 )
4263 }
4264
4265 fn let_stmt(g: &NodeIdGen, pat: AIRNode, val: AIRNode) -> AIRNode {
4266 node(
4267 g.next(),
4268 NodeKind::LetBinding {
4269 is_mut: false,
4270 pattern: Box::new(pat),
4271 ty: None,
4272 value: Box::new(val),
4273 },
4274 )
4275 }
4276
4277 fn assign_node(g: &NodeIdGen, name: &str, val: AIRNode) -> AIRNode {
4278 node(
4279 g.next(),
4280 NodeKind::Assign {
4281 op: AssignOp::Assign,
4282 target: Box::new(var(g, name)),
4283 value: Box::new(val),
4284 },
4285 )
4286 }
4287
4288 fn add(g: &NodeIdGen, left: AIRNode, right: AIRNode) -> AIRNode {
4289 node(
4290 g.next(),
4291 NodeKind::BinaryOp {
4292 op: BinOp::Add,
4293 left: Box::new(left),
4294 right: Box::new(right),
4295 },
4296 )
4297 }
4298
4299 fn lt(g: &NodeIdGen, left: AIRNode, right: AIRNode) -> AIRNode {
4300 node(
4301 g.next(),
4302 NodeKind::BinaryOp {
4303 op: BinOp::Lt,
4304 left: Box::new(left),
4305 right: Box::new(right),
4306 },
4307 )
4308 }
4309
4310 fn block(g: &NodeIdGen, stmts: Vec<AIRNode>, tail: Option<AIRNode>) -> AIRNode {
4311 node(
4312 g.next(),
4313 NodeKind::Block {
4314 stmts,
4315 tail: tail.map(Box::new),
4316 },
4317 )
4318 }
4319
4320 fn list_lit(g: &NodeIdGen, elems: Vec<AIRNode>) -> AIRNode {
4321 node(g.next(), NodeKind::ListLiteral { elems })
4322 }
4323
4324 #[tokio::test]
4327 async fn exec_stmt_let_binding_returns_none() {
4328 let mut interp = Interpreter::new();
4329 let g = gen();
4330 let stmt = let_stmt(&g, bind_pat(&g, "x"), int_lit(&g, 99));
4331 let result = interp.exec_stmt(&stmt).await.unwrap();
4332 assert_eq!(result, None);
4333 assert_eq!(interp.env.get("x"), Some(&Value::Int(99)));
4334 }
4335
4336 #[tokio::test]
4337 async fn exec_block_returns_tail_expression() {
4338 let mut interp = Interpreter::new();
4340 let g = gen();
4341 let blk = block(
4342 &g,
4343 vec![let_stmt(&g, bind_pat(&g, "a"), int_lit(&g, 3))],
4344 Some(add(&g, var(&g, "a"), int_lit(&g, 4))),
4345 );
4346 assert_eq!(interp.exec_block(&blk).await, Ok(Value::Int(7)));
4347 }
4348
4349 #[tokio::test]
4350 async fn block_scope_variables_do_not_leak() {
4351 let mut interp = Interpreter::new();
4353 let g = gen();
4354 let blk = block(
4355 &g,
4356 vec![let_stmt(&g, bind_pat(&g, "inner"), int_lit(&g, 99))],
4357 None,
4358 );
4359 interp.exec_block(&blk).await.unwrap();
4360 assert_eq!(interp.env.get("inner"), None);
4361 }
4362
4363 #[tokio::test]
4364 async fn for_loop_iterates_over_list() {
4365 let mut interp = Interpreter::new();
4367 let g = gen();
4368 interp.env.define("sum", Value::Int(0));
4369 let for_node = node(
4370 g.next(),
4371 NodeKind::For {
4372 pattern: Box::new(bind_pat(&g, "x")),
4373 iterable: Box::new(list_lit(
4374 &g,
4375 vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 3)],
4376 )),
4377 body: Box::new(assign_node(
4378 &g,
4379 "sum",
4380 add(&g, var(&g, "sum"), var(&g, "x")),
4381 )),
4382 },
4383 );
4384 assert_eq!(interp.eval_expr(&for_node).await, Ok(Value::Void));
4385 assert_eq!(interp.env.get("sum"), Some(&Value::Int(6)));
4386 }
4387
4388 #[tokio::test]
4389 async fn for_loop_break_exits_early() {
4390 let mut interp = Interpreter::new();
4392 let g = gen();
4393 let break_node = node(g.next(), NodeKind::Break { value: None });
4394 let for_node = node(
4395 g.next(),
4396 NodeKind::For {
4397 pattern: Box::new(bind_pat(&g, "x")),
4398 iterable: Box::new(list_lit(
4399 &g,
4400 vec![int_lit(&g, 1), int_lit(&g, 2), int_lit(&g, 3)],
4401 )),
4402 body: Box::new(break_node),
4403 },
4404 );
4405 assert_eq!(interp.eval_expr(&for_node).await, Ok(Value::Void));
4406 }
4407
4408 #[tokio::test]
4409 async fn while_loop_does_not_execute_when_false() {
4410 let mut interp = Interpreter::new();
4412 let g = gen();
4413 let cond = bool_lit(&g, false);
4414 let body = block(&g, vec![], None);
4415 let while_node = node(
4416 g.next(),
4417 NodeKind::While {
4418 condition: Box::new(cond),
4419 body: Box::new(body),
4420 },
4421 );
4422 assert_eq!(interp.eval_expr(&while_node).await, Ok(Value::Void));
4423 }
4424
4425 #[tokio::test]
4426 async fn while_loop_counts_to_three() {
4427 let mut interp = Interpreter::new();
4429 let g = gen();
4430 interp.env.define("count", Value::Int(0));
4431 let cond = lt(&g, var(&g, "count"), int_lit(&g, 3));
4432 let body = assign_node(&g, "count", add(&g, var(&g, "count"), int_lit(&g, 1)));
4433 let while_node = node(
4434 g.next(),
4435 NodeKind::While {
4436 condition: Box::new(cond),
4437 body: Box::new(body),
4438 },
4439 );
4440 assert_eq!(interp.eval_expr(&while_node).await, Ok(Value::Void));
4441 assert_eq!(interp.env.get("count"), Some(&Value::Int(3)));
4442 }
4443
4444 #[tokio::test]
4445 async fn loop_break_with_value() {
4446 let mut interp = Interpreter::new();
4448 let g = gen();
4449 let break_node = node(
4450 g.next(),
4451 NodeKind::Break {
4452 value: Some(Box::new(int_lit(&g, 42))),
4453 },
4454 );
4455 let loop_node = node(
4456 g.next(),
4457 NodeKind::Loop {
4458 body: Box::new(break_node),
4459 },
4460 );
4461 assert_eq!(interp.eval_expr(&loop_node).await, Ok(Value::Int(42)));
4462 }
4463
4464 #[tokio::test]
4465 async fn loop_break_without_value() {
4466 let mut interp = Interpreter::new();
4468 let g = gen();
4469 let break_node = node(g.next(), NodeKind::Break { value: None });
4470 let loop_node = node(
4471 g.next(),
4472 NodeKind::Loop {
4473 body: Box::new(break_node),
4474 },
4475 );
4476 assert_eq!(interp.eval_expr(&loop_node).await, Ok(Value::Void));
4477 }
4478
4479 #[tokio::test]
4480 async fn guard_passes_when_condition_true() {
4481 let mut interp = Interpreter::new();
4483 let g = gen();
4484 let else_blk = node(g.next(), NodeKind::Return { value: None });
4485 let guard_node = node(
4486 g.next(),
4487 NodeKind::Guard {
4488 let_pattern: None,
4489 condition: Box::new(bool_lit(&g, true)),
4490 else_block: Box::new(else_blk),
4491 },
4492 );
4493 assert_eq!(interp.eval_expr(&guard_node).await, Ok(Value::Void));
4494 }
4495
4496 #[tokio::test]
4497 async fn guard_else_diverges_when_condition_false() {
4498 let mut interp = Interpreter::new();
4500 let g = gen();
4501 let else_blk = node(g.next(), NodeKind::Return { value: None });
4502 let guard_node = node(
4503 g.next(),
4504 NodeKind::Guard {
4505 let_pattern: None,
4506 condition: Box::new(bool_lit(&g, false)),
4507 else_block: Box::new(else_blk),
4508 },
4509 );
4510 assert_eq!(
4511 interp.eval_expr(&guard_node).await,
4512 Err(RuntimeError::Return(Box::new(Value::Void)))
4513 );
4514 }
4515
4516 #[tokio::test]
4517 async fn let_binding_with_tuple_destructuring() {
4518 let mut interp = Interpreter::new();
4520 let g = gen();
4521 let tuple_pat = node(
4522 g.next(),
4523 NodeKind::TuplePat {
4524 elems: vec![bind_pat(&g, "a"), bind_pat(&g, "b")],
4525 },
4526 );
4527 let tuple_val = node(
4528 g.next(),
4529 NodeKind::TupleLiteral {
4530 elems: vec![int_lit(&g, 1), int_lit(&g, 2)],
4531 },
4532 );
4533 let stmt = let_stmt(&g, tuple_pat, tuple_val);
4534 assert_eq!(interp.exec_stmt(&stmt).await, Ok(None));
4535 assert_eq!(interp.env.get("a"), Some(&Value::Int(1)));
4536 assert_eq!(interp.env.get("b"), Some(&Value::Int(2)));
4537 }
4538
4539 #[tokio::test]
4542 async fn effect_op_with_no_handler_errors() {
4543 let mut interp = Interpreter::new();
4544 let g = gen();
4545 let effect_op = node(
4546 g.next(),
4547 NodeKind::EffectOp {
4548 effect: type_path("Log"),
4549 operation: ident("log"),
4550 args: vec![],
4551 },
4552 );
4553 let result = interp.eval_expr(&effect_op).await;
4554 assert!(matches!(result, Err(RuntimeError::NoEffectHandler { .. })));
4555 if let Err(RuntimeError::NoEffectHandler { effect }) = result {
4556 assert_eq!(effect, "Log");
4557 }
4558 }
4559
4560 #[tokio::test]
4561 async fn effect_op_dispatches_to_single_fn_handler() {
4562 let mut interp = Interpreter::new();
4563 let g = gen();
4564
4565 let handler_body = int_lit(&g, 42);
4567 interp.register_fn("my_log", vec!["msg".to_string()], handler_body);
4568
4569 let handler_val = interp.env.get("my_log").cloned().unwrap();
4571 interp
4572 .effect_handlers
4573 .set_module_handler("Log", handler_val);
4574
4575 let effect_op = node(
4577 g.next(),
4578 NodeKind::EffectOp {
4579 effect: type_path("Log"),
4580 operation: ident("log"),
4581 args: vec![AirArg {
4582 label: None,
4583 value: str_lit(&g, "hello"),
4584 }],
4585 },
4586 );
4587 let result = interp.eval_expr(&effect_op).await;
4588 assert_eq!(result, Ok(Value::Int(42)));
4589 }
4590
4591 #[tokio::test]
4592 async fn effect_op_dispatches_to_record_handler() {
4593 let mut interp = Interpreter::new();
4594 let g = gen();
4595
4596 let op_body = int_lit(&g, 99);
4598 interp.register_fn("_log_op", vec!["msg".to_string()], op_body);
4599 let op_fn = interp.env.get("_log_op").cloned().unwrap();
4600
4601 let mut fields = BTreeMap::new();
4603 fields.insert("log".to_string(), op_fn);
4604 let handler_record = Value::Record(RecordValue {
4605 type_name: "ConsoleLog".to_string(),
4606 fields,
4607 });
4608 interp
4609 .effect_handlers
4610 .set_module_handler("Log", handler_record);
4611
4612 let effect_op = node(
4613 g.next(),
4614 NodeKind::EffectOp {
4615 effect: type_path("Log"),
4616 operation: ident("log"),
4617 args: vec![AirArg {
4618 label: None,
4619 value: str_lit(&g, "test"),
4620 }],
4621 },
4622 );
4623 let result = interp.eval_expr(&effect_op).await;
4624 assert_eq!(result, Ok(Value::Int(99)));
4625 }
4626
4627 #[tokio::test]
4628 async fn handling_block_pushes_and_pops_handler() {
4629 let mut interp = Interpreter::new();
4630 let g = gen();
4631
4632 let handler_body = int_lit(&g, 7);
4634 interp.register_fn("test_handler", vec!["msg".to_string()], handler_body);
4635
4636 let effect_op = node(
4638 g.next(),
4639 NodeKind::EffectOp {
4640 effect: type_path("Log"),
4641 operation: ident("log"),
4642 args: vec![AirArg {
4643 label: None,
4644 value: str_lit(&g, "inside"),
4645 }],
4646 },
4647 );
4648
4649 let handling = node(
4650 g.next(),
4651 NodeKind::HandlingBlock {
4652 handlers: vec![AirHandlerPair {
4653 effect: type_path("Log"),
4654 handler: Box::new(var(&g, "test_handler")),
4655 }],
4656 body: Box::new(effect_op),
4657 },
4658 );
4659
4660 let result = interp.eval_expr(&handling).await;
4662 assert_eq!(result, Ok(Value::Int(7)));
4663
4664 assert!(interp.effect_handlers.resolve("Log").is_none());
4666 }
4667
4668 #[tokio::test]
4669 async fn handling_block_pops_on_error() {
4670 let mut interp = Interpreter::new();
4671 let g = gen();
4672
4673 let bad_body = var(&g, "nonexistent");
4675 let handler_body = int_lit(&g, 1);
4676 interp.register_fn("h", vec![], handler_body);
4677
4678 let handling = node(
4679 g.next(),
4680 NodeKind::HandlingBlock {
4681 handlers: vec![AirHandlerPair {
4682 effect: type_path("Log"),
4683 handler: Box::new(var(&g, "h")),
4684 }],
4685 body: Box::new(bad_body),
4686 },
4687 );
4688
4689 let result = interp.eval_expr(&handling).await;
4690 assert!(result.is_err());
4691 assert!(interp.effect_handlers.resolve("Log").is_none());
4693 }
4694
4695 #[tokio::test]
4696 async fn nested_handling_blocks_innermost_wins() {
4697 let mut interp = Interpreter::new();
4698 let g = gen();
4699
4700 let outer_body = int_lit(&g, 1);
4702 interp.register_fn("outer_h", vec!["m".to_string()], outer_body);
4703
4704 let inner_body = int_lit(&g, 2);
4706 interp.register_fn("inner_h", vec!["m".to_string()], inner_body);
4707
4708 let effect_op = node(
4709 g.next(),
4710 NodeKind::EffectOp {
4711 effect: type_path("Log"),
4712 operation: ident("log"),
4713 args: vec![AirArg {
4714 label: None,
4715 value: str_lit(&g, "test"),
4716 }],
4717 },
4718 );
4719
4720 let inner_handling = node(
4721 g.next(),
4722 NodeKind::HandlingBlock {
4723 handlers: vec![AirHandlerPair {
4724 effect: type_path("Log"),
4725 handler: Box::new(var(&g, "inner_h")),
4726 }],
4727 body: Box::new(effect_op),
4728 },
4729 );
4730
4731 let outer_handling = node(
4732 g.next(),
4733 NodeKind::HandlingBlock {
4734 handlers: vec![AirHandlerPair {
4735 effect: type_path("Log"),
4736 handler: Box::new(var(&g, "outer_h")),
4737 }],
4738 body: Box::new(inner_handling),
4739 },
4740 );
4741
4742 let result = interp.eval_expr(&outer_handling).await;
4743 assert_eq!(result, Ok(Value::Int(2)));
4744 }
4745
4746 #[tokio::test]
4747 async fn three_layer_resolution_local_over_module_over_project() {
4748 let mut interp = Interpreter::new();
4749 let g = gen();
4750
4751 let proj_body = int_lit(&g, 1);
4753 interp.register_fn("proj_h", vec!["m".to_string()], proj_body);
4754 let proj_val = interp.env.get("proj_h").cloned().unwrap();
4755 interp.effect_handlers.set_project_handler("Log", proj_val);
4756
4757 let mod_body = int_lit(&g, 2);
4759 interp.register_fn("mod_h", vec!["m".to_string()], mod_body);
4760 let mod_val = interp.env.get("mod_h").cloned().unwrap();
4761 interp.effect_handlers.set_module_handler("Log", mod_val);
4762
4763 let local_body = int_lit(&g, 3);
4765 interp.register_fn("local_h", vec!["m".to_string()], local_body);
4766
4767 let effect_op = node(
4768 g.next(),
4769 NodeKind::EffectOp {
4770 effect: type_path("Log"),
4771 operation: ident("log"),
4772 args: vec![AirArg {
4773 label: None,
4774 value: str_lit(&g, "test"),
4775 }],
4776 },
4777 );
4778
4779 let handling = node(
4780 g.next(),
4781 NodeKind::HandlingBlock {
4782 handlers: vec![AirHandlerPair {
4783 effect: type_path("Log"),
4784 handler: Box::new(var(&g, "local_h")),
4785 }],
4786 body: Box::new(effect_op),
4787 },
4788 );
4789
4790 let result = interp.eval_expr(&handling).await;
4792 assert_eq!(result, Ok(Value::Int(3)));
4793 }
4794
4795 #[tokio::test]
4796 async fn module_handle_registers_handler() {
4797 let mut interp = Interpreter::new();
4798 let g = gen();
4799
4800 let handler_body = int_lit(&g, 55);
4802 interp.register_fn("console_log", vec!["m".to_string()], handler_body);
4803
4804 let module_handle = node(
4806 g.next(),
4807 NodeKind::ModuleHandle {
4808 effect: type_path("Log"),
4809 handler: Box::new(var(&g, "console_log")),
4810 },
4811 );
4812 let result = interp.eval_expr(&module_handle).await;
4813 assert_eq!(result, Ok(Value::Void));
4814
4815 let effect_op = node(
4817 g.next(),
4818 NodeKind::EffectOp {
4819 effect: type_path("Log"),
4820 operation: ident("log"),
4821 args: vec![AirArg {
4822 label: None,
4823 value: str_lit(&g, "test"),
4824 }],
4825 },
4826 );
4827 let result = interp.eval_expr(&effect_op).await;
4828 assert_eq!(result, Ok(Value::Int(55)));
4829 }
4830
4831 #[tokio::test]
4832 async fn effect_decl_evaluates_to_void() {
4833 let mut interp = Interpreter::new();
4834 let g = gen();
4835 let effect_decl = node(
4836 g.next(),
4837 NodeKind::EffectDecl {
4838 annotations: vec![],
4839 visibility: bock_ast::Visibility::Private,
4840 name: ident("Log"),
4841 generic_params: vec![],
4842 components: vec![],
4843 operations: vec![],
4844 },
4845 );
4846 let result = interp.eval_expr(&effect_decl).await;
4847 assert_eq!(result, Ok(Value::Void));
4848 }
4849
4850 #[tokio::test]
4851 async fn effect_ref_evaluates_to_void() {
4852 let mut interp = Interpreter::new();
4853 let g = gen();
4854 let effect_ref = node(
4855 g.next(),
4856 NodeKind::EffectRef {
4857 path: type_path("Log"),
4858 },
4859 );
4860 let result = interp.eval_expr(&effect_ref).await;
4861 assert_eq!(result, Ok(Value::Void));
4862 }
4863
4864 #[tokio::test]
4865 async fn no_handler_error_message_is_clear() {
4866 let err = RuntimeError::NoEffectHandler {
4867 effect: "Log".to_string(),
4868 };
4869 let msg = err.to_string();
4870 assert!(msg.contains("Log"));
4871 assert!(msg.contains("handling"));
4872 assert!(msg.contains("handler"));
4873 }
4874
4875 #[tokio::test]
4876 async fn register_effect_dispatches_through_call() {
4877 let mut interp = Interpreter::new();
4878 let g = gen();
4879
4880 let empty_body = node(
4882 g.next(),
4883 NodeKind::Block {
4884 stmts: vec![],
4885 tail: None,
4886 },
4887 );
4888 let log_op = node(
4889 g.next(),
4890 NodeKind::FnDecl {
4891 annotations: vec![],
4892 visibility: bock_ast::Visibility::Public,
4893 is_async: false,
4894 name: ident("log"),
4895 generic_params: vec![],
4896 params: vec![],
4897 return_type: None,
4898 effect_clause: vec![],
4899 where_clause: vec![],
4900 body: Box::new(empty_body),
4901 },
4902 );
4903 interp.register_effect("Logger", &[log_op]);
4904
4905 let handler_body = int_lit(&g, 77);
4907 interp.register_fn("my_handler", vec!["msg".to_string()], handler_body);
4908 let handler_val = interp.env.get("my_handler").cloned().unwrap();
4909 interp
4910 .effect_handlers
4911 .set_module_handler("Logger", handler_val);
4912
4913 let call_node = node(
4915 g.next(),
4916 NodeKind::Call {
4917 callee: Box::new(var(&g, "log")),
4918 args: vec![AirArg {
4919 label: None,
4920 value: str_lit(&g, "test"),
4921 }],
4922 type_args: vec![],
4923 },
4924 );
4925 let result = interp.eval_expr(&call_node).await;
4926 assert_eq!(result, Ok(Value::Int(77)));
4927 }
4928
4929 #[tokio::test]
4932 async fn is_operator_int() {
4933 let mut interp = Interpreter::new();
4934 let g = gen();
4935 let n = node(
4937 g.next(),
4938 NodeKind::BinaryOp {
4939 op: BinOp::Is,
4940 left: Box::new(int_lit(&g, 42)),
4941 right: Box::new(str_lit(&g, "Int")),
4942 },
4943 );
4944 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
4945 }
4946
4947 #[tokio::test]
4948 async fn is_operator_wrong_type() {
4949 let mut interp = Interpreter::new();
4950 let g = gen();
4951 let n = node(
4953 g.next(),
4954 NodeKind::BinaryOp {
4955 op: BinOp::Is,
4956 left: Box::new(int_lit(&g, 42)),
4957 right: Box::new(str_lit(&g, "String")),
4958 },
4959 );
4960 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(false)));
4961 }
4962
4963 #[tokio::test]
4964 async fn is_operator_string() {
4965 let mut interp = Interpreter::new();
4966 let g = gen();
4967 let n = node(
4968 g.next(),
4969 NodeKind::BinaryOp {
4970 op: BinOp::Is,
4971 left: Box::new(str_lit(&g, "hello")),
4972 right: Box::new(str_lit(&g, "String")),
4973 },
4974 );
4975 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
4976 }
4977
4978 #[tokio::test]
4979 async fn is_operator_bool() {
4980 let mut interp = Interpreter::new();
4981 let g = gen();
4982 let n = node(
4983 g.next(),
4984 NodeKind::BinaryOp {
4985 op: BinOp::Is,
4986 left: Box::new(bool_lit(&g, true)),
4987 right: Box::new(str_lit(&g, "Bool")),
4988 },
4989 );
4990 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
4991 }
4992
4993 #[tokio::test]
4994 async fn is_operator_list() {
4995 let mut interp = Interpreter::new();
4996 let g = gen();
4997 let list_node = node(
4998 g.next(),
4999 NodeKind::ListLiteral {
5000 elems: vec![int_lit(&g, 1)],
5001 },
5002 );
5003 let n = node(
5004 g.next(),
5005 NodeKind::BinaryOp {
5006 op: BinOp::Is,
5007 left: Box::new(list_node),
5008 right: Box::new(str_lit(&g, "List")),
5009 },
5010 );
5011 assert_eq!(interp.eval_expr(&n).await, Ok(Value::Bool(true)));
5012 }
5013
5014 #[tokio::test]
5017 async fn match_on_enum_with_wildcard_succeeds() {
5018 let mut interp = Interpreter::new();
5019 let g = gen();
5020 interp.env.define(
5022 "color",
5023 Value::Enum(crate::value::EnumValue {
5024 type_name: "Color".to_string(),
5025 variant: "Red".to_string(),
5026 payload: None,
5027 }),
5028 );
5029 let match_expr = node(
5031 g.next(),
5032 NodeKind::Match {
5033 scrutinee: Box::new(var(&g, "color")),
5034 arms: vec![node(
5035 g.next(),
5036 NodeKind::MatchArm {
5037 pattern: Box::new(node(g.next(), NodeKind::WildcardPat)),
5038 guard: None,
5039 body: Box::new(int_lit(&g, 99)),
5040 },
5041 )],
5042 },
5043 );
5044 assert_eq!(interp.eval_expr(&match_expr).await, Ok(Value::Int(99)));
5045 }
5046
5047 #[tokio::test]
5050 async fn compound_assign_field() {
5051 let mut interp = Interpreter::new();
5052 let g = gen();
5053 let mut fields = BTreeMap::new();
5055 fields.insert("x".to_string(), Value::Int(10));
5056 fields.insert("y".to_string(), Value::Int(20));
5057 interp.env.define(
5058 "obj",
5059 Value::Record(RecordValue {
5060 type_name: "Point".to_string(),
5061 fields,
5062 }),
5063 );
5064 let assign = node(
5066 g.next(),
5067 NodeKind::Assign {
5068 op: AssignOp::AddAssign,
5069 target: Box::new(node(
5070 g.next(),
5071 NodeKind::FieldAccess {
5072 object: Box::new(var(&g, "obj")),
5073 field: ident("x"),
5074 },
5075 )),
5076 value: Box::new(int_lit(&g, 5)),
5077 },
5078 );
5079 assert_eq!(interp.eval_expr(&assign).await, Ok(Value::Void));
5080 let obj = interp.env.get("obj").unwrap().clone();
5082 if let Value::Record(rv) = obj {
5083 assert_eq!(rv.fields.get("x"), Some(&Value::Int(15)));
5084 assert_eq!(rv.fields.get("y"), Some(&Value::Int(20)));
5085 } else {
5086 panic!("expected Record");
5087 }
5088 }
5089
5090 #[tokio::test]
5091 async fn compound_assign_index() {
5092 let mut interp = Interpreter::new();
5093 let g = gen();
5094 interp.env.define(
5096 "list",
5097 Value::List(vec![Value::Int(10), Value::Int(20), Value::Int(30)]),
5098 );
5099 let assign = node(
5101 g.next(),
5102 NodeKind::Assign {
5103 op: AssignOp::AddAssign,
5104 target: Box::new(node(
5105 g.next(),
5106 NodeKind::Index {
5107 object: Box::new(var(&g, "list")),
5108 index: Box::new(int_lit(&g, 1)),
5109 },
5110 )),
5111 value: Box::new(int_lit(&g, 5)),
5112 },
5113 );
5114 assert_eq!(interp.eval_expr(&assign).await, Ok(Value::Void));
5115 let list = interp.env.get("list").unwrap().clone();
5116 assert_eq!(
5117 list,
5118 Value::List(vec![Value::Int(10), Value::Int(25), Value::Int(30)])
5119 );
5120 }
5121
5122 #[tokio::test]
5123 async fn assign_field_simple() {
5124 let mut interp = Interpreter::new();
5125 let g = gen();
5126 let mut fields = BTreeMap::new();
5127 fields.insert("name".to_string(), Value::String(BockString::new("old")));
5128 interp.env.define(
5129 "obj",
5130 Value::Record(RecordValue {
5131 type_name: "Item".to_string(),
5132 fields,
5133 }),
5134 );
5135 let assign = node(
5137 g.next(),
5138 NodeKind::Assign {
5139 op: AssignOp::Assign,
5140 target: Box::new(node(
5141 g.next(),
5142 NodeKind::FieldAccess {
5143 object: Box::new(var(&g, "obj")),
5144 field: ident("name"),
5145 },
5146 )),
5147 value: Box::new(str_lit(&g, "new")),
5148 },
5149 );
5150 assert_eq!(interp.eval_expr(&assign).await, Ok(Value::Void));
5151 let obj = interp.env.get("obj").unwrap().clone();
5152 if let Value::Record(rv) = obj {
5153 assert_eq!(
5154 rv.fields.get("name"),
5155 Some(&Value::String(BockString::new("new")))
5156 );
5157 } else {
5158 panic!("expected Record");
5159 }
5160 }
5161
5162 #[tokio::test]
5165 async fn descending_range_exclusive() {
5166 let result = range_to_vec(5, 1, false, 1);
5168 assert_eq!(
5169 result,
5170 vec![Value::Int(5), Value::Int(4), Value::Int(3), Value::Int(2)]
5171 );
5172 }
5173
5174 #[tokio::test]
5175 async fn descending_range_inclusive() {
5176 let result = range_to_vec(5, 1, true, 1);
5178 assert_eq!(
5179 result,
5180 vec![
5181 Value::Int(5),
5182 Value::Int(4),
5183 Value::Int(3),
5184 Value::Int(2),
5185 Value::Int(1),
5186 ]
5187 );
5188 }
5189
5190 #[tokio::test]
5191 async fn descending_range_explicit_negative_step() {
5192 let result = range_to_vec(10, 0, false, -2);
5194 assert_eq!(
5195 result,
5196 vec![
5197 Value::Int(10),
5198 Value::Int(8),
5199 Value::Int(6),
5200 Value::Int(4),
5201 Value::Int(2),
5202 ]
5203 );
5204 }
5205
5206 #[tokio::test]
5207 async fn ascending_range_still_works() {
5208 let result = range_to_vec(1, 5, false, 1);
5209 assert_eq!(
5210 result,
5211 vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]
5212 );
5213 }
5214
5215 #[tokio::test]
5218 async fn for_in_map() {
5219 let mut interp = Interpreter::new();
5220 let g = gen();
5221 let mut map = BTreeMap::new();
5223 map.insert(Value::Int(1), Value::String(BockString::new("a")));
5224 map.insert(Value::Int(2), Value::String(BockString::new("b")));
5225 interp.env.define("m", Value::Map(map));
5226 interp.env.define("result", Value::List(vec![]));
5227
5228 let for_expr = node(
5231 g.next(),
5232 NodeKind::For {
5233 pattern: Box::new(node(
5234 g.next(),
5235 NodeKind::TuplePat {
5236 elems: vec![
5237 node(
5238 g.next(),
5239 NodeKind::BindPat {
5240 name: ident("k"),
5241 is_mut: false,
5242 },
5243 ),
5244 node(
5245 g.next(),
5246 NodeKind::BindPat {
5247 name: ident("v"),
5248 is_mut: false,
5249 },
5250 ),
5251 ],
5252 },
5253 )),
5254 iterable: Box::new(var(&g, "m")),
5255 body: Box::new(node(
5256 g.next(),
5257 NodeKind::Assign {
5258 op: AssignOp::Assign,
5259 target: Box::new(var(&g, "result")),
5260 value: Box::new(node(
5261 g.next(),
5262 NodeKind::MethodCall {
5263 receiver: Box::new(var(&g, "result")),
5264 method: ident("push"),
5265 args: vec![AirArg {
5266 label: None,
5267 value: var(&g, "k"),
5268 }],
5269 type_args: vec![],
5270 },
5271 )),
5272 },
5273 )),
5274 },
5275 );
5276 assert_eq!(interp.eval_expr(&for_expr).await, Ok(Value::Void));
5277 let result = interp.env.get("result").unwrap().clone();
5278 assert_eq!(result, Value::List(vec![Value::Int(1), Value::Int(2)]));
5280 }
5281
5282 #[tokio::test]
5285 async fn for_in_lazy_map_iterator() {
5286 use crate::value::{IteratorKind, IteratorValue};
5287
5288 let mut interp = Interpreter::new();
5289 let g = gen();
5290
5291 let double_body = node(
5293 g.next(),
5294 NodeKind::BinaryOp {
5295 op: BinOp::Mul,
5296 left: Box::new(var(&g, "x")),
5297 right: Box::new(int_lit(&g, 2)),
5298 },
5299 );
5300 interp.register_fn("double", vec!["x".to_string()], double_body);
5301 let double_fn = match interp.env.get("double").unwrap().clone() {
5302 Value::Function(fv) => fv,
5303 _ => panic!("expected function"),
5304 };
5305
5306 let source = IteratorKind::List {
5308 items: vec![Value::Int(1), Value::Int(2), Value::Int(3)],
5309 pos: 0,
5310 };
5311 let map_iter = IteratorKind::Map {
5312 source: std::sync::Arc::new(std::sync::Mutex::new(source)),
5313 func: double_fn,
5314 };
5315 let iter_val = IteratorValue::new(map_iter);
5316 interp.env.define("it", Value::Iterator(iter_val));
5317 interp.env.define("result", Value::List(vec![]));
5318
5319 let for_expr = node(
5321 g.next(),
5322 NodeKind::For {
5323 pattern: Box::new(node(
5324 g.next(),
5325 NodeKind::BindPat {
5326 name: ident("item"),
5327 is_mut: false,
5328 },
5329 )),
5330 iterable: Box::new(var(&g, "it")),
5331 body: Box::new(node(
5332 g.next(),
5333 NodeKind::Assign {
5334 op: AssignOp::Assign,
5335 target: Box::new(var(&g, "result")),
5336 value: Box::new(node(
5337 g.next(),
5338 NodeKind::MethodCall {
5339 receiver: Box::new(var(&g, "result")),
5340 method: ident("push"),
5341 args: vec![AirArg {
5342 label: None,
5343 value: var(&g, "item"),
5344 }],
5345 type_args: vec![],
5346 },
5347 )),
5348 },
5349 )),
5350 },
5351 );
5352 assert_eq!(interp.eval_expr(&for_expr).await, Ok(Value::Void));
5353 let result = interp.env.get("result").unwrap().clone();
5354 assert_eq!(
5355 result,
5356 Value::List(vec![Value::Int(2), Value::Int(4), Value::Int(6)])
5357 );
5358 }
5359
5360 #[tokio::test]
5361 async fn for_in_lazy_filter_iterator() {
5362 use crate::value::{IteratorKind, IteratorValue};
5363
5364 let mut interp = Interpreter::new();
5365 let g = gen();
5366
5367 let pred_body = node(
5369 g.next(),
5370 NodeKind::BinaryOp {
5371 op: BinOp::Gt,
5372 left: Box::new(var(&g, "x")),
5373 right: Box::new(int_lit(&g, 2)),
5374 },
5375 );
5376 interp.register_fn("gt2", vec!["x".to_string()], pred_body);
5377 let gt2_fn = match interp.env.get("gt2").unwrap().clone() {
5378 Value::Function(fv) => fv,
5379 _ => panic!("expected function"),
5380 };
5381
5382 let source = IteratorKind::List {
5384 items: vec![
5385 Value::Int(1),
5386 Value::Int(2),
5387 Value::Int(3),
5388 Value::Int(4),
5389 Value::Int(5),
5390 ],
5391 pos: 0,
5392 };
5393 let filter_iter = IteratorKind::Filter {
5394 source: std::sync::Arc::new(std::sync::Mutex::new(source)),
5395 pred: gt2_fn,
5396 };
5397 let iter_val = IteratorValue::new(filter_iter);
5398 interp.env.define("it", Value::Iterator(iter_val));
5399 interp.env.define("result", Value::List(vec![]));
5400
5401 let for_expr = node(
5403 g.next(),
5404 NodeKind::For {
5405 pattern: Box::new(node(
5406 g.next(),
5407 NodeKind::BindPat {
5408 name: ident("item"),
5409 is_mut: false,
5410 },
5411 )),
5412 iterable: Box::new(var(&g, "it")),
5413 body: Box::new(node(
5414 g.next(),
5415 NodeKind::Assign {
5416 op: AssignOp::Assign,
5417 target: Box::new(var(&g, "result")),
5418 value: Box::new(node(
5419 g.next(),
5420 NodeKind::MethodCall {
5421 receiver: Box::new(var(&g, "result")),
5422 method: ident("push"),
5423 args: vec![AirArg {
5424 label: None,
5425 value: var(&g, "item"),
5426 }],
5427 type_args: vec![],
5428 },
5429 )),
5430 },
5431 )),
5432 },
5433 );
5434 assert_eq!(interp.eval_expr(&for_expr).await, Ok(Value::Void));
5435 let result = interp.env.get("result").unwrap().clone();
5436 assert_eq!(
5437 result,
5438 Value::List(vec![Value::Int(3), Value::Int(4), Value::Int(5)])
5439 );
5440 }
5441}