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