Skip to main content

ion_core/
interpreter.rs

1use crate::ast::*;
2use crate::env::Env;
3use crate::error::{ErrorKind, IonError};
4use crate::host_types::TypeRegistry;
5use crate::stdlib::OutputHandler;
6use crate::value::{IonFn, Value};
7use indexmap::IndexMap;
8use std::sync::Arc;
9
10/// Control flow signals that escape normal evaluation.
11enum Signal {
12    Return(Value),
13    Break {
14        label: Option<String>,
15        value: Value,
16    },
17    Continue {
18        label: Option<String>,
19    },
20}
21
22/// Returns true if a break/continue signal carrying `sig_label` should be
23/// caught by a loop carrying `loop_label`. Unlabeled signals are caught by
24/// the innermost loop regardless of that loop's own label (Rust semantics).
25fn signal_targets_loop(sig_label: &Option<String>, loop_label: &Option<String>) -> bool {
26    match sig_label {
27        None => true,
28        Some(want) => loop_label.as_deref() == Some(want.as_str()),
29    }
30}
31
32fn unmatched_label_msg(keyword: &str, label: Option<String>) -> String {
33    match label {
34        Some(name) => format!("{keyword} with unknown label '{name}"),
35        None => format!("{keyword} outside of loop"),
36    }
37}
38
39type IonResult = Result<Value, IonError>;
40type SignalResult = Result<Value, SignalOrError>;
41
42enum SignalOrError {
43    Signal(Signal),
44    Error(IonError),
45}
46
47impl From<IonError> for SignalOrError {
48    fn from(e: IonError) -> Self {
49        SignalOrError::Error(e)
50    }
51}
52
53impl From<Signal> for SignalOrError {
54    fn from(s: Signal) -> Self {
55        SignalOrError::Signal(s)
56    }
57}
58
59#[derive(Clone)]
60pub struct Limits {
61    pub max_call_depth: usize,
62    pub max_loop_iters: usize,
63}
64
65impl Default for Limits {
66    fn default() -> Self {
67        Self {
68            max_call_depth: 512,
69            max_loop_iters: 1_000_000,
70        }
71    }
72}
73
74pub struct Interpreter {
75    pub env: Env,
76    pub limits: Limits,
77    pub types: TypeRegistry,
78    call_depth: usize,
79    #[cfg(feature = "concurrency")]
80    nursery: Option<crate::async_rt::Nursery>,
81    #[cfg(feature = "concurrency")]
82    pub(crate) cancel_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
83}
84
85impl Default for Interpreter {
86    fn default() -> Self {
87        Self::new()
88    }
89}
90
91impl Interpreter {
92    pub fn new() -> Self {
93        Self::with_output(crate::stdlib::missing_output_handler())
94    }
95
96    pub fn with_output(output: Arc<dyn OutputHandler>) -> Self {
97        let mut env = Env::new();
98        register_builtins(&mut env, output);
99        Self {
100            env,
101            limits: Limits::default(),
102            types: TypeRegistry::new(),
103            call_depth: 0,
104            #[cfg(feature = "concurrency")]
105            nursery: None,
106            #[cfg(feature = "concurrency")]
107            cancel_flag: None,
108        }
109    }
110
111    pub fn eval_program(&mut self, program: &Program) -> IonResult {
112        match self.eval_stmts(&program.stmts) {
113            Ok(v) => Ok(v),
114            Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedErr => {
115                Ok(Value::Result(Err(Box::new(Value::Str(e.message.clone())))))
116            }
117            Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedNone => {
118                Ok(Value::Option(None))
119            }
120            Err(SignalOrError::Error(e)) => Err(e),
121            Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
122            Err(SignalOrError::Signal(Signal::Break { label, .. })) => {
123                Err(IonError::runtime(unmatched_label_msg("break", label), 0, 0))
124            }
125            Err(SignalOrError::Signal(Signal::Continue { label })) => Err(IonError::runtime(
126                unmatched_label_msg("continue", label),
127                0,
128                0,
129            )),
130        }
131    }
132
133    /// Create an interpreter with a pre-existing environment (for VM hybrid mode).
134    pub fn with_env(env: Env) -> Self {
135        Self {
136            env,
137            limits: Limits::default(),
138            types: TypeRegistry::new(),
139            call_depth: 0,
140            #[cfg(feature = "concurrency")]
141            nursery: None,
142            #[cfg(feature = "concurrency")]
143            cancel_flag: None,
144        }
145    }
146
147    /// Take ownership of the environment (for VM hybrid mode).
148    pub fn take_env(self) -> Env {
149        self.env
150    }
151
152    /// Evaluate a block of statements, returning the last value (public for VM).
153    pub fn eval_block(&mut self, stmts: &[Stmt]) -> IonResult {
154        match self.eval_stmts(stmts) {
155            Ok(v) => Ok(v),
156            Err(SignalOrError::Error(e)) => Err(e),
157            Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
158            Err(SignalOrError::Signal(Signal::Break { value, .. })) => Ok(value),
159            Err(SignalOrError::Signal(Signal::Continue { .. })) => Ok(Value::Unit),
160        }
161    }
162
163    /// Evaluate a single expression (public for VM).
164    pub fn eval_single_expr(&mut self, expr: &Expr) -> IonResult {
165        match self.eval_expr(expr) {
166            Ok(v) => Ok(v),
167            Err(SignalOrError::Error(e)) => Err(e),
168            Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
169            Err(SignalOrError::Signal(Signal::Break { value, .. })) => Ok(value),
170            Err(SignalOrError::Signal(Signal::Continue { .. })) => Ok(Value::Unit),
171        }
172    }
173
174    #[cfg(feature = "concurrency")]
175    fn check_cancelled(&self, line: usize, col: usize) -> Result<(), SignalOrError> {
176        if let Some(flag) = &self.cancel_flag {
177            if flag.load(std::sync::atomic::Ordering::Relaxed) {
178                return Err(IonError::runtime(ion_str!("task cancelled"), line, col).into());
179            }
180        }
181        Ok(())
182    }
183
184    fn eval_stmts(&mut self, stmts: &[Stmt]) -> SignalResult {
185        let mut last = Value::Unit;
186        for (i, stmt) in stmts.iter().enumerate() {
187            let is_last = i == stmts.len() - 1;
188            match &stmt.kind {
189                StmtKind::ExprStmt { expr, has_semi } => {
190                    let val = self.eval_expr(expr)?;
191                    if is_last && !has_semi {
192                        last = val;
193                    } else {
194                        last = Value::Unit;
195                    }
196                }
197                _ => {
198                    self.eval_stmt(stmt)?;
199                    last = Value::Unit;
200                }
201            }
202        }
203        Ok(last)
204    }
205
206    fn eval_stmt(&mut self, stmt: &Stmt) -> SignalResult {
207        match &stmt.kind {
208            StmtKind::Let {
209                mutable,
210                pattern,
211                type_ann,
212                value,
213            } => {
214                let val = self.eval_expr(value)?;
215                if let Some(ann) = type_ann {
216                    Self::check_type_ann(&val, ann, stmt.span)?;
217                }
218                self.bind_pattern(pattern, &val, *mutable, stmt.span)?;
219                Ok(Value::Unit)
220            }
221            StmtKind::FnDecl { name, params, body } => {
222                let captures = self.env.capture();
223                let func = Value::Fn(IonFn::new(
224                    name.clone(),
225                    params.clone(),
226                    body.clone(),
227                    captures,
228                ));
229                self.env.define(name.clone(), func, false);
230                Ok(Value::Unit)
231            }
232            StmtKind::ExprStmt { expr, .. } => {
233                self.eval_expr(expr)?;
234                Ok(Value::Unit)
235            }
236            StmtKind::For {
237                label,
238                pattern,
239                iter,
240                body,
241            } => {
242                let iter_val = self.eval_expr(iter)?;
243                let items = self.value_to_iter(&iter_val, iter.span)?;
244                for item in items {
245                    #[cfg(feature = "concurrency")]
246                    self.check_cancelled(stmt.span.line, stmt.span.col)?;
247                    self.env.push_scope();
248                    self.bind_pattern(pattern, &item, false, iter.span)?;
249                    match self.eval_stmts(body) {
250                        Ok(_) => {}
251                        Err(SignalOrError::Signal(Signal::Break {
252                            label: sig_label,
253                            value,
254                        })) => {
255                            self.env.pop_scope();
256                            if signal_targets_loop(&sig_label, label) {
257                                break;
258                            }
259                            return Err(Signal::Break {
260                                label: sig_label,
261                                value,
262                            }
263                            .into());
264                        }
265                        Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
266                            self.env.pop_scope();
267                            if signal_targets_loop(&sig_label, label) {
268                                continue;
269                            }
270                            return Err(Signal::Continue { label: sig_label }.into());
271                        }
272                        Err(e) => {
273                            self.env.pop_scope();
274                            return Err(e);
275                        }
276                    }
277                    self.env.pop_scope();
278                }
279                Ok(Value::Unit)
280            }
281            StmtKind::While { label, cond, body } => {
282                let mut iters = 0usize;
283                loop {
284                    #[cfg(feature = "concurrency")]
285                    self.check_cancelled(stmt.span.line, stmt.span.col)?;
286                    let c = self.eval_expr(cond)?;
287                    if !c.is_truthy() {
288                        break;
289                    }
290                    iters += 1;
291                    if iters > self.limits.max_loop_iters {
292                        return Err(IonError::runtime(
293                            ion_str!("maximum loop iterations exceeded").to_string(),
294                            stmt.span.line,
295                            stmt.span.col,
296                        )
297                        .into());
298                    }
299                    self.env.push_scope();
300                    match self.eval_stmts(body) {
301                        Ok(_) => {}
302                        Err(SignalOrError::Signal(Signal::Break {
303                            label: sig_label,
304                            value,
305                        })) => {
306                            self.env.pop_scope();
307                            if signal_targets_loop(&sig_label, label) {
308                                break;
309                            }
310                            return Err(Signal::Break {
311                                label: sig_label,
312                                value,
313                            }
314                            .into());
315                        }
316                        Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
317                            self.env.pop_scope();
318                            if signal_targets_loop(&sig_label, label) {
319                                continue;
320                            }
321                            return Err(Signal::Continue { label: sig_label }.into());
322                        }
323                        Err(e) => {
324                            self.env.pop_scope();
325                            return Err(e);
326                        }
327                    }
328                    self.env.pop_scope();
329                }
330                Ok(Value::Unit)
331            }
332            StmtKind::WhileLet {
333                label,
334                pattern,
335                expr,
336                body,
337            } => {
338                let mut iters = 0usize;
339                loop {
340                    #[cfg(feature = "concurrency")]
341                    self.check_cancelled(stmt.span.line, stmt.span.col)?;
342                    let val = self.eval_expr(expr)?;
343                    if !self.pattern_matches(pattern, &val) {
344                        break;
345                    }
346                    iters += 1;
347                    if iters > self.limits.max_loop_iters {
348                        return Err(IonError::runtime(
349                            ion_str!("maximum loop iterations exceeded").to_string(),
350                            stmt.span.line,
351                            stmt.span.col,
352                        )
353                        .into());
354                    }
355                    self.env.push_scope();
356                    self.bind_pattern(pattern, &val, false, expr.span)?;
357                    match self.eval_stmts(body) {
358                        Ok(_) => {}
359                        Err(SignalOrError::Signal(Signal::Break {
360                            label: sig_label,
361                            value,
362                        })) => {
363                            self.env.pop_scope();
364                            if signal_targets_loop(&sig_label, label) {
365                                break;
366                            }
367                            return Err(Signal::Break {
368                                label: sig_label,
369                                value,
370                            }
371                            .into());
372                        }
373                        Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
374                            self.env.pop_scope();
375                            if signal_targets_loop(&sig_label, label) {
376                                continue;
377                            }
378                            return Err(Signal::Continue { label: sig_label }.into());
379                        }
380                        Err(e) => {
381                            self.env.pop_scope();
382                            return Err(e);
383                        }
384                    }
385                    self.env.pop_scope();
386                }
387                Ok(Value::Unit)
388            }
389            StmtKind::Loop { label, body } => {
390                let mut iters = 0usize;
391                let result = loop {
392                    iters += 1;
393                    if iters > self.limits.max_loop_iters {
394                        return Err(IonError::runtime(
395                            ion_str!("maximum loop iterations exceeded").to_string(),
396                            stmt.span.line,
397                            stmt.span.col,
398                        )
399                        .into());
400                    }
401                    self.env.push_scope();
402                    match self.eval_stmts(body) {
403                        Ok(_) => {}
404                        Err(SignalOrError::Signal(Signal::Break {
405                            label: sig_label,
406                            value,
407                        })) => {
408                            self.env.pop_scope();
409                            if signal_targets_loop(&sig_label, label) {
410                                break value;
411                            }
412                            return Err(Signal::Break {
413                                label: sig_label,
414                                value,
415                            }
416                            .into());
417                        }
418                        Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
419                            self.env.pop_scope();
420                            if signal_targets_loop(&sig_label, label) {
421                                continue;
422                            }
423                            return Err(Signal::Continue { label: sig_label }.into());
424                        }
425                        Err(e) => {
426                            self.env.pop_scope();
427                            return Err(e);
428                        }
429                    }
430                    self.env.pop_scope();
431                };
432                Ok(result)
433            }
434            StmtKind::Break { label, value } => {
435                let v = match value {
436                    Some(expr) => self.eval_expr(expr)?,
437                    None => Value::Unit,
438                };
439                Err(Signal::Break {
440                    label: label.clone(),
441                    value: v,
442                }
443                .into())
444            }
445            StmtKind::Continue { label } => Err(Signal::Continue {
446                label: label.clone(),
447            }
448            .into()),
449            StmtKind::Return { value } => {
450                let v = match value {
451                    Some(expr) => self.eval_expr(expr)?,
452                    None => Value::Unit,
453                };
454                Err(Signal::Return(v).into())
455            }
456            StmtKind::Assign { target, op, value } => {
457                let rhs = self.eval_expr(value)?;
458                match target {
459                    AssignTarget::Ident(name) => {
460                        let final_val = match op {
461                            AssignOp::Eq => rhs,
462                            _ => {
463                                let lhs = self
464                                    .env
465                                    .get(name)
466                                    .ok_or_else(|| {
467                                        IonError::name(
468                                            format!("{}{}", ion_str!("undefined variable: "), name),
469                                            stmt.span.line,
470                                            stmt.span.col,
471                                        )
472                                    })?
473                                    .clone();
474                                self.apply_compound_op(*op, &lhs, &rhs, stmt.span)?
475                            }
476                        };
477                        self.env
478                            .set(name, final_val)
479                            .map_err(|msg| IonError::runtime(msg, stmt.span.line, stmt.span.col))?;
480                    }
481                    AssignTarget::Index(obj_expr, index_expr) => {
482                        let var_name = match &obj_expr.kind {
483                            ExprKind::Ident(name) => name.clone(),
484                            _ => {
485                                return Err(IonError::runtime(
486                                    ion_str!("index assignment only supported on variables"),
487                                    stmt.span.line,
488                                    stmt.span.col,
489                                )
490                                .into())
491                            }
492                        };
493                        let mut container = self
494                            .env
495                            .get(&var_name)
496                            .ok_or_else(|| {
497                                IonError::name(
498                                    format!("{}{}", ion_str!("undefined variable: "), var_name),
499                                    stmt.span.line,
500                                    stmt.span.col,
501                                )
502                            })?
503                            .clone();
504                        let index = self.eval_expr(index_expr)?;
505                        let final_val = match op {
506                            AssignOp::Eq => rhs,
507                            _ => {
508                                let old = self.index_access(&container, &index, stmt.span)?;
509                                // index_access returns Option-wrapped values; unwrap for compound assign
510                                let old = match old {
511                                    Value::Option(Some(v)) => *v,
512                                    other => other,
513                                };
514                                self.apply_compound_op(*op, &old, &rhs, stmt.span)?
515                            }
516                        };
517                        match (&mut container, &index) {
518                            (Value::List(items), Value::Int(i)) => {
519                                let idx = if *i < 0 { items.len() as i64 + i } else { *i } as usize;
520                                if idx >= items.len() {
521                                    return Err(IonError::runtime(
522                                        format!(
523                                            "{}{}{}",
524                                            ion_str!("index "),
525                                            i,
526                                            ion_str!(" out of range")
527                                        ),
528                                        stmt.span.line,
529                                        stmt.span.col,
530                                    )
531                                    .into());
532                                }
533                                items[idx] = final_val;
534                            }
535                            (Value::Dict(map), Value::Str(key)) => {
536                                map.insert(key.clone(), final_val);
537                            }
538                            _ => {
539                                return Err(IonError::type_err(
540                                    format!(
541                                        "{}{}",
542                                        ion_str!("cannot set index on "),
543                                        container.type_name()
544                                    ),
545                                    stmt.span.line,
546                                    stmt.span.col,
547                                )
548                                .into())
549                            }
550                        }
551                        self.env
552                            .set(&var_name, container)
553                            .map_err(|msg| IonError::runtime(msg, stmt.span.line, stmt.span.col))?;
554                    }
555                    AssignTarget::Field(obj_expr, field) => {
556                        let var_name = match &obj_expr.kind {
557                            ExprKind::Ident(name) => name.clone(),
558                            _ => {
559                                return Err(IonError::runtime(
560                                    ion_str!("field assignment only supported on variables"),
561                                    stmt.span.line,
562                                    stmt.span.col,
563                                )
564                                .into())
565                            }
566                        };
567                        let mut container = self
568                            .env
569                            .get(&var_name)
570                            .ok_or_else(|| {
571                                IonError::name(
572                                    format!("{}{}", ion_str!("undefined variable: "), var_name),
573                                    stmt.span.line,
574                                    stmt.span.col,
575                                )
576                            })?
577                            .clone();
578                        let final_val = match op {
579                            AssignOp::Eq => rhs,
580                            _ => {
581                                let old = self.field_access(&container, field, stmt.span)?;
582                                self.apply_compound_op(*op, &old, &rhs, stmt.span)?
583                            }
584                        };
585                        match &mut container {
586                            Value::Dict(map) => {
587                                map.insert(field.clone(), final_val);
588                            }
589                            Value::HostStruct { fields, .. } => {
590                                if fields.contains_key(field.as_str()) {
591                                    fields.insert(field.clone(), final_val);
592                                } else {
593                                    return Err(IonError::runtime(
594                                        format!(
595                                            "{}{}{}",
596                                            ion_str!("field '"),
597                                            field,
598                                            ion_str!("' not found")
599                                        ),
600                                        stmt.span.line,
601                                        stmt.span.col,
602                                    )
603                                    .into());
604                                }
605                            }
606                            _ => {
607                                return Err(IonError::type_err(
608                                    format!(
609                                        "{}{}",
610                                        ion_str!("cannot set field on "),
611                                        container.type_name()
612                                    ),
613                                    stmt.span.line,
614                                    stmt.span.col,
615                                )
616                                .into())
617                            }
618                        }
619                        self.env
620                            .set(&var_name, container)
621                            .map_err(|msg| IonError::runtime(msg, stmt.span.line, stmt.span.col))?;
622                    }
623                }
624                Ok(Value::Unit)
625            }
626            StmtKind::Use { path, imports } => {
627                // Resolve the module dict by walking the path segments
628                let root = self.env.get(&path[0]).ok_or_else(|| {
629                    SignalOrError::Error(IonError::name(
630                        format!("{}{}", ion_str!("undefined module: "), &path[0]),
631                        stmt.span.line,
632                        stmt.span.col,
633                    ))
634                })?;
635                let mut module_val = root.clone();
636                for seg in &path[1..] {
637                    match &module_val {
638                        Value::Dict(map) => {
639                            module_val = map.get(seg).cloned().ok_or_else(|| {
640                                SignalOrError::Error(IonError::name(
641                                    format!(
642                                        "{}{}{}{}",
643                                        ion_str!("'"),
644                                        seg,
645                                        ion_str!("' not found in module "),
646                                        &path[0]
647                                    ),
648                                    stmt.span.line,
649                                    stmt.span.col,
650                                ))
651                            })?;
652                        }
653                        _ => {
654                            return Err(IonError::type_err(
655                                format!(
656                                    "{}{}{}",
657                                    ion_str!("'"),
658                                    seg,
659                                    ion_str!("' is not a module")
660                                ),
661                                stmt.span.line,
662                                stmt.span.col,
663                            )
664                            .into())
665                        }
666                    }
667                }
668                // Now import from module_val (which should be a dict)
669                match imports {
670                    UseImports::Glob => {
671                        if let Value::Dict(map) = &module_val {
672                            for (name, val) in map {
673                                self.env.define(name.clone(), val.clone(), false);
674                            }
675                        } else {
676                            return Err(IonError::type_err(
677                                ion_str!("use target is not a module"),
678                                stmt.span.line,
679                                stmt.span.col,
680                            )
681                            .into());
682                        }
683                    }
684                    UseImports::Names(names) => {
685                        if let Value::Dict(map) = &module_val {
686                            for name in names {
687                                let val = map.get(name).ok_or_else(|| {
688                                    SignalOrError::Error(IonError::name(
689                                        format!(
690                                            "{}{}{}",
691                                            ion_str!("'"),
692                                            name,
693                                            ion_str!("' not found in module")
694                                        ),
695                                        stmt.span.line,
696                                        stmt.span.col,
697                                    ))
698                                })?;
699                                self.env.define(name.clone(), val.clone(), false);
700                            }
701                        } else {
702                            return Err(IonError::type_err(
703                                ion_str!("use target is not a module"),
704                                stmt.span.line,
705                                stmt.span.col,
706                            )
707                            .into());
708                        }
709                    }
710                    UseImports::Single(name) => {
711                        if let Value::Dict(map) = &module_val {
712                            let val = map.get(name).ok_or_else(|| {
713                                SignalOrError::Error(IonError::name(
714                                    format!(
715                                        "{}{}{}",
716                                        ion_str!("'"),
717                                        name,
718                                        ion_str!("' not found in module")
719                                    ),
720                                    stmt.span.line,
721                                    stmt.span.col,
722                                ))
723                            })?;
724                            self.env.define(name.clone(), val.clone(), false);
725                        } else {
726                            return Err(IonError::type_err(
727                                ion_str!("use target is not a module"),
728                                stmt.span.line,
729                                stmt.span.col,
730                            )
731                            .into());
732                        }
733                    }
734                }
735                Ok(Value::Unit)
736            }
737        }
738    }
739
740    fn eval_expr(&mut self, expr: &Expr) -> SignalResult {
741        let span = expr.span;
742        match &expr.kind {
743            ExprKind::Int(n) => Ok(Value::Int(*n)),
744            ExprKind::Float(n) => Ok(Value::Float(*n)),
745            ExprKind::Bool(b) => Ok(Value::Bool(*b)),
746            ExprKind::Str(s) => Ok(Value::Str(s.clone())),
747            ExprKind::Bytes(b) => Ok(Value::Bytes(b.clone())),
748            ExprKind::None => Ok(Value::Option(None)),
749            ExprKind::Unit => Ok(Value::Unit),
750
751            ExprKind::FStr(parts) => {
752                let mut result = String::new();
753                for part in parts {
754                    match part {
755                        FStrPart::Literal(s) => result.push_str(s),
756                        FStrPart::Expr(e) => {
757                            let val = self.eval_expr(e)?;
758                            result.push_str(&val.to_string());
759                        }
760                    }
761                }
762                Ok(Value::Str(result))
763            }
764
765            ExprKind::Ident(name) => self.env.get(name).cloned().ok_or_else(|| {
766                IonError::name(
767                    format!("{}{}", ion_str!("undefined variable: "), name),
768                    span.line,
769                    span.col,
770                )
771                .into()
772            }),
773
774            ExprKind::ModulePath(segments) => {
775                // Resolve a::b::c by walking dict fields from the root module
776                let root = self.env.get(&segments[0]).ok_or_else(|| {
777                    SignalOrError::Error(IonError::name(
778                        format!("{}{}", ion_str!("undefined module: "), &segments[0]),
779                        span.line,
780                        span.col,
781                    ))
782                })?;
783                let mut current = root.clone();
784                for seg in &segments[1..] {
785                    match &current {
786                        Value::Dict(map) => {
787                            current = map.get(seg).cloned().ok_or_else(|| {
788                                SignalOrError::Error(IonError::name(
789                                    format!(
790                                        "{}{}{}{}",
791                                        ion_str!("'"),
792                                        seg,
793                                        ion_str!("' not found in module "),
794                                        &segments[0]
795                                    ),
796                                    span.line,
797                                    span.col,
798                                ))
799                            })?;
800                        }
801                        _ => {
802                            return Err(IonError::type_err(
803                                format!(
804                                    "{}{}{}",
805                                    ion_str!("cannot access '"),
806                                    seg,
807                                    ion_str!("' on non-module value")
808                                ),
809                                span.line,
810                                span.col,
811                            )
812                            .into())
813                        }
814                    }
815                }
816                Ok(current)
817            }
818
819            ExprKind::SomeExpr(e) => {
820                let val = self.eval_expr(e)?;
821                Ok(Value::Option(Some(Box::new(val))))
822            }
823            ExprKind::OkExpr(e) => {
824                let val = self.eval_expr(e)?;
825                Ok(Value::Result(Ok(Box::new(val))))
826            }
827            ExprKind::ErrExpr(e) => {
828                let val = self.eval_expr(e)?;
829                Ok(Value::Result(Err(Box::new(val))))
830            }
831
832            ExprKind::List(items) => {
833                let mut vals = Vec::new();
834                for entry in items {
835                    match entry {
836                        ListEntry::Elem(expr) => vals.push(self.eval_expr(expr)?),
837                        ListEntry::Spread(expr) => match self.eval_expr(expr)? {
838                            Value::List(sub) => vals.extend(sub),
839                            other => {
840                                return Err(IonError::type_err(
841                                    format!(
842                                        "{}{}",
843                                        ion_str!("spread requires a list, got "),
844                                        other.type_name()
845                                    ),
846                                    span.line,
847                                    span.col,
848                                )
849                                .into())
850                            }
851                        },
852                    }
853                }
854                Ok(Value::List(vals))
855            }
856            ExprKind::Dict(entries) => {
857                let mut map = IndexMap::new();
858                for entry in entries {
859                    match entry {
860                        DictEntry::KeyValue(k, v) => {
861                            let key = self.eval_expr(k)?;
862                            let key_str = match key {
863                                Value::Str(s) => s,
864                                _ => {
865                                    return Err(IonError::type_err(
866                                        ion_str!("dict keys must be strings").to_string(),
867                                        span.line,
868                                        span.col,
869                                    )
870                                    .into())
871                                }
872                            };
873                            let val = self.eval_expr(v)?;
874                            map.insert(key_str, val);
875                        }
876                        DictEntry::Spread(expr) => {
877                            let val = self.eval_expr(expr)?;
878                            match val {
879                                Value::Dict(other) => {
880                                    for (k, v) in other {
881                                        map.insert(k, v);
882                                    }
883                                }
884                                _ => {
885                                    return Err(IonError::type_err(
886                                        ion_str!("spread requires a dict").to_string(),
887                                        span.line,
888                                        span.col,
889                                    )
890                                    .into())
891                                }
892                            }
893                        }
894                    }
895                }
896                Ok(Value::Dict(map))
897            }
898            ExprKind::Tuple(items) => {
899                let mut vals = Vec::new();
900                for item in items {
901                    vals.push(self.eval_expr(item)?);
902                }
903                Ok(Value::Tuple(vals))
904            }
905
906            ExprKind::ListComp {
907                expr,
908                pattern,
909                iter,
910                cond,
911            } => {
912                let iter_val = self.eval_expr(iter)?;
913                let items = self.value_to_iter(&iter_val, span)?;
914                let mut result = Vec::new();
915                for item in items {
916                    self.env.push_scope();
917                    self.bind_pattern(pattern, &item, false, span)?;
918                    let include = if let Some(c) = cond {
919                        let v = self.eval_expr(c)?;
920                        v.is_truthy()
921                    } else {
922                        true
923                    };
924                    if include {
925                        result.push(self.eval_expr(expr)?);
926                    }
927                    self.env.pop_scope();
928                }
929                Ok(Value::List(result))
930            }
931            ExprKind::DictComp {
932                key,
933                value,
934                pattern,
935                iter,
936                cond,
937            } => {
938                let iter_val = self.eval_expr(iter)?;
939                let items = self.value_to_iter(&iter_val, span)?;
940                let mut map = IndexMap::new();
941                for item in items {
942                    self.env.push_scope();
943                    self.bind_pattern(pattern, &item, false, span)?;
944                    let include = if let Some(c) = cond {
945                        let v = self.eval_expr(c)?;
946                        v.is_truthy()
947                    } else {
948                        true
949                    };
950                    if include {
951                        let k = self.eval_expr(key)?;
952                        let k_str = match k {
953                            Value::Str(s) => s,
954                            _ => {
955                                return Err(IonError::type_err(
956                                    ion_str!("dict comp keys must be strings").to_string(),
957                                    span.line,
958                                    span.col,
959                                )
960                                .into())
961                            }
962                        };
963                        let v = self.eval_expr(value)?;
964                        map.insert(k_str, v);
965                    }
966                    self.env.pop_scope();
967                }
968                Ok(Value::Dict(map))
969            }
970
971            ExprKind::BinOp { left, op, right } => {
972                // Short-circuit for && and ||
973                if matches!(op, BinOp::And) {
974                    let l = self.eval_expr(left)?;
975                    if !l.is_truthy() {
976                        return Ok(Value::Bool(false));
977                    }
978                    let r = self.eval_expr(right)?;
979                    return Ok(Value::Bool(r.is_truthy()));
980                }
981                if matches!(op, BinOp::Or) {
982                    let l = self.eval_expr(left)?;
983                    if l.is_truthy() {
984                        return Ok(Value::Bool(true));
985                    }
986                    let r = self.eval_expr(right)?;
987                    return Ok(Value::Bool(r.is_truthy()));
988                }
989                let l = self.eval_expr(left)?;
990                let r = self.eval_expr(right)?;
991                self.eval_binop(*op, &l, &r, span)
992            }
993
994            ExprKind::UnaryOp { op, expr } => {
995                let val = self.eval_expr(expr)?;
996                match op {
997                    UnaryOp::Neg => match val {
998                        Value::Int(n) => Ok(Value::Int(-n)),
999                        Value::Float(n) => Ok(Value::Float(-n)),
1000                        _ => Err(IonError::type_err(
1001                            format!("{}{}", ion_str!("cannot negate "), val.type_name()),
1002                            span.line,
1003                            span.col,
1004                        )
1005                        .into()),
1006                    },
1007                    UnaryOp::Not => Ok(Value::Bool(!val.is_truthy())),
1008                }
1009            }
1010
1011            ExprKind::Try(inner) => {
1012                let val = self.eval_expr(inner)?;
1013                match val {
1014                    Value::Result(Ok(v)) => Ok(*v),
1015                    Value::Result(Err(e)) => {
1016                        Err(IonError::propagated_err(e.to_string(), span.line, span.col).into())
1017                    }
1018                    Value::Option(Some(v)) => Ok(*v),
1019                    Value::Option(None) => {
1020                        Err(IonError::propagated_none(span.line, span.col).into())
1021                    }
1022                    _ => Err(IonError::type_err(
1023                        format!(
1024                            "{}{}",
1025                            ion_str!("? applied to non-Result/Option: "),
1026                            val.type_name()
1027                        ),
1028                        span.line,
1029                        span.col,
1030                    )
1031                    .into()),
1032                }
1033            }
1034
1035            ExprKind::PipeOp { left, right } => {
1036                let lval = self.eval_expr(left)?;
1037                // right should be a Call — insert lval as first argument
1038                match &right.kind {
1039                    ExprKind::Call { func, args } => {
1040                        let mut new_args = vec![CallArg {
1041                            name: None,
1042                            value: Expr {
1043                                kind: ExprKind::Int(0),
1044                                span, // placeholder
1045                            },
1046                        }];
1047                        new_args.extend(args.iter().cloned());
1048                        let func_val = self.eval_expr(func)?;
1049                        let mut arg_vals = vec![lval];
1050                        for arg in args {
1051                            arg_vals.push(self.eval_expr(&arg.value)?);
1052                        }
1053                        self.call_value(&func_val, &arg_vals, span)
1054                    }
1055                    ExprKind::Ident(_) => {
1056                        // Bare function name, call with lval as only arg
1057                        let func_val = self.eval_expr(right)?;
1058                        self.call_value(&func_val, &[lval], span)
1059                    }
1060                    _ => Err(IonError::runtime(
1061                        ion_str!("right side of |> must be a function call").to_string(),
1062                        span.line,
1063                        span.col,
1064                    )
1065                    .into()),
1066                }
1067            }
1068
1069            ExprKind::FieldAccess { expr, field } => {
1070                let val = self.eval_expr(expr)?;
1071                self.field_access(&val, field, span)
1072            }
1073
1074            ExprKind::Index { expr, index } => {
1075                let val = self.eval_expr(expr)?;
1076                let idx = self.eval_expr(index)?;
1077                self.index_access(&val, &idx, span)
1078            }
1079
1080            ExprKind::Slice {
1081                expr,
1082                start,
1083                end,
1084                inclusive,
1085            } => {
1086                let val = self.eval_expr(expr)?;
1087                let s = match start {
1088                    Some(e) => Some(self.eval_expr(e)?),
1089                    None => None,
1090                };
1091                let e = match end {
1092                    Some(e) => Some(self.eval_expr(e)?),
1093                    None => None,
1094                };
1095                self.slice_access(&val, s.as_ref(), e.as_ref(), *inclusive, span)
1096            }
1097
1098            ExprKind::MethodCall { expr, method, args } => {
1099                let receiver = self.eval_expr(expr)?;
1100                let mut arg_vals = Vec::new();
1101                for arg in args {
1102                    arg_vals.push(self.eval_expr(&arg.value)?);
1103                }
1104                self.method_call(&receiver, method, &arg_vals, span)
1105            }
1106
1107            ExprKind::Call { func, args } => {
1108                let func_val = self.eval_expr(func)?;
1109                let has_named = args.iter().any(|a| a.name.is_some());
1110                if has_named {
1111                    let mut evaluated: Vec<(Option<String>, Value)> = Vec::new();
1112                    for arg in args {
1113                        evaluated.push((arg.name.clone(), self.eval_expr(&arg.value)?));
1114                    }
1115                    self.call_with_named(&func_val, evaluated, span)
1116                } else {
1117                    let mut arg_vals = Vec::new();
1118                    for arg in args {
1119                        arg_vals.push(self.eval_expr(&arg.value)?);
1120                    }
1121                    self.call_value(&func_val, &arg_vals, span)
1122                }
1123            }
1124
1125            ExprKind::Lambda { params, body } => {
1126                let captures = self.env.capture();
1127                let fn_params: Vec<Param> = params
1128                    .iter()
1129                    .map(|p| Param {
1130                        name: p.clone(),
1131                        default: None,
1132                    })
1133                    .collect();
1134                // Wrap body expr into a block with one ExprStmt
1135                let body_stmts = vec![Stmt {
1136                    kind: StmtKind::ExprStmt {
1137                        expr: (**body).clone(),
1138                        has_semi: false,
1139                    },
1140                    span,
1141                }];
1142                Ok(Value::Fn(IonFn::new(
1143                    ion_str!("<lambda>").to_string(),
1144                    fn_params,
1145                    body_stmts,
1146                    captures,
1147                )))
1148            }
1149
1150            ExprKind::If {
1151                cond,
1152                then_body,
1153                else_body,
1154            } => {
1155                let c = self.eval_expr(cond)?;
1156                self.env.push_scope();
1157                let result = if c.is_truthy() {
1158                    self.eval_stmts(then_body)
1159                } else if let Some(else_stmts) = else_body {
1160                    self.eval_stmts(else_stmts)
1161                } else {
1162                    Ok(Value::Unit)
1163                };
1164                self.env.pop_scope();
1165                result
1166            }
1167
1168            ExprKind::IfLet {
1169                pattern,
1170                expr,
1171                then_body,
1172                else_body,
1173            } => {
1174                let val = self.eval_expr(expr)?;
1175                if self.pattern_matches(pattern, &val) {
1176                    self.env.push_scope();
1177                    self.bind_pattern(pattern, &val, false, span)?;
1178                    let result = self.eval_stmts(then_body);
1179                    self.env.pop_scope();
1180                    result
1181                } else if let Some(else_stmts) = else_body {
1182                    self.env.push_scope();
1183                    let result = self.eval_stmts(else_stmts);
1184                    self.env.pop_scope();
1185                    result
1186                } else {
1187                    Ok(Value::Unit)
1188                }
1189            }
1190
1191            ExprKind::Match { expr, arms } => {
1192                let val = self.eval_expr(expr)?;
1193                for arm in arms {
1194                    if self.pattern_matches(&arm.pattern, &val) {
1195                        self.env.push_scope();
1196                        self.bind_pattern(&arm.pattern, &val, false, span)?;
1197                        if let Some(guard) = &arm.guard {
1198                            let guard_val = self.eval_expr(guard)?;
1199                            if !guard_val.is_truthy() {
1200                                self.env.pop_scope();
1201                                continue;
1202                            }
1203                        }
1204                        let result = self.eval_expr(&arm.body);
1205                        self.env.pop_scope();
1206                        return result;
1207                    }
1208                }
1209                Err(IonError::runtime(
1210                    ion_str!("non-exhaustive match").to_string(),
1211                    span.line,
1212                    span.col,
1213                )
1214                .into())
1215            }
1216
1217            ExprKind::Block(stmts) => {
1218                self.env.push_scope();
1219                let result = self.eval_stmts(stmts);
1220                self.env.pop_scope();
1221                result
1222            }
1223
1224            ExprKind::LoopExpr(body) => {
1225                let no_label: Option<String> = None;
1226                let result = loop {
1227                    self.env.push_scope();
1228                    match self.eval_stmts(body) {
1229                        Ok(_) => {}
1230                        Err(SignalOrError::Signal(Signal::Break {
1231                            label: sig_label,
1232                            value,
1233                        })) => {
1234                            self.env.pop_scope();
1235                            if signal_targets_loop(&sig_label, &no_label) {
1236                                break value;
1237                            }
1238                            return Err(Signal::Break {
1239                                label: sig_label,
1240                                value,
1241                            }
1242                            .into());
1243                        }
1244                        Err(SignalOrError::Signal(Signal::Continue { label: sig_label })) => {
1245                            self.env.pop_scope();
1246                            if signal_targets_loop(&sig_label, &no_label) {
1247                                continue;
1248                            }
1249                            return Err(Signal::Continue { label: sig_label }.into());
1250                        }
1251                        Err(e) => {
1252                            self.env.pop_scope();
1253                            return Err(e);
1254                        }
1255                    }
1256                    self.env.pop_scope();
1257                };
1258                Ok(result)
1259            }
1260
1261            ExprKind::TryCatch { body, var, handler } => {
1262                self.env.push_scope();
1263                let result = self.eval_stmts(body);
1264                self.env.pop_scope();
1265                match result {
1266                    Ok(v) => Ok(v),
1267                    Err(SignalOrError::Signal(s)) => {
1268                        // Signals (return/break/continue) pass through — not errors
1269                        Err(SignalOrError::Signal(s))
1270                    }
1271                    Err(SignalOrError::Error(e)) => {
1272                        // Catch the error: bind error message to `var`, run handler
1273                        self.env.push_scope();
1274                        self.env
1275                            .define(var.clone(), Value::Str(e.message.clone()), false);
1276                        let handler_result = self.eval_stmts(handler);
1277                        self.env.pop_scope();
1278                        handler_result
1279                    }
1280                }
1281            }
1282
1283            ExprKind::Range {
1284                start,
1285                end,
1286                inclusive,
1287            } => {
1288                let s = self.eval_expr(start)?;
1289                let e = self.eval_expr(end)?;
1290                match (&s, &e) {
1291                    (Value::Int(a), Value::Int(b)) => Ok(Value::Range {
1292                        start: *a,
1293                        end: *b,
1294                        inclusive: *inclusive,
1295                    }),
1296                    _ => Err(IonError::type_err(
1297                        ion_str!("range requires integer bounds").to_string(),
1298                        span.line,
1299                        span.col,
1300                    )
1301                    .into()),
1302                }
1303            }
1304
1305            ExprKind::StructConstruct {
1306                name,
1307                fields,
1308                spread,
1309            } => {
1310                let mut field_map = IndexMap::new();
1311                if let Some(spread_expr) = spread {
1312                    let spread_val = self.eval_expr(spread_expr)?;
1313                    match spread_val {
1314                        Value::HostStruct { fields: sf, .. } => {
1315                            for (k, v) in sf {
1316                                field_map.insert(k, v);
1317                            }
1318                        }
1319                        _ => {
1320                            return Err(IonError::type_err(
1321                                ion_str!("spread in struct constructor requires a struct")
1322                                    .to_string(),
1323                                span.line,
1324                                span.col,
1325                            )
1326                            .into())
1327                        }
1328                    }
1329                }
1330                for (fname, fexpr) in fields {
1331                    let val = self.eval_expr(fexpr)?;
1332                    field_map.insert(fname.clone(), val);
1333                }
1334                self.types
1335                    .construct_struct(name, field_map)
1336                    .map_err(|msg| IonError::runtime(msg, span.line, span.col).into())
1337            }
1338            ExprKind::EnumVariant { enum_name, variant } => self
1339                .types
1340                .construct_enum(enum_name, variant, vec![])
1341                .map_err(|msg| IonError::runtime(msg, span.line, span.col).into()),
1342            ExprKind::EnumVariantCall {
1343                enum_name,
1344                variant,
1345                args,
1346            } => {
1347                let mut vals = Vec::new();
1348                for arg in args {
1349                    vals.push(self.eval_expr(arg)?);
1350                }
1351                self.types
1352                    .construct_enum(enum_name, variant, vals)
1353                    .map_err(|msg| IonError::runtime(msg, span.line, span.col).into())
1354            }
1355
1356            // Concurrency
1357            #[cfg(feature = "concurrency")]
1358            ExprKind::AsyncBlock(body) => self.eval_async_block(body, span),
1359            #[cfg(feature = "concurrency")]
1360            ExprKind::SpawnExpr(expr) => self.eval_spawn(expr, span),
1361            #[cfg(feature = "concurrency")]
1362            ExprKind::AwaitExpr(expr) => self.eval_await(expr, span),
1363            #[cfg(feature = "concurrency")]
1364            ExprKind::SelectExpr(branches) => self.eval_select(branches, span),
1365
1366            #[cfg(not(feature = "concurrency"))]
1367            ExprKind::AsyncBlock(_)
1368            | ExprKind::SpawnExpr(_)
1369            | ExprKind::AwaitExpr(_)
1370            | ExprKind::SelectExpr(_) => Err(IonError::runtime(
1371                ion_str!("concurrency features require the 'concurrency' cargo feature")
1372                    .to_string(),
1373                span.line,
1374                span.col,
1375            )
1376            .into()),
1377        }
1378    }
1379
1380    // --- Helpers ---
1381
1382    fn eval_binop(&self, op: BinOp, l: &Value, r: &Value, span: Span) -> SignalResult {
1383        match op {
1384            BinOp::Add => match (l, r) {
1385                (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a + b)),
1386                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
1387                (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
1388                (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a + *b as f64)),
1389                (Value::Str(a), Value::Str(b)) => Ok(Value::Str(format!("{}{}", a, b))),
1390                (Value::Bytes(a), Value::Bytes(b)) => {
1391                    let mut result = a.clone();
1392                    result.extend(b);
1393                    Ok(Value::Bytes(result))
1394                }
1395                _ => Err(self.type_mismatch_err(ion_str!("+"), l, r, span)),
1396            },
1397            BinOp::Sub => match (l, r) {
1398                (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a - b)),
1399                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
1400                (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 - b)),
1401                (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a - *b as f64)),
1402                _ => Err(self.type_mismatch_err(ion_str!("-"), l, r, span)),
1403            },
1404            BinOp::Mul => match (l, r) {
1405                (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a * b)),
1406                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
1407                (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 * b)),
1408                (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a * *b as f64)),
1409                (Value::Str(s), Value::Int(n)) | (Value::Int(n), Value::Str(s)) => {
1410                    Ok(Value::Str(s.repeat(*n as usize)))
1411                }
1412                _ => Err(self.type_mismatch_err(ion_str!("*"), l, r, span)),
1413            },
1414            BinOp::Div => match (l, r) {
1415                (Value::Int(a), Value::Int(b)) => {
1416                    if *b == 0 {
1417                        Err(IonError::runtime(
1418                            ion_str!("division by zero").to_string(),
1419                            span.line,
1420                            span.col,
1421                        )
1422                        .into())
1423                    } else {
1424                        Ok(Value::Int(a / b))
1425                    }
1426                }
1427                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a / b)),
1428                (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 / b)),
1429                (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a / *b as f64)),
1430                _ => Err(self.type_mismatch_err(ion_str!("/"), l, r, span)),
1431            },
1432            BinOp::Mod => match (l, r) {
1433                (Value::Int(a), Value::Int(b)) => {
1434                    if *b == 0 {
1435                        Err(IonError::runtime(
1436                            ion_str!("modulo by zero").to_string(),
1437                            span.line,
1438                            span.col,
1439                        )
1440                        .into())
1441                    } else {
1442                        Ok(Value::Int(a % b))
1443                    }
1444                }
1445                _ => Err(self.type_mismatch_err(ion_str!("%"), l, r, span)),
1446            },
1447            BinOp::Eq => Ok(Value::Bool(l == r)),
1448            BinOp::Ne => Ok(Value::Bool(l != r)),
1449            BinOp::Lt => self.compare_values(l, r, span, |o| o == std::cmp::Ordering::Less),
1450            BinOp::Gt => self.compare_values(l, r, span, |o| o == std::cmp::Ordering::Greater),
1451            BinOp::Le => self.compare_values(l, r, span, |o| o != std::cmp::Ordering::Greater),
1452            BinOp::Ge => self.compare_values(l, r, span, |o| o != std::cmp::Ordering::Less),
1453            BinOp::And | BinOp::Or => unreachable!(), // handled in eval_expr
1454            BinOp::BitAnd => match (l, r) {
1455                (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a & b)),
1456                _ => Err(self.type_mismatch_err(ion_str!("&"), l, r, span)),
1457            },
1458            BinOp::BitOr => match (l, r) {
1459                (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a | b)),
1460                _ => Err(self.type_mismatch_err(ion_str!("|"), l, r, span)),
1461            },
1462            BinOp::BitXor => match (l, r) {
1463                (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a ^ b)),
1464                _ => Err(self.type_mismatch_err(ion_str!("^"), l, r, span)),
1465            },
1466            BinOp::Shl => match (l, r) {
1467                (Value::Int(a), Value::Int(b)) if (0..64).contains(b) => Ok(Value::Int(a << b)),
1468                (Value::Int(_), Value::Int(b)) => Err(IonError::runtime(
1469                    format!("shift count {} is out of range 0..64", b),
1470                    span.line,
1471                    span.col,
1472                )
1473                .into()),
1474                _ => Err(self.type_mismatch_err(ion_str!("<<"), l, r, span)),
1475            },
1476            BinOp::Shr => match (l, r) {
1477                (Value::Int(a), Value::Int(b)) if (0..64).contains(b) => Ok(Value::Int(a >> b)),
1478                (Value::Int(_), Value::Int(b)) => Err(IonError::runtime(
1479                    format!("shift count {} is out of range 0..64", b),
1480                    span.line,
1481                    span.col,
1482                )
1483                .into()),
1484                _ => Err(self.type_mismatch_err(ion_str!(">>"), l, r, span)),
1485            },
1486        }
1487    }
1488
1489    fn compare_values(
1490        &self,
1491        l: &Value,
1492        r: &Value,
1493        span: Span,
1494        f: impl Fn(std::cmp::Ordering) -> bool,
1495    ) -> SignalResult {
1496        let ord = match (l, r) {
1497            (Value::Int(a), Value::Int(b)) => a.cmp(b),
1498            (Value::Float(a), Value::Float(b)) => {
1499                a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1500            }
1501            (Value::Int(a), Value::Float(b)) => (*a as f64)
1502                .partial_cmp(b)
1503                .unwrap_or(std::cmp::Ordering::Equal),
1504            (Value::Float(a), Value::Int(b)) => a
1505                .partial_cmp(&(*b as f64))
1506                .unwrap_or(std::cmp::Ordering::Equal),
1507            (Value::Str(a), Value::Str(b)) => a.cmp(b),
1508            _ => return Err(self.type_mismatch_err(ion_str!("compare"), l, r, span)),
1509        };
1510        Ok(Value::Bool(f(ord)))
1511    }
1512
1513    fn type_mismatch_err(
1514        &self,
1515        op: impl std::fmt::Display,
1516        l: &Value,
1517        r: &Value,
1518        span: Span,
1519    ) -> SignalOrError {
1520        IonError::type_err(
1521            format!(
1522                "{}{}{}{}{}{}",
1523                ion_str!("cannot apply '"),
1524                op,
1525                ion_str!("' to "),
1526                l.type_name(),
1527                ion_str!(" and "),
1528                r.type_name(),
1529            ),
1530            span.line,
1531            span.col,
1532        )
1533        .into()
1534    }
1535
1536    fn apply_compound_op(
1537        &self,
1538        op: AssignOp,
1539        lhs: &Value,
1540        rhs: &Value,
1541        span: Span,
1542    ) -> SignalResult {
1543        match op {
1544            AssignOp::PlusEq => self.eval_binop(BinOp::Add, lhs, rhs, span),
1545            AssignOp::MinusEq => self.eval_binop(BinOp::Sub, lhs, rhs, span),
1546            AssignOp::StarEq => self.eval_binop(BinOp::Mul, lhs, rhs, span),
1547            AssignOp::SlashEq => self.eval_binop(BinOp::Div, lhs, rhs, span),
1548            AssignOp::Eq => unreachable!(),
1549        }
1550    }
1551
1552    fn field_access(&self, val: &Value, field: &str, span: Span) -> SignalResult {
1553        match val {
1554            Value::Dict(map) => Ok(match map.get(field) {
1555                Some(v) => v.clone(),
1556                None => Value::Option(None),
1557            }),
1558            Value::HostStruct { fields, .. } => Ok(match fields.get(field) {
1559                Some(v) => v.clone(),
1560                None => {
1561                    return Err(IonError::type_err(
1562                        format!(
1563                            "{}{}{}",
1564                            ion_str!("no field '"),
1565                            field,
1566                            ion_str!("' on struct")
1567                        ),
1568                        span.line,
1569                        span.col,
1570                    )
1571                    .into())
1572                }
1573            }),
1574            _ => Err(IonError::type_err(
1575                format!("{}{}", ion_str!("cannot access field on "), val.type_name()),
1576                span.line,
1577                span.col,
1578            )
1579            .into()),
1580        }
1581    }
1582
1583    fn index_access(&self, val: &Value, idx: &Value, span: Span) -> SignalResult {
1584        match (val, idx) {
1585            (Value::List(items), Value::Int(i)) => {
1586                let index = if *i < 0 { items.len() as i64 + i } else { *i } as usize;
1587                items.get(index).cloned().ok_or_else(|| {
1588                    IonError::runtime(
1589                        ion_str!("list index out of bounds").to_string(),
1590                        span.line,
1591                        span.col,
1592                    )
1593                    .into()
1594                })
1595            }
1596            (Value::Dict(map), Value::Str(key)) => Ok(match map.get(key.as_str()) {
1597                Some(v) => v.clone(),
1598                None => Value::Option(None),
1599            }),
1600            (Value::Bytes(bytes), Value::Int(i)) => {
1601                let index = if *i < 0 { bytes.len() as i64 + i } else { *i } as usize;
1602                bytes
1603                    .get(index)
1604                    .map(|&b| Value::Int(b as i64))
1605                    .ok_or_else(|| {
1606                        IonError::runtime(
1607                            ion_str!("bytes index out of bounds").to_string(),
1608                            span.line,
1609                            span.col,
1610                        )
1611                        .into()
1612                    })
1613            }
1614            (Value::Str(s), Value::Int(i)) => {
1615                let index = if *i < 0 {
1616                    s.chars().count() as i64 + i
1617                } else {
1618                    *i
1619                } as usize;
1620                s.chars()
1621                    .nth(index)
1622                    .map(|c| Value::Str(c.to_string()))
1623                    .ok_or_else(|| {
1624                        IonError::runtime(
1625                            ion_str!("string index out of bounds").to_string(),
1626                            span.line,
1627                            span.col,
1628                        )
1629                        .into()
1630                    })
1631            }
1632            (Value::Tuple(items), Value::Int(i)) => {
1633                let index = *i as usize;
1634                items.get(index).cloned().ok_or_else(|| {
1635                    IonError::runtime(
1636                        ion_str!("tuple index out of bounds").to_string(),
1637                        span.line,
1638                        span.col,
1639                    )
1640                    .into()
1641                })
1642            }
1643            _ => Err(IonError::type_err(
1644                format!(
1645                    "{}{}{}{}",
1646                    ion_str!("cannot index "),
1647                    val.type_name(),
1648                    ion_str!(" with "),
1649                    idx.type_name(),
1650                ),
1651                span.line,
1652                span.col,
1653            )
1654            .into()),
1655        }
1656    }
1657
1658    fn slice_access(
1659        &self,
1660        val: &Value,
1661        start: Option<&Value>,
1662        end: Option<&Value>,
1663        inclusive: bool,
1664        span: Span,
1665    ) -> SignalResult {
1666        let get_idx = |v: Option<&Value>, default: i64| -> Result<i64, SignalOrError> {
1667            match v {
1668                Some(Value::Int(n)) => Ok(*n),
1669                None => Ok(default),
1670                Some(other) => Err(IonError::type_err(
1671                    format!(
1672                        "{}{}",
1673                        ion_str!("slice index must be int, got "),
1674                        other.type_name()
1675                    ),
1676                    span.line,
1677                    span.col,
1678                )
1679                .into()),
1680            }
1681        };
1682
1683        match val {
1684            Value::List(items) => {
1685                let len = items.len() as i64;
1686                let s = get_idx(start, 0)?;
1687                let e = get_idx(end, len)?;
1688                let s = s.max(0).min(len) as usize;
1689                let e = if inclusive {
1690                    (e + 1).max(0).min(len) as usize
1691                } else {
1692                    e.max(0).min(len) as usize
1693                };
1694                Ok(Value::List(items[s..e].to_vec()))
1695            }
1696            Value::Str(string) => {
1697                let chars: Vec<char> = string.chars().collect();
1698                let len = chars.len() as i64;
1699                let s = get_idx(start, 0)?;
1700                let e = get_idx(end, len)?;
1701                let s = s.max(0).min(len) as usize;
1702                let e = if inclusive {
1703                    (e + 1).max(0).min(len) as usize
1704                } else {
1705                    e.max(0).min(len) as usize
1706                };
1707                Ok(Value::Str(chars[s..e].iter().collect()))
1708            }
1709            Value::Bytes(bytes) => {
1710                let len = bytes.len() as i64;
1711                let s = get_idx(start, 0)?;
1712                let e = get_idx(end, len)?;
1713                let s = s.max(0).min(len) as usize;
1714                let e = if inclusive {
1715                    (e + 1).max(0).min(len) as usize
1716                } else {
1717                    e.max(0).min(len) as usize
1718                };
1719                Ok(Value::Bytes(bytes[s..e].to_vec()))
1720            }
1721            _ => Err(IonError::type_err(
1722                format!("{}{}", ion_str!("cannot slice "), val.type_name()),
1723                span.line,
1724                span.col,
1725            )
1726            .into()),
1727        }
1728    }
1729
1730    fn method_call(
1731        &mut self,
1732        receiver: &Value,
1733        method: &str,
1734        args: &[Value],
1735        span: Span,
1736    ) -> SignalResult {
1737        // Universal methods available on all types
1738        if method == "to_string" {
1739            return Ok(Value::Str(format!("{}", receiver)));
1740        }
1741        match receiver {
1742            Value::List(items) => self.list_method(items, method, args, span),
1743            Value::Tuple(items) => self.tuple_method(items, method, args, span),
1744            Value::Str(s) => self.string_method(s, method, args, span),
1745            Value::Bytes(b) => self.bytes_method(b, method, args, span),
1746            Value::Dict(map) => match method {
1747                "map" => {
1748                    let func = &args[0];
1749                    let mut result = indexmap::IndexMap::new();
1750                    for (k, v) in map {
1751                        let mapped =
1752                            self.call_value(func, &[Value::Str(k.clone()), v.clone()], span)?;
1753                        result.insert(k.clone(), mapped);
1754                    }
1755                    Ok(Value::Dict(result))
1756                }
1757                "filter" => {
1758                    let func = &args[0];
1759                    let mut result = indexmap::IndexMap::new();
1760                    for (k, v) in map {
1761                        let keep =
1762                            self.call_value(func, &[Value::Str(k.clone()), v.clone()], span)?;
1763                        if keep.is_truthy() {
1764                            result.insert(k.clone(), v.clone());
1765                        }
1766                    }
1767                    Ok(Value::Dict(result))
1768                }
1769                _ => self.dict_method(map, method, args, span),
1770            },
1771            Value::Set(items) => self.set_method(items, method, args, span),
1772            Value::Option(opt) => self.option_method(opt.clone(), method, args, span),
1773            Value::Result(res) => self.result_method(res.clone(), method, args, span),
1774            Value::Range {
1775                start,
1776                end,
1777                inclusive,
1778            } => match method {
1779                "len" => Ok(Value::Int(Value::range_len(*start, *end, *inclusive))),
1780                "contains" => {
1781                    let val = args[0]
1782                        .as_int()
1783                        .ok_or_else(|| {
1784                            IonError::type_err(
1785                                ion_str!("range.contains requires int"),
1786                                span.line,
1787                                span.col,
1788                            )
1789                        })
1790                        .map_err(SignalOrError::from)?;
1791                    let in_range = if *inclusive {
1792                        val >= *start && val <= *end
1793                    } else {
1794                        val >= *start && val < *end
1795                    };
1796                    Ok(Value::Bool(in_range))
1797                }
1798                "to_list" => Ok(Value::List(Value::range_to_list(*start, *end, *inclusive))),
1799                // For other list-like methods, materialize and delegate
1800                _ => {
1801                    let items = Value::range_to_list(*start, *end, *inclusive);
1802                    self.list_method(&items, method, args, span)
1803                }
1804            },
1805            Value::Cell(cell) => self.cell_method(cell, method, args, span),
1806            #[cfg(feature = "concurrency")]
1807            Value::Task(handle) => self.task_method(handle, method, args, span),
1808            #[cfg(feature = "concurrency")]
1809            Value::Channel(ch) => self.channel_method(ch, method, args, span),
1810            _ => Err(IonError::type_err(
1811                format!(
1812                    "{}{}{}{}",
1813                    ion_str!("no method '"),
1814                    method,
1815                    ion_str!("' on "),
1816                    receiver.type_name(),
1817                ),
1818                span.line,
1819                span.col,
1820            )
1821            .into()),
1822        }
1823    }
1824
1825    fn list_method(
1826        &mut self,
1827        items: &[Value],
1828        method: &str,
1829        args: &[Value],
1830        span: Span,
1831    ) -> SignalResult {
1832        match method {
1833            "len" => Ok(Value::Int(items.len() as i64)),
1834            "push" => {
1835                let mut new_list = items.to_vec();
1836                new_list.push(args[0].clone());
1837                Ok(Value::List(new_list))
1838            }
1839            "pop" => {
1840                if items.is_empty() {
1841                    Ok(Value::Tuple(vec![Value::List(vec![]), Value::Option(None)]))
1842                } else {
1843                    let mut new_list = items.to_vec();
1844                    let popped = new_list.pop().unwrap();
1845                    Ok(Value::Tuple(vec![
1846                        Value::List(new_list),
1847                        Value::Option(Some(Box::new(popped))),
1848                    ]))
1849                }
1850            }
1851            "map" => {
1852                let func = &args[0];
1853                let mut result = Vec::new();
1854                for item in items {
1855                    result.push(self.call_value(func, std::slice::from_ref(item), span)?);
1856                }
1857                Ok(Value::List(result))
1858            }
1859            "filter" => {
1860                let func = &args[0];
1861                let mut result = Vec::new();
1862                for item in items {
1863                    let keep = self.call_value(func, std::slice::from_ref(item), span)?;
1864                    if keep.is_truthy() {
1865                        result.push(item.clone());
1866                    }
1867                }
1868                Ok(Value::List(result))
1869            }
1870            "fold" => {
1871                let mut acc = args[0].clone();
1872                let func = &args[1];
1873                for item in items {
1874                    acc = self.call_value(func, &[acc, item.clone()], span)?;
1875                }
1876                Ok(acc)
1877            }
1878            "flat_map" => {
1879                let func = &args[0];
1880                let mut result = Vec::new();
1881                for item in items {
1882                    let mapped = self.call_value(func, std::slice::from_ref(item), span)?;
1883                    match mapped {
1884                        Value::List(sub) => result.extend(sub),
1885                        other => result.push(other),
1886                    }
1887                }
1888                Ok(Value::List(result))
1889            }
1890            "any" => {
1891                let func = &args[0];
1892                for item in items {
1893                    let v = self.call_value(func, std::slice::from_ref(item), span)?;
1894                    if v.is_truthy() {
1895                        return Ok(Value::Bool(true));
1896                    }
1897                }
1898                Ok(Value::Bool(false))
1899            }
1900            "all" => {
1901                let func = &args[0];
1902                for item in items {
1903                    let v = self.call_value(func, std::slice::from_ref(item), span)?;
1904                    if !v.is_truthy() {
1905                        return Ok(Value::Bool(false));
1906                    }
1907                }
1908                Ok(Value::Bool(true))
1909            }
1910            "first" => Ok(match items.first() {
1911                Some(v) => Value::Option(Some(Box::new(v.clone()))),
1912                None => Value::Option(None),
1913            }),
1914            "last" => Ok(match items.last() {
1915                Some(v) => Value::Option(Some(Box::new(v.clone()))),
1916                None => Value::Option(None),
1917            }),
1918            "reverse" => {
1919                let mut rev = items.to_vec();
1920                rev.reverse();
1921                Ok(Value::List(rev))
1922            }
1923            "sort" => {
1924                if !items.is_empty() {
1925                    let first_type = std::mem::discriminant(&items[0]);
1926                    for item in items.iter().skip(1) {
1927                        if std::mem::discriminant(item) != first_type {
1928                            return Err(IonError::type_err(
1929                                ion_str!("sort() requires all elements to be the same type")
1930                                    .to_string(),
1931                                span.line,
1932                                span.col,
1933                            )
1934                            .into());
1935                        }
1936                    }
1937                }
1938                let mut sorted = items.to_vec();
1939                sorted.sort_by(|a, b| match (a, b) {
1940                    (Value::Int(x), Value::Int(y)) => x.cmp(y),
1941                    (Value::Float(x), Value::Float(y)) => {
1942                        x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
1943                    }
1944                    (Value::Str(x), Value::Str(y)) => x.cmp(y),
1945                    _ => std::cmp::Ordering::Equal,
1946                });
1947                Ok(Value::List(sorted))
1948            }
1949            "sort_by" => {
1950                let func = &args[0];
1951                let mut sorted = items.to_vec();
1952                let mut err: Option<SignalOrError> = None;
1953                let func_clone = func.clone();
1954                sorted.sort_by(|a, b| {
1955                    if err.is_some() {
1956                        return std::cmp::Ordering::Equal;
1957                    }
1958                    match self.call_value(&func_clone, &[a.clone(), b.clone()], span) {
1959                        Ok(Value::Int(n)) => {
1960                            if n < 0 {
1961                                std::cmp::Ordering::Less
1962                            } else if n > 0 {
1963                                std::cmp::Ordering::Greater
1964                            } else {
1965                                std::cmp::Ordering::Equal
1966                            }
1967                        }
1968                        Ok(_) => {
1969                            err = Some(
1970                                IonError::type_err(
1971                                    ion_str!("sort_by comparator must return an int").to_string(),
1972                                    span.line,
1973                                    span.col,
1974                                )
1975                                .into(),
1976                            );
1977                            std::cmp::Ordering::Equal
1978                        }
1979                        Err(e) => {
1980                            err = Some(e);
1981                            std::cmp::Ordering::Equal
1982                        }
1983                    }
1984                });
1985                if let Some(e) = err {
1986                    return Err(e);
1987                }
1988                Ok(Value::List(sorted))
1989            }
1990            "flatten" => {
1991                let mut result = Vec::new();
1992                for item in items {
1993                    if let Value::List(inner) = item {
1994                        result.extend(inner.iter().cloned());
1995                    } else {
1996                        result.push(item.clone());
1997                    }
1998                }
1999                Ok(Value::List(result))
2000            }
2001            "zip" => {
2002                if let Value::List(other) = &args[0] {
2003                    let result: Vec<Value> = items
2004                        .iter()
2005                        .zip(other.iter())
2006                        .map(|(a, b)| Value::Tuple(vec![a.clone(), b.clone()]))
2007                        .collect();
2008                    Ok(Value::List(result))
2009                } else {
2010                    Err(IonError::type_err(
2011                        ion_str!("zip requires a list argument").to_string(),
2012                        span.line,
2013                        span.col,
2014                    )
2015                    .into())
2016                }
2017            }
2018            "contains" => {
2019                let target = &args[0];
2020                Ok(Value::Bool(items.iter().any(|v| v == target)))
2021            }
2022            "join" => {
2023                let sep = if args.is_empty() {
2024                    String::new()
2025                } else {
2026                    args[0]
2027                        .as_str()
2028                        .ok_or_else(|| {
2029                            IonError::type_err(
2030                                ion_str!("join separator must be a string").to_string(),
2031                                span.line,
2032                                span.col,
2033                            )
2034                        })?
2035                        .to_string()
2036                };
2037                let parts: Vec<String> = items.iter().map(|v| v.to_string()).collect();
2038                Ok(Value::Str(parts.join(&sep)))
2039            }
2040            "enumerate" => Ok(Value::List(
2041                items
2042                    .iter()
2043                    .enumerate()
2044                    .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
2045                    .collect(),
2046            )),
2047            "is_empty" => Ok(Value::Bool(items.is_empty())),
2048            "index" => {
2049                let target = &args[0];
2050                Ok(match items.iter().position(|v| v == target) {
2051                    Some(i) => Value::Option(Some(Box::new(Value::Int(i as i64)))),
2052                    None => Value::Option(None),
2053                })
2054            }
2055            "count" => {
2056                let target = &args[0];
2057                Ok(Value::Int(
2058                    items.iter().filter(|v| *v == target).count() as i64
2059                ))
2060            }
2061            "slice" => {
2062                let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2063                let end = args
2064                    .get(1)
2065                    .and_then(|a| a.as_int())
2066                    .map(|n| n as usize)
2067                    .unwrap_or(items.len());
2068                let start = start.min(items.len());
2069                let end = end.min(items.len());
2070                Ok(Value::List(items[start..end].to_vec()))
2071            }
2072            "dedup" => {
2073                let mut result: Vec<Value> = Vec::new();
2074                for item in items {
2075                    if result.last() != Some(item) {
2076                        result.push(item.clone());
2077                    }
2078                }
2079                Ok(Value::List(result))
2080            }
2081            "unique" => {
2082                let mut seen = Vec::new();
2083                let mut result = Vec::new();
2084                for item in items {
2085                    if !seen.contains(item) {
2086                        seen.push(item.clone());
2087                        result.push(item.clone());
2088                    }
2089                }
2090                Ok(Value::List(result))
2091            }
2092            "min" => {
2093                if items.is_empty() {
2094                    return Ok(Value::Option(None));
2095                }
2096                let mut min = &items[0];
2097                for item in items.iter().skip(1) {
2098                    match (min, item) {
2099                        (Value::Int(a), Value::Int(b)) => {
2100                            if b < a {
2101                                min = item;
2102                            }
2103                        }
2104                        (Value::Float(a), Value::Float(b)) => {
2105                            if b < a {
2106                                min = item;
2107                            }
2108                        }
2109                        (Value::Str(a), Value::Str(b)) => {
2110                            if b < a {
2111                                min = item;
2112                            }
2113                        }
2114                        _ => {
2115                            return Err(IonError::type_err(
2116                                ion_str!("min() requires homogeneous comparable elements")
2117                                    .to_string(),
2118                                span.line,
2119                                span.col,
2120                            )
2121                            .into())
2122                        }
2123                    }
2124                }
2125                Ok(Value::Option(Some(Box::new(min.clone()))))
2126            }
2127            "max" => {
2128                if items.is_empty() {
2129                    return Ok(Value::Option(None));
2130                }
2131                let mut max = &items[0];
2132                for item in items.iter().skip(1) {
2133                    match (max, item) {
2134                        (Value::Int(a), Value::Int(b)) => {
2135                            if b > a {
2136                                max = item;
2137                            }
2138                        }
2139                        (Value::Float(a), Value::Float(b)) => {
2140                            if b > a {
2141                                max = item;
2142                            }
2143                        }
2144                        (Value::Str(a), Value::Str(b)) => {
2145                            if b > a {
2146                                max = item;
2147                            }
2148                        }
2149                        _ => {
2150                            return Err(IonError::type_err(
2151                                ion_str!("max() requires homogeneous comparable elements")
2152                                    .to_string(),
2153                                span.line,
2154                                span.col,
2155                            )
2156                            .into())
2157                        }
2158                    }
2159                }
2160                Ok(Value::Option(Some(Box::new(max.clone()))))
2161            }
2162            "sum" => {
2163                let mut int_sum: i64 = 0;
2164                let mut float_sum: f64 = 0.0;
2165                let mut has_float = false;
2166                for item in items {
2167                    match item {
2168                        Value::Int(n) => int_sum += n,
2169                        Value::Float(f) => {
2170                            has_float = true;
2171                            float_sum += f;
2172                        }
2173                        _ => {
2174                            return Err(IonError::type_err(
2175                                ion_str!("sum() requires numeric elements").to_string(),
2176                                span.line,
2177                                span.col,
2178                            )
2179                            .into())
2180                        }
2181                    }
2182                }
2183                if has_float {
2184                    Ok(Value::Float(float_sum + int_sum as f64))
2185                } else {
2186                    Ok(Value::Int(int_sum))
2187                }
2188            }
2189            "window" => {
2190                let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2191                    IonError::type_err(
2192                        ion_str!("window requires int argument").to_string(),
2193                        span.line,
2194                        span.col,
2195                    )
2196                })? as usize;
2197                if n == 0 {
2198                    return Err(IonError::runtime(
2199                        ion_str!("window size must be > 0").to_string(),
2200                        span.line,
2201                        span.col,
2202                    )
2203                    .into());
2204                }
2205                let result: Vec<Value> =
2206                    items.windows(n).map(|w| Value::List(w.to_vec())).collect();
2207                Ok(Value::List(result))
2208            }
2209            "chunk" => {
2210                let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2211                    IonError::type_err(
2212                        ion_str!("chunk requires int argument").to_string(),
2213                        span.line,
2214                        span.col,
2215                    )
2216                })? as usize;
2217                if n == 0 {
2218                    return Err(IonError::type_err(
2219                        ion_str!("chunk size must be > 0").to_string(),
2220                        span.line,
2221                        span.col,
2222                    )
2223                    .into());
2224                }
2225                let result: Vec<Value> = items.chunks(n).map(|c| Value::List(c.to_vec())).collect();
2226                Ok(Value::List(result))
2227            }
2228            "reduce" => {
2229                if items.is_empty() {
2230                    return Err(IonError::type_err(
2231                        ion_str!("reduce on empty list").to_string(),
2232                        span.line,
2233                        span.col,
2234                    )
2235                    .into());
2236                }
2237                let func = &args[0];
2238                let mut acc = items[0].clone();
2239                for item in items.iter().skip(1) {
2240                    acc = self.call_value(func, &[acc, item.clone()], span)?;
2241                }
2242                Ok(acc)
2243            }
2244            _ => Err(IonError::type_err(
2245                format!(
2246                    "{}{}{}",
2247                    ion_str!("no method '"),
2248                    method,
2249                    ion_str!("' on list")
2250                ),
2251                span.line,
2252                span.col,
2253            )
2254            .into()),
2255        }
2256    }
2257
2258    fn set_method(
2259        &self,
2260        items: &[Value],
2261        method: &str,
2262        args: &[Value],
2263        span: Span,
2264    ) -> SignalResult {
2265        match method {
2266            "len" => Ok(Value::Int(items.len() as i64)),
2267            "contains" => {
2268                let target = &args[0];
2269                Ok(Value::Bool(items.iter().any(|v| v == target)))
2270            }
2271            "is_empty" => Ok(Value::Bool(items.is_empty())),
2272            "add" => {
2273                let val = &args[0];
2274                let mut new = items.to_vec();
2275                if !new.iter().any(|v| v == val) {
2276                    new.push(val.clone());
2277                }
2278                Ok(Value::Set(new))
2279            }
2280            "remove" => {
2281                let val = &args[0];
2282                let new: Vec<Value> = items.iter().filter(|v| *v != val).cloned().collect();
2283                Ok(Value::Set(new))
2284            }
2285            "union" => {
2286                if let Value::Set(other) = &args[0] {
2287                    let mut new = items.to_vec();
2288                    for v in other {
2289                        if !new.iter().any(|x| x == v) {
2290                            new.push(v.clone());
2291                        }
2292                    }
2293                    Ok(Value::Set(new))
2294                } else {
2295                    Err(IonError::type_err(
2296                        ion_str!("union requires a set argument").to_string(),
2297                        span.line,
2298                        span.col,
2299                    )
2300                    .into())
2301                }
2302            }
2303            "intersection" => {
2304                if let Value::Set(other) = &args[0] {
2305                    let new: Vec<Value> = items
2306                        .iter()
2307                        .filter(|v| other.iter().any(|x| x == *v))
2308                        .cloned()
2309                        .collect();
2310                    Ok(Value::Set(new))
2311                } else {
2312                    Err(IonError::type_err(
2313                        ion_str!("intersection requires a set argument").to_string(),
2314                        span.line,
2315                        span.col,
2316                    )
2317                    .into())
2318                }
2319            }
2320            "difference" => {
2321                if let Value::Set(other) = &args[0] {
2322                    let new: Vec<Value> = items
2323                        .iter()
2324                        .filter(|v| !other.iter().any(|x| x == *v))
2325                        .cloned()
2326                        .collect();
2327                    Ok(Value::Set(new))
2328                } else {
2329                    Err(IonError::type_err(
2330                        ion_str!("difference requires a set argument").to_string(),
2331                        span.line,
2332                        span.col,
2333                    )
2334                    .into())
2335                }
2336            }
2337            "to_list" => Ok(Value::List(items.to_vec())),
2338            _ => Err(IonError::type_err(
2339                format!(
2340                    "{}{}{}",
2341                    ion_str!("no method '"),
2342                    method,
2343                    ion_str!("' on set")
2344                ),
2345                span.line,
2346                span.col,
2347            )
2348            .into()),
2349        }
2350    }
2351
2352    fn cell_method(
2353        &mut self,
2354        cell: &std::sync::Arc<std::sync::Mutex<Value>>,
2355        method: &str,
2356        args: &[Value],
2357        span: Span,
2358    ) -> SignalResult {
2359        match method {
2360            "get" => {
2361                let inner = cell.lock().unwrap();
2362                Ok(inner.clone())
2363            }
2364            "set" => {
2365                if args.is_empty() {
2366                    return Err(IonError::runtime(
2367                        ion_str!("cell.set() requires 1 argument").to_string(),
2368                        span.line,
2369                        span.col,
2370                    )
2371                    .into());
2372                }
2373                let mut inner = cell.lock().unwrap();
2374                *inner = args[0].clone();
2375                Ok(Value::Unit)
2376            }
2377            "update" => {
2378                if args.is_empty() {
2379                    return Err(IonError::runtime(
2380                        ion_str!("cell.update() requires a function argument").to_string(),
2381                        span.line,
2382                        span.col,
2383                    )
2384                    .into());
2385                }
2386                let current = { cell.lock().unwrap().clone() };
2387                let new_val = self.call_value(&args[0], &[current], span)?;
2388                let mut inner = cell.lock().unwrap();
2389                *inner = new_val.clone();
2390                Ok(new_val)
2391            }
2392            _ => Err(IonError::type_err(
2393                format!(
2394                    "{}{}{}",
2395                    ion_str!("no method '"),
2396                    method,
2397                    ion_str!("' on cell"),
2398                ),
2399                span.line,
2400                span.col,
2401            )
2402            .into()),
2403        }
2404    }
2405
2406    fn tuple_method(
2407        &self,
2408        items: &[Value],
2409        method: &str,
2410        args: &[Value],
2411        span: Span,
2412    ) -> SignalResult {
2413        match method {
2414            "len" => Ok(Value::Int(items.len() as i64)),
2415            "contains" => {
2416                let target = &args[0];
2417                Ok(Value::Bool(items.iter().any(|v| v == target)))
2418            }
2419            "to_list" => Ok(Value::List(items.to_vec())),
2420            _ => Err(IonError::type_err(
2421                format!(
2422                    "{}{}{}",
2423                    ion_str!("no method '"),
2424                    method,
2425                    ion_str!("' on tuple")
2426                ),
2427                span.line,
2428                span.col,
2429            )
2430            .into()),
2431        }
2432    }
2433
2434    fn string_method(&self, s: &str, method: &str, args: &[Value], span: Span) -> SignalResult {
2435        match method {
2436            "len" => Ok(Value::Int(s.len() as i64)),
2437            "contains" => match &args[0] {
2438                Value::Str(sub) => Ok(Value::Bool(s.contains(sub.as_str()))),
2439                Value::Int(code) => {
2440                    let ch = char::from_u32(*code as u32).ok_or_else(|| {
2441                        IonError::type_err(
2442                            ion_str!("invalid char code").to_string(),
2443                            span.line,
2444                            span.col,
2445                        )
2446                    })?;
2447                    Ok(Value::Bool(s.contains(ch)))
2448                }
2449                _ => Err(IonError::type_err(
2450                    ion_str!("contains requires string or int argument").to_string(),
2451                    span.line,
2452                    span.col,
2453                )
2454                .into()),
2455            },
2456            "starts_with" => {
2457                let sub = args[0].as_str().ok_or_else(|| {
2458                    IonError::type_err(
2459                        ion_str!("starts_with requires string argument").to_string(),
2460                        span.line,
2461                        span.col,
2462                    )
2463                })?;
2464                Ok(Value::Bool(s.starts_with(sub)))
2465            }
2466            "ends_with" => {
2467                let sub = args[0].as_str().ok_or_else(|| {
2468                    IonError::type_err(
2469                        ion_str!("ends_with requires string argument").to_string(),
2470                        span.line,
2471                        span.col,
2472                    )
2473                })?;
2474                Ok(Value::Bool(s.ends_with(sub)))
2475            }
2476            "trim" => Ok(Value::Str(s.trim().to_string())),
2477            "to_upper" => Ok(Value::Str(s.to_uppercase())),
2478            "to_lower" => Ok(Value::Str(s.to_lowercase())),
2479            "split" => {
2480                let delim = args[0].as_str().ok_or_else(|| {
2481                    IonError::type_err(
2482                        ion_str!("split requires string argument").to_string(),
2483                        span.line,
2484                        span.col,
2485                    )
2486                })?;
2487                let parts: Vec<Value> = s.split(delim).map(|p| Value::Str(p.to_string())).collect();
2488                Ok(Value::List(parts))
2489            }
2490            "replace" => {
2491                let from = args[0].as_str().ok_or_else(|| {
2492                    IonError::type_err(
2493                        ion_str!("replace requires string arguments").to_string(),
2494                        span.line,
2495                        span.col,
2496                    )
2497                })?;
2498                let to = args[1].as_str().ok_or_else(|| {
2499                    IonError::type_err(
2500                        ion_str!("replace requires string arguments").to_string(),
2501                        span.line,
2502                        span.col,
2503                    )
2504                })?;
2505                Ok(Value::Str(s.replace(from, to)))
2506            }
2507            "chars" => {
2508                let chars: Vec<Value> = s.chars().map(|c| Value::Str(c.to_string())).collect();
2509                Ok(Value::List(chars))
2510            }
2511            "char_len" => Ok(Value::Int(s.chars().count() as i64)),
2512            "is_empty" => Ok(Value::Bool(s.is_empty())),
2513            "trim_start" => Ok(Value::Str(s.trim_start().to_string())),
2514            "trim_end" => Ok(Value::Str(s.trim_end().to_string())),
2515            "repeat" => {
2516                let n = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2517                    IonError::type_err(
2518                        ion_str!("repeat requires int argument").to_string(),
2519                        span.line,
2520                        span.col,
2521                    )
2522                })?;
2523                Ok(Value::Str(s.repeat(n as usize)))
2524            }
2525            "find" => {
2526                let sub = args[0].as_str().ok_or_else(|| {
2527                    IonError::type_err(
2528                        ion_str!("find requires string argument").to_string(),
2529                        span.line,
2530                        span.col,
2531                    )
2532                })?;
2533                Ok(match s.find(sub) {
2534                    Some(byte_idx) => {
2535                        let char_idx = s[..byte_idx].chars().count();
2536                        Value::Option(Some(Box::new(Value::Int(char_idx as i64))))
2537                    }
2538                    None => Value::Option(None),
2539                })
2540            }
2541            "to_int" => Ok(match s.trim().parse::<i64>() {
2542                std::result::Result::Ok(n) => Value::Result(Ok(Box::new(Value::Int(n)))),
2543                std::result::Result::Err(e) => {
2544                    Value::Result(Err(Box::new(Value::Str(e.to_string()))))
2545                }
2546            }),
2547            "to_float" => Ok(match s.trim().parse::<f64>() {
2548                std::result::Result::Ok(f) => Value::Result(Ok(Box::new(Value::Float(f)))),
2549                std::result::Result::Err(e) => {
2550                    Value::Result(Err(Box::new(Value::Str(e.to_string()))))
2551                }
2552            }),
2553            "bytes" => {
2554                let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
2555                Ok(Value::List(bytes))
2556            }
2557            "strip_prefix" => {
2558                let pre = args[0].as_str().ok_or_else(|| {
2559                    IonError::type_err(
2560                        ion_str!("strip_prefix requires string argument").to_string(),
2561                        span.line,
2562                        span.col,
2563                    )
2564                })?;
2565                Ok(Value::Str(s.strip_prefix(pre).unwrap_or(s).to_string()))
2566            }
2567            "strip_suffix" => {
2568                let suf = args[0].as_str().ok_or_else(|| {
2569                    IonError::type_err(
2570                        ion_str!("strip_suffix requires string argument").to_string(),
2571                        span.line,
2572                        span.col,
2573                    )
2574                })?;
2575                Ok(Value::Str(s.strip_suffix(suf).unwrap_or(s).to_string()))
2576            }
2577            "pad_start" => {
2578                let width = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2579                    IonError::type_err(
2580                        ion_str!("pad_start requires int argument").to_string(),
2581                        span.line,
2582                        span.col,
2583                    )
2584                })? as usize;
2585                let ch = args
2586                    .get(1)
2587                    .and_then(|a| a.as_str())
2588                    .and_then(|s| s.chars().next())
2589                    .unwrap_or(' ');
2590                let char_len = s.chars().count();
2591                if char_len >= width {
2592                    Ok(Value::Str(s.to_string()))
2593                } else {
2594                    let pad: String = std::iter::repeat_n(ch, width - char_len).collect();
2595                    Ok(Value::Str(format!("{}{}", pad, s)))
2596                }
2597            }
2598            "pad_end" => {
2599                let width = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2600                    IonError::type_err(
2601                        ion_str!("pad_end requires int argument").to_string(),
2602                        span.line,
2603                        span.col,
2604                    )
2605                })? as usize;
2606                let ch = args
2607                    .get(1)
2608                    .and_then(|a| a.as_str())
2609                    .and_then(|s| s.chars().next())
2610                    .unwrap_or(' ');
2611                let char_len = s.chars().count();
2612                if char_len >= width {
2613                    Ok(Value::Str(s.to_string()))
2614                } else {
2615                    let pad: String = std::iter::repeat_n(ch, width - char_len).collect();
2616                    Ok(Value::Str(format!("{}{}", s, pad)))
2617                }
2618            }
2619            "reverse" => Ok(Value::Str(s.chars().rev().collect())),
2620            "slice" => {
2621                let chars: Vec<char> = s.chars().collect();
2622                let char_count = chars.len();
2623                let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2624                let end = args
2625                    .get(1)
2626                    .and_then(|a| a.as_int())
2627                    .map(|n| n as usize)
2628                    .unwrap_or(char_count);
2629                let start = start.min(char_count);
2630                let end = end.min(char_count);
2631                Ok(Value::Str(chars[start..end].iter().collect()))
2632            }
2633            _ => Err(IonError::type_err(
2634                format!(
2635                    "{}{}{}",
2636                    ion_str!("no method '"),
2637                    method,
2638                    ion_str!("' on string")
2639                ),
2640                span.line,
2641                span.col,
2642            )
2643            .into()),
2644        }
2645    }
2646
2647    fn bytes_method(&self, bytes: &[u8], method: &str, args: &[Value], span: Span) -> SignalResult {
2648        match method {
2649            "len" => Ok(Value::Int(bytes.len() as i64)),
2650            "is_empty" => Ok(Value::Bool(bytes.is_empty())),
2651            "contains" => {
2652                let byte = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2653                    IonError::type_err(
2654                        ion_str!("bytes.contains() requires an int argument").to_string(),
2655                        span.line,
2656                        span.col,
2657                    )
2658                })?;
2659                Ok(Value::Bool(bytes.contains(&(byte as u8))))
2660            }
2661            "slice" => {
2662                let start = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
2663                let end = args
2664                    .get(1)
2665                    .and_then(|a| a.as_int())
2666                    .map(|n| n as usize)
2667                    .unwrap_or(bytes.len());
2668                let start = start.min(bytes.len());
2669                let end = end.min(bytes.len());
2670                Ok(Value::Bytes(bytes[start..end].to_vec()))
2671            }
2672            "to_list" => Ok(Value::List(
2673                bytes.iter().map(|&b| Value::Int(b as i64)).collect(),
2674            )),
2675            "to_str" => match std::str::from_utf8(bytes) {
2676                std::result::Result::Ok(s) => {
2677                    Ok(Value::Result(Ok(Box::new(Value::Str(s.to_string())))))
2678                }
2679                std::result::Result::Err(e) => {
2680                    Ok(Value::Result(Err(Box::new(Value::Str(format!("{}", e))))))
2681                }
2682            },
2683            "to_hex" => {
2684                let hex: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
2685                Ok(Value::Str(hex))
2686            }
2687            "find" => {
2688                let needle = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2689                    IonError::type_err(
2690                        ion_str!("bytes.find() requires an int argument").to_string(),
2691                        span.line,
2692                        span.col,
2693                    )
2694                })?;
2695                let pos = bytes.iter().position(|&b| b == needle as u8);
2696                Ok(match pos {
2697                    Some(i) => Value::Option(Some(Box::new(Value::Int(i as i64)))),
2698                    None => Value::Option(None),
2699                })
2700            }
2701            "reverse" => {
2702                let mut rev = bytes.to_vec();
2703                rev.reverse();
2704                Ok(Value::Bytes(rev))
2705            }
2706            "push" => {
2707                let byte = args.first().and_then(|a| a.as_int()).ok_or_else(|| {
2708                    IonError::type_err(
2709                        ion_str!("bytes.push() requires an int argument").to_string(),
2710                        span.line,
2711                        span.col,
2712                    )
2713                })?;
2714                let mut new = bytes.to_vec();
2715                new.push(byte as u8);
2716                Ok(Value::Bytes(new))
2717            }
2718            _ => Err(IonError::type_err(
2719                format!(
2720                    "{}{}{}{}",
2721                    ion_str!("no method '"),
2722                    method,
2723                    ion_str!("' on "),
2724                    ion_str!("bytes"),
2725                ),
2726                span.line,
2727                span.col,
2728            )
2729            .into()),
2730        }
2731    }
2732
2733    fn dict_method(
2734        &self,
2735        map: &IndexMap<String, Value>,
2736        method: &str,
2737        args: &[Value],
2738        span: Span,
2739    ) -> SignalResult {
2740        match method {
2741            "len" => Ok(Value::Int(map.len() as i64)),
2742            "keys" => Ok(Value::List(
2743                map.keys().map(|k| Value::Str(k.clone())).collect(),
2744            )),
2745            "values" => Ok(Value::List(map.values().cloned().collect())),
2746            "entries" => Ok(Value::List(
2747                map.iter()
2748                    .map(|(k, v)| Value::Tuple(vec![Value::Str(k.clone()), v.clone()]))
2749                    .collect(),
2750            )),
2751            "contains_key" => {
2752                let key = args[0].as_str().ok_or_else(|| {
2753                    IonError::type_err(
2754                        ion_str!("contains_key requires string argument").to_string(),
2755                        span.line,
2756                        span.col,
2757                    )
2758                })?;
2759                Ok(Value::Bool(map.contains_key(key)))
2760            }
2761            "get" => {
2762                let key = args[0].as_str().ok_or_else(|| {
2763                    IonError::type_err(
2764                        ion_str!("get requires string argument").to_string(),
2765                        span.line,
2766                        span.col,
2767                    )
2768                })?;
2769                Ok(match map.get(key) {
2770                    Some(v) => Value::Option(Some(Box::new(v.clone()))),
2771                    None => Value::Option(None),
2772                })
2773            }
2774            "insert" => {
2775                if args.len() < 2 {
2776                    return Err(IonError::runtime(
2777                        ion_str!("insert requires 2 arguments: key, value").to_string(),
2778                        span.line,
2779                        span.col,
2780                    )
2781                    .into());
2782                }
2783                let key = args[0].as_str().ok_or_else(|| {
2784                    IonError::type_err(
2785                        ion_str!("insert requires string key").to_string(),
2786                        span.line,
2787                        span.col,
2788                    )
2789                })?;
2790                let mut new_map = map.clone();
2791                new_map.insert(key.to_string(), args[1].clone());
2792                Ok(Value::Dict(new_map))
2793            }
2794            "remove" => {
2795                let key = args[0].as_str().ok_or_else(|| {
2796                    IonError::type_err(
2797                        ion_str!("remove requires string key").to_string(),
2798                        span.line,
2799                        span.col,
2800                    )
2801                })?;
2802                let mut new_map = map.clone();
2803                new_map.shift_remove(key);
2804                Ok(Value::Dict(new_map))
2805            }
2806            "merge" => {
2807                if let Value::Dict(other) = &args[0] {
2808                    let mut new_map = map.clone();
2809                    for (k, v) in other {
2810                        new_map.insert(k.clone(), v.clone());
2811                    }
2812                    Ok(Value::Dict(new_map))
2813                } else {
2814                    Err(IonError::type_err(
2815                        ion_str!("merge requires a dict argument").to_string(),
2816                        span.line,
2817                        span.col,
2818                    )
2819                    .into())
2820                }
2821            }
2822            "is_empty" => Ok(Value::Bool(map.is_empty())),
2823            "update" => {
2824                if let Value::Dict(other) = &args[0] {
2825                    let mut new_map = map.clone();
2826                    for (k, v) in other {
2827                        new_map.insert(k.clone(), v.clone());
2828                    }
2829                    Ok(Value::Dict(new_map))
2830                } else {
2831                    Err(IonError::type_err(
2832                        ion_str!("update requires a dict argument").to_string(),
2833                        span.line,
2834                        span.col,
2835                    )
2836                    .into())
2837                }
2838            }
2839            "keys_of" => {
2840                let target = &args[0];
2841                let keys: Vec<Value> = map
2842                    .iter()
2843                    .filter(|(_, v)| *v == target)
2844                    .map(|(k, _)| Value::Str(k.clone()))
2845                    .collect();
2846                Ok(Value::List(keys))
2847            }
2848            "zip" => {
2849                if let Value::Dict(other) = &args[0] {
2850                    let mut result = indexmap::IndexMap::new();
2851                    for (k, v) in map {
2852                        if let Some(ov) = other.get(k) {
2853                            result.insert(k.clone(), Value::Tuple(vec![v.clone(), ov.clone()]));
2854                        }
2855                    }
2856                    Ok(Value::Dict(result))
2857                } else {
2858                    Err(IonError::type_err(
2859                        ion_str!("zip requires a dict argument").to_string(),
2860                        span.line,
2861                        span.col,
2862                    )
2863                    .into())
2864                }
2865            }
2866            _ => Err(IonError::type_err(
2867                format!(
2868                    "{}{}{}",
2869                    ion_str!("no method '"),
2870                    method,
2871                    ion_str!("' on dict")
2872                ),
2873                span.line,
2874                span.col,
2875            )
2876            .into()),
2877        }
2878    }
2879
2880    fn option_method(
2881        &mut self,
2882        opt: Option<Box<Value>>,
2883        method: &str,
2884        args: &[Value],
2885        span: Span,
2886    ) -> SignalResult {
2887        match method {
2888            "is_some" => Ok(Value::Bool(opt.is_some())),
2889            "is_none" => Ok(Value::Bool(opt.is_none())),
2890            "unwrap" => match opt {
2891                Some(v) => Ok(*v),
2892                None => {
2893                    Err(
2894                        IonError::runtime(ion_str!("called unwrap on None"), span.line, span.col)
2895                            .into(),
2896                    )
2897                }
2898            },
2899            "unwrap_or" => match opt {
2900                Some(v) => Ok(*v),
2901                None => Ok(args[0].clone()),
2902            },
2903            "expect" => match opt {
2904                Some(v) => Ok(*v),
2905                None => {
2906                    let default_msg = ion_str!("expect failed");
2907                    let msg = args[0].as_str().unwrap_or(&default_msg);
2908                    Err(IonError::runtime(msg.to_string(), span.line, span.col).into())
2909                }
2910            },
2911            "map" => {
2912                let func = args[0].clone();
2913                match opt {
2914                    Some(v) => {
2915                        let result = self.call_value(&func, &[*v], span)?;
2916                        Ok(Value::Option(Some(Box::new(result))))
2917                    }
2918                    None => Ok(Value::Option(None)),
2919                }
2920            }
2921            "and_then" => {
2922                let func = args[0].clone();
2923                match opt {
2924                    Some(v) => self.call_value(&func, &[*v], span),
2925                    None => Ok(Value::Option(None)),
2926                }
2927            }
2928            "or_else" => {
2929                let func = args[0].clone();
2930                match opt {
2931                    Some(v) => Ok(Value::Option(Some(v))),
2932                    None => self.call_value(&func, &[], span),
2933                }
2934            }
2935            "unwrap_or_else" => {
2936                let func = args[0].clone();
2937                match opt {
2938                    Some(v) => Ok(*v),
2939                    None => self.call_value(&func, &[], span),
2940                }
2941            }
2942            _ => Err(IonError::type_err(
2943                format!(
2944                    "{}{}{}",
2945                    ion_str!("no method '"),
2946                    method,
2947                    ion_str!("' on Option")
2948                ),
2949                span.line,
2950                span.col,
2951            )
2952            .into()),
2953        }
2954    }
2955
2956    fn result_method(
2957        &mut self,
2958        res: Result<Box<Value>, Box<Value>>,
2959        method: &str,
2960        args: &[Value],
2961        span: Span,
2962    ) -> SignalResult {
2963        match method {
2964            "is_ok" => Ok(Value::Bool(res.is_ok())),
2965            "is_err" => Ok(Value::Bool(res.is_err())),
2966            "unwrap" => match res {
2967                Ok(v) => Ok(*v),
2968                Err(e) => Err(IonError::runtime(
2969                    format!("{}{}", ion_str!("called unwrap on Err: "), e),
2970                    span.line,
2971                    span.col,
2972                )
2973                .into()),
2974            },
2975            "unwrap_or" => match res {
2976                Ok(v) => Ok(*v),
2977                Err(_) => Ok(args[0].clone()),
2978            },
2979            "expect" => match res {
2980                Ok(v) => Ok(*v),
2981                Err(e) => {
2982                    let default_msg = ion_str!("expect failed");
2983                    let msg = args[0].as_str().unwrap_or(&default_msg);
2984                    Err(IonError::runtime(format!("{}: {}", msg, e), span.line, span.col).into())
2985                }
2986            },
2987            "map" => {
2988                let func = args[0].clone();
2989                match res {
2990                    Ok(v) => {
2991                        let result = self.call_value(&func, &[*v], span)?;
2992                        Ok(Value::Result(Ok(Box::new(result))))
2993                    }
2994                    Err(e) => Ok(Value::Result(Err(e))),
2995                }
2996            }
2997            "map_err" => {
2998                let func = args[0].clone();
2999                match res {
3000                    Ok(v) => Ok(Value::Result(Ok(v))),
3001                    Err(e) => {
3002                        let result = self.call_value(&func, &[*e], span)?;
3003                        Ok(Value::Result(Err(Box::new(result))))
3004                    }
3005                }
3006            }
3007            "and_then" => {
3008                let func = args[0].clone();
3009                match res {
3010                    Ok(v) => self.call_value(&func, &[*v], span),
3011                    Err(e) => Ok(Value::Result(Err(e))),
3012                }
3013            }
3014            "or_else" => {
3015                let func = args[0].clone();
3016                match res {
3017                    Ok(v) => Ok(Value::Result(Ok(v))),
3018                    Err(e) => self.call_value(&func, &[*e], span),
3019                }
3020            }
3021            "unwrap_or_else" => {
3022                let func = args[0].clone();
3023                match res {
3024                    Ok(v) => Ok(*v),
3025                    Err(e) => self.call_value(&func, &[*e], span),
3026                }
3027            }
3028            _ => Err(IonError::type_err(
3029                format!(
3030                    "{}{}{}",
3031                    ion_str!("no method '"),
3032                    method,
3033                    ion_str!("' on Result")
3034                ),
3035                span.line,
3036                span.col,
3037            )
3038            .into()),
3039        }
3040    }
3041
3042    fn call_value(&mut self, func: &Value, args: &[Value], span: Span) -> SignalResult {
3043        match func {
3044            Value::Fn(ion_fn) => {
3045                if self.call_depth >= self.limits.max_call_depth {
3046                    return Err(IonError::runtime(
3047                        ion_str!("maximum call depth exceeded").to_string(),
3048                        span.line,
3049                        span.col,
3050                    )
3051                    .into());
3052                }
3053                self.call_depth += 1;
3054                self.env.push_scope();
3055                let result = (|| {
3056                    // Load captures
3057                    for (name, val) in &ion_fn.captures {
3058                        self.env.define(name.clone(), val.clone(), false);
3059                    }
3060                    // Bind parameters
3061                    for (i, param) in ion_fn.params.iter().enumerate() {
3062                        let val = if i < args.len() {
3063                            args[i].clone()
3064                        } else if let Some(default) = &param.default {
3065                            self.eval_expr(default)?
3066                        } else {
3067                            return Err(IonError::runtime(
3068                                format!(
3069                                    "{}{}{}{}{}{}",
3070                                    ion_str!("function '"),
3071                                    ion_fn.name,
3072                                    ion_str!("' expected "),
3073                                    ion_fn.params.len(),
3074                                    ion_str!(" arguments, got "),
3075                                    args.len(),
3076                                ),
3077                                span.line,
3078                                span.col,
3079                            )
3080                            .into());
3081                        };
3082                        self.env.define(param.name.clone(), val, false);
3083                    }
3084                    self.eval_stmts(&ion_fn.body)
3085                })();
3086                self.env.pop_scope();
3087                self.call_depth -= 1;
3088                match result {
3089                    Ok(v) => Ok(v),
3090                    Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
3091                    Err(SignalOrError::Signal(Signal::Break { label, .. })) => Err(
3092                        IonError::runtime(unmatched_label_msg("break", label), span.line, span.col)
3093                            .into(),
3094                    ),
3095                    Err(SignalOrError::Signal(Signal::Continue { label })) => {
3096                        Err(IonError::runtime(
3097                            unmatched_label_msg("continue", label),
3098                            span.line,
3099                            span.col,
3100                        )
3101                        .into())
3102                    }
3103                    Err(SignalOrError::Error(e)) => {
3104                        // Convert ? propagation into values at function boundary
3105                        if e.kind == ErrorKind::PropagatedErr {
3106                            Ok(Value::Result(Err(Box::new(Value::Str(e.message.clone())))))
3107                        } else if e.kind == ErrorKind::PropagatedNone {
3108                            Ok(Value::Option(None))
3109                        } else {
3110                            Err(e.into())
3111                        }
3112                    }
3113                }
3114            }
3115            #[cfg(feature = "concurrency")]
3116            Value::BuiltinFn(ref name, _) if name == "timeout" => self.builtin_timeout(args, span),
3117            Value::BuiltinFn(_, func) => {
3118                func(args).map_err(|msg| IonError::runtime(msg, span.line, span.col).into())
3119            }
3120            Value::BuiltinClosure(_, func) => func
3121                .call(args)
3122                .map_err(|msg| IonError::runtime(msg, span.line, span.col).into()),
3123            _ => Err(IonError::type_err(
3124                format!("{}{}", ion_str!("not callable: "), func.type_name()),
3125                span.line,
3126                span.col,
3127            )
3128            .into()),
3129        }
3130    }
3131
3132    #[cfg(feature = "concurrency")]
3133    fn builtin_timeout(&mut self, args: &[Value], span: Span) -> SignalResult {
3134        if args.len() < 2 {
3135            return Err(IonError::runtime(
3136                ion_str!("timeout(ms, fn) requires 2 arguments").to_string(),
3137                span.line,
3138                span.col,
3139            )
3140            .into());
3141        }
3142        let ms = args[0].as_int().ok_or_else(|| {
3143            SignalOrError::Error(IonError::runtime(
3144                ion_str!("timeout: first argument must be int (ms)").to_string(),
3145                span.line,
3146                span.col,
3147            ))
3148        })?;
3149        let func = args[1].clone();
3150        // Call the function synchronously but with a timeout via spawn + join_timeout
3151        let captured_env = self.env.capture();
3152        let limits = self.limits.clone();
3153        let types = self.types.clone();
3154        let cancel_flag = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
3155        let task = crate::async_rt::spawn_task_with_cancel(cancel_flag, move |flag| {
3156            let mut child = Interpreter::new();
3157            child.limits = limits;
3158            child.types = types;
3159            child.cancel_flag = Some(flag);
3160            for (name, val) in captured_env {
3161                child.env.define(name, val, false);
3162            }
3163            child
3164                .call_value(&func, &[], crate::ast::Span { line: 0, col: 0 })
3165                .map_err(|e| match e {
3166                    SignalOrError::Error(err) => err,
3167                    SignalOrError::Signal(_) => {
3168                        IonError::runtime(ion_str!("unexpected signal in timeout"), 0, 0)
3169                    }
3170                })
3171        });
3172        match task.join_timeout(std::time::Duration::from_millis(ms as u64)) {
3173            Some(Ok(val)) => Ok(Value::Option(Some(Box::new(val)))),
3174            Some(Err(e)) => Err(e.into()),
3175            None => {
3176                // Timer expired — signal cancellation so the runaway
3177                // task terminates at the next statement boundary.
3178                task.cancel();
3179                Ok(Value::Option(None))
3180            }
3181        }
3182    }
3183
3184    fn call_with_named(
3185        &mut self,
3186        func: &Value,
3187        named_args: Vec<(Option<String>, Value)>,
3188        span: Span,
3189    ) -> SignalResult {
3190        match func {
3191            Value::Fn(ion_fn) => {
3192                // Reorder named args to match parameter positions
3193                let mut ordered = vec![None; ion_fn.params.len()];
3194                let mut pos_idx = 0;
3195                for (name, val) in named_args {
3196                    if let Some(name) = name {
3197                        // Find param by name
3198                        let param_idx = ion_fn
3199                            .params
3200                            .iter()
3201                            .position(|p| p.name == name)
3202                            .ok_or_else(|| {
3203                                IonError::runtime(
3204                                    format!(
3205                                        "{}{}{}{}",
3206                                        ion_str!("unknown parameter '"),
3207                                        name,
3208                                        ion_str!("' for function '"),
3209                                        ion_fn.name,
3210                                    ),
3211                                    span.line,
3212                                    span.col,
3213                                )
3214                            })?;
3215                        ordered[param_idx] = Some(val);
3216                    } else {
3217                        // Positional arg — fill next empty slot
3218                        while pos_idx < ordered.len() && ordered[pos_idx].is_some() {
3219                            pos_idx += 1;
3220                        }
3221                        if pos_idx < ordered.len() {
3222                            ordered[pos_idx] = Some(val);
3223                            pos_idx += 1;
3224                        }
3225                    }
3226                }
3227                // Keep as Option<Value> so we can distinguish "not provided" from "provided Unit"
3228                let opt_args: Vec<Option<Value>> = ordered;
3229                // Use call_value with the reordered args, but handle defaults specially
3230                if self.call_depth >= self.limits.max_call_depth {
3231                    return Err(IonError::runtime(
3232                        ion_str!("maximum call depth exceeded").to_string(),
3233                        span.line,
3234                        span.col,
3235                    )
3236                    .into());
3237                }
3238                self.call_depth += 1;
3239                self.env.push_scope();
3240                let result = (|| {
3241                    for (name, val) in &ion_fn.captures {
3242                        self.env.define(name.clone(), val.clone(), false);
3243                    }
3244                    for (i, param) in ion_fn.params.iter().enumerate() {
3245                        let val = if i < opt_args.len() && opt_args[i].is_some() {
3246                            opt_args[i].clone().unwrap()
3247                        } else if let Some(default) = &param.default {
3248                            self.eval_expr(default)?
3249                        } else {
3250                            return Err(IonError::runtime(
3251                                format!(
3252                                    "{}{}{}",
3253                                    ion_str!("missing argument '"),
3254                                    param.name,
3255                                    ion_str!("'"),
3256                                ),
3257                                span.line,
3258                                span.col,
3259                            )
3260                            .into());
3261                        };
3262                        self.env.define(param.name.clone(), val, false);
3263                    }
3264                    self.eval_stmts(&ion_fn.body)
3265                })();
3266                self.env.pop_scope();
3267                self.call_depth -= 1;
3268                match result {
3269                    Ok(v) => Ok(v),
3270                    Err(SignalOrError::Signal(Signal::Return(v))) => Ok(v),
3271                    Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedErr => {
3272                        Ok(Value::Result(Err(Box::new(Value::Str(e.message.clone())))))
3273                    }
3274                    Err(SignalOrError::Error(e)) if e.kind == ErrorKind::PropagatedNone => {
3275                        Ok(Value::Option(None))
3276                    }
3277                    Err(e) => Err(e),
3278                }
3279            }
3280            _ => {
3281                // For builtins, just pass positional values
3282                let args: Vec<Value> = named_args.into_iter().map(|(_, v)| v).collect();
3283                self.call_value(func, &args, span)
3284            }
3285        }
3286    }
3287
3288    fn check_type_ann(val: &Value, ann: &TypeAnn, span: Span) -> Result<(), SignalOrError> {
3289        let matches = match ann {
3290            TypeAnn::Simple(name) => match name.as_str() {
3291                "int" => matches!(val, Value::Int(_)),
3292                "float" => matches!(val, Value::Float(_)),
3293                "bool" => matches!(val, Value::Bool(_)),
3294                "string" => matches!(val, Value::Str(_)),
3295                "bytes" => matches!(val, Value::Bytes(_)),
3296                "list" => matches!(val, Value::List(_)),
3297                "dict" => matches!(val, Value::Dict(_)),
3298                "tuple" => matches!(val, Value::Tuple(_)),
3299                "set" => matches!(val, Value::Set(_)),
3300                "fn" => matches!(
3301                    val,
3302                    Value::Fn(_) | Value::BuiltinFn(_, _) | Value::BuiltinClosure(_, _)
3303                ),
3304                "cell" => matches!(val, Value::Cell(_)),
3305                "any" => true,
3306                _ => true, // unknown types pass (forward compatibility)
3307            },
3308            TypeAnn::Option(_) => matches!(val, Value::Option(_)),
3309            TypeAnn::Result(_, _) => matches!(val, Value::Result(_)),
3310            TypeAnn::List(_) => matches!(val, Value::List(_)),
3311            TypeAnn::Dict(_, _) => matches!(val, Value::Dict(_)),
3312        };
3313        if !matches {
3314            return Err(IonError::type_err(
3315                format!(
3316                    "{}{}{}{}",
3317                    ion_str!("type mismatch: expected "),
3318                    Self::type_ann_name(ann),
3319                    ion_str!(", got "),
3320                    val.type_name()
3321                ),
3322                span.line,
3323                span.col,
3324            )
3325            .into());
3326        }
3327        Ok(())
3328    }
3329
3330    fn type_ann_name(ann: &TypeAnn) -> String {
3331        match ann {
3332            TypeAnn::Simple(name) => name.clone(),
3333            TypeAnn::Option(inner) => format!("Option<{}>", Self::type_ann_name(inner)),
3334            TypeAnn::Result(ok, err) => {
3335                format!(
3336                    "Result<{}, {}>",
3337                    Self::type_ann_name(ok),
3338                    Self::type_ann_name(err)
3339                )
3340            }
3341            TypeAnn::List(inner) => format!("list<{}>", Self::type_ann_name(inner)),
3342            TypeAnn::Dict(k, v) => {
3343                format!(
3344                    "dict<{}, {}>",
3345                    Self::type_ann_name(k),
3346                    Self::type_ann_name(v)
3347                )
3348            }
3349        }
3350    }
3351
3352    fn value_to_iter(&self, val: &Value, span: Span) -> Result<Vec<Value>, SignalOrError> {
3353        match val {
3354            Value::List(items) => Ok(items.clone()),
3355            Value::Set(items) => Ok(items.clone()),
3356            Value::Tuple(items) => Ok(items.clone()),
3357            Value::Dict(map) => Ok(map
3358                .iter()
3359                .map(|(k, v)| Value::Tuple(vec![Value::Str(k.clone()), v.clone()]))
3360                .collect()),
3361            Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
3362            Value::Bytes(bytes) => Ok(bytes.iter().map(|&b| Value::Int(b as i64)).collect()),
3363            Value::Range {
3364                start,
3365                end,
3366                inclusive,
3367            } => Ok(Value::range_to_list(*start, *end, *inclusive)),
3368            _ => Err(IonError::type_err(
3369                format!("{}{}", ion_str!("cannot iterate over "), val.type_name()),
3370                span.line,
3371                span.col,
3372            )
3373            .into()),
3374        }
3375    }
3376
3377    // --- Pattern Matching ---
3378
3379    fn pattern_matches(&self, pattern: &Pattern, val: &Value) -> bool {
3380        match (pattern, val) {
3381            (Pattern::Wildcard, _) => true,
3382            (Pattern::Ident(_), _) => true,
3383            (Pattern::Int(a), Value::Int(b)) => a == b,
3384            (Pattern::Float(a), Value::Float(b)) => a == b,
3385            (Pattern::Bool(a), Value::Bool(b)) => a == b,
3386            (Pattern::Str(a), Value::Str(b)) => a == b,
3387            (Pattern::Bytes(a), Value::Bytes(b)) => a == b,
3388            (Pattern::None, Value::Option(None)) => true,
3389            (Pattern::Some(p), Value::Option(Some(v))) => self.pattern_matches(p, v),
3390            (Pattern::Ok(p), Value::Result(Ok(v))) => self.pattern_matches(p, v),
3391            (Pattern::Err(p), Value::Result(Err(v))) => self.pattern_matches(p, v),
3392            (Pattern::Tuple(pats), Value::Tuple(vals)) => {
3393                pats.len() == vals.len()
3394                    && pats
3395                        .iter()
3396                        .zip(vals)
3397                        .all(|(p, v)| self.pattern_matches(p, v))
3398            }
3399            (Pattern::List(pats, rest), Value::List(vals)) => {
3400                if rest.is_some() {
3401                    vals.len() >= pats.len()
3402                        && pats
3403                            .iter()
3404                            .zip(vals)
3405                            .all(|(p, v)| self.pattern_matches(p, v))
3406                } else {
3407                    pats.len() == vals.len()
3408                        && pats
3409                            .iter()
3410                            .zip(vals)
3411                            .all(|(p, v)| self.pattern_matches(p, v))
3412                }
3413            }
3414            (
3415                Pattern::EnumVariant {
3416                    enum_name,
3417                    variant,
3418                    fields,
3419                },
3420                Value::HostEnum {
3421                    enum_name: en,
3422                    variant: v,
3423                    data,
3424                },
3425            ) => {
3426                if enum_name != en || variant != v {
3427                    return false;
3428                }
3429                match fields {
3430                    EnumPatternFields::None => data.is_empty(),
3431                    EnumPatternFields::Positional(pats) => {
3432                        pats.len() == data.len()
3433                            && pats
3434                                .iter()
3435                                .zip(data)
3436                                .all(|(p, v)| self.pattern_matches(p, v))
3437                    }
3438                    EnumPatternFields::Named(_) => false, // named fields not applicable to enum data
3439                }
3440            }
3441            (
3442                Pattern::Struct { name, fields },
3443                Value::HostStruct {
3444                    type_name,
3445                    fields: val_fields,
3446                },
3447            ) => {
3448                if name != type_name {
3449                    return false;
3450                }
3451                fields.iter().all(|(fname, fpat)| {
3452                    match val_fields.get(fname) {
3453                        Some(v) => match fpat {
3454                            Some(p) => self.pattern_matches(p, v),
3455                            None => true, // just binding, always matches
3456                        },
3457                        None => false,
3458                    }
3459                })
3460            }
3461            _ => false,
3462        }
3463    }
3464
3465    fn bind_pattern(
3466        &mut self,
3467        pattern: &Pattern,
3468        val: &Value,
3469        mutable: bool,
3470        span: Span,
3471    ) -> Result<(), SignalOrError> {
3472        match (pattern, val) {
3473            (Pattern::Wildcard, _) => Ok(()),
3474            (Pattern::Ident(name), _) => {
3475                self.env.define(name.clone(), val.clone(), mutable);
3476                Ok(())
3477            }
3478            (
3479                Pattern::Int(_)
3480                | Pattern::Float(_)
3481                | Pattern::Bool(_)
3482                | Pattern::Str(_)
3483                | Pattern::Bytes(_)
3484                | Pattern::None,
3485                _,
3486            ) => Ok(()),
3487            (Pattern::Some(p), Value::Option(Some(v))) => self.bind_pattern(p, v, mutable, span),
3488            (Pattern::Ok(p), Value::Result(Ok(v))) => self.bind_pattern(p, v, mutable, span),
3489            (Pattern::Err(p), Value::Result(Err(v))) => self.bind_pattern(p, v, mutable, span),
3490            (Pattern::Tuple(pats), Value::Tuple(vals)) => {
3491                for (p, v) in pats.iter().zip(vals) {
3492                    self.bind_pattern(p, v, mutable, span)?;
3493                }
3494                Ok(())
3495            }
3496            (Pattern::List(pats, rest), Value::List(vals)) => {
3497                for (p, v) in pats.iter().zip(vals) {
3498                    self.bind_pattern(p, v, mutable, span)?;
3499                }
3500                if let Some(rest_pat) = rest {
3501                    let rest_vals = vals[pats.len()..].to_vec();
3502                    self.bind_pattern(rest_pat, &Value::List(rest_vals), mutable, span)?;
3503                }
3504                Ok(())
3505            }
3506            (Pattern::EnumVariant { fields, .. }, Value::HostEnum { data, .. }) => match fields {
3507                EnumPatternFields::None => Ok(()),
3508                EnumPatternFields::Positional(pats) => {
3509                    for (p, v) in pats.iter().zip(data) {
3510                        self.bind_pattern(p, v, mutable, span)?;
3511                    }
3512                    Ok(())
3513                }
3514                EnumPatternFields::Named(_) => Ok(()),
3515            },
3516            (
3517                Pattern::Struct { fields, .. },
3518                Value::HostStruct {
3519                    fields: val_fields, ..
3520                },
3521            ) => {
3522                for (fname, fpat) in fields {
3523                    if let Some(v) = val_fields.get(fname) {
3524                        match fpat {
3525                            Some(p) => self.bind_pattern(p, v, mutable, span)?,
3526                            None => self.env.define(fname.clone(), v.clone(), mutable),
3527                        }
3528                    }
3529                }
3530                Ok(())
3531            }
3532            _ => Err(IonError::runtime(
3533                ion_str!("pattern match failed in binding").to_string(),
3534                span.line,
3535                span.col,
3536            )
3537            .into()),
3538        }
3539    }
3540}
3541
3542#[cfg(feature = "concurrency")]
3543impl Interpreter {
3544    fn eval_async_block(&mut self, body: &[Stmt], _span: Span) -> SignalResult {
3545        use crate::async_rt::Nursery;
3546
3547        // Save and set nursery for this scope
3548        let prev_nursery = self.nursery.take();
3549        self.nursery = Some(Nursery::new());
3550
3551        self.env.push_scope();
3552        let result = self.eval_stmts(body);
3553        self.env.pop_scope();
3554
3555        // Join all spawned tasks (structured concurrency)
3556        let nursery = self.nursery.take().unwrap();
3557        self.nursery = prev_nursery;
3558
3559        if let Err(e) = nursery.join_all() {
3560            return Err(e.into());
3561        }
3562
3563        result
3564    }
3565
3566    fn eval_spawn(&mut self, expr: &Expr, span: Span) -> SignalResult {
3567        use std::sync::Arc;
3568
3569        // Require being inside an async block
3570        if self.nursery.is_none() {
3571            return Err(IonError::runtime(
3572                ion_str!("spawn is only allowed inside async {}").to_string(),
3573                span.line,
3574                span.col,
3575            )
3576            .into());
3577        }
3578
3579        // Capture current environment for the spawned task
3580        let captured_env = self.env.capture();
3581        let expr_clone = expr.clone();
3582        let limits = self.limits.clone();
3583        let types = self.types.clone();
3584
3585        let cancel_flag = Arc::new(std::sync::atomic::AtomicBool::new(false));
3586        let task_handle: Arc<dyn crate::async_rt::TaskHandle> =
3587            crate::async_rt::spawn_task_with_cancel(cancel_flag, move |flag| {
3588                let mut child = Interpreter::new();
3589                child.limits = limits;
3590                child.types = types;
3591                child.cancel_flag = Some(flag);
3592                // Load captured environment
3593                for (name, val) in captured_env {
3594                    child.env.define(name, val, false);
3595                }
3596                // Evaluate the expression
3597                let program = crate::ast::Program {
3598                    stmts: vec![crate::ast::Stmt {
3599                        kind: crate::ast::StmtKind::ExprStmt {
3600                            expr: expr_clone,
3601                            has_semi: false,
3602                        },
3603                        span: crate::ast::Span { line: 0, col: 0 },
3604                    }],
3605                };
3606                child.eval_program(&program)
3607            });
3608
3609        // Register with nursery
3610        if let Some(nursery) = &mut self.nursery {
3611            nursery.spawn(task_handle.clone());
3612        }
3613
3614        Ok(Value::Task(task_handle))
3615    }
3616
3617    fn eval_await(&mut self, expr: &Expr, span: Span) -> SignalResult {
3618        let val = self.eval_expr(expr)?;
3619        match val {
3620            Value::Task(handle) => handle.join().map_err(SignalOrError::Error),
3621            _ => Err(IonError::type_err(
3622                format!("{}{}", ion_str!("cannot await "), val.type_name()),
3623                span.line,
3624                span.col,
3625            )
3626            .into()),
3627        }
3628    }
3629
3630    fn eval_select(&mut self, branches: &[crate::ast::SelectBranch], span: Span) -> SignalResult {
3631        use std::sync::Arc;
3632
3633        // Spawn all branch futures as tasks
3634        let mut tasks: Vec<(usize, Arc<dyn crate::async_rt::TaskHandle>)> = Vec::new();
3635        for (i, branch) in branches.iter().enumerate() {
3636            let captured_env = self.env.capture();
3637            let expr_clone = branch.future_expr.clone();
3638            let limits = self.limits.clone();
3639            let types = self.types.clone();
3640
3641            let cancel_flag = Arc::new(std::sync::atomic::AtomicBool::new(false));
3642            let handle = crate::async_rt::spawn_task_with_cancel(cancel_flag, move |flag| {
3643                let mut child = Interpreter::new();
3644                child.limits = limits;
3645                child.types = types;
3646                child.cancel_flag = Some(flag);
3647                for (name, val) in captured_env {
3648                    child.env.define(name, val, false);
3649                }
3650                let program = crate::ast::Program {
3651                    stmts: vec![crate::ast::Stmt {
3652                        kind: crate::ast::StmtKind::ExprStmt {
3653                            expr: expr_clone,
3654                            has_semi: false,
3655                        },
3656                        span: crate::ast::Span { line: 0, col: 0 },
3657                    }],
3658                };
3659                child.eval_program(&program)
3660            });
3661            tasks.push((i, handle));
3662        }
3663
3664        // Wait for the first task to complete (condvar-based, no polling)
3665        let handles: Vec<_> = tasks.iter().map(|(_, h)| h.clone()).collect();
3666        let (winner_idx, result) = crate::async_rt::wait_any(&handles);
3667
3668        // Signal cancellation to losing branches so they terminate at
3669        // the next statement boundary instead of burning CPU.
3670        for (i, h) in handles.iter().enumerate() {
3671            if i != winner_idx {
3672                h.cancel();
3673            }
3674        }
3675
3676        let result = result?;
3677        let branch = &branches[tasks[winner_idx].0];
3678        self.env.push_scope();
3679        self.bind_pattern(&branch.pattern, &result, false, span)?;
3680        let body_result = self.eval_expr(&branch.body);
3681        self.env.pop_scope();
3682        body_result
3683    }
3684
3685    fn task_method(
3686        &self,
3687        handle: &std::sync::Arc<dyn crate::async_rt::TaskHandle>,
3688        method: &str,
3689        args: &[Value],
3690        span: Span,
3691    ) -> SignalResult {
3692        match method {
3693            "is_finished" => Ok(Value::Bool(handle.is_finished())),
3694            "cancel" => {
3695                handle.cancel();
3696                Ok(Value::Unit)
3697            }
3698            "is_cancelled" => Ok(Value::Bool(handle.is_cancelled())),
3699            "await_timeout" => {
3700                let ms = args.first().and_then(|v| v.as_int()).ok_or_else(|| {
3701                    IonError::runtime(
3702                        ion_str!("await_timeout requires int (ms)").to_string(),
3703                        span.line,
3704                        span.col,
3705                    )
3706                })?;
3707                match handle.join_timeout(std::time::Duration::from_millis(ms as u64)) {
3708                    Some(result) => {
3709                        let val = result.map_err(SignalOrError::Error)?;
3710                        Ok(Value::Option(Some(Box::new(val))))
3711                    }
3712                    None => Ok(Value::Option(None)),
3713                }
3714            }
3715            _ => Err(IonError::type_err(
3716                format!(
3717                    "{}{}{}",
3718                    ion_str!("no method '"),
3719                    method,
3720                    ion_str!("' on Task")
3721                ),
3722                span.line,
3723                span.col,
3724            )
3725            .into()),
3726        }
3727    }
3728
3729    fn channel_method(
3730        &self,
3731        ch: &crate::async_rt::ChannelEnd,
3732        method: &str,
3733        args: &[Value],
3734        span: Span,
3735    ) -> SignalResult {
3736        use crate::async_rt::ChannelEnd;
3737        match (ch, method) {
3738            (ChannelEnd::Sender(tx), "send") => {
3739                if args.is_empty() {
3740                    return Err(IonError::runtime(
3741                        ion_str!("send requires a value").to_string(),
3742                        span.line,
3743                        span.col,
3744                    )
3745                    .into());
3746                }
3747                tx.send(args[0].clone()).map_err(|e| {
3748                    IonError::runtime(
3749                        format!("{}{}", ion_str!("channel send failed: "), e.message),
3750                        span.line,
3751                        span.col,
3752                    )
3753                })?;
3754                Ok(Value::Unit)
3755            }
3756            (ChannelEnd::Sender(tx), "close") => {
3757                tx.close();
3758                Ok(Value::Unit)
3759            }
3760            (ChannelEnd::Receiver(rx), "recv") => match rx.recv() {
3761                Some(v) => Ok(Value::Option(Some(Box::new(v)))),
3762                None => Ok(Value::Option(None)),
3763            },
3764            (ChannelEnd::Receiver(rx), "try_recv") => match rx.try_recv() {
3765                Some(v) => Ok(Value::Option(Some(Box::new(v)))),
3766                None => Ok(Value::Option(None)),
3767            },
3768            (ChannelEnd::Receiver(rx), "recv_timeout") => {
3769                if args.is_empty() {
3770                    return Err(IonError::runtime(
3771                        ion_str!("recv_timeout requires a timeout in ms").to_string(),
3772                        span.line,
3773                        span.col,
3774                    )
3775                    .into());
3776                }
3777                let ms = args[0].as_int().ok_or_else(|| {
3778                    IonError::runtime(
3779                        ion_str!("recv_timeout requires int (ms)").to_string(),
3780                        span.line,
3781                        span.col,
3782                    )
3783                })?;
3784                match rx.recv_timeout(std::time::Duration::from_millis(ms as u64)) {
3785                    Some(v) => Ok(Value::Option(Some(Box::new(v)))),
3786                    None => Ok(Value::Option(None)),
3787                }
3788            }
3789            _ => Err(IonError::type_err(
3790                format!(
3791                    "{}{}{}",
3792                    ion_str!("no method '"),
3793                    method,
3794                    ion_str!("' on Channel")
3795                ),
3796                span.line,
3797                span.col,
3798            )
3799            .into()),
3800        }
3801    }
3802}
3803
3804pub fn register_builtins(env: &mut Env, output: Arc<dyn OutputHandler>) {
3805    // I/O: moved to io:: module
3806    // Math: moved to math:: module
3807    // JSON/Msgpack: moved to json:: module
3808
3809    env.define(
3810        ion_str!("len").to_string(),
3811        Value::BuiltinFn(ion_str!("len").to_string(), |args| {
3812            if args.is_empty() {
3813                return Err(ion_str!("len() requires 1 argument"));
3814            }
3815            match &args[0] {
3816                Value::List(items) => Ok(Value::Int(items.len() as i64)),
3817                Value::Str(s) => Ok(Value::Int(s.len() as i64)),
3818                Value::Dict(map) => Ok(Value::Int(map.len() as i64)),
3819                Value::Bytes(b) => Ok(Value::Int(b.len() as i64)),
3820                _ => Err(format!(
3821                    "{}{}",
3822                    ion_str!("len() not supported for "),
3823                    args[0].type_name()
3824                )),
3825            }
3826        }),
3827        false,
3828    );
3829    env.define(
3830        ion_str!("range").to_string(),
3831        Value::BuiltinFn(ion_str!("range").to_string(), |args| match args.len() {
3832            1 => {
3833                let n = args[0].as_int().ok_or(ion_str!("range requires int"))?;
3834                Ok(Value::Range {
3835                    start: 0,
3836                    end: n,
3837                    inclusive: false,
3838                })
3839            }
3840            2 => {
3841                let start = args[0].as_int().ok_or(ion_str!("range requires int"))?;
3842                let end = args[1].as_int().ok_or(ion_str!("range requires int"))?;
3843                Ok(Value::Range {
3844                    start,
3845                    end,
3846                    inclusive: false,
3847                })
3848            }
3849            _ => Err(ion_str!("range takes 1 or 2 arguments").to_string()),
3850        }),
3851        false,
3852    );
3853    env.define(
3854        ion_str!("set"),
3855        Value::BuiltinFn(ion_str!("set"), |args| {
3856            if args.is_empty() {
3857                return Ok(Value::Set(vec![]));
3858            }
3859            match &args[0] {
3860                Value::List(items) => {
3861                    let mut unique = Vec::new();
3862                    for v in items {
3863                        if !unique.iter().any(|x| x == v) {
3864                            unique.push(v.clone());
3865                        }
3866                    }
3867                    Ok(Value::Set(unique))
3868                }
3869                _ => Err(ion_str!("set() requires a list argument")),
3870            }
3871        }),
3872        false,
3873    );
3874    env.define(
3875        ion_str!("cell"),
3876        Value::BuiltinFn(ion_str!("cell"), |args| {
3877            if args.len() != 1 {
3878                return Err(ion_str!("cell() takes 1 argument"));
3879            }
3880            Ok(Value::Cell(std::sync::Arc::new(std::sync::Mutex::new(
3881                args[0].clone(),
3882            ))))
3883        }),
3884        false,
3885    );
3886    env.define(
3887        ion_str!("type_of").to_string(),
3888        Value::BuiltinFn(ion_str!("type_of").to_string(), |args| {
3889            if args.is_empty() {
3890                return Err(ion_str!("type_of() requires 1 argument"));
3891            }
3892            Ok(Value::Str(args[0].type_name().to_string()))
3893        }),
3894        false,
3895    );
3896    // json_encode, json_decode, abs, min, max → moved to json:: / math:: modules
3897    env.define(
3898        ion_str!("str").to_string(),
3899        Value::BuiltinFn(ion_str!("str").to_string(), |args| {
3900            if args.len() != 1 {
3901                return Err(ion_str!("str takes 1 argument"));
3902            }
3903            Ok(Value::Str(args[0].to_string()))
3904        }),
3905        false,
3906    );
3907    env.define(
3908        ion_str!("int").to_string(),
3909        Value::BuiltinFn(ion_str!("int").to_string(), |args| {
3910            if args.len() != 1 {
3911                return Err(ion_str!("int takes 1 argument"));
3912            }
3913            match &args[0] {
3914                Value::Int(n) => Ok(Value::Int(*n)),
3915                Value::Float(n) => Ok(Value::Int(*n as i64)),
3916                Value::Str(s) => s.parse::<i64>().map(Value::Int).map_err(|_| {
3917                    format!(
3918                        "{}{}{}",
3919                        ion_str!("cannot convert '"),
3920                        s,
3921                        ion_str!("' to int")
3922                    )
3923                }),
3924                Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
3925                _ => Err(format!(
3926                    "{}{}",
3927                    ion_str!("cannot convert "),
3928                    args[0].type_name()
3929                )),
3930            }
3931        }),
3932        false,
3933    );
3934    env.define(
3935        ion_str!("float").to_string(),
3936        Value::BuiltinFn(ion_str!("float").to_string(), |args| {
3937            if args.len() != 1 {
3938                return Err(ion_str!("float takes 1 argument"));
3939            }
3940            match &args[0] {
3941                Value::Float(n) => Ok(Value::Float(*n)),
3942                Value::Int(n) => Ok(Value::Float(*n as f64)),
3943                Value::Str(s) => s.parse::<f64>().map(Value::Float).map_err(|_| {
3944                    format!(
3945                        "{}{}{}",
3946                        ion_str!("cannot convert '"),
3947                        s,
3948                        ion_str!("' to float")
3949                    )
3950                }),
3951                _ => Err(format!(
3952                    "{}{}",
3953                    ion_str!("cannot convert "),
3954                    args[0].type_name()
3955                )),
3956            }
3957        }),
3958        false,
3959    );
3960    // floor, ceil, round, pow, sqrt, clamp → moved to math:: module
3961    // join → moved to str:: module
3962    // json_encode_pretty, msgpack_encode, msgpack_decode → moved to json:: module
3963    env.define(
3964        ion_str!("enumerate").to_string(),
3965        Value::BuiltinFn(ion_str!("enumerate").to_string(), |args| {
3966            if args.len() != 1 {
3967                return Err(ion_str!("enumerate takes 1 argument"));
3968            }
3969            match &args[0] {
3970                Value::List(items) => Ok(Value::List(
3971                    items
3972                        .iter()
3973                        .enumerate()
3974                        .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
3975                        .collect(),
3976                )),
3977                Value::Str(s) => Ok(Value::List(
3978                    s.chars()
3979                        .enumerate()
3980                        .map(|(i, c)| {
3981                            Value::Tuple(vec![Value::Int(i as i64), Value::Str(c.to_string())])
3982                        })
3983                        .collect(),
3984                )),
3985                Value::Dict(map) => Ok(Value::List(
3986                    map.iter()
3987                        .enumerate()
3988                        .map(|(i, (k, v))| {
3989                            Value::Tuple(vec![
3990                                Value::Int(i as i64),
3991                                Value::Tuple(vec![Value::Str(k.clone()), v.clone()]),
3992                            ])
3993                        })
3994                        .collect(),
3995                )),
3996                _ => Err(format!(
3997                    "{}{}",
3998                    ion_str!("enumerate() not supported for "),
3999                    args[0].type_name()
4000                )),
4001            }
4002        }),
4003        false,
4004    );
4005
4006    env.define(
4007        ion_str!("bytes").to_string(),
4008        Value::BuiltinFn(ion_str!("bytes").to_string(), |args| match args.first() {
4009            Some(Value::List(items)) => {
4010                let mut bytes = Vec::with_capacity(items.len());
4011                for item in items {
4012                    let n = item
4013                        .as_int()
4014                        .ok_or_else(|| ion_str!("bytes() list items must be ints"))?;
4015                    if !(0..=255).contains(&n) {
4016                        return Err(format!("{}{}", ion_str!("byte value out of range: "), n));
4017                    }
4018                    bytes.push(n as u8);
4019                }
4020                Ok(Value::Bytes(bytes))
4021            }
4022            Some(Value::Str(s)) => Ok(Value::Bytes(s.as_bytes().to_vec())),
4023            Some(Value::Int(n)) if *n >= 0 && *n <= 10_000_000 => {
4024                Ok(Value::Bytes(vec![0u8; *n as usize]))
4025            }
4026            Some(Value::Int(n)) => Err(format!(
4027                "bytes(n): size {} is out of range (0..10_000_000)",
4028                n
4029            )),
4030            None => Ok(Value::Bytes(Vec::new())),
4031            _ => Err(format!(
4032                "{}{}",
4033                ion_str!("bytes() not supported for "),
4034                args[0].type_name()
4035            )),
4036        }),
4037        false,
4038    );
4039    env.define(
4040        ion_str!("bytes_from_hex").to_string(),
4041        Value::BuiltinFn(ion_str!("bytes_from_hex").to_string(), |args| {
4042            if args.len() != 1 {
4043                return Err(ion_str!("bytes_from_hex takes 1 argument"));
4044            }
4045            let s = args[0]
4046                .as_str()
4047                .ok_or_else(|| ion_str!("bytes_from_hex requires a string"))?;
4048            if !s.is_ascii() {
4049                return Err(ion_str!("bytes_from_hex requires an ASCII hex string"));
4050            }
4051            if s.len() % 2 != 0 {
4052                return Err(ion_str!("hex string must have even length").to_string());
4053            }
4054            let mut bytes = Vec::with_capacity(s.len() / 2);
4055            for i in (0..s.len()).step_by(2) {
4056                let byte = u8::from_str_radix(&s[i..i + 2], 16)
4057                    .map_err(|_| format!("{}{}", ion_str!("invalid hex: "), &s[i..i + 2]))?;
4058                bytes.push(byte);
4059            }
4060            Ok(Value::Bytes(bytes))
4061        }),
4062        false,
4063    );
4064
4065    env.define(
4066        ion_str!("assert").to_string(),
4067        Value::BuiltinFn(ion_str!("assert").to_string(), |args| {
4068            if args.is_empty() {
4069                return Err(ion_str!("assert requires at least 1 argument").to_string());
4070            }
4071            let condition = match &args[0] {
4072                Value::Bool(b) => *b,
4073                _ => {
4074                    return Err(format!(
4075                        "{}{}",
4076                        ion_str!("assert condition must be bool, got "),
4077                        args[0].type_name()
4078                    ))
4079                }
4080            };
4081            if !condition {
4082                let msg = if args.len() > 1 {
4083                    args[1].to_string()
4084                } else {
4085                    ion_str!("assertion failed").to_string()
4086                };
4087                return Err(msg);
4088            }
4089            Ok(Value::Unit)
4090        }),
4091        false,
4092    );
4093
4094    env.define(
4095        ion_str!("assert_eq").to_string(),
4096        Value::BuiltinFn(ion_str!("assert_eq").to_string(), |args| {
4097            if args.len() < 2 {
4098                return Err(ion_str!("assert_eq requires at least 2 arguments").to_string());
4099            }
4100            if args[0] != args[1] {
4101                let msg = if args.len() > 2 {
4102                    format!(
4103                        "{}{}{}{}{}",
4104                        args[2],
4105                        ion_str!(": expected "),
4106                        args[0],
4107                        ion_str!(", got "),
4108                        args[1]
4109                    )
4110                } else {
4111                    format!(
4112                        "{}{}{}{}",
4113                        ion_str!("assertion failed: expected "),
4114                        args[0],
4115                        ion_str!(", got "),
4116                        args[1]
4117                    )
4118                };
4119                return Err(msg);
4120            }
4121            Ok(Value::Unit)
4122        }),
4123        false,
4124    );
4125
4126    #[cfg(feature = "concurrency")]
4127    {
4128        env.define(
4129            ion_str!("sleep").to_string(),
4130            Value::BuiltinFn(ion_str!("sleep").to_string(), |args| {
4131                let ms = args
4132                    .first()
4133                    .and_then(|v| v.as_int())
4134                    .ok_or(ion_str!("sleep requires int (ms)"))?;
4135                crate::async_rt::sleep(std::time::Duration::from_millis(ms as u64));
4136                Ok(Value::Unit)
4137            }),
4138            false,
4139        );
4140        env.define(
4141            ion_str!("timeout").to_string(),
4142            Value::BuiltinFn(ion_str!("timeout").to_string(), |_args| {
4143                // Actual implementation is in call_value (needs interpreter context)
4144                Err(ion_str!("timeout: internal error (should not reach here)"))
4145            }),
4146            false,
4147        );
4148        env.define(
4149            ion_str!("channel").to_string(),
4150            Value::BuiltinFn(ion_str!("channel").to_string(), |args| {
4151                let buffer = if args.is_empty() {
4152                    16
4153                } else {
4154                    args[0]
4155                        .as_int()
4156                        .ok_or(ion_str!("channel buffer size must be int"))?
4157                        as usize
4158                };
4159                let (tx, rx) = crate::async_rt::create_channel(buffer);
4160                Ok(Value::Tuple(vec![tx, rx]))
4161            }),
4162            false,
4163        );
4164    }
4165
4166    // Register stdlib modules (math, json, io)
4167    crate::stdlib::register_stdlib_with_output(env, output);
4168}