Skip to main content

konjure_lang/
runtime.rs

1//! Persistent, ordered ECS execution with atomic ticks and action calls.
2use crate::ast::*;
3use crate::data::{self, AxisIndex, DType, Scalar, Tensor};
4use crate::native::{
5    NativeRegistry, is_builtin, monomorphic_signature, valid_builtin_specialization,
6};
7use crate::program::{MAIN, Program};
8use crate::value::*;
9use serde::{Deserialize, Serialize};
10use std::collections::{BTreeMap, BTreeSet};
11
12/// Resource bounds apply to each initialization, invocation, and tick.
13#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(default)]
15pub struct Limits {
16    /// Maximum expression, statement, call, and loop steps per transaction.
17    pub fuel_per_operation: usize,
18    /// Maximum simultaneous function and constructor calls.
19    pub max_call_depth: usize,
20    /// Maximum live ECS entities.
21    pub max_entities: usize,
22    /// Maximum retained log entries; older entries are evicted.
23    pub max_logs: usize,
24    /// Maximum retained execution trace entries; older entries are evicted.
25    pub max_trace: usize,
26    /// Maximum list elements or record fields in one value.
27    pub max_collection: usize,
28    /// Maximum UTF-8 bytes in a string or rendered log entry.
29    pub max_string_bytes: usize,
30    /// Maximum nested runtime values and expression evaluation depth.
31    pub max_value_depth: usize,
32    /// Maximum total scalar/container nodes and initialized lifecycle memberships.
33    pub max_stored_values: usize,
34    /// Conservative payload/container bytes allowed in persistent globals and components.
35    pub max_storage_bytes: usize,
36    /// Conservative bytes of produced or cloned values across one operation.
37    /// This also bounds temporary locals and arguments before they reach persistent state.
38    pub max_allocation_bytes: usize,
39}
40impl Default for Limits {
41    fn default() -> Self {
42        Self {
43            fuel_per_operation: 100_000,
44            max_call_depth: 64,
45            max_entities: 2048,
46            max_logs: 256,
47            max_trace: 2048,
48            max_collection: 4096,
49            max_string_bytes: 65536,
50            max_value_depth: 64,
51            max_stored_values: 100_000,
52            max_storage_bytes: 8 * 1024 * 1024,
53            max_allocation_bytes: 32 * 1024 * 1024,
54        }
55    }
56}
57/// One stable entity with at most one component of each nominal class.
58#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60pub struct EntitySnapshot {
61    /// Stable creation-order entity ID; IDs are never reused.
62    #[cfg_attr(feature = "typescript", ts(type = "number"))]
63    pub id: u64,
64    /// One value per nominal component class, sorted by class key.
65    pub components: BTreeMap<String, Value>,
66    /// Source span of the spawn call that created this entity.
67    pub source: Span,
68}
69/// A bounded chronological execution record, linked to the exact source expression.
70#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
71#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
72pub struct TraceEntry {
73    /// Executed expression or statement source range.
74    pub span: Span,
75    /// Execution operation, such as statement, call, system, or spawn.
76    pub event: String,
77    /// Current system or action entity, if execution has one.
78    #[cfg_attr(feature = "typescript", ts(type = "number | null"))]
79    pub entity: Option<u64>,
80}
81/// Immutable inspection output. All maps and entity queries use stable ordering.
82#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
83#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
84pub struct Snapshot {
85    /// Whether final lifecycle callbacks have completed; finished machines are immutable.
86    pub finished: bool,
87    /// Number of accepted simulation ticks.
88    #[cfg_attr(feature = "typescript", ts(type = "number"))]
89    pub tick: u64,
90    /// Accumulated accepted tick durations in seconds.
91    pub time: f64,
92    /// Owned global values; imported-module bindings use qualified keys.
93    pub globals: BTreeMap<String, Value>,
94    /// Live entities in ascending ID order.
95    pub entities: Vec<EntitySnapshot>,
96    /// Recent print output in chronological order; no external I/O is performed.
97    pub logs: Vec<String>,
98    /// Recent source-linked operations from the accepted transaction.
99    pub trace: Vec<TraceEntry>,
100}
101#[derive(Clone)]
102struct Binding {
103    value: Value,
104    ty: Type,
105    module: String,
106    mutable: bool,
107}
108#[derive(Clone, Default)]
109struct State {
110    finished: bool,
111    // Membership is checkpointed with values so failed callbacks never consume init/done.
112    initialized: BTreeSet<(usize, u64, Vec<String>)>,
113    initialized_bytes: usize,
114    pending_despawns: BTreeSet<u64>,
115    tick: u64,
116    time: f64,
117    next_id: u64,
118    globals: BTreeMap<String, Binding>,
119    entities: BTreeMap<u64, EntitySnapshot>,
120    logs: Vec<String>,
121    trace: Vec<TraceEntry>,
122}
123/// A linked program and its persistent state. Clone creates an independent checkpoint.
124#[derive(Clone)]
125pub struct Machine {
126    program: Program,
127    registry: NativeRegistry,
128    limits: Limits,
129    state: State,
130}
131impl Machine {
132    /// Initialize supplied modules once in dependency order, then the main module.
133    /// # Errors
134    /// Rejects invalid initializers or resource limits without creating a machine.
135    pub fn new(program: Program, limits: Limits) -> Result<Self, Diagnostic> {
136        Self::with_registry(program, limits, NativeRegistry::default())
137    }
138    /// Construct with explicitly registered pure host operations.
139    ///
140    /// # Errors
141    /// Returns initializer/type/resource diagnostics exactly as [`Self::new`].
142    pub fn with_registry(
143        program: Program,
144        limits: Limits,
145        registry: NativeRegistry,
146    ) -> Result<Self, Diagnostic> {
147        if program.native_signatures != registry.signatures() {
148            return Err(Diagnostic::new(
149                "native_registry_mismatch",
150                "machine native signatures must match the registry used to compile the program",
151                Span::new(MAIN, 0, 0),
152            ));
153        }
154        let mut machine = Self {
155            program,
156            limits,
157            registry,
158            state: State {
159                next_id: 1,
160                ..State::default()
161            },
162        };
163        let mut evaluator = Evaluator::new(
164            &machine.program,
165            &machine.registry,
166            &machine.limits,
167            &mut machine.state,
168        );
169        for module in &machine.program.order {
170            let mut env = Env::default();
171            for statement in &machine.program.modules[module].statements {
172                let result = evaluator
173                    .statement(statement, module, &mut env, true)
174                    .map_err(|error| evaluator.host_propagation(error));
175                if !matches!(result?, Flow::Next) {
176                    return Err(Diagnostic::new(
177                        "invalid_control",
178                        "ret/break/continue are not allowed at module scope",
179                        statement.span.clone(),
180                    ));
181                }
182            }
183        }
184        evaluator.flush_despawns()?;
185        evaluator.check_storage(&Span::new(MAIN, 0, 0))?;
186        Ok(machine)
187    }
188    /// A read-only projection of the accepted state.
189    pub fn snapshot(&self) -> Snapshot {
190        Snapshot {
191            finished: self.state.finished,
192            tick: self.state.tick,
193            time: self.state.time,
194            globals: self
195                .state
196                .globals
197                .iter()
198                .map(|(k, b)| (k.clone(), b.value.clone()))
199                .collect(),
200            entities: self.state.entities.values().cloned().collect(),
201            logs: self.state.logs.clone(),
202            trace: self.state.trace.clone(),
203        }
204    }
205
206    /// Returns the current fixed tick and terminal lifecycle state without
207    /// cloning entities, logs, traces, or component values.
208    #[must_use]
209    pub const fn execution_state(&self) -> (u64, bool) {
210        (self.state.tick, self.state.finished)
211    }
212    /// The immutable linked program that defines this machine.
213    pub fn program(&self) -> &Program {
214        &self.program
215    }
216    /// The resource limits applied to every accepted transaction.
217    pub fn limits(&self) -> &Limits {
218        &self.limits
219    }
220    /// Run all systems in linked source order. A system visits its starting query
221    /// in ascending entity-ID order. Spawns are visible to subsequent systems.
222    /// New tuples run `init` immediately before their first `frame`. A despawn
223    /// request takes effect after the current callback writes its bindings back;
224    /// initialized tuples then run `done` before the entity is deleted.
225    /// # Errors
226    /// Any error rolls back the complete tick, including logs and entity IDs.
227    pub fn tick(&mut self, dt: f64) -> Result<Snapshot, Diagnostic> {
228        self.ensure_running()?;
229        let span = Span::new(MAIN, 0, 0);
230        if !dt.is_finite() || dt <= 0.0 {
231            return Err(Diagnostic::new(
232                "invalid_timestep",
233                "dt must be finite and positive",
234                span,
235            ));
236        }
237        let mut state = self.state.clone();
238        state.trace.clear();
239        state.time += dt;
240        if !state.time.is_finite() {
241            return Err(Diagnostic::new(
242                "numeric_overflow",
243                "simulation time overflow",
244                span,
245            ));
246        }
247        state.tick = state.tick.checked_add(1).ok_or_else(|| {
248            Diagnostic::new("tick_overflow", "tick counter exhausted", span.clone())
249        })?;
250        let mut evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut state);
251        for (index, (module, system)) in self.program.systems.iter().enumerate() {
252            evaluator.consume(&system.span)?;
253            let selectors = system
254                .bindings
255                .iter()
256                .map(|binding| {
257                    let Type::Named(name) = &binding.ty else {
258                        unreachable!("checked system query")
259                    };
260                    self.program.query_classes(module, name, &binding.span)
261                })
262                .collect::<Result<Vec<_>, _>>()?;
263            let mut targets = Vec::new();
264            let entity_ids: Vec<_> = evaluator.state.entities.keys().copied().collect();
265            for id in entity_ids {
266                evaluator.consume(&system.span)?;
267                let entity = &evaluator.state.entities[&id];
268                if selectors.len() == 1 {
269                    for key in &selectors[0] {
270                        if entity.components.contains_key(key) {
271                            targets.push((id, vec![key.clone()]));
272                        }
273                    }
274                } else {
275                    let keys: Vec<_> = selectors.iter().map(|keys| keys[0].clone()).collect();
276                    if keys.iter().all(|key| entity.components.contains_key(key)) {
277                        targets.push((id, keys));
278                    }
279                }
280            }
281            for (id, keys) in targets {
282                if !evaluator.state.entities.contains_key(&id)
283                    || evaluator.state.pending_despawns.contains(&id)
284                {
285                    continue;
286                }
287                if evaluator.initialize_membership(index, id, &keys, &system.span)? {
288                    evaluator.system_callback(index, id, &keys, "init", dt)?;
289                    evaluator.flush_despawns()?;
290                }
291                if !evaluator.state.entities.contains_key(&id) {
292                    continue;
293                }
294                evaluator.system_callback(index, id, &keys, "frame", dt)?;
295                evaluator.flush_despawns()?;
296            }
297        }
298        evaluator.check_storage(&span)?;
299        self.state = state;
300        Ok(self.snapshot())
301    }
302    /// Complete initialized system memberships in system/entity/component order.
303    /// Repeated calls return the same snapshot. No callback runs for an unvisited tuple.
304    /// # Errors
305    /// A failed callback rolls back all cleanup and keeps the machine running.
306    pub fn finish(&mut self) -> Result<Snapshot, Diagnostic> {
307        if self.state.finished {
308            return Ok(self.snapshot());
309        }
310        let mut state = self.state.clone();
311        state.trace.clear();
312        let mut evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut state);
313        while let Some((index, id, keys)) = evaluator.state.initialized.pop_first() {
314            evaluator.state.initialized_bytes -= membership_bytes(&keys);
315            evaluator.system_callback(index, id, &keys, "done", 0.0)?;
316            evaluator.flush_despawns()?;
317        }
318        evaluator.check_storage(&Span::new(MAIN, 0, 0))?;
319        state.finished = true;
320        self.state = state;
321        Ok(self.snapshot())
322    }
323    fn ensure_running(&self) -> Result<(), Diagnostic> {
324        if self.state.finished {
325            Err(Diagnostic::new(
326                "machine_finished",
327                "finished machines cannot accept state changes; create a new machine to restart",
328                Span::new(MAIN, 0, 0),
329            ))
330        } else {
331            Ok(())
332        }
333    }
334    /// Invoke a main or explicitly imported/exported function transactionally.
335    /// This is the shared action entry for UI controls, tests, and agent adapters.
336    ///
337    /// # Errors
338    /// Rejects inaccessible/unknown names, invalid arguments or execution, and
339    /// exhausted budgets. The previously accepted state remains unchanged.
340    pub fn invoke(&mut self, name: &str, arguments: Vec<Value>) -> Result<Value, Diagnostic> {
341        self.ensure_running()?;
342        let mut state = self.state.clone();
343        state.trace.clear();
344        let mut evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut state);
345        let span = self.program.modules[MAIN]
346            .functions
347            .get(name)
348            .map(|f| f.span.clone())
349            .unwrap_or_else(|| Span::new(MAIN, 0, 0));
350        for value in &arguments {
351            evaluator.validate_external_value(value, &span)?;
352        }
353        let value = evaluator.call_name(name, arguments, MAIN, &span)?;
354        evaluator.flush_despawns()?;
355        evaluator.check_storage(&span)?;
356        self.state = state;
357        Ok(value)
358    }
359    /// Invoke the typed function stored in an exact component field. The host
360    /// supplies no function identity; private callbacks retained by the program
361    /// remain callable. Lookup, execution, logs and world edits commit together.
362    ///
363    /// # Errors
364    /// Rejects missing entity/component/field, non-callable fields, mismatched
365    /// arguments and exhausted budgets, preserving the accepted state.
366    pub fn invoke_component(
367        &mut self,
368        id: u64,
369        component: &str,
370        field: &str,
371        arguments: Vec<Value>,
372    ) -> Result<Value, Diagnostic> {
373        self.ensure_running()?;
374        let span = Span::new(MAIN, 0, 0);
375        let mut state = self.state.clone();
376        state.trace.clear();
377        let mut evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut state);
378        let value = evaluator.component_value(id, component, &span)?;
379        let callback = value.field(field).cloned().ok_or_else(|| {
380            evaluator.error(
381                "unknown_field",
382                format!("{component} has no field `{field}`"),
383                &span,
384            )
385        })?;
386        for value in &arguments {
387            evaluator.validate_external_value(value, &span)?;
388        }
389        let result = evaluator.call_value(callback, arguments, &span)?;
390        evaluator.flush_despawns()?;
391        evaluator.check_storage(&span)?;
392        self.state = state;
393        Ok(result)
394    }
395    /// Dispatch a method on exactly one component. Multiple matching components
396    /// are ambiguous and rejected; mutation and logs commit together on success.
397    ///
398    /// # Errors
399    /// Rejects missing/ambiguous methods, invalid arguments, conflicting component
400    /// writes, and execution limits without changing accepted state.
401    pub fn invoke_entity(
402        &mut self,
403        id: u64,
404        name: &str,
405        arguments: Vec<Value>,
406    ) -> Result<Value, Diagnostic> {
407        self.ensure_running()?;
408        let span = Span::new(MAIN, 0, 0);
409        let entity = self.state.entities.get(&id).ok_or_else(|| {
410            Diagnostic::new(
411                "unknown_entity",
412                format!("entity {id} does not exist"),
413                span.clone(),
414            )
415        })?;
416        let matches: Vec<_> = entity
417            .components
418            .iter()
419            .filter_map(|(key, value)| {
420                self.program
421                    .methods
422                    .get(key)
423                    .and_then(|m| m.get(name))
424                    .map(|f| (key.clone(), value.clone(), f.clone()))
425            })
426            .collect();
427        let [(key, original, function)] = matches.as_slice() else {
428            return Err(Diagnostic::new(
429                if matches.is_empty() {
430                    "unknown_method"
431                } else {
432                    "ambiguous_method"
433                },
434                format!(
435                    "entity {id} has {} components implementing `{name}`; exactly one is required",
436                    matches.len()
437                ),
438                span,
439            ));
440        };
441        let mut state = self.state.clone();
442        state.trace.clear();
443        let mut evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut state);
444        evaluator.entity = Some(id);
445        for value in &arguments {
446            evaluator.validate_external_value(value, &function.span)?;
447        }
448        let (result, updated) = evaluator.function(
449            function,
450            arguments,
451            &function.span.module,
452            Some(original.clone()),
453            &function.span,
454        )?;
455        if let Some(updated) = updated
456            && let Some(entity) = evaluator.state.entities.get_mut(&id)
457            && let Some(current) = entity.components.get(key)
458        {
459            if current != original && updated != *original && current != &updated {
460                return Err(Diagnostic::new(
461                    "conflicting_component_write",
462                    "method changed self and explicitly set the same component",
463                    function.span.clone(),
464                ));
465            }
466            if updated != *original {
467                entity.components.insert(key.clone(), updated);
468            }
469        }
470        evaluator.flush_despawns()?;
471        evaluator.check_storage(&function.span)?;
472        self.state = state;
473        Ok(result)
474    }
475    /// Atomically attach or replace validated components from an explicit host
476    /// adapter. Existing entity identity and spawn source are preserved.
477    ///
478    /// # Errors
479    /// Rejects unknown entities, malformed records, and storage limit violations.
480    pub fn upsert_components(&mut self, patches: &[(u64, Value)]) -> Result<(), Diagnostic> {
481        self.ensure_running()?;
482        let mut candidate = self.state.clone();
483        let evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut candidate);
484        for (_, value) in patches {
485            evaluator.validate_external_value(value, &Span::new(MAIN, 0, 0))?;
486        }
487        self.upsert_components_for_adapter(patches)
488    }
489    pub(crate) fn upsert_components_for_adapter(
490        &mut self,
491        patches: &[(u64, Value)],
492    ) -> Result<(), Diagnostic> {
493        let mut state = self.state.clone();
494        let span = Span::new("host", 0, 0);
495        let mut evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut state);
496        for (id, value) in patches {
497            let add = evaluator
498                .state
499                .entities
500                .get(id)
501                .is_some_and(|e| !e.components.contains_key(value.type_name()));
502            evaluator.set_component(*id, value.clone(), add, &span)?;
503        }
504        evaluator.check_storage(&span)?;
505        self.state = state;
506        Ok(())
507    }
508    /// Atomically replace existing components from a host adapter (for example,
509    /// a physics solver). Class fields and all storage limits are revalidated.
510    ///
511    /// # Errors
512    /// Rejects missing entities/components, malformed records, and storage limits;
513    /// either the complete patch is accepted or none of it is applied.
514    pub fn patch_components(&mut self, patches: &[(u64, Value)]) -> Result<(), Diagnostic> {
515        self.ensure_running()?;
516        let mut candidate = self.state.clone();
517        let evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut candidate);
518        for (_, value) in patches {
519            evaluator.validate_external_value(value, &Span::new(MAIN, 0, 0))?;
520        }
521        self.patch_components_for_adapter(patches)
522    }
523    pub(crate) fn patch_components_for_adapter(
524        &mut self,
525        patches: &[(u64, Value)],
526    ) -> Result<(), Diagnostic> {
527        let mut state = self.state.clone();
528        let mut evaluator = Evaluator::new(&self.program, &self.registry, &self.limits, &mut state);
529        let span = Span::new("host", 0, 0);
530        for (id, value) in patches {
531            evaluator.set_component(*id, value.clone(), false, &span)?;
532        }
533        evaluator.check_storage(&span)?;
534        self.state = state;
535        Ok(())
536    }
537    /// Roll back this machine when a composed operation fails.
538    ///
539    /// # Errors
540    /// Propagates the closure diagnostic without replacing this machine. The
541    /// closure must include any adapter state in its own transaction; this method
542    /// cannot undo external I/O, global Rust state, or effects on other objects.
543    pub fn transaction<T>(
544        &mut self,
545        operation: impl FnOnce(&mut Self) -> Result<T, Diagnostic>,
546    ) -> Result<T, Diagnostic> {
547        let mut candidate = self.clone();
548        let result = operation(&mut candidate)?;
549        *self = candidate;
550        Ok(result)
551    }
552}
553#[derive(Clone)]
554struct Env {
555    scopes: Vec<BTreeMap<String, Binding>>,
556}
557impl Default for Env {
558    fn default() -> Self {
559        Self {
560            scopes: vec![BTreeMap::new()],
561        }
562    }
563}
564impl Env {
565    fn bind(&mut self, name: &str, value: Value, mutable: bool, module: &str) {
566        let ty = infer_type(&value);
567        self.scopes.last_mut().expect("scope exists").insert(
568            name.into(),
569            Binding {
570                value,
571                ty,
572                module: module.into(),
573                mutable,
574            },
575        );
576    }
577    fn get(&self, name: &str) -> Option<&Binding> {
578        self.scopes.iter().rev().find_map(|s| s.get(name))
579    }
580    fn get_mut(&mut self, name: &str) -> Option<&mut Binding> {
581        self.scopes.iter_mut().rev().find_map(|s| s.get_mut(name))
582    }
583}
584fn expression_path(expr: &Expr) -> Option<String> {
585    match &expr.kind {
586        ExprKind::Name(n) => Some(n.clone()),
587        ExprKind::Field { object, field } => {
588            Some(format!("{}.{}", expression_path(object)?, field))
589        }
590        _ => None,
591    }
592}
593fn value_bytes(value: &Value) -> usize {
594    let base = std::mem::size_of::<Value>();
595    match value {
596        Value::Enum(value) => value.payload.iter().fold(
597            base.saturating_add(type_bytes(&value.ty))
598                .saturating_add(value.case.len())
599                .saturating_add(value.origin.as_ref().map_or(0, |origin| {
600                    std::mem::size_of::<Span>().saturating_add(origin.module.len())
601                })),
602            |size, value| size.saturating_add(value_bytes(value)),
603        ),
604        Value::Text(s) => base.saturating_add(s.len()),
605        Value::Bin(b) => base.saturating_add(b.byte_len()),
606        Value::Tensor(t) => base
607            .saturating_add(t.storage_byte_len())
608            .saturating_add(t.shape().len().saturating_mul(16)),
609        Value::ComponentReference(reference) => base.saturating_add(reference.class.len()),
610        Value::Function(function) => {
611            let target = match &function.target {
612                FunctionTarget::DataMethod {
613                    receiver_type,
614                    method,
615                    type_argument,
616                    receiver,
617                } => type_bytes(receiver_type)
618                    .saturating_add(method.len())
619                    .saturating_add(type_argument.as_deref().map_or(0, type_bytes))
620                    .saturating_add(value_bytes(receiver)),
621                FunctionTarget::EnumConstructor { ty, case } => {
622                    type_bytes(ty).saturating_add(case.len())
623                }
624                FunctionTarget::Named { module, name } => module.len().saturating_add(name.len()),
625                FunctionTarget::Native { name } => name.len(),
626                FunctionTarget::Builtin {
627                    name,
628                    type_argument,
629                } => name
630                    .len()
631                    .saturating_add(type_argument.as_deref().map_or(0, type_bytes)),
632                FunctionTarget::BoundMethod {
633                    class,
634                    method,
635                    receiver,
636                } => class
637                    .len()
638                    .saturating_add(method.len())
639                    .saturating_add(value_bytes(receiver)),
640                FunctionTarget::EntityMethod { class, method, .. } => {
641                    class.len().saturating_add(method.len())
642                }
643            };
644            base.saturating_add(target)
645                .saturating_add(type_bytes(&function.signature))
646        }
647        Value::List(values) => values
648            .iter()
649            .fold(base, |n, v| n.saturating_add(value_bytes(v))),
650        Value::Record(record) => {
651            record
652                .fields
653                .iter()
654                .fold(base.saturating_add(record.class.len()), |n, (k, v)| {
655                    n.saturating_add(k.len() + 64)
656                        .saturating_add(value_bytes(v))
657                })
658        }
659        _ => base,
660    }
661}
662fn type_bytes(ty: &Type) -> usize {
663    let base = std::mem::size_of::<Type>();
664    match ty {
665        Type::Applied { name, arguments } => arguments
666            .iter()
667            .fold(base.saturating_add(name.len()), |size, ty| {
668                size.saturating_add(type_bytes(ty))
669            }),
670        Type::Named(name) => base.saturating_add(name.len()),
671        Type::List(inner) | Type::Tensor(inner) | Type::ComponentReference(inner) => {
672            base.saturating_add(type_bytes(inner))
673        }
674        Type::Function {
675            parameters,
676            returns,
677        } => parameters
678            .iter()
679            .fold(base.saturating_add(type_bytes(returns)), |size, ty| {
680                size.saturating_add(type_bytes(ty))
681            }),
682    }
683}
684fn membership_bytes(keys: &[String]) -> usize {
685    keys.iter()
686        .fold(96usize, |total, key| total.saturating_add(key.len() + 32))
687}
688fn canonical_type_scope(ty: &Type) -> &str {
689    match ty {
690        Type::List(inner) | Type::Tensor(inner) | Type::ComponentReference(inner) => {
691            canonical_type_scope(inner)
692        }
693        Type::Function { .. } | Type::Applied { .. } => MAIN,
694        Type::Named(name) => name.rsplit_once('.').map_or(MAIN, |(module, _)| module),
695    }
696}
697fn infer_type(value: &Value) -> Type {
698    match value {
699        Value::Enum(value) => value.ty.clone(),
700        Value::Tensor(t) => data::tensor_type(Type::named(data::dtype_name(t.dtype()))),
701        Value::List(v) => Type::List(Box::new(
702            v.first()
703                .map(infer_type)
704                .unwrap_or_else(|| Type::named("Any")),
705        )),
706        Value::Function(function) => function.signature.clone(),
707        Value::ComponentReference(reference) => {
708            Type::ComponentReference(Box::new(Type::named(&reference.class)))
709        }
710        _ => Type::named(value.type_name()),
711    }
712}
713enum Flow {
714    Next,
715    Return(Value),
716    Break,
717    Continue,
718}
719struct Evaluator<'a> {
720    program: &'a Program,
721    registry: &'a NativeRegistry,
722    limits: &'a Limits,
723    state: &'a mut State,
724    fuel: usize,
725    depth: usize,
726    expression_depth: usize,
727    allocated_bytes: usize,
728    entity: Option<u64>,
729    // A private nonlocal-return slot. The sentinel diagnostic only unwinds Rust
730    // evaluation frames; a checked function boundary consumes it as ordinary data.
731    propagated: Option<Value>,
732}
733impl<'a> Evaluator<'a> {
734    fn new(
735        program: &'a Program,
736        registry: &'a NativeRegistry,
737        limits: &'a Limits,
738        state: &'a mut State,
739    ) -> Self {
740        Self {
741            propagated: None,
742            program,
743            registry,
744            limits,
745            state,
746            fuel: limits.fuel_per_operation,
747            depth: 0,
748            expression_depth: 0,
749            allocated_bytes: 0,
750            entity: None,
751        }
752    }
753    fn initialize_membership(
754        &mut self,
755        index: usize,
756        id: u64,
757        keys: &[String],
758        span: &Span,
759    ) -> Result<bool, Diagnostic> {
760        let membership = (index, id, keys.to_vec());
761        if self.state.initialized.contains(&membership) {
762            return Ok(false);
763        }
764        let bytes = self
765            .state
766            .initialized_bytes
767            .saturating_add(membership_bytes(keys));
768        if self.state.initialized.len() >= self.limits.max_stored_values
769            || bytes > self.limits.max_storage_bytes
770        {
771            return Err(self.error(
772                "storage_limit",
773                "system lifecycle membership exceeds storage budget",
774                span,
775            ));
776        }
777        self.state.initialized.insert(membership);
778        self.state.initialized_bytes = bytes;
779        Ok(true)
780    }
781    fn system_callback(
782        &mut self,
783        index: usize,
784        id: u64,
785        keys: &[String],
786        name: &str,
787        dt: f64,
788    ) -> Result<(), Diagnostic> {
789        let (module, system) = &self.program.systems[index];
790        let Some(callback) = system
791            .callbacks
792            .iter()
793            .find(|callback| callback.name == name)
794        else {
795            return Ok(());
796        };
797        self.consume(&callback.span)?;
798        let Some(entity) = self.state.entities.get(&id) else {
799            return Ok(());
800        };
801        let mut originals = Vec::new();
802        let mut env = Env::default();
803        for (binding, key) in system.bindings.iter().zip(keys) {
804            let Some(value) = entity.components.get(key).cloned() else {
805                return Ok(());
806            };
807            originals.push(value.clone());
808            // The selected concrete class is the value identity even for a trait query.
809            let class_module = self
810                .program
811                .class_by_key(key)
812                .map(|(m, _)| m)
813                .ok_or_else(|| {
814                    self.error(
815                        "unknown_class",
816                        "query component has no declaration",
817                        &binding.span,
818                    )
819                })?;
820            env.scopes[0].insert(
821                binding.name.clone(),
822                Binding {
823                    value,
824                    ty: Type::named(key.clone()),
825                    module: class_module.into(),
826                    mutable: true,
827                },
828            );
829        }
830        env.bind("entity", Value::Entity(id), false, module);
831        env.bind("time", Value::Number(self.state.time), false, module);
832        env.bind("tick", Value::Number(self.state.tick as f64), false, module);
833        if let Some(parameter) = callback.parameters.first() {
834            env.bind(&parameter.name, Value::Number(dt), false, module);
835        }
836        let previous_entity = self.entity.replace(id);
837        self.record(&callback.span, format!("system {}.{name}", system.name));
838        if self.depth >= self.limits.max_call_depth {
839            return Err(self.error(
840                "call_depth",
841                "lifecycle callback exceeds call-depth limit",
842                &callback.span,
843            ));
844        }
845        self.depth += 1;
846        let result = self.block(&callback.body, module, &mut env);
847        self.depth -= 1;
848        self.entity = previous_entity;
849        let result = result.map_err(|error| self.host_propagation(error));
850        match result? {
851            Flow::Next | Flow::Return(Value::Unit) => {}
852            Flow::Return(value) => self.accept_lifecycle_result(value, &callback.span)?,
853            _ => {
854                return Err(self.error(
855                    "invalid_control",
856                    "system callback must return Unit",
857                    &callback.span,
858                ));
859            }
860        }
861        for ((binding, key), original) in system.bindings.iter().zip(keys).zip(originals) {
862            let updated = &env.scopes[0][&binding.name].value;
863            if let Some(entity) = self.state.entities.get_mut(&id)
864                && let Some(current) = entity.components.get(key)
865            {
866                if current != &original && updated != &original && current != updated {
867                    return Err(self.error(
868                        "conflicting_component_write",
869                        "callback changed a bound component and explicitly set the same component",
870                        &callback.span,
871                    ));
872                }
873                if updated != &original {
874                    entity.components.insert(key.clone(), updated.clone());
875                }
876            }
877        }
878        Ok(())
879    }
880    // Despawn is deferred until the enclosing callback/action has written its bound values
881    // back. Cleanup therefore observes the latest component state, and can fail atomically.
882    fn flush_despawns(&mut self) -> Result<(), Diagnostic> {
883        while let Some(id) = self.state.pending_despawns.pop_first() {
884            let memberships: Vec<_> = self
885                .state
886                .initialized
887                .iter()
888                .filter(|(_, member_id, _)| *member_id == id)
889                .cloned()
890                .collect();
891            for (index, member_id, keys) in memberships {
892                // Remove before entering user cleanup, preventing repeated done on recursion.
893                if self
894                    .state
895                    .initialized
896                    .remove(&(index, member_id, keys.clone()))
897                {
898                    self.state.initialized_bytes -= membership_bytes(&keys);
899                }
900                self.system_callback(index, id, &keys, "done", 0.0)?;
901            }
902            self.state.entities.remove(&id);
903            self.state.pending_despawns.remove(&id);
904        }
905        Ok(())
906    }
907    fn error(&self, code: &str, message: impl Into<String>, span: &Span) -> Diagnostic {
908        Diagnostic::new(code, message, span.clone())
909    }
910    fn consume(&mut self, span: &Span) -> Result<(), Diagnostic> {
911        if self.fuel == 0 {
912            return Err(self.error(
913                "fuel_exhausted",
914                "execution exceeded its instruction budget",
915                span,
916            ));
917        }
918        self.fuel -= 1;
919        Ok(())
920    }
921    fn record(&mut self, span: &Span, event: String) {
922        if self.limits.max_trace == 0 {
923            return;
924        }
925        if self.state.trace.len() >= self.limits.max_trace {
926            self.state.trace.remove(0);
927        }
928        self.state.trace.push(TraceEntry {
929            span: span.clone(),
930            event,
931            entity: self.entity,
932        });
933    }
934    fn check_value(&self, value: &Value, span: &Span) -> Result<usize, Diagnostic> {
935        fn visit(v: &Value, depth: usize, limits: &Limits) -> Result<usize, &'static str> {
936            if depth > limits.max_value_depth {
937                return Err("value nesting limit exceeded");
938            }
939            match v {
940                Value::Enum(value) => {
941                    if value
942                        .origin
943                        .as_ref()
944                        .is_some_and(|origin| origin.module.len() > limits.max_string_bytes)
945                    {
946                        return Err("enum origin size limit exceeded");
947                    }
948                    let mut pending = vec![(&value.ty, depth + 1)];
949                    let mut count = 1;
950                    while let Some((ty, level)) = pending.pop() {
951                        if level > limits.max_value_depth {
952                            return Err("enum type nesting limit exceeded");
953                        }
954                        count += 1;
955                        match ty {
956                            Type::Applied { name, arguments } => {
957                                if name.len() > limits.max_string_bytes
958                                    || arguments.len() > limits.max_collection
959                                {
960                                    return Err("enum type size limit exceeded");
961                                }
962                                pending.extend(arguments.iter().map(|ty| (ty, level + 1)));
963                            }
964                            Type::Named(name) => {
965                                if name.len() > limits.max_string_bytes {
966                                    return Err("enum type name limit exceeded");
967                                }
968                            }
969                            Type::List(inner)
970                            | Type::Tensor(inner)
971                            | Type::ComponentReference(inner) => pending.push((inner, level + 1)),
972                            Type::Function {
973                                parameters,
974                                returns,
975                            } => {
976                                if parameters.len() > limits.max_collection {
977                                    return Err("function arity limit exceeded");
978                                }
979                                pending.push((returns, level + 1));
980                                pending.extend(parameters.iter().map(|ty| (ty, level + 1)));
981                            }
982                        }
983                        if count > limits.max_stored_values {
984                            return Err("enum type size limit exceeded");
985                        }
986                    }
987                    if value.payload.len() > limits.max_collection
988                        || value.case.len() > limits.max_string_bytes
989                    {
990                        return Err("enum payload limit exceeded");
991                    }
992                    for item in &value.payload {
993                        count += visit(item, depth + 1, limits)?;
994                        if count > limits.max_stored_values {
995                            return Err("enum value limit exceeded");
996                        }
997                    }
998                    Ok(count)
999                }
1000                Value::Number(n) if !n.is_finite() => Err("numbers must be finite"),
1001                Value::Text(s) if s.len() > limits.max_string_bytes => {
1002                    Err("string byte limit exceeded")
1003                }
1004                Value::Bin(b) if b.byte_len() > limits.max_storage_bytes => {
1005                    Err("binary byte limit exceeded")
1006                }
1007                Value::Tensor(t)
1008                    if t.storage_byte_len() > limits.max_storage_bytes
1009                        || t.shape().len() > limits.max_value_depth =>
1010                {
1011                    Err("tensor byte or rank limit exceeded")
1012                }
1013                Value::Function(function) => {
1014                    fn check_type(
1015                        ty: &Type,
1016                        depth: usize,
1017                        limits: &Limits,
1018                    ) -> Result<usize, &'static str> {
1019                        if depth > limits.max_value_depth {
1020                            return Err("function type nesting limit exceeded");
1021                        }
1022                        match ty {
1023                            Type::Applied { name, arguments } => {
1024                                if name.len() > limits.max_string_bytes
1025                                    || arguments.len() > limits.max_collection
1026                                {
1027                                    return Err("applied type size limit exceeded");
1028                                }
1029                                let mut count = 1;
1030                                for ty in arguments {
1031                                    count += check_type(ty, depth + 1, limits)?;
1032                                }
1033                                Ok(count)
1034                            }
1035                            Type::Named(name) => {
1036                                if name.len() > limits.max_string_bytes {
1037                                    return Err("type name byte limit exceeded");
1038                                }
1039                                Ok(1)
1040                            }
1041                            Type::List(inner)
1042                            | Type::Tensor(inner)
1043                            | Type::ComponentReference(inner) => {
1044                                Ok(1 + check_type(inner, depth + 1, limits)?)
1045                            }
1046                            Type::Function {
1047                                parameters,
1048                                returns,
1049                            } => {
1050                                if parameters.len() > limits.max_collection {
1051                                    return Err("function arity limit exceeded");
1052                                }
1053                                let mut count = 1 + check_type(returns, depth + 1, limits)?;
1054                                for ty in parameters {
1055                                    count += check_type(ty, depth + 1, limits)?;
1056                                }
1057                                Ok(count)
1058                            }
1059                        }
1060                    }
1061                    let mut count = check_type(&function.signature, depth + 1, limits)?;
1062                    if let FunctionTarget::Builtin {
1063                        type_argument: Some(ty),
1064                        ..
1065                    } = &function.target
1066                    {
1067                        count += check_type(ty, depth + 1, limits)?;
1068                    }
1069                    if let FunctionTarget::BoundMethod { receiver, .. }
1070                    | FunctionTarget::DataMethod { receiver, .. } = &function.target
1071                    {
1072                        count += visit(receiver, depth + 1, limits)?;
1073                    }
1074                    match &function.target {
1075                        FunctionTarget::DataMethod {
1076                            receiver_type,
1077                            type_argument,
1078                            ..
1079                        } => {
1080                            count += check_type(receiver_type, depth + 1, limits)?;
1081                            if let Some(ty) = type_argument {
1082                                count += check_type(ty, depth + 1, limits)?;
1083                            }
1084                        }
1085                        FunctionTarget::EnumConstructor { ty, .. } => {
1086                            count += check_type(ty, depth + 1, limits)?
1087                        }
1088                        _ => {}
1089                    }
1090                    if count > limits.max_stored_values {
1091                        return Err("function value size limit exceeded");
1092                    }
1093                    Ok(count)
1094                }
1095                Value::List(values) => {
1096                    if values.len() > limits.max_collection {
1097                        return Err("collection length limit exceeded");
1098                    }
1099                    let mut count = 1;
1100                    for v in values {
1101                        count += visit(v, depth + 1, limits)?;
1102                        if count > limits.max_stored_values {
1103                            return Err("value size limit exceeded");
1104                        }
1105                    }
1106                    Ok(count)
1107                }
1108                Value::Record(r) => {
1109                    if r.fields.len() > limits.max_collection {
1110                        return Err("record field limit exceeded");
1111                    }
1112                    let mut count = 1;
1113                    for v in r.fields.values() {
1114                        count += visit(v, depth + 1, limits)?;
1115                        if count > limits.max_stored_values {
1116                            return Err("value size limit exceeded");
1117                        }
1118                    }
1119                    Ok(count)
1120                }
1121                _ => Ok(1),
1122            }
1123        }
1124        let nodes = visit(value, 0, self.limits).map_err(|e| self.error("value_limit", e, span))?;
1125        fn validate_media(value: &Value) -> Result<(), String> {
1126            match value {
1127                Value::Enum(value) => {
1128                    for value in &value.payload {
1129                        validate_media(value)?;
1130                    }
1131                }
1132                Value::Record(record) => {
1133                    for field in record.fields.values() {
1134                        validate_media(field)?;
1135                    }
1136                    crate::media_values::validate(value)?;
1137                }
1138                Value::List(values) => {
1139                    for value in values {
1140                        validate_media(value)?;
1141                    }
1142                }
1143                Value::Function(function) => {
1144                    if let FunctionTarget::BoundMethod { receiver, .. }
1145                    | FunctionTarget::DataMethod { receiver, .. } = &function.target
1146                    {
1147                        validate_media(receiver)?;
1148                    }
1149                }
1150                _ => {}
1151            }
1152            Ok(())
1153        }
1154        validate_media(value).map_err(|message| self.error("invalid_media", message, span))?;
1155        if value_bytes(value) > self.limits.max_allocation_bytes {
1156            return Err(self.error(
1157                "allocation_limit",
1158                "single value exceeds operation byte budget",
1159                span,
1160            ));
1161        }
1162        Ok(nodes)
1163    }
1164    fn check_storage(&self, span: &Span) -> Result<(), Diagnostic> {
1165        let mut count = self.state.initialized.len();
1166        let mut bytes = self.state.initialized_bytes;
1167        if count > self.limits.max_stored_values || bytes > self.limits.max_storage_bytes {
1168            return Err(self.error(
1169                "storage_limit",
1170                "system lifecycle membership exceeds storage budget",
1171                span,
1172            ));
1173        }
1174        for value in self.state.globals.values().map(|b| &b.value).chain(
1175            self.state
1176                .entities
1177                .values()
1178                .flat_map(|e| e.components.values()),
1179        ) {
1180            count += self.check_value(value, span)?;
1181            bytes = bytes.saturating_add(value_bytes(value));
1182            if bytes > self.limits.max_storage_bytes {
1183                return Err(self.error("storage_limit", "stored value byte budget exceeded", span));
1184            }
1185            if count > self.limits.max_stored_values {
1186                return Err(self.error(
1187                    "storage_limit",
1188                    "total stored value budget exceeded",
1189                    span,
1190                ));
1191            }
1192        }
1193        Ok(())
1194    }
1195    fn matches_type(
1196        &self,
1197        value: &Value,
1198        ty: &Type,
1199        module: &str,
1200        span: &Span,
1201    ) -> Result<bool, Diagnostic> {
1202        match ty {
1203            Type::Applied { .. } => {
1204                let Value::Enum(value) = value else {
1205                    return Ok(false);
1206                };
1207                let expected = self.program.canonical_type(module, ty, span)?;
1208                Ok(enum_type_compatible(&value.ty, &expected))
1209            }
1210            Type::Function { .. } => {
1211                let Value::Function(function) = value else {
1212                    return Ok(false);
1213                };
1214                let expected = self.program.canonical_type(module, ty, span)?;
1215                Ok(function.signature == expected)
1216            }
1217            Type::ComponentReference(inner) => {
1218                let Value::ComponentReference(reference) = value else {
1219                    return Ok(false);
1220                };
1221                Ok(Type::named(&reference.class)
1222                    == self.program.canonical_type(module, inner, span)?)
1223            }
1224            Type::Tensor(inner) => {
1225                Ok(matches!(value,Value::Tensor(t) if data::dtype(inner)==Some(t.dtype())))
1226            }
1227            Type::List(inner) => {
1228                if let Value::List(values) = value {
1229                    for value in values {
1230                        if !self.matches_type(value, inner, module, span)? {
1231                            return Ok(false);
1232                        }
1233                    }
1234                    Ok(true)
1235                } else {
1236                    Ok(false)
1237                }
1238            }
1239            Type::Named(name) => {
1240                if name == "Any" {
1241                    return Ok(true);
1242                }
1243                if name == "List" {
1244                    return Ok(matches!(value, Value::List(_)));
1245                }
1246                if data::is_primitive(name) {
1247                    return Ok(value.type_name() == name);
1248                }
1249                let (m, n) = self.program.resolve(module, name, span)?;
1250                let key = Program::key(&m, &n);
1251                if let Value::Enum(value) = value {
1252                    return Ok(value.ty == Type::named(&key));
1253                }
1254                if let Value::Record(record) = value {
1255                    Ok(record.class == key
1256                        || self
1257                            .program
1258                            .implementations
1259                            .contains(&(record.class.clone(), key)))
1260                } else {
1261                    Ok(false)
1262                }
1263            }
1264        }
1265    }
1266    fn expect_type(
1267        &self,
1268        value: &Value,
1269        ty: &Type,
1270        module: &str,
1271        span: &Span,
1272    ) -> Result<(), Diagnostic> {
1273        if !self.matches_type(value, ty, module, span)? {
1274            return Err(self.error(
1275                "type_mismatch",
1276                format!("expected {ty:?}, got {}", value.type_name()),
1277                span,
1278            ));
1279        }
1280        Ok(())
1281    }
1282    fn validate_external_value(&self, value: &Value, span: &Span) -> Result<(), Diagnostic> {
1283        self.validate_value(value, span, true)
1284    }
1285    fn validate_value(&self, value: &Value, span: &Span, external: bool) -> Result<(), Diagnostic> {
1286        self.check_value(value, span)?;
1287        match value {
1288            Value::Enum(value) => {
1289                let payload = self.enum_payload(&value.ty, &value.case, span)?;
1290                if payload.len() != value.payload.len() {
1291                    return Err(self.error(
1292                        "invalid_enum",
1293                        "enum payload arity differs from declaration",
1294                        span,
1295                    ));
1296                }
1297                if external {
1298                    self.program.has_type(MAIN, &value.ty, span)?;
1299                    if value.origin.is_some() {
1300                        return Err(self.error(
1301                            "invalid_enum",
1302                            "external enum values cannot claim compiler source attribution",
1303                            span,
1304                        ));
1305                    }
1306                }
1307                for (item, ty) in value.payload.iter().zip(payload) {
1308                    self.expect_type(item, &ty, canonical_type_scope(&ty), span)?;
1309                    self.validate_value(item, span, external)?;
1310                }
1311                Ok(())
1312            }
1313            Value::Record(record) => {
1314                self.validate_record(value, span)?;
1315                if external {
1316                    for value in record.fields.values() {
1317                        self.validate_value(value, span, true)?;
1318                    }
1319                }
1320                Ok(())
1321            }
1322            Value::Function(function) => self.validate_function(function, span, external),
1323            Value::ComponentReference(reference) => {
1324                if self.program.class_by_key(&reference.class).is_none() {
1325                    return Err(self.error(
1326                        "unknown_class",
1327                        "unknown component reference class",
1328                        span,
1329                    ));
1330                }
1331                if external {
1332                    self.program.class(MAIN, &reference.class, span)?;
1333                }
1334                Ok(())
1335            }
1336            Value::List(values) => {
1337                for value in values {
1338                    self.validate_value(value, span, external)?;
1339                }
1340                Ok(())
1341            }
1342            _ => Ok(()),
1343        }
1344    }
1345    fn validate_record(&self, value: &Value, span: &Span) -> Result<(), Diagnostic> {
1346        self.check_value(value, span)?;
1347        let Value::Record(record) = value else {
1348            return Err(self.error(
1349                "expected_component",
1350                "components must be class records",
1351                span,
1352            ));
1353        };
1354        let Some((module, class)) = self.program.class_by_key(&record.class) else {
1355            return Err(self.error(
1356                "unknown_class",
1357                format!("unknown class `{}`", record.class),
1358                span,
1359            ));
1360        };
1361        if record.fields.len() != class.fields.len() {
1362            return Err(self.error(
1363                "invalid_record",
1364                "record fields do not match class declaration",
1365                span,
1366            ));
1367        }
1368        for field in &class.fields {
1369            let value = record.fields.get(&field.name).ok_or_else(|| {
1370                self.error(
1371                    "missing_field",
1372                    format!("missing field `{}`", field.name),
1373                    span,
1374                )
1375            })?;
1376            self.expect_type(value, &field.ty, module, span)?;
1377            self.validate_value(value, span, false)?;
1378        }
1379        Ok(())
1380    }
1381    fn block(
1382        &mut self,
1383        statements: &[Stmt],
1384        module: &str,
1385        env: &mut Env,
1386    ) -> Result<Flow, Diagnostic> {
1387        env.scopes.push(BTreeMap::new());
1388        let result = (|| {
1389            for statement in statements {
1390                let flow = self.statement(statement, module, env, false)?;
1391                if !matches!(flow, Flow::Next) {
1392                    return Ok(flow);
1393                }
1394            }
1395            Ok(Flow::Next)
1396        })();
1397        env.scopes.pop();
1398        result
1399    }
1400    fn statement(
1401        &mut self,
1402        statement: &Stmt,
1403        module: &str,
1404        env: &mut Env,
1405        global: bool,
1406    ) -> Result<Flow, Diagnostic> {
1407        self.consume(&statement.span)?;
1408        self.record(&statement.span, "statement".into());
1409        // Keep the recursive expression/return path in a small frame. Large
1410        // binding and loop temporaries must not consume every callback's stack.
1411        match &statement.kind {
1412            StmtKind::Expr(expr) => {
1413                self.eval(expr, module, env)?;
1414                Ok(Flow::Next)
1415            }
1416            StmtKind::Return(expr) => Ok(Flow::Return(match expr {
1417                Some(expr) => self.eval(expr, module, env)?,
1418                None => Value::Unit,
1419            })),
1420            StmtKind::Let { .. } => self.statement_let(statement, module, env, global),
1421            StmtKind::Assign { .. } => self.statement_assign(statement, module, env, global),
1422            StmtKind::If { .. } => self.statement_if(statement, module, env, global),
1423            StmtKind::While { .. } => self.statement_while(statement, module, env, global),
1424            StmtKind::For { .. } => self.statement_for(statement, module, env, global),
1425            StmtKind::Break => Ok(Flow::Break),
1426            StmtKind::Continue => Ok(Flow::Continue),
1427        }
1428    }
1429    fn statement_let(
1430        &mut self,
1431        statement: &Stmt,
1432        module: &str,
1433        env: &mut Env,
1434        global: bool,
1435    ) -> Result<Flow, Diagnostic> {
1436        match &statement.kind {
1437            StmtKind::Let {
1438                name,
1439                ty,
1440                mutable,
1441                value,
1442            } => {
1443                let value = self.eval(value, module, env)?;
1444                let annotated = ty.is_some();
1445                let (ty, type_module) = if let Some(ty) = ty {
1446                    (ty.clone(), module.to_owned())
1447                } else {
1448                    let span = &statement.span;
1449                    let ty = self
1450                        .program
1451                        .binding_types
1452                        .get(&(span.module.clone(), span.start, span.end))
1453                        .ok_or_else(|| {
1454                            self.error("missing_checked_type", "binding has no compiled type", span)
1455                        })?
1456                        .clone();
1457                    // Canonical imported types are interpreted in their own defining module,
1458                    // including private types returned through a public function signature.
1459                    let scope = canonical_type_scope(&ty).to_owned();
1460                    (ty, scope)
1461                };
1462                if annotated {
1463                    self.program.has_type(&type_module, &ty, &statement.span)?;
1464                }
1465                self.expect_type(&value, &ty, &type_module, &statement.span)?;
1466                let binding = Binding {
1467                    value,
1468                    ty,
1469                    module: type_module,
1470                    mutable: *mutable,
1471                };
1472                let bindings = if global {
1473                    &mut self.state.globals
1474                } else {
1475                    env.scopes.last_mut().expect("scope exists")
1476                };
1477                let key = if global {
1478                    Program::key(module, name)
1479                } else {
1480                    name.clone()
1481                };
1482                if bindings.contains_key(&key) {
1483                    return Err(Diagnostic::new(
1484                        "duplicate_binding",
1485                        format!("`{name}` is already bound in this scope"),
1486                        statement.span.clone(),
1487                    ));
1488                }
1489                bindings.insert(key, binding);
1490            }
1491            _ => unreachable!("statement dispatcher preserves the variant"),
1492        }
1493        Ok(Flow::Next)
1494    }
1495    fn statement_assign(
1496        &mut self,
1497        statement: &Stmt,
1498        module: &str,
1499        env: &mut Env,
1500        _global: bool,
1501    ) -> Result<Flow, Diagnostic> {
1502        match &statement.kind {
1503            StmtKind::Assign { target, value } => {
1504                let target = self.freeze_location(target, module, env)?;
1505                let value = self.eval(value, module, env)?;
1506                self.assign(&target, value, module, env)?;
1507            }
1508            _ => unreachable!("statement dispatcher preserves the variant"),
1509        }
1510        Ok(Flow::Next)
1511    }
1512    fn statement_if(
1513        &mut self,
1514        statement: &Stmt,
1515        module: &str,
1516        env: &mut Env,
1517        _global: bool,
1518    ) -> Result<Flow, Diagnostic> {
1519        match &statement.kind {
1520            StmtKind::If {
1521                condition,
1522                then_body,
1523                else_body,
1524            } => {
1525                let condition = self.eval(condition, module, env)?;
1526                let Value::Bool(condition) = condition else {
1527                    return Err(self.error(
1528                        "type_mismatch",
1529                        "if condition requires Bool",
1530                        &statement.span,
1531                    ));
1532                };
1533                self.block(if condition { then_body } else { else_body }, module, env)
1534            }
1535            _ => unreachable!("statement dispatcher preserves the variant"),
1536        }
1537    }
1538    fn statement_while(
1539        &mut self,
1540        statement: &Stmt,
1541        module: &str,
1542        env: &mut Env,
1543        _global: bool,
1544    ) -> Result<Flow, Diagnostic> {
1545        match &statement.kind {
1546            StmtKind::While { condition, body } => loop {
1547                self.consume(&statement.span)?;
1548                let value = self.eval(condition, module, env)?;
1549                let Value::Bool(value) = value else {
1550                    return Err(self.error(
1551                        "type_mismatch",
1552                        "while condition requires Bool",
1553                        &condition.span,
1554                    ));
1555                };
1556                if !value {
1557                    break;
1558                }
1559                match self.block(body, module, env)? {
1560                    Flow::Return(value) => return Ok(Flow::Return(value)),
1561                    Flow::Break => break,
1562                    Flow::Next | Flow::Continue => (),
1563                }
1564            },
1565            _ => unreachable!("statement dispatcher preserves the variant"),
1566        }
1567        Ok(Flow::Next)
1568    }
1569    fn statement_for(
1570        &mut self,
1571        statement: &Stmt,
1572        module: &str,
1573        env: &mut Env,
1574        _global: bool,
1575    ) -> Result<Flow, Diagnostic> {
1576        match &statement.kind {
1577            StmtKind::For {
1578                name,
1579                collection,
1580                body,
1581            } => {
1582                let collection = self.eval(collection, module, env)?;
1583                let Value::List(values) = collection else {
1584                    return Err(self.error(
1585                        "type_mismatch",
1586                        "for-in requires a List",
1587                        &statement.span,
1588                    ));
1589                };
1590                for value in values {
1591                    self.consume(&statement.span)?;
1592                    env.scopes.push(BTreeMap::new());
1593                    env.bind(name, value, false, module);
1594                    let flow = self.block(body, module, env);
1595                    env.scopes.pop();
1596                    match flow? {
1597                        Flow::Return(value) => return Ok(Flow::Return(value)),
1598                        Flow::Break => break,
1599                        Flow::Next | Flow::Continue => (),
1600                    }
1601                }
1602            }
1603            _ => unreachable!("statement dispatcher preserves the variant"),
1604        }
1605        Ok(Flow::Next)
1606    }
1607    fn lookup(
1608        &self,
1609        name: &str,
1610        module: &str,
1611        env: &Env,
1612        span: &Span,
1613    ) -> Result<Value, Diagnostic> {
1614        if let Some(binding) = env.get(name) {
1615            return Ok(binding.value.clone());
1616        }
1617        if name == "pi" {
1618            return Ok(Value::Number(std::f64::consts::PI));
1619        }
1620        let key = Program::key(module, name);
1621        if let Some(binding) = self.state.globals.get(&key) {
1622            return Ok(binding.value.clone());
1623        }
1624        self.named_function_value(name, module, span)
1625    }
1626    fn namespace(&self, expr: &Expr, module: &str) -> Option<String> {
1627        let name = expression_path(expr)?;
1628        (self.program.modules[module].imports.contains(&name) || name == "builtin").then_some(name)
1629    }
1630    fn freeze_location(
1631        &mut self,
1632        expr: &Expr,
1633        module: &str,
1634        env: &mut Env,
1635    ) -> Result<Expr, Diagnostic> {
1636        let kind = match &expr.kind {
1637            ExprKind::Propagate(value) => {
1638                ExprKind::Propagate(Box::new(self.freeze_location(value, module, env)?))
1639            }
1640            ExprKind::Field { object, field } => ExprKind::Field {
1641                object: Box::new(self.freeze_location(object, module, env)?),
1642                field: field.clone(),
1643            },
1644            ExprKind::Index { object, indices } => {
1645                let object = self.freeze_location(object, module, env)?;
1646                let mut frozen = Vec::with_capacity(indices.len());
1647                for index in indices {
1648                    frozen.push(match index {
1649                        IndexExpr::Index(index) => {
1650                            IndexExpr::Index(self.freeze_index(index, module, env)?)
1651                        }
1652                        IndexExpr::Slice { start, stop, step } => IndexExpr::Slice {
1653                            start: start
1654                                .as_ref()
1655                                .map(|expr| self.freeze_index(expr, module, env).map(Box::new))
1656                                .transpose()?,
1657                            stop: stop
1658                                .as_ref()
1659                                .map(|expr| self.freeze_index(expr, module, env).map(Box::new))
1660                                .transpose()?,
1661                            step: step
1662                                .as_ref()
1663                                .map(|expr| self.freeze_index(expr, module, env).map(Box::new))
1664                                .transpose()?,
1665                        },
1666                        IndexExpr::NewAxis => IndexExpr::NewAxis,
1667                        IndexExpr::Ellipsis => IndexExpr::Ellipsis,
1668                    });
1669                }
1670                ExprKind::Index {
1671                    object: Box::new(object),
1672                    indices: frozen,
1673                }
1674            }
1675            _ => return Ok(expr.clone()),
1676        };
1677        Ok(Expr {
1678            kind,
1679            span: expr.span.clone(),
1680        })
1681    }
1682    fn freeze_index(
1683        &mut self,
1684        index: &Expr,
1685        module: &str,
1686        env: &mut Env,
1687    ) -> Result<Expr, Diagnostic> {
1688        let number = self.index(index, module, env)?;
1689        Ok(Expr {
1690            kind: ExprKind::Number(number.to_string()),
1691            span: index.span.clone(),
1692        })
1693    }
1694    fn eval(&mut self, expr: &Expr, module: &str, env: &mut Env) -> Result<Value, Diagnostic> {
1695        if self.expression_depth >= self.limits.max_value_depth {
1696            return Err(self.error(
1697                "expression_depth",
1698                "expression nesting limit exceeded",
1699                &expr.span,
1700            ));
1701        }
1702        self.expression_depth += 1;
1703        let result = self.eval_inner(expr, module, env);
1704        self.expression_depth -= 1;
1705        result
1706    }
1707    // Keep dispatch frames small: the interpreter's configured recursion limits
1708    // must fire before the native stack, including in unoptimized host builds.
1709    fn eval_inner(
1710        &mut self,
1711        expr: &Expr,
1712        module: &str,
1713        env: &mut Env,
1714    ) -> Result<Value, Diagnostic> {
1715        self.consume(&expr.span)?;
1716        if let Some(name) = expression_path(expr) {
1717            let unbound = !matches!(&expr.kind,ExprKind::Name(name) if env.get(name).is_some()||self.state.globals.contains_key(&Program::key(module,name)));
1718            if unbound && let Some(value) = self.case_expression(&name, None, module, &expr.span)? {
1719                return self.complete_expression(value, &expr.span);
1720            }
1721        }
1722        // Return one Result from the dispatch instead of allocating a separate
1723        // error-unwind temporary for every arm. Recursive calls must fit the
1724        // configured language depth on the native test thread's normal stack.
1725        let result = match &expr.kind {
1726            ExprKind::TypedLiteral { value, .. } => self.eval(value, module, env),
1727            ExprKind::Assign { target, value } => {
1728                self.eval_assignment(target, value, module, env, &expr.span)
1729            }
1730            ExprKind::TensorLiteral { values, dtype } => {
1731                self.eval_tensor_literal(values, dtype, module, env, &expr.span)
1732            }
1733            ExprKind::Match { value, arms } => {
1734                self.eval_match(value, arms, module, env, &expr.span)
1735            }
1736            ExprKind::Propagate(value) => self.eval_propagate(value, module, env, &expr.span),
1737            ExprKind::TypedMethod { object, method, ty } => {
1738                self.eval_typed_method(object, method, ty, module, env, &expr.span)
1739            }
1740            ExprKind::Number(raw) => self.literal(raw, &expr.span),
1741            ExprKind::Bool(value) => Ok(Value::Bool(*value)),
1742            ExprKind::Text(value) => Ok(Value::Text(value.clone().into())),
1743            ExprKind::Unit => Ok(Value::Unit),
1744            ExprKind::Name(name) => self.lookup(name, module, env, &expr.span),
1745            ExprKind::List(values) => self.eval_list(values, module, env, &expr.span),
1746            ExprKind::Record { class, fields } => {
1747                self.construct(class, fields, module, env, &expr.span)
1748            }
1749            ExprKind::Unary { op, value } => self.eval_unary(op, value, module, env, &expr.span),
1750            ExprKind::Binary { op, left, right } => {
1751                self.eval_binary(op, left, right, module, env, &expr.span)
1752            }
1753            ExprKind::Field { object, field } => {
1754                self.eval_field(object, field, module, env, &expr.span)
1755            }
1756            ExprKind::Index { object, indices } => {
1757                self.eval_checked_index(object, indices, module, env, &expr.span)
1758            }
1759            ExprKind::TypedFunction { name, ty } => {
1760                self.typed_function_value(name, ty, module, &expr.span)
1761            }
1762            ExprKind::TypedCall {
1763                name,
1764                ty,
1765                arguments,
1766            } => self.eval_typed_call(name, ty, arguments, module, env, &expr.span),
1767            ExprKind::Call { callee, arguments } => {
1768                self.eval_call(callee, arguments, module, env, &expr.span)
1769            }
1770        };
1771        self.complete_expression(result?, &expr.span)
1772    }
1773    fn eval_typed_method(
1774        &mut self,
1775        object: &Expr,
1776        method: &str,
1777        ty: &Type,
1778        module: &str,
1779        env: &mut Env,
1780        span: &Span,
1781    ) -> Result<Value, Diagnostic> {
1782        let value = self.eval(object, module, env)?;
1783        self.data_method_value(value, method, Some(ty), span)
1784    }
1785    fn eval_checked_index(
1786        &mut self,
1787        object: &Expr,
1788        indices: &[IndexExpr],
1789        module: &str,
1790        env: &mut Env,
1791        span: &Span,
1792    ) -> Result<Value, Diagnostic> {
1793        let result = self.eval_index(object, indices, module, env, span);
1794        self.data_result(result, span)
1795    }
1796    fn eval_assignment(
1797        &mut self,
1798        target: &Expr,
1799        value: &Expr,
1800        module: &str,
1801        env: &mut Env,
1802        span: &Span,
1803    ) -> Result<Value, Diagnostic> {
1804        let result = (|| {
1805            let location = self.freeze_location(target, module, env)?;
1806            let replacement = self.eval(value, module, env)?;
1807            self.assign(&location, replacement, module, env)
1808                .map(|()| Value::Unit)
1809        })();
1810        self.data_result(result, span)
1811    }
1812    fn eval_match(
1813        &mut self,
1814        input: &Expr,
1815        arms: &[MatchArm],
1816        module: &str,
1817        env: &mut Env,
1818        span: &Span,
1819    ) -> Result<Value, Diagnostic> {
1820        let value = self.eval(input, module, env)?;
1821        for arm in arms {
1822            let mut bindings = Vec::new();
1823            if self.pattern_matches(&arm.pattern, &value, &mut bindings, &arm.span)? {
1824                env.scopes.push(BTreeMap::new());
1825                for (name, value) in bindings {
1826                    env.bind(&name, value, false, module);
1827                }
1828                let result = self.eval(&arm.value, module, env);
1829                env.scopes.pop();
1830                return result;
1831            }
1832        }
1833        Err(self.error(
1834            "invalid_match",
1835            "checked exhaustive match found no case",
1836            span,
1837        ))
1838    }
1839    fn eval_propagate(
1840        &mut self,
1841        expr: &Expr,
1842        module: &str,
1843        env: &mut Env,
1844        span: &Span,
1845    ) -> Result<Value, Diagnostic> {
1846        let Value::Enum(mut value) = self.eval(expr, module, env)? else {
1847            return Err(self.error(
1848                "invalid_propagation",
1849                "expected checked algebraic value",
1850                span,
1851            ));
1852        };
1853        if matches!(value.case.as_str(), "Ok" | "Some") {
1854            Ok(value.payload.remove(0))
1855        } else {
1856            self.propagated = Some(Value::Enum(value));
1857            Err(self.error("__return_algebraic", "internal checked propagation", span))
1858        }
1859    }
1860    fn complete_expression(&mut self, value: Value, span: &Span) -> Result<Value, Diagnostic> {
1861        self.check_value(&value, span)?;
1862        if let Some(ty) =
1863            self.program
1864                .expression_types
1865                .get(&(span.module.clone(), span.start, span.end))
1866        {
1867            self.expect_type(&value, ty, canonical_type_scope(ty), span)?;
1868        }
1869        self.allocated_bytes = self.allocated_bytes.saturating_add(value_bytes(&value));
1870        if self.allocated_bytes > self.limits.max_allocation_bytes {
1871            return Err(self.error(
1872                "allocation_limit",
1873                "operation value allocation budget exceeded",
1874                span,
1875            ));
1876        }
1877        Ok(value)
1878    }
1879    fn eval_list(
1880        &mut self,
1881        expressions: &[Expr],
1882        module: &str,
1883        env: &mut Env,
1884        span: &Span,
1885    ) -> Result<Value, Diagnostic> {
1886        if expressions.len() > self.limits.max_collection {
1887            return Err(self.error("collection_limit", "list exceeds collection limit", span));
1888        }
1889        self.arguments(expressions, module, env).map(Value::List)
1890    }
1891    fn eval_unary(
1892        &mut self,
1893        op: &str,
1894        expr: &Expr,
1895        module: &str,
1896        env: &mut Env,
1897        span: &Span,
1898    ) -> Result<Value, Diagnostic> {
1899        if op == "-"
1900            && let ExprKind::Number(raw) = &expr.kind
1901        {
1902            return self.literal(&format!("-{raw}"), span);
1903        }
1904        let value = self.eval(expr, module, env)?;
1905        let result = match (op, value) {
1906            ("-", Value::Number(n)) => Ok(Value::Number(-n)),
1907            ("-", Value::Scalar(n)) => n
1908                .neg()
1909                .map(data::scalar_value)
1910                .map_err(|e| self.data_error(e, span)),
1911            ("-", Value::Tensor(t)) => {
1912                self.data_budget(t.len(), t.len(), span)?;
1913                t.neg()
1914                    .map(|t| Value::Tensor(Box::new(t)))
1915                    .map_err(|e| self.data_error(e, span))
1916            }
1917            ("!", Value::Bool(b)) => Ok(Value::Bool(!b)),
1918            _ => Err(self.error("type_mismatch", format!("invalid operand for `{op}`"), span)),
1919        };
1920        if op == "-" {
1921            self.data_result(result, span)
1922        } else {
1923            result
1924        }
1925    }
1926    fn eval_binary(
1927        &mut self,
1928        op: &str,
1929        left: &Expr,
1930        right: &Expr,
1931        module: &str,
1932        env: &mut Env,
1933        span: &Span,
1934    ) -> Result<Value, Diagnostic> {
1935        let left = self.eval(left, module, env)?;
1936        if op == "and" && left == Value::Bool(false) {
1937            return Ok(Value::Bool(false));
1938        }
1939        if op == "or" && left == Value::Bool(true) {
1940            return Ok(Value::Bool(true));
1941        }
1942        let right = self.eval(right, module, env)?;
1943        let algebraic = matches!(op, "+" | "-" | "*" | "/" | "%" | "@")
1944            && (data::as_scalar(&left).is_some() || matches!(left, Value::Tensor(_)));
1945        let result = self.binary(op, left, right, span);
1946        if algebraic {
1947            self.data_result(result, span)
1948        } else {
1949            result
1950        }
1951    }
1952    fn eval_field(
1953        &mut self,
1954        object: &Expr,
1955        field: &str,
1956        module: &str,
1957        env: &mut Env,
1958        span: &Span,
1959    ) -> Result<Value, Diagnostic> {
1960        if let Some(prefix) = self.namespace(object, module) {
1961            let (m, n) = self
1962                .program
1963                .resolve(module, &format!("{prefix}.{field}"), span)?;
1964            if let Some(binding) = self.state.globals.get(&Program::key(&m, &n)) {
1965                Ok(binding.value.clone())
1966            } else {
1967                self.named_function_value(&format!("{prefix}.{field}"), module, span)
1968            }
1969        } else {
1970            self.field(object, field, module, env, span)
1971        }
1972    }
1973    fn eval_index(
1974        &mut self,
1975        object: &Expr,
1976        indices: &[IndexExpr],
1977        module: &str,
1978        env: &mut Env,
1979        span: &Span,
1980    ) -> Result<Value, Diagnostic> {
1981        let value = self.eval(object, module, env)?;
1982        let selectors = self.selectors(indices, module, env)?;
1983        self.index_value(value, &selectors, span)
1984    }
1985    fn eval_typed_call(
1986        &mut self,
1987        name: &str,
1988        ty: &Type,
1989        arguments: &[Expr],
1990        module: &str,
1991        env: &mut Env,
1992        span: &Span,
1993    ) -> Result<Value, Diagnostic> {
1994        let selected = self.program.canonical_type(module, ty, span)?;
1995        let args = self.arguments(arguments, module, env)?;
1996        self.call_builtin_value(
1997            name.strip_prefix("builtin.").unwrap_or(name),
1998            Some(&selected),
1999            args,
2000            span,
2001        )
2002    }
2003    fn typed_function_value(
2004        &self,
2005        name: &str,
2006        ty: &Type,
2007        module: &str,
2008        span: &Span,
2009    ) -> Result<Value, Diagnostic> {
2010        let name = name.strip_prefix("builtin.").unwrap_or(name);
2011        let selected = self.program.canonical_type(module, ty, span)?;
2012        let signature = if name == "convert" {
2013            self.program
2014                .expression_types
2015                .get(&(span.module.clone(), span.start, span.end))
2016                .cloned()
2017                .ok_or_else(|| {
2018                    self.error(
2019                        "invalid_function",
2020                        "missing checked conversion signature",
2021                        span,
2022                    )
2023                })?
2024        } else {
2025            self.program
2026                .typed_builtin_signature(name, &selected, span)?
2027        };
2028        Ok(Value::Function(Box::new(FunctionValue {
2029            target: FunctionTarget::Builtin {
2030                name: name.into(),
2031                type_argument: Some(Box::new(selected)),
2032            },
2033            signature,
2034        })))
2035    }
2036    fn eval_call(
2037        &mut self,
2038        callee: &Expr,
2039        arguments: &[Expr],
2040        module: &str,
2041        env: &mut Env,
2042        span: &Span,
2043    ) -> Result<Value, Diagnostic> {
2044        if let Some(name) = expression_path(callee) {
2045            let unbound = !matches!(&callee.kind,ExprKind::Name(name) if env.get(name).is_some()||self.state.globals.contains_key(&Program::key(module,name)));
2046            if unbound
2047                && (matches!(name.as_str(), "Ok" | "Err" | "Some" | "None")
2048                    || self
2049                        .program
2050                        .variant(module, &name, span)
2051                        .ok()
2052                        .flatten()
2053                        .is_some())
2054            {
2055                let args = self.arguments(arguments, module, env)?;
2056                if let Some(value) = self.case_expression(&name, Some(args), module, span)? {
2057                    return Ok(value);
2058                }
2059            }
2060        }
2061        // The checker records a type for first-class callees; legacy direct
2062        // intrinsics, constructors, and immediate methods have no callee type.
2063        let result = if self.program.expression_types.contains_key(&(
2064            callee.span.module.clone(),
2065            callee.span.start,
2066            callee.span.end,
2067        )) {
2068            let callback = self.eval(callee, module, env)?;
2069            let args = self.arguments(arguments, module, env)?;
2070            self.call_value(callback, args, span)?
2071        } else {
2072            match &callee.kind {
2073                ExprKind::Name(name) => {
2074                    let args = self.arguments(arguments, module, env)?;
2075                    self.call_name(name, args, module, span)?
2076                }
2077                ExprKind::Field { object, field } => {
2078                    if let Some(prefix) = self.namespace(object, module) {
2079                        let args = self.arguments(arguments, module, env)?;
2080                        self.call_name(&format!("{prefix}.{field}"), args, module, span)?
2081                    } else {
2082                        self.method(object, field, arguments, module, env, span)?
2083                    }
2084                }
2085                _ => {
2086                    return Err(self.error(
2087                        "invalid_call",
2088                        "callee must have a function type",
2089                        span,
2090                    ));
2091                }
2092            }
2093        };
2094        Ok(result)
2095    }
2096    fn field(
2097        &mut self,
2098        object: &Expr,
2099        field: &str,
2100        module: &str,
2101        env: &mut Env,
2102        span: &Span,
2103    ) -> Result<Value, Diagnostic> {
2104        let value = self.eval(object, module, env)?;
2105        if !crate::methods::signatures(&infer_type(&value), field, None).is_empty() {
2106            return self.data_method_value(value, field, None, span);
2107        }
2108        if let Some(value) = value.field(field) {
2109            return Ok(value.clone());
2110        }
2111        let (class, target) = match value {
2112            Value::Record(record) => {
2113                let class = record.class.clone();
2114                (
2115                    class.clone(),
2116                    FunctionTarget::BoundMethod {
2117                        class,
2118                        method: field.into(),
2119                        receiver: Box::new(Value::Record(record)),
2120                    },
2121                )
2122            }
2123            Value::ComponentReference(reference) => (
2124                reference.class.clone(),
2125                FunctionTarget::EntityMethod {
2126                    class: reference.class,
2127                    method: field.into(),
2128                    entity: reference.entity,
2129                },
2130            ),
2131            _ => {
2132                return Err(self.error(
2133                    "unknown_field",
2134                    "method capture requires a class or bound component",
2135                    span,
2136                ));
2137            }
2138        };
2139        let function = self
2140            .program
2141            .methods
2142            .get(&class)
2143            .and_then(|methods| methods.get(field))
2144            .ok_or_else(|| {
2145                self.error(
2146                    "unknown_method",
2147                    format!("{class} has no method `{field}`"),
2148                    span,
2149                )
2150            })?;
2151        Ok(Value::Function(Box::new(FunctionValue {
2152            target,
2153            signature: self.function_signature(function)?,
2154        })))
2155    }
2156
2157    fn index(&mut self, index: &Expr, module: &str, env: &mut Env) -> Result<isize, Diagnostic> {
2158        let value = self.eval(index, module, env)?;
2159        self.signed_index_value(&value, &index.span)
2160    }
2161    fn binary(
2162        &mut self,
2163        op: &str,
2164        left: Value,
2165        right: Value,
2166        span: &Span,
2167    ) -> Result<Value, Diagnostic> {
2168        if op == "==" || op == "!=" {
2169            self.data_budget(
2170                0,
2171                display_work(&left).saturating_add(display_work(&right)),
2172                span,
2173            )?;
2174            let equal =
2175                if let (Some(a), Some(b)) = (data::as_scalar(&left), data::as_scalar(&right)) {
2176                    a.compare(b).map_err(|e| self.data_error(e, span))?.is_eq()
2177                } else {
2178                    left == right
2179                };
2180            return Ok(Value::Bool(if op == "==" { equal } else { !equal }));
2181        }
2182        if let (Some(a), Some(b)) = (data::as_scalar(&left), data::as_scalar(&right)) {
2183            if let Some(operation) = data::binary_op(op) {
2184                return a
2185                    .binary(operation, b)
2186                    .map(data::scalar_value)
2187                    .map_err(|e| self.data_error(e, span));
2188            }
2189            let order = a.compare(b).map_err(|e| self.data_error(e, span))?;
2190            return Ok(Value::Bool(match op {
2191                "<" => order.is_lt(),
2192                "<=" => order.is_le(),
2193                ">" => order.is_gt(),
2194                ">=" => order.is_ge(),
2195                _ => return Err(self.error("unknown_operator", "invalid numeric operator", span)),
2196            }));
2197        }
2198        if matches!(&left, Value::Tensor(_)) || matches!(&right, Value::Tensor(_)) {
2199            return self.tensor_binary(op, left, right, span);
2200        }
2201        match (left, right) {
2202            (Value::Number(a), Value::Number(b)) => Ok(match op {
2203                "+" => Value::Number(a + b),
2204                "-" => Value::Number(a - b),
2205                "*" => Value::Number(a * b),
2206                "/" => {
2207                    if b == 0.0 {
2208                        return Err(self.error("division_by_zero", "division by zero", span));
2209                    }
2210                    Value::Number(a / b)
2211                }
2212                "%" => {
2213                    if b == 0.0 {
2214                        return Err(self.error("division_by_zero", "remainder by zero", span));
2215                    }
2216                    Value::Number(a % b)
2217                }
2218                "<" => Value::Bool(a < b),
2219                "<=" => Value::Bool(a <= b),
2220                ">" => Value::Bool(a > b),
2221                ">=" => Value::Bool(a >= b),
2222                _ => {
2223                    return Err(self.error(
2224                        "type_mismatch",
2225                        format!("invalid numeric operator `{op}`"),
2226                        span,
2227                    ));
2228                }
2229            }),
2230            (Value::Bool(a), Value::Bool(b)) => match op {
2231                "and" => Ok(Value::Bool(a && b)),
2232                "or" => Ok(Value::Bool(a || b)),
2233                _ => Err(self.error(
2234                    "type_mismatch",
2235                    format!("invalid Bool operator `{op}`"),
2236                    span,
2237                )),
2238            },
2239            (Value::Text(a), Value::Text(b)) if op == "+" => {
2240                if a.len().saturating_add(b.len()) > self.limits.max_string_bytes {
2241                    return Err(self.error("value_limit", "string byte limit exceeded", span));
2242                }
2243                Ok(Value::Text(format!("{a}{b}").into()))
2244            }
2245            (Value::Bin(a), Value::Bin(b)) if op == "+" => {
2246                let size = a.byte_len().saturating_add(b.byte_len());
2247                self.data_budget(size, size, span)?;
2248                let mut bytes = Vec::with_capacity(size);
2249                bytes.extend_from_slice(a.as_bytes());
2250                bytes.extend_from_slice(b.as_bytes());
2251                Ok(Value::Bin(bytes.into()))
2252            }
2253            _ => Err(self.error(
2254                "type_mismatch",
2255                format!("incompatible operands for `{op}`"),
2256                span,
2257            )),
2258        }
2259    }
2260    fn construct(
2261        &mut self,
2262        name: &str,
2263        fields: &BTreeMap<String, Expr>,
2264        module: &str,
2265        env: &mut Env,
2266        span: &Span,
2267    ) -> Result<Value, Diagnostic> {
2268        if self.depth >= self.limits.max_call_depth {
2269            return Err(self.error(
2270                "call_depth",
2271                "constructor nesting exceeds call-depth limit",
2272                span,
2273            ));
2274        }
2275        let (class_module, class) = self.program.class(module, name, span)?;
2276        let class = class.clone();
2277        for name in fields.keys() {
2278            if !class.fields.iter().any(|f| &f.name == name) {
2279                return Err(self.error(
2280                    "unknown_field",
2281                    format!("{} has no field `{name}`", class.name),
2282                    span,
2283                ));
2284            }
2285        }
2286        self.depth += 1;
2287        let result = (|| {
2288            let mut supplied = BTreeMap::new();
2289            let mut ordered: Vec<_> = fields.iter().collect();
2290            ordered.sort_by_key(|(_, expr)| expr.span.start);
2291            for (name, expr) in ordered {
2292                supplied.insert(name.clone(), self.eval(expr, module, env)?);
2293            }
2294            self.construct_values(&class_module, &class, supplied, span)
2295        })();
2296        self.depth -= 1;
2297        result
2298    }
2299    fn construct_values(
2300        &mut self,
2301        class_module: &str,
2302        class: &Class,
2303        mut supplied: BTreeMap<String, Value>,
2304        span: &Span,
2305    ) -> Result<Value, Diagnostic> {
2306        let mut values = BTreeMap::new();
2307        for field in &class.fields {
2308            let value = if let Some(value) = supplied.remove(&field.name) {
2309                value
2310            } else if let Some(default) = &field.default {
2311                self.eval(default, class_module, &mut Env::default())?
2312            } else {
2313                return Err(self.error(
2314                    "missing_field",
2315                    format!("missing field `{}` for `{}`", field.name, class.name),
2316                    span,
2317                ));
2318            };
2319            self.expect_type(&value, &field.ty, class_module, span)?;
2320            values.insert(field.name.clone(), value);
2321        }
2322        Ok(Value::Record(RecordValue {
2323            class: Program::key(class_module, &class.name),
2324            fields: values,
2325        }))
2326    }
2327    fn function_signature(&self, function: &Function) -> Result<Type, Diagnostic> {
2328        self.program.canonical_type(
2329            &function.span.module,
2330            &Type::function(
2331                function.parameters.iter().map(|p| p.ty.clone()).collect(),
2332                function.returns.clone(),
2333            ),
2334            &function.span,
2335        )
2336    }
2337    fn named_function_value(
2338        &self,
2339        name: &str,
2340        module: &str,
2341        span: &Span,
2342    ) -> Result<Value, Diagnostic> {
2343        let native_name = name.strip_prefix("builtin.").unwrap_or(name);
2344        let (target, signature) = if is_builtin(native_name) {
2345            let signature = monomorphic_signature(native_name)
2346                .or_else(|| {
2347                    self.program
2348                        .expression_types
2349                        .get(&(span.module.clone(), span.start, span.end))
2350                        .cloned()
2351                })
2352                .ok_or_else(|| {
2353                    self.error(
2354                        "function_specialization",
2355                        "builtin function value requires a concrete signature",
2356                        span,
2357                    )
2358                })?;
2359            let type_argument = match (&signature, native_name) {
2360                (Type::Function { returns, .. }, "get") => Some(returns.clone()),
2361                (Type::Function { returns, .. }, "bind") => match returns.as_ref() {
2362                    Type::ComponentReference(inner) => Some(inner.clone()),
2363                    _ => None,
2364                },
2365                _ => None,
2366            };
2367            (
2368                FunctionTarget::Builtin {
2369                    name: native_name.into(),
2370                    type_argument,
2371                },
2372                signature,
2373            )
2374        } else if let Some(native) = self.registry.functions.get(native_name) {
2375            (
2376                FunctionTarget::Native {
2377                    name: native_name.into(),
2378                },
2379                self.program.canonical_type(
2380                    MAIN,
2381                    &Type::function(native.parameters.clone(), native.returns.clone()),
2382                    span,
2383                )?,
2384            )
2385        } else {
2386            let (target, local) = self.program.resolve(module, name, span)?;
2387            let function = self.program.modules[&target]
2388                .functions
2389                .get(&local)
2390                .ok_or_else(|| {
2391                    self.error(
2392                        "unknown_name",
2393                        format!("unknown function value `{name}`"),
2394                        span,
2395                    )
2396                })?;
2397            (
2398                FunctionTarget::Named {
2399                    module: target,
2400                    name: local,
2401                },
2402                self.function_signature(function)?,
2403            )
2404        };
2405        Ok(Value::Function(Box::new(FunctionValue {
2406            target,
2407            signature,
2408        })))
2409    }
2410    fn valid_signature_type(&self, ty: &Type) -> bool {
2411        match ty {
2412            Type::Applied { name, arguments } => {
2413                matches!((name.as_str(), arguments.len()), ("Res", 2) | ("Opt", 1))
2414                    && arguments.iter().all(|ty| self.valid_signature_type(ty))
2415            }
2416            Type::Named(name) => {
2417                data::is_primitive(name)
2418                    || self.program.enum_by_key(name).is_some()
2419                    || self.program.class_by_key(name).is_some()
2420                    || self.program.modules.iter().any(|(module, declarations)| {
2421                        declarations
2422                            .traits
2423                            .keys()
2424                            .any(|local| Program::key(module, local) == *name)
2425                    })
2426            }
2427            Type::Tensor(inner) => data::dtype(inner).is_some(),
2428            Type::List(inner) => self.valid_signature_type(inner),
2429            Type::ComponentReference(inner) => {
2430                matches!(inner.as_ref(), Type::Named(name) if self.program.class_by_key(name).is_some())
2431            }
2432            Type::Function {
2433                parameters,
2434                returns,
2435            } => {
2436                parameters.iter().all(|ty| self.valid_signature_type(ty))
2437                    && self.valid_signature_type(returns)
2438            }
2439        }
2440    }
2441    fn validate_function(
2442        &self,
2443        value: &FunctionValue,
2444        span: &Span,
2445        external: bool,
2446    ) -> Result<(), Diagnostic> {
2447        if !self.valid_signature_type(&value.signature) {
2448            return Err(self.error(
2449                "invalid_function",
2450                "function signatures require known concrete types",
2451                span,
2452            ));
2453        }
2454        let known = match &value.target {
2455            FunctionTarget::EnumConstructor { ty, case } => {
2456                if external {
2457                    self.program.has_type(MAIN, ty, span)?;
2458                }
2459                Type::function(self.enum_payload(ty, case, span)?, ty.clone())
2460            }
2461            FunctionTarget::DataMethod {
2462                receiver_type,
2463                method,
2464                type_argument,
2465                receiver,
2466            } => {
2467                self.validate_value(receiver, span, external)?;
2468                self.expect_type(
2469                    receiver,
2470                    receiver_type,
2471                    canonical_type_scope(receiver_type),
2472                    span,
2473                )?;
2474                if !crate::methods::signatures(receiver_type, method, type_argument.as_deref())
2475                    .contains(&value.signature)
2476                {
2477                    return Err(self.error(
2478                        "invalid_function",
2479                        "invalid data method signature or identity",
2480                        span,
2481                    ));
2482                }
2483                value.signature.clone()
2484            }
2485            FunctionTarget::Named { module, name } => {
2486                if external {
2487                    self.program
2488                        .resolve(MAIN, &format!("{module}.{name}"), span)?;
2489                }
2490                let function = self
2491                    .program
2492                    .modules
2493                    .get(module)
2494                    .and_then(|m| m.functions.get(name))
2495                    .ok_or_else(|| {
2496                        self.error("invalid_function", "unknown function identity", span)
2497                    })?;
2498                self.function_signature(function)?
2499            }
2500            FunctionTarget::Native { name } => {
2501                let native = self.registry.functions.get(name).ok_or_else(|| {
2502                    self.error("invalid_function", "unknown native function identity", span)
2503                })?;
2504                self.program.canonical_type(
2505                    MAIN,
2506                    &Type::function(native.parameters.clone(), native.returns.clone()),
2507                    span,
2508                )?
2509            }
2510            FunctionTarget::Builtin {
2511                name,
2512                type_argument,
2513            } => {
2514                if let Some(selected) = type_argument {
2515                    if !self.valid_signature_type(selected) {
2516                        return Err(self.error(
2517                            "invalid_function",
2518                            "invalid builtin type argument",
2519                            span,
2520                        ));
2521                    }
2522                    if external {
2523                        self.program.has_type(MAIN, selected, span)?;
2524                    }
2525                    if name == "convert" {
2526                        if !data::valid_convert_signature(selected, &value.signature) {
2527                            return Err(self.error(
2528                                "invalid_function",
2529                                "invalid conversion signature",
2530                                span,
2531                            ));
2532                        }
2533                        value.signature.clone()
2534                    } else {
2535                        self.program
2536                            .typed_builtin_signature(name, selected, span)
2537                            .map_err(|diagnostic| {
2538                                self.error("invalid_function", diagnostic.message, span)
2539                            })?
2540                    }
2541                } else {
2542                    if !valid_builtin_specialization(name, &value.signature, |class| {
2543                        self.program.class_by_key(class).is_some()
2544                    }) {
2545                        return Err(self.error(
2546                            "invalid_function",
2547                            "invalid builtin specialization",
2548                            span,
2549                        ));
2550                    }
2551                    value.signature.clone()
2552                }
2553            }
2554            FunctionTarget::BoundMethod {
2555                class,
2556                method,
2557                receiver,
2558            } => {
2559                if self
2560                    .program
2561                    .mutating_methods
2562                    .contains(&(class.clone(), method.clone()))
2563                {
2564                    return Err(self.error(
2565                        "mutating_method_capture",
2566                        "mutating callbacks must bind an entity component",
2567                        span,
2568                    ));
2569                }
2570                if receiver.type_name() != class {
2571                    return Err(self.error(
2572                        "invalid_function",
2573                        "bound method receiver has the wrong class",
2574                        span,
2575                    ));
2576                }
2577                self.validate_value(receiver, span, external)?;
2578                if external {
2579                    self.program.class(MAIN, class, span)?;
2580                }
2581                let function = self
2582                    .program
2583                    .methods
2584                    .get(class)
2585                    .and_then(|m| m.get(method))
2586                    .ok_or_else(|| {
2587                        self.error("invalid_function", "unknown bound method identity", span)
2588                    })?;
2589                self.function_signature(function)?
2590            }
2591            FunctionTarget::EntityMethod { class, method, .. } => {
2592                if external {
2593                    self.program.class(MAIN, class, span)?;
2594                }
2595                let function = self
2596                    .program
2597                    .methods
2598                    .get(class)
2599                    .and_then(|m| m.get(method))
2600                    .ok_or_else(|| {
2601                        self.error("invalid_function", "unknown entity method identity", span)
2602                    })?;
2603                self.function_signature(function)?
2604            }
2605        };
2606        if known != value.signature {
2607            return Err(self.error(
2608                "invalid_function",
2609                "function signature does not match its declared identity",
2610                span,
2611            ));
2612        }
2613        Ok(())
2614    }
2615    fn component_value(&self, id: u64, class: &str, span: &Span) -> Result<Value, Diagnostic> {
2616        if self.state.pending_despawns.contains(&id) {
2617            return Err(self.error("unknown_entity", "callback entity is pending despawn", span));
2618        }
2619        let entity = self.state.entities.get(&id).ok_or_else(|| {
2620            self.error(
2621                "unknown_entity",
2622                format!("entity {id} does not exist"),
2623                span,
2624            )
2625        })?;
2626        entity.components.get(class).cloned().ok_or_else(|| {
2627            self.error(
2628                "missing_component",
2629                format!("entity {id} has no `{class}`"),
2630                span,
2631            )
2632        })
2633    }
2634    fn call_value(
2635        &mut self,
2636        value: Value,
2637        args: Vec<Value>,
2638        span: &Span,
2639    ) -> Result<Value, Diagnostic> {
2640        self.consume(span)?;
2641        let Value::Function(function) = value else {
2642            return Err(self.error("invalid_call", "callee is not a function", span));
2643        };
2644        self.validate_function(&function, span, false)?;
2645        let Type::Function {
2646            parameters,
2647            returns,
2648        } = &function.signature
2649        else {
2650            return Err(self.error("invalid_function", "missing function signature", span));
2651        };
2652        if args.len() != parameters.len() {
2653            return Err(self.error(
2654                "arity",
2655                format!(
2656                    "callback expects {} arguments, got {}",
2657                    parameters.len(),
2658                    args.len()
2659                ),
2660                span,
2661            ));
2662        }
2663        for (value, ty) in args.iter().zip(parameters) {
2664            self.expect_type(value, ty, canonical_type_scope(ty), span)?;
2665        }
2666        let result = match &function.target {
2667            FunctionTarget::EnumConstructor { ty, case } => Value::algebraic(crate::EnumValue {
2668                origin: None,
2669                ty: ty.clone(),
2670                case: case.clone(),
2671                payload: args,
2672            }),
2673            FunctionTarget::DataMethod {
2674                receiver,
2675                method,
2676                type_argument,
2677                ..
2678            } => self.call_data_method(
2679                receiver.as_ref().clone(),
2680                method,
2681                type_argument.as_deref(),
2682                args,
2683                returns,
2684                span,
2685            )?,
2686            FunctionTarget::Named { module, name } => {
2687                let program = self.program;
2688                let declaration = &program.modules[module].functions[name];
2689                self.function(declaration, args, module, None, span)?.0
2690            }
2691            FunctionTarget::Native { name } => self.call_name(name, args, MAIN, span)?,
2692            FunctionTarget::Builtin {
2693                name,
2694                type_argument,
2695            } => self.call_builtin_value(name, type_argument.as_deref(), args, span)?,
2696            FunctionTarget::BoundMethod {
2697                class,
2698                method,
2699                receiver,
2700            } => {
2701                let program = self.program;
2702                let declaration = &program.methods[class][method];
2703                self.function(
2704                    declaration,
2705                    args,
2706                    &declaration.span.module,
2707                    Some(receiver.as_ref().clone()),
2708                    span,
2709                )?
2710                .0
2711            }
2712            FunctionTarget::EntityMethod {
2713                class,
2714                method,
2715                entity,
2716            } => self.call_entity_method(*entity, class, method, args, span)?,
2717        };
2718        self.expect_type(&result, returns, canonical_type_scope(returns), span)?;
2719        Ok(result)
2720    }
2721    fn call_builtin_value(
2722        &mut self,
2723        name: &str,
2724        type_argument: Option<&Type>,
2725        mut args: Vec<Value>,
2726        span: &Span,
2727    ) -> Result<Value, Diagnostic> {
2728        if let Some(selected) = type_argument {
2729            if matches!(name, "tensor" | "convert") {
2730                let result = self.typed_data_builtin(name, selected, args, span);
2731                return if name == "tensor" {
2732                    self.result_with_type(
2733                        result,
2734                        &data::result_type(data::tensor_type(selected.clone())),
2735                        span,
2736                    )
2737                } else {
2738                    result
2739                };
2740            }
2741            let Type::Named(key) = selected else {
2742                return Err(self.error(
2743                    "invalid_function",
2744                    "typed builtin requires a nominal type argument",
2745                    span,
2746                ));
2747            };
2748            args.push(Value::Text(key.clone().into()));
2749            self.builtin(name, args, canonical_type_scope(selected), span)
2750        } else {
2751            self.builtin(name, args, MAIN, span)
2752        }
2753    }
2754    fn call_entity_method(
2755        &mut self,
2756        id: u64,
2757        class: &str,
2758        method: &str,
2759        args: Vec<Value>,
2760        span: &Span,
2761    ) -> Result<Value, Diagnostic> {
2762        let original = self.component_value(id, class, span)?;
2763        let declaration = self
2764            .program
2765            .methods
2766            .get(class)
2767            .and_then(|m| m.get(method))
2768            .cloned()
2769            .ok_or_else(|| {
2770                self.error("unknown_method", "component has no callback method", span)
2771            })?;
2772        let prior_entity = self.entity.replace(id);
2773        let outcome = self.function(
2774            &declaration,
2775            args,
2776            &declaration.span.module,
2777            Some(original.clone()),
2778            span,
2779        );
2780        self.entity = prior_entity;
2781        let (result, updated) = outcome?;
2782        if let Some(updated) = updated
2783            && let Some(entity) = self.state.entities.get_mut(&id)
2784            && let Some(current) = entity.components.get(class)
2785        {
2786            if current != &original && updated != original && current != &updated {
2787                return Err(self.error(
2788                    "conflicting_component_write",
2789                    "method changed self and explicitly set the same component",
2790                    span,
2791                ));
2792            }
2793            if updated != original {
2794                entity.components.insert(class.into(), updated);
2795            }
2796        }
2797        Ok(result)
2798    }
2799    fn call_name(
2800        &mut self,
2801        name: &str,
2802        args: Vec<Value>,
2803        module: &str,
2804        span: &Span,
2805    ) -> Result<Value, Diagnostic> {
2806        self.consume(span)?;
2807        let native_name = name.strip_prefix("builtin.").unwrap_or(name);
2808        if is_builtin(native_name) {
2809            return self.builtin(native_name, args, module, span);
2810        }
2811        if let Some(native) = self.registry.functions.get(native_name) {
2812            if args.len() != native.parameters.len() {
2813                return Err(self.error(
2814                    "arity",
2815                    format!(
2816                        "`{name}` expects {} arguments, got {}",
2817                        native.parameters.len(),
2818                        args.len()
2819                    ),
2820                    span,
2821                ));
2822            }
2823            for (value, ty) in args.iter().zip(&native.parameters) {
2824                self.expect_type(value, ty, MAIN, span)?;
2825            }
2826            let result = (native.call)(&args)
2827                .map_err(|message| self.error("native_error", message, span))?;
2828            self.validate_external_value(&result, span)?;
2829            self.expect_type(&result, &native.returns, MAIN, span)?;
2830            return Ok(result);
2831        }
2832        let (target, local) = self.program.resolve(module, name, span)?;
2833        if let Some(function) = self.program.modules[&target].functions.get(&local).cloned() {
2834            return self
2835                .function(&function, args, &target, None, span)
2836                .map(|(value, _)| value);
2837        }
2838        if let Some(class) = self.program.modules[&target].classes.get(&local).cloned() {
2839            if args.len() > class.fields.len() {
2840                return Err(self.error(
2841                    "arity",
2842                    format!("class `{name}` has {} fields", class.fields.len()),
2843                    span,
2844                ));
2845            }
2846            let fields = args
2847                .into_iter()
2848                .zip(class.fields.iter())
2849                .map(|(value, field)| (field.name.clone(), value))
2850                .collect::<BTreeMap<_, _>>();
2851            if self.depth >= self.limits.max_call_depth {
2852                return Err(self.error(
2853                    "call_depth",
2854                    "constructor nesting exceeds call-depth limit",
2855                    span,
2856                ));
2857            }
2858            self.depth += 1;
2859            let result = self.construct_values(&target, &class, fields, span);
2860            self.depth -= 1;
2861            return result;
2862        }
2863        Err(self.error(
2864            "unknown_function",
2865            format!("unknown function `{name}`"),
2866            span,
2867        ))
2868    }
2869    fn function(
2870        &mut self,
2871        function: &Function,
2872        args: Vec<Value>,
2873        module: &str,
2874        self_value: Option<Value>,
2875        span: &Span,
2876    ) -> Result<(Value, Option<Value>), Diagnostic> {
2877        if self.depth >= self.limits.max_call_depth {
2878            return Err(self.error(
2879                "call_depth",
2880                "function nesting exceeds call-depth limit",
2881                span,
2882            ));
2883        }
2884        if args.len() != function.parameters.len() {
2885            return Err(self.error(
2886                "arity",
2887                format!(
2888                    "`{}` expects {} arguments, got {}",
2889                    function.name,
2890                    function.parameters.len(),
2891                    args.len()
2892                ),
2893                span,
2894            ));
2895        }
2896        let mut env = Env::default();
2897        for (parameter, value) in function.parameters.iter().zip(args) {
2898            self.expect_type(&value, &parameter.ty, module, span)?;
2899            env.scopes[0].insert(
2900                parameter.name.clone(),
2901                Binding {
2902                    value,
2903                    ty: parameter.ty.clone(),
2904                    module: module.into(),
2905                    mutable: false,
2906                },
2907            );
2908        }
2909        if let Some(value) = self_value {
2910            env.bind("self", value, true, module);
2911        }
2912        self.record(span, format!("call {}", function.name));
2913        self.depth += 1;
2914        let result = self.block(&function.body, module, &mut env);
2915        self.depth -= 1;
2916        let result = match result {
2917            Err(error) if error.code == "__return_algebraic" => {
2918                let mut value = self.propagated.take().expect("propagation owns its value");
2919                if let Value::Enum(value) = &mut value {
2920                    value.ty =
2921                        self.program
2922                            .canonical_type(module, &function.returns, &function.span)?;
2923                }
2924                Ok(Flow::Return(value))
2925            }
2926            other => other,
2927        };
2928        let value = match result? {
2929            Flow::Return(value) => value,
2930            Flow::Next => Value::Unit,
2931            _ => {
2932                return Err(self.error(
2933                    "invalid_control",
2934                    "break/continue require a loop",
2935                    &function.span,
2936                ));
2937            }
2938        };
2939        self.expect_type(&value, &function.returns, module, &function.span)?;
2940        Ok((value, env.get("self").map(|b| b.value.clone())))
2941    }
2942    fn arguments(
2943        &mut self,
2944        arguments: &[Expr],
2945        module: &str,
2946        env: &mut Env,
2947    ) -> Result<Vec<Value>, Diagnostic> {
2948        let mut args = Vec::new();
2949        for argument in arguments {
2950            args.push(self.eval(argument, module, env)?);
2951        }
2952        Ok(args)
2953    }
2954    fn method(
2955        &mut self,
2956        object: &Expr,
2957        name: &str,
2958        arguments: &[Expr],
2959        module: &str,
2960        env: &mut Env,
2961        span: &Span,
2962    ) -> Result<Value, Diagnostic> {
2963        let location = self.freeze_location(object, module, env)?;
2964        let value = self.eval(&location, module, env)?;
2965        let args = self.arguments(arguments, module, env)?;
2966        let Value::Record(record) = &value else {
2967            return Err(self.error("unknown_method", "methods require a class value", span));
2968        };
2969        let function = self
2970            .program
2971            .methods
2972            .get(&record.class)
2973            .and_then(|methods| methods.get(name))
2974            .cloned()
2975            .ok_or_else(|| {
2976                self.error(
2977                    "unknown_method",
2978                    format!("{} has no method `{name}`", record.class),
2979                    span,
2980                )
2981            })?;
2982        let method_module = function.span.module.clone();
2983        let (result, updated) =
2984            self.function(&function, args, &method_module, Some(value.clone()), span)?;
2985        if let Some(updated) = updated
2986            && updated != value
2987        {
2988            let current = self.eval(&location, module, env)?;
2989            if current != value && current != updated {
2990                return Err(self.error(
2991                    "conflicting_receiver_write",
2992                    "method arguments or body changed the receiver through another binding",
2993                    span,
2994                ));
2995            }
2996            self.assign(&location, updated, module, env)?;
2997        }
2998        Ok(result)
2999    }
3000    fn assign(
3001        &mut self,
3002        target: &Expr,
3003        value: Value,
3004        module: &str,
3005        env: &mut Env,
3006    ) -> Result<(), Diagnostic> {
3007        enum Part {
3008            Field(String),
3009            Index(Vec<AxisIndex>),
3010        }
3011        fn decompose(expr: &Expr) -> Option<(&str, Vec<&Expr>)> {
3012            match &expr.kind {
3013                ExprKind::Propagate(value) => decompose(value),
3014                ExprKind::Name(n) => Some((n, Vec::new())),
3015                ExprKind::Field { object, .. } | ExprKind::Index { object, .. } => {
3016                    let (name, mut path) = decompose(object)?;
3017                    path.push(expr);
3018                    Some((name, path))
3019                }
3020                _ => None,
3021            }
3022        }
3023        let (name, path) = decompose(target).ok_or_else(|| {
3024            self.error(
3025                "invalid_assignment",
3026                "assignment requires a variable, field, or list element",
3027                &target.span,
3028            )
3029        })?;
3030        let mut parts = Vec::new();
3031        for expr in path {
3032            match &expr.kind {
3033                ExprKind::Field { field, .. } => parts.push(Part::Field(field.clone())),
3034                ExprKind::Index { indices, .. } => {
3035                    parts.push(Part::Index(self.selectors(indices, module, env)?))
3036                }
3037                _ => unreachable!(),
3038            }
3039        }
3040        let key = Program::key(module, name);
3041        let binding = env
3042            .get(name)
3043            .or_else(|| self.state.globals.get(&key))
3044            .cloned()
3045            .ok_or_else(|| {
3046                self.error(
3047                    "unknown_name",
3048                    format!("unknown variable `{name}`"),
3049                    &target.span,
3050                )
3051            })?;
3052        if !binding.mutable {
3053            return Err(self.error(
3054                "immutable_binding",
3055                format!("`{name}` is immutable; declare it with `let mut`"),
3056                &target.span,
3057            ));
3058        }
3059        let mut replacement = binding.value.clone();
3060        let mut destination = &mut replacement;
3061        let mut parts = parts.into_iter().peekable();
3062        let mut value = Some(value);
3063        while let Some(part) = parts.next() {
3064            if let Part::Index(indices) = &part
3065                && let Value::Tensor(tensor) = &*destination
3066            {
3067                let mut selections = vec![indices.clone()];
3068                for part in parts.by_ref() {
3069                    let Part::Index(indices) = part else {
3070                        return Err(self.error(
3071                            "invalid_assignment",
3072                            "tensor elements have no assignable fields",
3073                            &target.span,
3074                        ));
3075                    };
3076                    selections.push(indices);
3077                }
3078                let assigned = value.take().expect("assignment consumes its value once");
3079                let updated = self.assign_tensor(tensor, &selections, assigned, &target.span)?;
3080                *destination = Value::Tensor(Box::new(updated));
3081                break;
3082            }
3083            destination = match (part, destination) {
3084                (Part::Field(field), Value::Record(record)) => {
3085                    record.fields.get_mut(&field).ok_or_else(|| {
3086                        self.error(
3087                            "unknown_field",
3088                            format!("unknown field `{field}`"),
3089                            &target.span,
3090                        )
3091                    })?
3092                }
3093                (Part::Index(indices), Value::List(values)) => {
3094                    let [AxisIndex::Index(index)] = indices.as_slice() else {
3095                        return Err(self.error(
3096                            "invalid_index",
3097                            "List assignment requires one scalar index",
3098                            &target.span,
3099                        ));
3100                    };
3101                    let index = normalize_index(*index, values.len()).ok_or_else(|| {
3102                        self.error(
3103                            "index_out_of_bounds",
3104                            "list index is out of bounds",
3105                            &target.span,
3106                        )
3107                    })?;
3108                    &mut values[index]
3109                }
3110                _ => {
3111                    return Err(self.error(
3112                        "invalid_assignment",
3113                        "assignment path does not match value shape",
3114                        &target.span,
3115                    ));
3116                }
3117            };
3118        }
3119        if let Some(value) = value {
3120            *destination = value;
3121        }
3122        self.check_value(&replacement, &target.span)?;
3123        self.expect_type(&replacement, &binding.ty, &binding.module, &target.span)?;
3124        self.validate_value(&replacement, &target.span, false)?;
3125        if let Some(binding) = env.get_mut(name) {
3126            binding.value = replacement;
3127        } else if let Some(binding) = self.state.globals.get_mut(&key) {
3128            binding.value = replacement;
3129        }
3130        Ok(())
3131    }
3132    fn assign_tensor(
3133        &mut self,
3134        tensor: &Tensor,
3135        selections: &[Vec<AxisIndex>],
3136        value: Value,
3137        span: &Span,
3138    ) -> Result<Tensor, Diagnostic> {
3139        let mut selected = tensor.clone();
3140        for indices in selections {
3141            if indices
3142                .iter()
3143                .all(|index| matches!(index, AxisIndex::Index(_)))
3144            {
3145                let indices = indices
3146                    .iter()
3147                    .map(|index| {
3148                        if let AxisIndex::Index(index) = index {
3149                            *index
3150                        } else {
3151                            unreachable!()
3152                        }
3153                    })
3154                    .collect::<Vec<_>>();
3155                // Scalar indexing has the same full-rank contract for reading and writing.
3156                selected
3157                    .get(&indices)
3158                    .map_err(|e| self.data_error(e, span))?;
3159            }
3160            selected = selected
3161                .slice(indices)
3162                .map_err(|e| self.data_error(e, span))?;
3163        }
3164        let (replacement_dtype, replacement_shape) = match &value {
3165            Value::Tensor(replacement) => (replacement.dtype(), replacement.shape()),
3166            value => {
3167                let scalar = data::as_scalar(value).ok_or_else(|| {
3168                    self.error(
3169                        "type_mismatch",
3170                        "tensor assignment requires matching numeric data",
3171                        span,
3172                    )
3173                })?;
3174                (scalar.dtype(), &[][..])
3175            }
3176        };
3177        if replacement_dtype != tensor.dtype() {
3178            return Err(self.error("type_mismatch", "tensor assignment dtypes must match", span));
3179        }
3180        if broadcast_shape(replacement_shape, selected.shape()).as_deref() != Some(selected.shape())
3181        {
3182            return Err(self.error(
3183                "shape_mismatch",
3184                "replacement cannot broadcast into the selected tensor shape",
3185                span,
3186            ));
3187        }
3188        let work = tensor.len().saturating_add(selected.len());
3189        // The SDK copies logical source bytes once and creates one independent packed
3190        // result. Account Vec-to-Arc transfer peak before either allocation occurs.
3191        let bytes = tensor.byte_len();
3192        let transient = bytes.saturating_mul(2).saturating_add(16);
3193        if bytes > self.limits.max_storage_bytes
3194            || transient
3195                > self
3196                    .limits
3197                    .max_allocation_bytes
3198                    .saturating_sub(self.allocated_bytes)
3199        {
3200            return Err(self.error(
3201                "allocation_limit",
3202                "tensor assignment exceeds packed copy budget",
3203                span,
3204            ));
3205        }
3206        self.data_budget(0, work, span)?;
3207        let rhs = match value {
3208            Value::Tensor(tensor) => *tensor,
3209            value => {
3210                let scalar = data::as_scalar(&value).expect("replacement checked above");
3211                Tensor::from_scalars(scalar.dtype(), vec![], vec![scalar])
3212                    .map_err(|e| self.data_error(e, span))?
3213            }
3214        };
3215        let updated = tensor
3216            .with_index_path(selections, &rhs)
3217            .map_err(|e| self.data_error(e, span))?;
3218        self.allocated_bytes = self.allocated_bytes.saturating_add(updated.byte_len());
3219        Ok(updated)
3220    }
3221    fn set_component(
3222        &mut self,
3223        id: u64,
3224        value: Value,
3225        add: bool,
3226        span: &Span,
3227    ) -> Result<(), Diagnostic> {
3228        self.validate_record(&value, span)?;
3229        let class = value.type_name().to_owned();
3230        let entity = self.state.entities.get_mut(&id).ok_or_else(|| {
3231            Diagnostic::new(
3232                "unknown_entity",
3233                format!("entity {id} does not exist"),
3234                span.clone(),
3235            )
3236        })?;
3237        if entity.components.contains_key(&class) == add {
3238            return Err(Diagnostic::new(
3239                if add {
3240                    "duplicate_component"
3241                } else {
3242                    "missing_component"
3243                },
3244                format!(
3245                    "entity {id} {} component `{class}`",
3246                    if add { "already has" } else { "does not have" }
3247                ),
3248                span.clone(),
3249            ));
3250        }
3251        entity.components.insert(class, value);
3252        Ok(())
3253    }
3254    fn range_values(&self, args: &[Value], span: &Span) -> Result<Value, Diagnostic> {
3255        let bounds = match args {
3256            [Value::Number(end)] => (0.0, *end),
3257            [Value::Number(start), Value::Number(end)] => (*start, *end),
3258            _ => return Err(self.error("arity", "range expects one or two numeric bounds", span)),
3259        };
3260        let (start, end) = bounds;
3261        if start.fract() != 0.0
3262            || end.fract() != 0.0
3263            || !start.is_finite()
3264            || !end.is_finite()
3265            || start.abs() > 9_007_199_254_740_991.0
3266            || end.abs() > 9_007_199_254_740_991.0
3267        {
3268            return Err(self.data_error(konjure_sdk::data::DataError::Bounds, span));
3269        }
3270        let length = (end - start).max(0.0);
3271        if length > self.limits.max_collection as f64 {
3272            return Err(self.error("collection_limit", "range exceeds collection limit", span));
3273        }
3274        Ok(Value::List(
3275            (0..length as usize)
3276                .map(|index| Value::Number(start + index as f64))
3277                .collect(),
3278        ))
3279    }
3280    fn builtin(
3281        &mut self,
3282        name: &str,
3283        args: Vec<Value>,
3284        module: &str,
3285        span: &Span,
3286    ) -> Result<Value, Diagnostic> {
3287        if name == "range" {
3288            let result = self.range_values(&args, span);
3289            return self.result_with_type(
3290                result,
3291                &data::result_type(Type::List(Box::new(Type::named("f64")))),
3292                span,
3293            );
3294        }
3295
3296        if matches!(
3297            name,
3298            "shape" | "reshape" | "transpose" | "sum" | "matmul" | "utf8" | "bytes" | "decode"
3299        ) {
3300            return self.data_builtin(name, args, span);
3301        }
3302        let arity = match name {
3303            "add" | "get" | "set" | "has" | "bind" | "min" | "max" | "pow" | "append" => 2,
3304            "clamp" => 3,
3305            "noop" => 0,
3306            "range" => args.len(),
3307            _ => 1,
3308        };
3309        if args.len() != arity {
3310            return Err(self.error(
3311                "arity",
3312                format!("`{name}` expects {arity} arguments, got {}", args.len()),
3313                span,
3314            ));
3315        }
3316        let entity_id = |value: &Value| {
3317            if let Value::Entity(id) = value {
3318                Ok(*id)
3319            } else {
3320                Err(Diagnostic::new(
3321                    "type_mismatch",
3322                    "expected Entity",
3323                    span.clone(),
3324                ))
3325            }
3326        };
3327        let number = |value: &Value| {
3328            if let Value::Number(n) = value {
3329                Ok(*n)
3330            } else {
3331                Err(Diagnostic::new(
3332                    "type_mismatch",
3333                    "expected Number",
3334                    span.clone(),
3335                ))
3336            }
3337        };
3338        let class_key = |value: &Value| -> Result<String, Diagnostic> {
3339            let Value::Text(name) = value else {
3340                return Err(Diagnostic::new(
3341                    "type_mismatch",
3342                    "component name requires String",
3343                    span.clone(),
3344                ));
3345            };
3346            let (m, c) = self.program.class(module, name, span)?;
3347            Ok(Program::key(&m, &c.name))
3348        };
3349        let value = match name {
3350            "noop" => Value::algebraic(crate::EnumValue {
3351                origin: None,
3352                ty: data::result_type(Type::named("Unit")),
3353                case: "Ok".into(),
3354                payload: vec![Value::Unit],
3355            }),
3356            "ignore_number" => {
3357                number(&args[0])?;
3358                Value::algebraic(crate::EnumValue {
3359                    origin: None,
3360                    ty: data::result_type(Type::named("Unit")),
3361                    case: "Ok".into(),
3362                    payload: vec![Value::Unit],
3363                })
3364            }
3365            "bind" => {
3366                let entity = entity_id(&args[0])?;
3367                let class = class_key(&args[1])?;
3368                self.component_value(entity, &class, span)?;
3369                Value::ComponentReference(ComponentReference { entity, class })
3370            }
3371            "spawn" => {
3372                self.validate_record(&args[0], span)?;
3373                if self.state.entities.len() >= self.limits.max_entities {
3374                    return Err(self.error("entity_limit", "entity capacity exceeded", span));
3375                }
3376                let id = self.state.next_id;
3377                self.state.next_id = id
3378                    .checked_add(1)
3379                    .ok_or_else(|| self.error("entity_limit", "entity IDs exhausted", span))?;
3380                self.state.entities.insert(
3381                    id,
3382                    EntitySnapshot {
3383                        id,
3384                        components: BTreeMap::from([(
3385                            args[0].type_name().to_owned(),
3386                            args[0].clone(),
3387                        )]),
3388                        source: span.clone(),
3389                    },
3390                );
3391                self.record(span, format!("spawn {id}"));
3392                Value::Entity(id)
3393            }
3394            "add" | "set" => {
3395                self.set_component(entity_id(&args[0])?, args[1].clone(), name == "add", span)?;
3396                Value::Unit
3397            }
3398            "get" | "has" => {
3399                let id = entity_id(&args[0])?;
3400                let class = class_key(&args[1])?;
3401                let entity = self.state.entities.get(&id).ok_or_else(|| {
3402                    self.error(
3403                        "unknown_entity",
3404                        format!("entity {id} does not exist"),
3405                        span,
3406                    )
3407                })?;
3408                if name == "has" {
3409                    Value::Bool(entity.components.contains_key(&class))
3410                } else {
3411                    entity.components.get(&class).cloned().ok_or_else(|| {
3412                        self.error(
3413                            "missing_component",
3414                            format!("entity {id} has no `{class}`"),
3415                            span,
3416                        )
3417                    })?
3418                }
3419            }
3420            "query" => {
3421                let Value::Text(name) = &args[0] else {
3422                    return Err(self.error(
3423                        "type_mismatch",
3424                        "query requires a class or trait name String",
3425                        span,
3426                    ));
3427                };
3428                let classes = self.program.query_classes(module, name, span)?;
3429                Value::List(
3430                    self.state
3431                        .entities
3432                        .values()
3433                        .filter(|e| classes.iter().any(|c| e.components.contains_key(c)))
3434                        .map(|e| Value::Entity(e.id))
3435                        .collect(),
3436                )
3437            }
3438            "despawn" => {
3439                let id = entity_id(&args[0])?;
3440                if !self.state.entities.contains_key(&id) {
3441                    return Err(self.error(
3442                        "unknown_entity",
3443                        format!("entity {id} does not exist"),
3444                        span,
3445                    ));
3446                }
3447                self.state.pending_despawns.insert(id);
3448                Value::Unit
3449            }
3450            "print" => {
3451                self.data_budget(0, display_work(&args[0]), span)?;
3452                let available = self
3453                    .limits
3454                    .max_allocation_bytes
3455                    .saturating_sub(self.allocated_bytes);
3456                let mut text = BoundedText {
3457                    value: String::new(),
3458                    limit: self.limits.max_string_bytes.min(available),
3459                };
3460                std::fmt::write(&mut text, format_args!("{}", args[0])).map_err(|_| {
3461                    self.error(
3462                        if available < self.limits.max_string_bytes {
3463                            "allocation_limit"
3464                        } else {
3465                            "value_limit"
3466                        },
3467                        "printed value exceeds string or allocation budget",
3468                        span,
3469                    )
3470                })?;
3471                let text = text.value;
3472                self.allocated_bytes = self.allocated_bytes.saturating_add(text.len());
3473                if self.limits.max_logs > 0 {
3474                    if self.state.logs.len() >= self.limits.max_logs {
3475                        self.state.logs.remove(0);
3476                    }
3477                    self.state.logs.push(text);
3478                }
3479                Value::Unit
3480            }
3481            "len" => match &args[0] {
3482                Value::List(v) => Value::Number(v.len() as f64),
3483                Value::Text(s) => Value::Number(s.chars().count() as f64),
3484                Value::Bin(b) => Value::Number(b.byte_len() as f64),
3485                Value::Tensor(t) => Value::Number(t.len() as f64),
3486                _ => return Err(self.error("type_mismatch", "len requires List or String", span)),
3487            },
3488            "append" => {
3489                let Value::List(mut values) = args[0].clone() else {
3490                    return Err(self.error(
3491                        "type_mismatch",
3492                        "append requires List as first argument",
3493                        span,
3494                    ));
3495                };
3496                if !self.program.expression_types.contains_key(&(
3497                    span.module.clone(),
3498                    span.start,
3499                    span.end,
3500                )) && let Some(first) = values.first()
3501                    && infer_type(first) != infer_type(&args[1])
3502                {
3503                    return Err(self.error(
3504                        "type_mismatch",
3505                        "appended value has a different element type",
3506                        span,
3507                    ));
3508                }
3509                if values.len() >= self.limits.max_collection {
3510                    return Err(self.error(
3511                        "collection_limit",
3512                        "append exceeds collection limit",
3513                        span,
3514                    ));
3515                }
3516                values.push(args[1].clone());
3517                Value::List(values)
3518            }
3519            "sin" => Value::Number(libm::sin(number(&args[0])?)),
3520            "cos" => Value::Number(libm::cos(number(&args[0])?)),
3521            "sqrt" => Value::Number(libm::sqrt(number(&args[0])?)),
3522            "abs" => Value::Number(libm::fabs(number(&args[0])?)),
3523            "floor" => Value::Number(libm::floor(number(&args[0])?)),
3524            "ceil" => Value::Number(libm::ceil(number(&args[0])?)),
3525            "min" => Value::Number(number(&args[0])?.min(number(&args[1])?)),
3526            "max" => Value::Number(number(&args[0])?.max(number(&args[1])?)),
3527            "pow" => Value::Number(libm::pow(number(&args[0])?, number(&args[1])?)),
3528            "clamp" => {
3529                let (n, min, max) = (number(&args[0])?, number(&args[1])?, number(&args[2])?);
3530                if min > max {
3531                    return Err(self.error(
3532                        "invalid_bounds",
3533                        "clamp minimum exceeds maximum",
3534                        span,
3535                    ));
3536                }
3537                Value::Number(n.clamp(min, max))
3538            }
3539            _ => {
3540                return Err(self.error(
3541                    "unknown_function",
3542                    format!("unknown builtin `{name}`"),
3543                    span,
3544                ));
3545            }
3546        };
3547        if matches!(&value,Value::Number(value) if !value.is_finite()) {
3548            return Err(self.data_error(konjure_sdk::data::DataError::NonFinite, span));
3549        }
3550        self.check_value(&value, span)?;
3551        Ok(value)
3552    }
3553}
3554
3555impl Evaluator<'_> {
3556    fn data_error(&self, error: konjure_sdk::data::DataError, span: &Span) -> Diagnostic {
3557        use konjure_sdk::data::DataError;
3558        let code = match error {
3559            DataError::Overflow => "numeric_overflow",
3560            DataError::DivisionByZero => "division_by_zero",
3561            DataError::InexactConversion => "inexact_conversion",
3562            DataError::Bounds => "index_out_of_bounds",
3563            DataError::InvalidSlice => "invalid_slice",
3564            DataError::ShapeMismatch | DataError::InvalidShape => "shape_mismatch",
3565            DataError::AllocationLimit => "allocation_limit",
3566            _ => "invalid_data",
3567        };
3568        let mut diagnostic = self.error(code, error.to_string(), span);
3569        diagnostic.data_error = Some(error.name().into());
3570        diagnostic
3571    }
3572    fn literal(&self, raw: &str, span: &Span) -> Result<Value, Diagnostic> {
3573        let dtype = self
3574            .program
3575            .expression_types
3576            .get(&(span.module.clone(), span.start, span.end))
3577            .and_then(data::dtype)
3578            .unwrap_or(DType::F64);
3579        Scalar::parse(dtype, raw)
3580            .map(data::scalar_value)
3581            .map_err(|e| self.data_error(e, span))
3582    }
3583    /// Preflight allocation and arithmetic work before invoking an allocating SDK operation.
3584    fn data_budget(&mut self, elements: usize, work: usize, span: &Span) -> Result<(), Diagnostic> {
3585        let bytes = elements
3586            .checked_mul(16)
3587            .ok_or_else(|| self.error("allocation_limit", "data size overflow", span))?;
3588        if bytes > self.limits.max_storage_bytes
3589            || bytes
3590                > self
3591                    .limits
3592                    .max_allocation_bytes
3593                    .saturating_sub(self.allocated_bytes)
3594        {
3595            return Err(self.error(
3596                "allocation_limit",
3597                "data operation exceeds byte budget",
3598                span,
3599            ));
3600        }
3601        if work > self.fuel {
3602            return Err(self.error(
3603                "fuel_exhausted",
3604                "data operation exceeds remaining operation work budget",
3605                span,
3606            ));
3607        }
3608        self.fuel -= work;
3609        Ok(())
3610    }
3611    fn signed_index_value(&self, value: &Value, span: &Span) -> Result<isize, Diagnostic> {
3612        let scalar = data::as_scalar(value)
3613            .ok_or_else(|| self.error("invalid_index", "index requires an integer", span))?;
3614        scalar
3615            .convert(DType::I128)
3616            .ok()
3617            .and_then(|s| s.to_string().parse::<i32>().ok())
3618            .map(|index| index as isize)
3619            .ok_or_else(|| {
3620                self.error(
3621                    "invalid_index",
3622                    "index must be an exact integer in the portable signed 32-bit index range",
3623                    span,
3624                )
3625            })
3626    }
3627    fn dimensions(&self, value: &Value, span: &Span) -> Result<Vec<usize>, Diagnostic> {
3628        let Value::List(values) = value else {
3629            return Err(self.error("type_mismatch", "shape requires List[f64]", span));
3630        };
3631        if values.len() > self.limits.max_value_depth {
3632            return Err(self.error("value_limit", "tensor rank limit exceeded", span));
3633        }
3634        values
3635            .iter()
3636            .map(|value| {
3637                let n = self.signed_index_value(value, span).map_err(|_| {
3638                    self.data_error(konjure_sdk::data::DataError::InvalidShape, span)
3639                })?;
3640                usize::try_from(n).map_err(|_| {
3641                    self.error(
3642                        "invalid_shape",
3643                        "shape dimensions and axis permutations must be nonnegative",
3644                        span,
3645                    )
3646                })
3647            })
3648            .collect()
3649    }
3650    fn selectors(
3651        &mut self,
3652        indices: &[IndexExpr],
3653        module: &str,
3654        env: &mut Env,
3655    ) -> Result<Vec<AxisIndex>, Diagnostic> {
3656        let mut result = Vec::with_capacity(indices.len());
3657        for index in indices {
3658            result.push(match index {
3659                IndexExpr::Index(expr) => {
3660                    let value = self.eval(expr, module, env)?;
3661                    AxisIndex::Index(self.signed_index_value(&value, &expr.span)?)
3662                }
3663                IndexExpr::Slice { start, stop, step } => {
3664                    let mut eval_bound =
3665                        |expr: &Option<Box<Expr>>| -> Result<Option<isize>, Diagnostic> {
3666                            expr.as_ref()
3667                                .map(|expr| {
3668                                    let value = self.eval(expr, module, env)?;
3669                                    self.signed_index_value(&value, &expr.span)
3670                                })
3671                                .transpose()
3672                        };
3673                    let start = eval_bound(start)?;
3674                    let stop = eval_bound(stop)?;
3675                    let step = eval_bound(step)?.unwrap_or(1);
3676                    AxisIndex::Slice { start, stop, step }
3677                }
3678                IndexExpr::NewAxis => AxisIndex::NewAxis,
3679                IndexExpr::Ellipsis => AxisIndex::Ellipsis,
3680            });
3681        }
3682        Ok(result)
3683    }
3684    fn index_value(
3685        &mut self,
3686        value: Value,
3687        indices: &[AxisIndex],
3688        span: &Span,
3689    ) -> Result<Value, Diagnostic> {
3690        match value {
3691            Value::Tensor(t) => {
3692                if indices.iter().all(|i| matches!(i, AxisIndex::Index(_))) {
3693                    let indices = indices
3694                        .iter()
3695                        .map(|i| {
3696                            if let AxisIndex::Index(n) = i {
3697                                *n
3698                            } else {
3699                                unreachable!()
3700                            }
3701                        })
3702                        .collect::<Vec<_>>();
3703                    t.get(&indices)
3704                        .map(data::scalar_value)
3705                        .map_err(|e| self.data_error(e, span))
3706                } else {
3707                    t.slice(indices)
3708                        .map(|tensor| Value::Tensor(Box::new(tensor)))
3709                        .map_err(|e| self.data_error(e, span))
3710                }
3711            }
3712            Value::List(values) => {
3713                let [AxisIndex::Index(index)] = indices else {
3714                    return Err(self.error(
3715                        "invalid_index",
3716                        "Lists require one scalar index",
3717                        span,
3718                    ));
3719                };
3720                let index = normalize_index(*index, values.len()).ok_or_else(|| {
3721                    self.error("index_out_of_bounds", "list index out of bounds", span)
3722                })?;
3723                Ok(values[index].clone())
3724            }
3725            Value::Bin(bytes) => {
3726                let [selector] = indices else {
3727                    return Err(self.error(
3728                        "invalid_index",
3729                        "Bin requires one index or slice",
3730                        span,
3731                    ));
3732                };
3733                if let AxisIndex::Index(index) = selector {
3734                    let index = normalize_index(*index, bytes.byte_len()).ok_or_else(|| {
3735                        self.error("index_out_of_bounds", "byte index out of bounds", span)
3736                    })?;
3737                    return Scalar::from_bits(DType::U8, bytes.as_bytes()[index] as u128)
3738                        .map(data::scalar_value)
3739                        .map_err(|e| self.data_error(e, span));
3740                }
3741                self.data_budget(bytes.byte_len(), bytes.byte_len(), span)?;
3742                let selected = sequence_indices(selector, bytes.byte_len())
3743                    .map_err(|e| self.error("invalid_slice", e, span))?;
3744                Ok(Value::Bin(
3745                    selected
3746                        .into_iter()
3747                        .map(|i| bytes.as_bytes()[i])
3748                        .collect::<Vec<_>>()
3749                        .into(),
3750                ))
3751            }
3752            Value::Text(text) => {
3753                let [selector] = indices else {
3754                    return Err(self.error(
3755                        "invalid_index",
3756                        "Str requires one index or slice",
3757                        span,
3758                    ));
3759                };
3760                self.data_budget(text.len(), text.chars().count(), span)?;
3761                let chars = text.chars().collect::<Vec<_>>();
3762                let selected = sequence_indices(selector, chars.len())
3763                    .map_err(|e| self.error("invalid_slice", e, span))?;
3764                Ok(Value::Text(
3765                    selected
3766                        .into_iter()
3767                        .map(|i| chars[i])
3768                        .collect::<String>()
3769                        .into(),
3770                ))
3771            }
3772            _ => Err(self.error(
3773                "type_mismatch",
3774                "indexing requires Tensor, List, Str or Bin",
3775                span,
3776            )),
3777        }
3778    }
3779    fn typed_data_builtin(
3780        &mut self,
3781        name: &str,
3782        target: &Type,
3783        args: Vec<Value>,
3784        span: &Span,
3785    ) -> Result<Value, Diagnostic> {
3786        let dtype = data::dtype(target)
3787            .ok_or_else(|| self.error("invalid_dtype", "type argument must be numeric", span))?;
3788        match (name, args.as_slice()) {
3789            ("tensor", [Value::List(values), shape]) => {
3790                let shape = self.dimensions(shape, span)?;
3791                let elements = shape
3792                    .iter()
3793                    .try_fold(1usize, |a, b| a.checked_mul(*b))
3794                    .ok_or_else(|| {
3795                        self.error("allocation_limit", "tensor shape product overflow", span)
3796                    })?;
3797                if elements != values.len() {
3798                    return Err(self.error(
3799                        "shape_mismatch",
3800                        "shape product must equal element count",
3801                        span,
3802                    ));
3803                }
3804                self.data_budget(elements, elements, span)?;
3805                let values = values
3806                    .iter()
3807                    .map(|value| {
3808                        data::as_scalar(value)
3809                            .filter(|s| s.dtype() == dtype)
3810                            .ok_or_else(|| {
3811                                self.error(
3812                                    "type_mismatch",
3813                                    "tensor element dtype must match T",
3814                                    span,
3815                                )
3816                            })
3817                    })
3818                    .collect::<Result<Vec<_>, _>>()?;
3819                Tensor::from_scalars(dtype, shape, values)
3820                    .map(|tensor| Value::Tensor(Box::new(tensor)))
3821                    .map_err(|e| self.data_error(e, span))
3822            }
3823            ("convert", [Value::Tensor(t)]) => {
3824                self.data_budget(t.len(), t.len(), span)?;
3825                t.convert(dtype)
3826                    .map(|tensor| Value::Tensor(Box::new(tensor)))
3827                    .map_err(|e| self.data_error(e, span))
3828            }
3829            ("convert", [value]) => data::as_scalar(value)
3830                .ok_or_else(|| self.error("type_mismatch", "convert requires numeric data", span))?
3831                .convert(dtype)
3832                .map(data::scalar_value)
3833                .map_err(|e| self.data_error(e, span)),
3834            _ => Err(self.error("arity", format!("invalid arguments for {name}[T]"), span)),
3835        }
3836    }
3837    fn tensor_binary(
3838        &mut self,
3839        op: &str,
3840        left: Value,
3841        right: Value,
3842        span: &Span,
3843    ) -> Result<Value, Diagnostic> {
3844        let scalar_tensor = |value: Value| -> Result<Tensor, Diagnostic> {
3845            match value {
3846                Value::Tensor(t) => Ok(*t),
3847                value => {
3848                    let s = data::as_scalar(&value).ok_or_else(|| {
3849                        self.error(
3850                            "type_mismatch",
3851                            "tensor arithmetic requires numeric operands",
3852                            span,
3853                        )
3854                    })?;
3855                    Tensor::from_scalars(s.dtype(), vec![], vec![s])
3856                        .map_err(|e| self.data_error(e, span))
3857                }
3858            }
3859        };
3860        let left = scalar_tensor(left)?;
3861        let right = scalar_tensor(right)?;
3862        let (elements, work) = tensor_work(&left, &right, op == "@").ok_or_else(|| {
3863            self.error(
3864                "shape_mismatch",
3865                "incompatible tensor shapes or shape product overflow",
3866                span,
3867            )
3868        })?;
3869        self.data_budget(elements, work, span)?;
3870        let result = if op == "@" {
3871            left.matmul(&right)
3872        } else {
3873            let operation = data::binary_op(op).ok_or_else(|| {
3874                self.error("unknown_operator", "unsupported tensor operator", span)
3875            })?;
3876            left.binary(operation, &right)
3877        };
3878        result
3879            .map(|tensor| Value::Tensor(Box::new(tensor)))
3880            .map_err(|e| self.data_error(e, span))
3881    }
3882    fn data_builtin(
3883        &mut self,
3884        name: &str,
3885        args: Vec<Value>,
3886        span: &Span,
3887    ) -> Result<Value, Diagnostic> {
3888        match (name, args.as_slice()) {
3889            ("utf8", [Value::Text(text)]) => {
3890                self.data_budget(text.len(), text.len(), span)?;
3891                Ok(Value::Bin(text.as_bytes().into()))
3892            }
3893            ("decode", [Value::Bin(bytes)]) => {
3894                self.data_budget(bytes.byte_len(), bytes.byte_len(), span)?;
3895                std::str::from_utf8(bytes.as_bytes())
3896                    .map(|s| Value::Text(s.into()))
3897                    .map_err(|_| self.error("invalid_utf8", "Bin contains invalid UTF-8", span))
3898            }
3899            ("bytes", [Value::List(values)]) => {
3900                self.data_budget(values.len(), values.len(), span)?;
3901                let bytes = values
3902                    .iter()
3903                    .map(|v| {
3904                        data::as_scalar(v)
3905                            .filter(|s| s.dtype() == DType::U8)
3906                            .map(|s| s.bits() as u8)
3907                            .ok_or_else(|| {
3908                                self.error("type_mismatch", "bytes requires List[u8]", span)
3909                            })
3910                    })
3911                    .collect::<Result<Vec<_>, _>>()?;
3912                Ok(Value::Bin(bytes.into()))
3913            }
3914            ("shape", [Value::Tensor(t)]) => Ok(Value::List(
3915                t.shape().iter().map(|d| Value::Number(*d as f64)).collect(),
3916            )),
3917            ("reshape", [Value::Tensor(t), shape]) => {
3918                let shape = self.dimensions(shape, span)?;
3919                self.data_budget(t.len(), t.len(), span)?;
3920                t.reshape(shape)
3921                    .map(|tensor| Value::Tensor(Box::new(tensor)))
3922                    .map_err(|e| self.data_error(e, span))
3923            }
3924            ("transpose", [Value::Tensor(t)]) => {
3925                let axes = (0..t.shape().len()).rev().collect::<Vec<_>>();
3926                t.transpose(&axes)
3927                    .map(|tensor| Value::Tensor(Box::new(tensor)))
3928                    .map_err(|e| self.data_error(e, span))
3929            }
3930            ("transpose", [Value::Tensor(t), axes]) => {
3931                let axes = self.dimensions(axes, span)?;
3932                t.transpose(&axes)
3933                    .map(|tensor| Value::Tensor(Box::new(tensor)))
3934                    .map_err(|e| self.data_error(e, span))
3935            }
3936            ("sum", [Value::Tensor(t)]) => {
3937                self.data_budget(1, t.len(), span)?;
3938                t.sum()
3939                    .map(data::scalar_value)
3940                    .map_err(|e| self.data_error(e, span))
3941            }
3942            ("matmul", [Value::Tensor(a), Value::Tensor(b)]) => self.tensor_binary(
3943                "@",
3944                Value::Tensor(a.clone()),
3945                Value::Tensor(b.clone()),
3946                span,
3947            ),
3948            _ => Err(self.error(
3949                "type_mismatch",
3950                format!("invalid arguments for {name}"),
3951                span,
3952            )),
3953        }
3954    }
3955}
3956fn enum_type_compatible(actual: &Type, expected: &Type) -> bool {
3957    actual == expected
3958        || *actual == Type::named("Never")
3959        || match (actual, expected) {
3960            (
3961                Type::Applied {
3962                    name: a,
3963                    arguments: av,
3964                },
3965                Type::Applied {
3966                    name: b,
3967                    arguments: bv,
3968                },
3969            ) => {
3970                a == b
3971                    && av.len() == bv.len()
3972                    && av.iter().zip(bv).all(|(a, b)| enum_type_compatible(a, b))
3973            }
3974            _ => false,
3975        }
3976}
3977impl Evaluator<'_> {
3978    fn host_propagation(&mut self, error: Diagnostic) -> Diagnostic {
3979        if error.code != "__return_algebraic" {
3980            return error;
3981        }
3982        match self.propagated.take() {
3983            Some(value) => self.algebraic_error(&value, &error.span),
3984            None => self.error("invalid_control", "missing propagated value", &error.span),
3985        }
3986    }
3987    fn algebraic_error(&self, value: &Value, span: &Span) -> Diagnostic {
3988        let case = match value {
3989            Value::Enum(value) if value.case == "Err" => value
3990                .payload
3991                .first()
3992                .map(ToString::to_string)
3993                .unwrap_or_else(|| "Unknown".into()),
3994            _ => "Absent".into(),
3995        };
3996        let code = match case.as_str() {
3997            "Overflow" => "numeric_overflow",
3998            "DivisionByZero" => "division_by_zero",
3999            "InexactConversion" => "inexact_conversion",
4000            "Bounds" => "index_out_of_bounds",
4001            "InvalidUtf8" => "invalid_utf8",
4002            "InvalidShape" | "ShapeMismatch" => "shape_mismatch",
4003            "InvalidSlice" => "invalid_slice",
4004            _ => "data_error",
4005        };
4006        let origin = match value {
4007            Value::Enum(value) => value.origin.as_ref().unwrap_or(span),
4008            _ => span,
4009        };
4010        let mut diagnostic =
4011            self.error(code, format!("operation returned DataError.{case}"), origin);
4012        diagnostic.data_error = Some(case);
4013        diagnostic
4014    }
4015    fn accept_lifecycle_result(&self, value: Value, span: &Span) -> Result<(), Diagnostic> {
4016        if let Value::Enum(result) = &value {
4017            if result.case == "Ok" && result.payload == vec![Value::Unit] {
4018                return Ok(());
4019            }
4020            if result.case == "Err" {
4021                return Err(self.algebraic_error(&value, span));
4022            }
4023        }
4024        Err(self.error(
4025            "invalid_control",
4026            "lifecycle must return Unit or Res[Unit,DataError]",
4027            span,
4028        ))
4029    }
4030    fn enum_payload(&self, ty: &Type, case: &str, span: &Span) -> Result<Vec<Type>, Diagnostic> {
4031        if let Some(payload) = data::enum_payload(ty, case) {
4032            return Ok(payload);
4033        }
4034        let Type::Named(key) = ty else {
4035            return Err(self.error("invalid_enum", "unknown applied enum case", span));
4036        };
4037        let (module, declaration) = self
4038            .program
4039            .enum_by_key(key)
4040            .ok_or_else(|| self.error("invalid_enum", "unknown enum identity", span))?;
4041        let variant = declaration
4042            .cases
4043            .iter()
4044            .find(|variant| variant.name == case)
4045            .ok_or_else(|| self.error("invalid_enum", "unknown enum case", span))?;
4046        variant
4047            .payload
4048            .iter()
4049            .map(|ty| self.program.canonical_type(module, ty, span))
4050            .collect()
4051    }
4052    fn case_expression(
4053        &self,
4054        name: &str,
4055        args: Option<Vec<Value>>,
4056        module: &str,
4057        span: &Span,
4058    ) -> Result<Option<Value>, Diagnostic> {
4059        let resolved = if matches!(name, "Ok" | "Err" | "Some" | "None") {
4060            None
4061        } else {
4062            match self.program.variant(module, name, span) {
4063                Ok(value) => value,
4064                Err(error) if error.code == "unknown_module" || error.code == "private_name" => {
4065                    return Ok(None);
4066                }
4067                Err(error) => return Err(error),
4068            }
4069        };
4070        if resolved.is_none() && !matches!(name, "Ok" | "Err" | "Some" | "None") {
4071            return Ok(None);
4072        }
4073        let checked = self
4074            .program
4075            .expression_types
4076            .get(&(span.module.clone(), span.start, span.end))
4077            .ok_or_else(|| self.error("invalid_enum", "missing checked enum type", span))?;
4078        let (owner, payload) = match checked {
4079            Type::Function {
4080                parameters,
4081                returns,
4082            } => (returns.as_ref().clone(), parameters.clone()),
4083            ty => (ty.clone(), vec![]),
4084        };
4085        let case = resolved
4086            .map(|(_, case, _)| case)
4087            .unwrap_or_else(|| name.into());
4088        if let Some(args) = args {
4089            return Ok(Some(Value::algebraic(crate::EnumValue {
4090                origin: None,
4091                ty: owner,
4092                case,
4093                payload: args,
4094            })));
4095        }
4096        if payload.is_empty() {
4097            Ok(Some(Value::algebraic(crate::EnumValue {
4098                origin: None,
4099                ty: owner,
4100                case,
4101                payload: vec![],
4102            })))
4103        } else {
4104            Ok(Some(Value::Function(Box::new(FunctionValue {
4105                target: FunctionTarget::EnumConstructor { ty: owner, case },
4106                signature: checked.clone(),
4107            }))))
4108        }
4109    }
4110    fn data_method_value(
4111        &self,
4112        receiver: Value,
4113        method: &str,
4114        argument: Option<&Type>,
4115        span: &Span,
4116    ) -> Result<Value, Diagnostic> {
4117        let receiver_type = self
4118            .program
4119            .method_receivers
4120            .get(&(span.module.clone(), span.start, span.end))
4121            .cloned()
4122            .unwrap_or_else(|| infer_type(&receiver));
4123        let checked =
4124            self.program
4125                .expression_types
4126                .get(&(span.module.clone(), span.start, span.end));
4127        let signatures = crate::methods::signatures(&receiver_type, method, argument);
4128        let signature = checked
4129            .filter(|ty| signatures.contains(ty))
4130            .cloned()
4131            .or_else(|| (signatures.len() == 1).then(|| signatures[0].clone()))
4132            .ok_or_else(|| {
4133                self.error(
4134                    "invalid_function",
4135                    "missing checked data method signature",
4136                    span,
4137                )
4138            })?;
4139        Ok(Value::Function(Box::new(FunctionValue {
4140            target: FunctionTarget::DataMethod {
4141                receiver_type,
4142                method: method.into(),
4143                type_argument: argument.cloned().map(Box::new),
4144                receiver: Box::new(receiver),
4145            },
4146            signature,
4147        })))
4148    }
4149    fn data_result(
4150        &self,
4151        result: Result<Value, Diagnostic>,
4152        span: &Span,
4153    ) -> Result<Value, Diagnostic> {
4154        let ty = self
4155            .program
4156            .expression_types
4157            .get(&(span.module.clone(), span.start, span.end))
4158            .cloned()
4159            .ok_or_else(|| self.error("invalid_enum", "missing checked result type", span))?;
4160        self.result_with_type(result, &ty, span)
4161    }
4162    fn result_with_type(
4163        &self,
4164        result: Result<Value, Diagnostic>,
4165        ty: &Type,
4166        _span: &Span,
4167    ) -> Result<Value, Diagnostic> {
4168        let (case, payload, origin) = match result {
4169            Ok(value) => ("Ok", value, None),
4170            Err(error) => {
4171                let case = if let Some(case) = error.data_error.as_deref() {
4172                    case
4173                } else {
4174                    match error.code.as_str() {
4175                        "numeric_overflow" => "Overflow",
4176                        "division_by_zero" => "DivisionByZero",
4177                        "inexact_conversion" => "InexactConversion",
4178                        "index_out_of_bounds" => "Bounds",
4179                        "invalid_index" => "Bounds",
4180                        "invalid_bounds" => "Bounds",
4181                        "invalid_slice" => "InvalidSlice",
4182                        "shape_mismatch" => "ShapeMismatch",
4183                        "invalid_shape" => "InvalidShape",
4184                        "invalid_utf8" => "InvalidUtf8",
4185                        "non_finite" => "NonFinite",
4186                        "invalid_data" => "InvalidLiteral",
4187                        _ => return Err(error),
4188                    }
4189                };
4190                (
4191                    "Err",
4192                    Value::algebraic(crate::EnumValue {
4193                        origin: None,
4194                        ty: Type::named("DataError"),
4195                        case: case.into(),
4196                        payload: vec![],
4197                    }),
4198                    Some(error.span.clone()),
4199                )
4200            }
4201        };
4202        Ok(Value::algebraic(crate::EnumValue {
4203            origin,
4204            ty: ty.clone(),
4205            case: case.into(),
4206            payload: vec![payload],
4207        }))
4208    }
4209    fn call_data_method(
4210        &mut self,
4211        receiver: Value,
4212        method: &str,
4213        argument: Option<&Type>,
4214        args: Vec<Value>,
4215        returns: &Type,
4216        span: &Span,
4217    ) -> Result<Value, Diagnostic> {
4218        if method == "get" {
4219            let result = match (&receiver, args.as_slice()) {
4220                (Value::Tensor(t), [Value::List(indices)]) => {
4221                    let indices = indices
4222                        .iter()
4223                        .map(|value| self.signed_index_value(value, span))
4224                        .collect::<Result<Vec<_>, _>>();
4225                    indices.and_then(|indices| {
4226                        t.get(&indices)
4227                            .map(data::scalar_value)
4228                            .map_err(|e| self.data_error(e, span))
4229                    })
4230                }
4231                (Value::List(values), [index]) => {
4232                    self.signed_index_value(index, span).and_then(|index| {
4233                        normalize_index(index, values.len())
4234                            .map(|index| values[index].clone())
4235                            .ok_or_else(|| {
4236                                self.error("index_out_of_bounds", "list index out of bounds", span)
4237                            })
4238                    })
4239                }
4240                (Value::Text(text), [index]) => {
4241                    self.signed_index_value(index, span).and_then(|index| {
4242                        normalize_index(index, text.as_str().chars().count())
4243                            .and_then(|index| text.as_str().chars().nth(index))
4244                            .map(|ch| Value::Text(ch.to_string().into()))
4245                            .ok_or_else(|| {
4246                                self.error("index_out_of_bounds", "text index out of bounds", span)
4247                            })
4248                    })
4249                }
4250                (Value::Bin(bytes), [index]) => {
4251                    self.signed_index_value(index, span).and_then(|index| {
4252                        normalize_index(index, bytes.byte_len())
4253                            .map(|index| {
4254                                data::scalar_value(
4255                                    Scalar::from_bits(DType::U8, bytes.as_bytes()[index] as u128)
4256                                        .expect("u8 byte"),
4257                                )
4258                            })
4259                            .ok_or_else(|| {
4260                                self.error("index_out_of_bounds", "byte index out of bounds", span)
4261                            })
4262                    })
4263                }
4264                _ => return Err(self.error("type_mismatch", "invalid get method arguments", span)),
4265            };
4266            return match result {
4267                Ok(value) => Ok(Value::algebraic(crate::EnumValue {
4268                    origin: None,
4269                    ty: returns.clone(),
4270                    case: "Some".into(),
4271                    payload: vec![value],
4272                })),
4273                Err(error)
4274                    if matches!(
4275                        error.code.as_str(),
4276                        "index_out_of_bounds"
4277                            | "shape_mismatch"
4278                            | "invalid_shape"
4279                            | "invalid_index"
4280                    ) =>
4281                {
4282                    Ok(Value::algebraic(crate::EnumValue {
4283                        origin: None,
4284                        ty: returns.clone(),
4285                        case: "None".into(),
4286                        payload: vec![],
4287                    }))
4288                }
4289                Err(error) => Err(error),
4290            };
4291        }
4292        let mut all = vec![receiver];
4293        all.extend(args);
4294        let result = if method == "convert" {
4295            self.typed_data_builtin(
4296                "convert",
4297                argument.expect("checked conversion target"),
4298                all,
4299                span,
4300            )
4301        } else if matches!(
4302            method,
4303            "utf8" | "decode" | "shape" | "reshape" | "transpose" | "sum" | "matmul"
4304        ) {
4305            self.data_builtin(method, all, span)
4306        } else {
4307            self.builtin(method, all, MAIN, span)
4308        };
4309        if matches!(returns,Type::Applied{name,..}if name=="Res") {
4310            self.result_with_type(result, returns, span)
4311        } else {
4312            result
4313        }
4314    }
4315    fn eval_tensor_literal(
4316        &mut self,
4317        values: &[Expr],
4318        dtype: &Type,
4319        module: &str,
4320        env: &mut Env,
4321        span: &Span,
4322    ) -> Result<Value, Diagnostic> {
4323        fn dimensions(values: &[Expr]) -> Vec<usize> {
4324            let mut shape = vec![values.len()];
4325            if let Some(Expr {
4326                kind: ExprKind::List(children),
4327                ..
4328            }) = values.first()
4329            {
4330                shape.extend(dimensions(children));
4331            }
4332            shape
4333        }
4334        fn count(values: &[Expr]) -> usize {
4335            values
4336                .iter()
4337                .map(|value| {
4338                    if let ExprKind::List(children) = &value.kind {
4339                        count(children)
4340                    } else {
4341                        1
4342                    }
4343                })
4344                .sum()
4345        }
4346        fn collect(
4347            evaluator: &mut Evaluator<'_>,
4348            values: &[Expr],
4349            module: &str,
4350            env: &mut Env,
4351            output: &mut Vec<Scalar>,
4352        ) -> Result<(), Diagnostic> {
4353            for value in values {
4354                if let ExprKind::List(children) = &value.kind {
4355                    collect(evaluator, children, module, env, output)?;
4356                } else {
4357                    let scalar = evaluator.eval(value, module, env)?;
4358                    output.push(data::as_scalar(&scalar).expect("checked numeric tensor element"));
4359                }
4360            }
4361            Ok(())
4362        }
4363        let elements = count(values);
4364        self.data_budget(elements, elements, span)?;
4365        let mut packed = Vec::with_capacity(elements);
4366        collect(self, values, module, env, &mut packed)?;
4367        Tensor::from_scalars(
4368            data::dtype(dtype).expect("checked tensor dtype"),
4369            dimensions(values),
4370            packed,
4371        )
4372        .map(|tensor| Value::Tensor(Box::new(tensor)))
4373        .map_err(|error| self.data_error(error, span))
4374    }
4375    fn pattern_matches(
4376        &self,
4377        pattern: &Pattern,
4378        value: &Value,
4379        bindings: &mut Vec<(String, Value)>,
4380        _span: &Span,
4381    ) -> Result<bool, Diagnostic> {
4382        Ok(match pattern {
4383            Pattern::Wildcard => true,
4384            Pattern::Binding(name) => {
4385                bindings.push((name.clone(), value.clone()));
4386                true
4387            }
4388            Pattern::Case { name, payload } => {
4389                if let Value::Enum(value) = value {
4390                    if name.rsplit('.').next() != Some(value.case.as_str())
4391                        || payload.len() != value.payload.len()
4392                    {
4393                        false
4394                    } else {
4395                        let mut matched = true;
4396                        for (pattern, value) in payload.iter().zip(&value.payload) {
4397                            if !self.pattern_matches(pattern, value, bindings, _span)? {
4398                                matched = false;
4399                                break;
4400                            }
4401                        }
4402                        matched
4403                    }
4404                } else {
4405                    false
4406                }
4407            }
4408            Pattern::Record { fields, .. } => {
4409                if let Value::Record(value) = value {
4410                    let mut matched = true;
4411                    for (name, pattern) in fields {
4412                        if !self.pattern_matches(pattern, &value.fields[name], bindings, _span)? {
4413                            matched = false;
4414                            break;
4415                        }
4416                    }
4417                    matched
4418                } else {
4419                    false
4420                }
4421            }
4422            Pattern::Literal(expr) => match &expr.kind {
4423                ExprKind::Number(raw) => data::as_scalar(value)
4424                    .is_some_and(|value| Scalar::parse(value.dtype(), raw) == Ok(value)),
4425                ExprKind::Unary { op, value: expr } if op == "-" => {
4426                    if let ExprKind::Number(raw) = &expr.kind {
4427                        data::as_scalar(value).is_some_and(|value| {
4428                            Scalar::parse(value.dtype(), &format!("-{raw}")) == Ok(value)
4429                        })
4430                    } else {
4431                        false
4432                    }
4433                }
4434                ExprKind::Bool(expected) => value == &Value::Bool(*expected),
4435                ExprKind::Text(expected) => {
4436                    matches!(value,Value::Text(value)if value.as_str()==expected)
4437                }
4438                ExprKind::Unit => value == &Value::Unit,
4439                _ => false,
4440            },
4441        })
4442    }
4443}
4444fn normalize_index(index: isize, length: usize) -> Option<usize> {
4445    let length = isize::try_from(length).ok()?;
4446    let index = if index < 0 {
4447        length.checked_add(index)?
4448    } else {
4449        index
4450    };
4451    if index >= 0 && index < length {
4452        usize::try_from(index).ok()
4453    } else {
4454        None
4455    }
4456}
4457fn sequence_indices(selector: &AxisIndex, length: usize) -> Result<Vec<usize>, &'static str> {
4458    match selector {
4459        AxisIndex::Index(index) => normalize_index(*index, length)
4460            .map(|i| vec![i])
4461            .ok_or("index out of bounds"),
4462        AxisIndex::Slice { start, stop, step } => {
4463            if *step == 0 {
4464                return Err("slice step cannot be zero");
4465            }
4466            let length = isize::try_from(length).map_err(|_| "sequence too long")?;
4467            let bound = |value: Option<isize>, default: isize| -> isize {
4468                value
4469                    .map(|v| {
4470                        let v = if v < 0 { length.saturating_add(v) } else { v };
4471                        if *step > 0 {
4472                            v.clamp(0, length)
4473                        } else {
4474                            v.clamp(-1, length - 1)
4475                        }
4476                    })
4477                    .unwrap_or(default)
4478            };
4479            let mut current = bound(*start, if *step > 0 { 0 } else { length - 1 });
4480            let stop = bound(*stop, if *step > 0 { length } else { -1 });
4481            let mut selected = Vec::new();
4482            while if *step > 0 {
4483                current < stop
4484            } else {
4485                current > stop
4486            } {
4487                selected.push(current as usize);
4488                let Some(next) = current.checked_add(*step) else {
4489                    break;
4490                };
4491                current = next;
4492            }
4493            Ok(selected)
4494        }
4495        _ => Err("Str and Bin slices do not accept new axes or ellipsis"),
4496    }
4497}
4498fn broadcast_shape(left: &[usize], right: &[usize]) -> Option<Vec<usize>> {
4499    let rank = left.len().max(right.len());
4500    let mut shape = vec![1; rank];
4501    for (axis, out) in shape.iter_mut().enumerate() {
4502        let a = left.len().checked_sub(rank - axis).map_or(1, |i| left[i]);
4503        let b = right.len().checked_sub(rank - axis).map_or(1, |i| right[i]);
4504        if a != b && a != 1 && b != 1 {
4505            return None;
4506        }
4507        *out = if a == 1 { b } else { a };
4508    }
4509    Some(shape)
4510}
4511fn tensor_work(a: &Tensor, b: &Tensor, matmul: bool) -> Option<(usize, usize)> {
4512    if matmul {
4513        let aa = a.shape();
4514        let bb = b.shape();
4515        if aa.len() < 2 || bb.len() < 2 || aa[aa.len() - 1] != bb[bb.len() - 2] {
4516            return None;
4517        }
4518        let batch = broadcast_shape(&aa[..aa.len() - 2], &bb[..bb.len() - 2])?;
4519        let batches = batch.iter().try_fold(1usize, |n, d| n.checked_mul(*d))?;
4520        let output = batches
4521            .checked_mul(aa[aa.len() - 2])?
4522            .checked_mul(bb[bb.len() - 1])?;
4523        Some((
4524            output,
4525            output.checked_mul(aa[aa.len() - 1])?.checked_mul(2)?,
4526        ))
4527    } else {
4528        let shape = broadcast_shape(a.shape(), b.shape())?;
4529        let count = shape.iter().try_fold(1usize, |n, d| n.checked_mul(*d))?;
4530        Some((count, count))
4531    }
4532}
4533
4534struct BoundedText {
4535    value: String,
4536    limit: usize,
4537}
4538impl std::fmt::Write for BoundedText {
4539    fn write_str(&mut self, text: &str) -> std::fmt::Result {
4540        if text.len() > self.limit.saturating_sub(self.value.len()) {
4541            return Err(std::fmt::Error);
4542        }
4543        self.value.push_str(text);
4544        Ok(())
4545    }
4546}
4547fn display_work(value: &Value) -> usize {
4548    match value {
4549        Value::Enum(value) => value.payload.iter().fold(1usize, |work, value| {
4550            work.saturating_add(display_work(value))
4551        }),
4552        Value::Tensor(t) => t.len(),
4553        Value::Text(s) => s.len(),
4554        Value::Bin(b) => b.byte_len(),
4555        Value::Record(r) => r
4556            .fields
4557            .values()
4558            .fold(1usize, |n, v| n.saturating_add(display_work(v))),
4559        Value::List(values) => values
4560            .iter()
4561            .fold(1usize, |n, v| n.saturating_add(display_work(v))),
4562        _ => 1,
4563    }
4564}