1use 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#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(default)]
15pub struct Limits {
16 pub fuel_per_operation: usize,
18 pub max_call_depth: usize,
20 pub max_entities: usize,
22 pub max_logs: usize,
24 pub max_trace: usize,
26 pub max_collection: usize,
28 pub max_string_bytes: usize,
30 pub max_value_depth: usize,
32 pub max_stored_values: usize,
34 pub max_storage_bytes: usize,
36 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#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60pub struct EntitySnapshot {
61 #[cfg_attr(feature = "typescript", ts(type = "number"))]
63 pub id: u64,
64 pub components: BTreeMap<String, Value>,
66 pub source: Span,
68}
69#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
71#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
72pub struct TraceEntry {
73 pub span: Span,
75 pub event: String,
77 #[cfg_attr(feature = "typescript", ts(type = "number | null"))]
79 pub entity: Option<u64>,
80}
81#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
83#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
84pub struct Snapshot {
85 pub finished: bool,
87 #[cfg_attr(feature = "typescript", ts(type = "number"))]
89 pub tick: u64,
90 pub time: f64,
92 pub globals: BTreeMap<String, Value>,
94 pub entities: Vec<EntitySnapshot>,
96 pub logs: Vec<String>,
98 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 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#[derive(Clone)]
125pub struct Machine {
126 program: Program,
127 registry: NativeRegistry,
128 limits: Limits,
129 state: State,
130}
131impl Machine {
132 pub fn new(program: Program, limits: Limits) -> Result<Self, Diagnostic> {
136 Self::with_registry(program, limits, NativeRegistry::default())
137 }
138 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 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 #[must_use]
209 pub const fn execution_state(&self) -> (u64, bool) {
210 (self.state.tick, self.state.finished)
211 }
212 pub fn program(&self) -> &Program {
214 &self.program
215 }
216 pub fn limits(&self) -> &Limits {
218 &self.limits
219 }
220 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 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 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 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 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 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 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 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 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 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(¶meter.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 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 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 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 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 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 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 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, ¶meter.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 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 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 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}