1use std::collections::HashMap;
36use std::sync::Arc;
37use std::rc::Rc;
38use std::cell::RefCell;
39
40use async_recursion::async_recursion;
41
42use crate::ast::stmt::{BinaryOpKind, Block, ClosureBody, CompressionCodec, Expr, Literal, MatchArm, ReadSource, Stmt, TypeExpr};
43use crate::intern::{Interner, Symbol};
44
45fn wire_compression_of(codec: CompressionCodec) -> crate::concurrency::marshal::WireCompression {
47 use crate::concurrency::marshal::WireCompression;
48 match codec {
49 CompressionCodec::Deflate => WireCompression::Deflate,
50 CompressionCodec::Lz4 => WireCompression::Lz4,
51 CompressionCodec::Zstd => WireCompression::Zstd,
52 }
53}
54use crate::analysis::{PolicyRegistry, PolicyCondition};
55
56use logicaffeine_system::fs::Vfs;
58use logicaffeine_runtime::{ChanId, RtPayload, SelectArm, TaskId};
59use crate::concurrency::bridge::{BlockingRequest, Yield, YieldFuture, YieldState};
60use crate::concurrency::driver::{ErrSink, InterpreterTask};
61use crate::concurrency::marshal;
62
63pub type OutputCallback = Rc<RefCell<dyn FnMut(String)>>;
66
67#[derive(Debug, Clone)]
74pub struct StructValue {
75 pub type_name: String,
76 pub fields: HashMap<String, RuntimeValue>,
77}
78
79#[derive(Debug, Clone)]
81pub struct InductiveValue {
82 pub inductive_type: String,
83 pub constructor: String,
84 pub args: Vec<RuntimeValue>,
85}
86
87#[derive(Debug, Clone)]
89pub struct ClosureValue {
90 pub body_index: usize,
91 pub captured_env: HashMap<Symbol, RuntimeValue>,
92 pub param_names: Vec<Symbol>,
93 pub generated: Option<std::rc::Rc<crate::concurrency::marshal::GenExpr>>,
99}
100
101pub type MapStorage =
111 indexmap::IndexMap<RuntimeValue, RuntimeValue, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
112
113#[derive(Debug, Clone)]
124pub enum ListRepr {
125 Boxed(Vec<RuntimeValue>),
126 Ints(Vec<i64>),
127 IntsI32(Vec<i32>),
136 Floats(Vec<f64>),
137 Bools(Vec<bool>),
138 Strings { data: Vec<u8>, ends: Vec<u32>, cache: RefCell<Vec<Option<Rc<String>>>> },
153 Structs { type_name: String, field_names: Vec<String>, columns: Vec<ListRepr> },
165 Inductives {
177 inductive_type: String,
178 ctor_dict: Vec<String>,
179 ctors: Vec<u32>,
180 ranks: Vec<u32>,
181 arg_cols: Vec<Vec<ListRepr>>,
182 },
183 WireStructs {
193 bytes: Rc<Vec<u8>>,
194 type_name: String,
195 field_names: Vec<String>,
196 len: usize,
197 },
198 WireColumn {
205 bytes: Rc<Vec<u8>>,
206 len: usize,
207 floats: bool,
208 },
209}
210
211impl ListRepr {
212 pub fn from_record_list_view(bytes: Rc<Vec<u8>>) -> Option<ListRepr> {
215 let (type_name, field_names, len) = {
216 let view = crate::concurrency::marshal::view_message(&bytes)?;
217 view.structs_schema()?
218 };
219 Some(ListRepr::WireStructs { bytes, type_name, field_names, len })
220 }
221
222 pub fn from_aligned_column_view(bytes: Rc<Vec<u8>>) -> Option<ListRepr> {
226 let view = crate::concurrency::marshal::view_message(&bytes)?;
227 if let Some(s) = view.as_i64_slice() {
228 return Some(ListRepr::WireColumn { len: s.len(), floats: false, bytes });
229 }
230 if let Some(s) = view.as_f64_slice() {
231 return Some(ListRepr::WireColumn { len: s.len(), floats: true, bytes });
232 }
233 None
234 }
235
236 pub fn from_received_view(bytes: Rc<Vec<u8>>) -> Option<ListRepr> {
239 Self::from_record_list_view(bytes.clone()).or_else(|| Self::from_aligned_column_view(bytes))
240 }
241
242 fn wire_column_get(bytes: &[u8], floats: bool, i: usize) -> Option<RuntimeValue> {
243 let view = crate::concurrency::marshal::view_message(bytes)?;
244 if floats {
245 view.as_f64_slice()?.get(i).map(|&f| RuntimeValue::Float(f))
246 } else {
247 view.as_i64_slice()?.get(i).map(|&n| RuntimeValue::Int(n))
248 }
249 }
250
251 fn wire_column_to_values(bytes: &[u8], floats: bool) -> Vec<RuntimeValue> {
252 let Some(view) = crate::concurrency::marshal::view_message(bytes) else { return Vec::new() };
253 if floats {
254 view.as_f64_slice().map(|s| s.iter().map(|&f| RuntimeValue::Float(f)).collect()).unwrap_or_default()
255 } else {
256 view.as_i64_slice().map(|s| s.iter().map(|&n| RuntimeValue::Int(n)).collect()).unwrap_or_default()
257 }
258 }
259
260 fn wire_struct_row(
263 bytes: &[u8],
264 type_name: &str,
265 field_names: &[String],
266 len: usize,
267 i: usize,
268 ) -> Option<RuntimeValue> {
269 if i >= len {
270 return None;
271 }
272 let view = crate::concurrency::marshal::view_message(bytes)?;
273 let mut fields = HashMap::with_capacity(field_names.len());
274 for name in field_names {
275 let cell = view.structs_row_field_value(i, name)?;
276 fields.insert(name.clone(), cell);
277 }
278 Some(RuntimeValue::Struct(Box::new(StructValue { type_name: type_name.to_string(), fields })))
279 }
280}
281
282impl ListRepr {
283 pub fn from_values(values: Vec<RuntimeValue>) -> ListRepr {
284 if values.iter().all(|v| matches!(v, RuntimeValue::Int(_))) {
285 ListRepr::Ints(
286 values
287 .into_iter()
288 .map(|v| match v {
289 RuntimeValue::Int(n) => n,
290 _ => unreachable!(),
291 })
292 .collect(),
293 )
294 } else if values.iter().all(|v| matches!(v, RuntimeValue::Float(_))) {
295 ListRepr::Floats(
296 values
297 .into_iter()
298 .map(|v| match v {
299 RuntimeValue::Float(f) => f,
300 _ => unreachable!(),
301 })
302 .collect(),
303 )
304 } else if values.iter().all(|v| matches!(v, RuntimeValue::Bool(_))) {
305 ListRepr::Bools(
306 values
307 .into_iter()
308 .map(|v| match v {
309 RuntimeValue::Bool(b) => b,
310 _ => unreachable!(),
311 })
312 .collect(),
313 )
314 } else if !values.is_empty() && values.iter().all(|v| matches!(v, RuntimeValue::Text(_))) {
315 let mut data = Vec::new();
319 let mut ends = Vec::with_capacity(values.len());
320 for v in &values {
321 if let RuntimeValue::Text(s) = v {
322 data.extend_from_slice(s.as_bytes());
323 ends.push(data.len() as u32);
324 }
325 }
326 ListRepr::strings(data, ends)
327 } else if let Some((type_name, field_names)) = Self::struct_schema(&values) {
328 let columns = field_names
331 .iter()
332 .map(|fname| {
333 ListRepr::from_values(
334 values
335 .iter()
336 .map(|v| match v {
337 RuntimeValue::Struct(sv) => sv.fields.get(fname).cloned().unwrap(),
338 _ => unreachable!("struct_schema guaranteed all-struct"),
339 })
340 .collect(),
341 )
342 })
343 .collect();
344 ListRepr::Structs { type_name, field_names, columns }
345 } else if let Some(inductives) = Self::build_inductives(&values) {
346 inductives
347 } else {
348 ListRepr::Boxed(values)
349 }
350 }
351
352 pub(crate) fn build_inductives(values: &[RuntimeValue]) -> Option<ListRepr> {
357 let inductive_type = match values.first()? {
358 RuntimeValue::Inductive(i) => i.inductive_type.clone(),
359 _ => return None,
360 };
361 let mut ctor_dict: Vec<String> = Vec::new();
362 let mut ctors: Vec<u32> = Vec::with_capacity(values.len());
363 let mut ranks: Vec<u32> = Vec::with_capacity(values.len());
364 let mut counts: Vec<u32> = Vec::new();
365 let mut grouped: Vec<Vec<Vec<RuntimeValue>>> = Vec::new();
367 for v in values {
368 let iv = match v {
369 RuntimeValue::Inductive(i) if i.inductive_type == inductive_type => i,
370 _ => return None,
371 };
372 let c = match ctor_dict.iter().position(|n| n == &iv.constructor) {
373 Some(c) => {
374 if grouped[c].len() != iv.args.len() {
375 return None; }
377 c
378 }
379 None => {
380 ctor_dict.push(iv.constructor.clone());
381 counts.push(0);
382 grouped.push(vec![Vec::new(); iv.args.len()]);
383 ctor_dict.len() - 1
384 }
385 };
386 ctors.push(c as u32);
387 ranks.push(counts[c]);
388 counts[c] += 1;
389 for (j, a) in iv.args.iter().enumerate() {
390 grouped[c][j].push(a.clone());
391 }
392 }
393 let arg_cols: Vec<Vec<ListRepr>> = grouped
394 .into_iter()
395 .map(|cols| cols.into_iter().map(ListRepr::from_values).collect())
396 .collect();
397 Some(ListRepr::Inductives { inductive_type, ctor_dict, ctors, ranks, arg_cols })
398 }
399
400 fn inductive_row(
402 inductive_type: &str,
403 ctor_dict: &[String],
404 ctors: &[u32],
405 ranks: &[u32],
406 arg_cols: &[Vec<ListRepr>],
407 i: usize,
408 ) -> Option<RuntimeValue> {
409 let c = *ctors.get(i)? as usize;
410 let r = ranks[i] as usize;
411 let mut args = Vec::with_capacity(arg_cols[c].len());
412 for col in &arg_cols[c] {
413 args.push(col.get(r)?);
414 }
415 Some(RuntimeValue::Inductive(Box::new(InductiveValue {
416 inductive_type: inductive_type.to_string(),
417 constructor: ctor_dict[c].clone(),
418 args,
419 })))
420 }
421
422 fn struct_schema(values: &[RuntimeValue]) -> Option<(String, Vec<String>)> {
427 let first = match values.first()? {
428 RuntimeValue::Struct(s) => s,
429 _ => return None,
430 };
431 let mut names: Vec<String> = first.fields.keys().cloned().collect();
432 names.sort();
433 if names.is_empty() {
436 return None;
437 }
438 for item in values {
439 match item {
440 RuntimeValue::Struct(s)
441 if s.type_name == first.type_name
442 && s.fields.len() == names.len()
443 && names.iter().all(|n| s.fields.contains_key(n)) => {}
444 _ => return None,
445 }
446 }
447 Some((first.type_name.clone(), names))
448 }
449
450 fn struct_row(type_name: &str, field_names: &[String], columns: &[ListRepr], i: usize) -> Option<RuntimeValue> {
452 if i >= columns.first().map_or(0, |c| c.len()) {
453 return None;
454 }
455 let mut fields = std::collections::HashMap::with_capacity(field_names.len());
456 for (j, fname) in field_names.iter().enumerate() {
457 fields.insert(fname.clone(), columns[j].get(i)?);
458 }
459 Some(RuntimeValue::Struct(Box::new(StructValue { type_name: type_name.to_string(), fields })))
460 }
461
462 pub fn strings(data: Vec<u8>, ends: Vec<u32>) -> ListRepr {
464 ListRepr::Strings { data, ends, cache: RefCell::new(Vec::new()) }
465 }
466
467 fn string_at(data: &[u8], ends: &[u32], i: usize) -> Option<String> {
470 let end = *ends.get(i)? as usize;
471 let start = if i == 0 { 0 } else { ends[i - 1] as usize };
472 std::str::from_utf8(data.get(start..end)?).ok().map(str::to_string)
473 }
474
475 pub fn len(&self) -> usize {
476 match self {
477 ListRepr::Boxed(v) => v.len(),
478 ListRepr::Ints(v) => v.len(),
479 ListRepr::IntsI32(v) => v.len(),
480 ListRepr::Floats(v) => v.len(),
481 ListRepr::Bools(v) => v.len(),
482 ListRepr::Strings { ends, .. } => ends.len(),
483 ListRepr::Structs { columns, .. } => columns.first().map_or(0, |c| c.len()),
484 ListRepr::Inductives { ctors, .. } => ctors.len(),
485 ListRepr::WireStructs { len, .. } | ListRepr::WireColumn { len, .. } => *len,
486 }
487 }
488
489 pub fn is_empty(&self) -> bool {
490 self.len() == 0
491 }
492
493 pub fn storage_label(&self) -> &'static str {
498 match self {
499 ListRepr::Boxed(_) => "boxed values",
500 ListRepr::Ints(_) => "packed Vec<i64>",
501 ListRepr::IntsI32(_) => "packed Vec<i32> (narrowed)",
502 ListRepr::Floats(_) => "packed Vec<f64>",
503 ListRepr::Bools(_) => "packed Vec<bool>",
504 ListRepr::Strings { .. } => "flat string buffer",
505 ListRepr::Structs { .. } => "columnar (struct-of-arrays)",
506 ListRepr::Inductives { .. } => "columnar tagged-union",
507 ListRepr::WireStructs { .. } | ListRepr::WireColumn { .. } => "wire view (lazy)",
508 }
509 }
510
511 pub fn truncate(&mut self, n: usize) {
515 match self {
516 ListRepr::Boxed(v) => v.truncate(n),
517 ListRepr::Ints(v) => v.truncate(n),
518 ListRepr::IntsI32(v) => v.truncate(n),
519 ListRepr::Floats(v) => v.truncate(n),
520 ListRepr::Bools(v) => v.truncate(n),
521 ListRepr::Strings { data, ends, cache } => {
522 if n < ends.len() {
523 let cut = if n == 0 { 0 } else { ends[n - 1] as usize };
524 data.truncate(cut);
525 ends.truncate(n);
526 let mut c = cache.borrow_mut();
527 if !c.is_empty() {
528 c.truncate(n);
529 }
530 }
531 }
532 ListRepr::Structs { columns, .. } => {
533 for c in columns.iter_mut() {
534 c.truncate(n);
535 }
536 }
537 ListRepr::Inductives { .. } => {
540 if n < self.len() {
541 self.make_boxed().truncate(n);
542 }
543 }
544 ListRepr::WireStructs { .. } | ListRepr::WireColumn { .. } => {
546 if n < self.len() {
547 self.make_boxed().truncate(n);
548 }
549 }
550 }
551 }
552
553 pub fn get(&self, i: usize) -> Option<RuntimeValue> {
555 match self {
556 ListRepr::Boxed(v) => v.get(i).cloned(),
557 ListRepr::Ints(v) => v.get(i).map(|&n| RuntimeValue::Int(n)),
558 ListRepr::IntsI32(v) => v.get(i).map(|&n| RuntimeValue::Int(n as i64)),
559 ListRepr::Floats(v) => v.get(i).map(|&f| RuntimeValue::Float(f)),
560 ListRepr::Bools(v) => v.get(i).map(|&b| RuntimeValue::Bool(b)),
561 ListRepr::Strings { data, ends, cache } => {
562 if i >= ends.len() {
563 return None;
564 }
565 let mut c = cache.borrow_mut();
566 if c.is_empty() {
569 c.resize(ends.len(), None);
570 }
571 if c[i].is_none() {
572 c[i] = Some(Rc::new(Self::string_at(data, ends, i)?));
573 }
574 c[i].clone().map(RuntimeValue::Text)
575 }
576 ListRepr::Structs { type_name, field_names, columns } => {
577 Self::struct_row(type_name, field_names, columns, i)
578 }
579 ListRepr::Inductives { inductive_type, ctor_dict, ctors, ranks, arg_cols } => {
580 Self::inductive_row(inductive_type, ctor_dict, ctors, ranks, arg_cols, i)
581 }
582 ListRepr::WireStructs { bytes, type_name, field_names, len } => {
583 Self::wire_struct_row(bytes, type_name, field_names, *len, i)
584 }
585 ListRepr::WireColumn { bytes, floats, .. } => Self::wire_column_get(bytes, *floats, i),
586 }
587 }
588
589 pub fn get_field(&self, i: usize, name: &str) -> Option<RuntimeValue> {
594 match self {
595 ListRepr::Structs { field_names, columns, .. } => {
596 let j = field_names.iter().position(|f| f == name)?;
597 columns[j].get(i)
598 }
599 ListRepr::WireStructs { bytes, .. } => {
602 crate::concurrency::marshal::view_message(bytes)?.structs_row_field_value(i, name)
603 }
604 _ => None,
605 }
606 }
607
608 pub fn column(&self, name: &str) -> Option<&ListRepr> {
612 match self {
613 ListRepr::Structs { field_names, columns, .. } => {
614 let j = field_names.iter().position(|f| f == name)?;
615 Some(&columns[j])
616 }
617 _ => None,
618 }
619 }
620
621 fn make_boxed(&mut self) -> &mut Vec<RuntimeValue> {
623 match self {
624 ListRepr::Boxed(v) => v,
625 ListRepr::Ints(v) => {
626 let boxed = v.drain(..).map(RuntimeValue::Int).collect();
627 *self = ListRepr::Boxed(boxed);
628 match self {
629 ListRepr::Boxed(v) => v,
630 _ => unreachable!(),
631 }
632 }
633 ListRepr::Floats(v) => {
634 let boxed = v.drain(..).map(RuntimeValue::Float).collect();
635 *self = ListRepr::Boxed(boxed);
636 match self {
637 ListRepr::Boxed(v) => v,
638 _ => unreachable!(),
639 }
640 }
641 ListRepr::IntsI32(v) => {
642 let boxed = v.drain(..).map(|n| RuntimeValue::Int(n as i64)).collect();
643 *self = ListRepr::Boxed(boxed);
644 match self {
645 ListRepr::Boxed(v) => v,
646 _ => unreachable!(),
647 }
648 }
649 ListRepr::Bools(v) => {
650 let boxed = v.drain(..).map(RuntimeValue::Bool).collect();
651 *self = ListRepr::Boxed(boxed);
652 match self {
653 ListRepr::Boxed(v) => v,
654 _ => unreachable!(),
655 }
656 }
657 ListRepr::Strings { data, ends, .. } => {
658 let boxed = (0..ends.len())
659 .filter_map(|i| Self::string_at(data, ends, i).map(|s| RuntimeValue::Text(Rc::new(s))))
660 .collect();
661 *self = ListRepr::Boxed(boxed);
662 match self {
663 ListRepr::Boxed(v) => v,
664 _ => unreachable!(),
665 }
666 }
667 ListRepr::Structs { type_name, field_names, columns } => {
668 let n = columns.first().map_or(0, |c| c.len());
669 debug_assert!(columns.iter().all(|c| c.len() == n), "columnar struct columns must share one length");
672 let boxed = (0..n)
673 .filter_map(|i| Self::struct_row(type_name, field_names, columns, i))
674 .collect();
675 *self = ListRepr::Boxed(boxed);
676 match self {
677 ListRepr::Boxed(v) => v,
678 _ => unreachable!(),
679 }
680 }
681 ListRepr::Inductives { inductive_type, ctor_dict, ctors, ranks, arg_cols } => {
682 let n = ctors.len();
683 debug_assert_eq!(ranks.len(), n, "ranks and ctors must agree");
684 let boxed = (0..n)
685 .filter_map(|i| Self::inductive_row(inductive_type, ctor_dict, ctors, ranks, arg_cols, i))
686 .collect();
687 *self = ListRepr::Boxed(boxed);
688 match self {
689 ListRepr::Boxed(v) => v,
690 _ => unreachable!(),
691 }
692 }
693 ListRepr::WireStructs { bytes, type_name, field_names, len } => {
696 let len = *len;
697 let boxed: Vec<RuntimeValue> = {
698 let view = crate::concurrency::marshal::view_message(bytes);
699 (0..len)
700 .filter_map(|i| {
701 view.as_ref().and_then(|v| {
702 let mut fields = HashMap::with_capacity(field_names.len());
703 for name in field_names.iter() {
704 let cell = v.structs_row_field_value(i, name)?;
705 fields.insert(name.clone(), cell);
706 }
707 Some(RuntimeValue::Struct(Box::new(StructValue {
708 type_name: type_name.clone(),
709 fields,
710 })))
711 })
712 })
713 .collect()
714 };
715 *self = ListRepr::Boxed(boxed);
716 match self {
717 ListRepr::Boxed(v) => v,
718 _ => unreachable!(),
719 }
720 }
721 ListRepr::WireColumn { bytes, floats, .. } => {
722 let boxed = Self::wire_column_to_values(bytes, *floats);
723 *self = ListRepr::Boxed(boxed);
724 match self {
725 ListRepr::Boxed(v) => v,
726 _ => unreachable!(),
727 }
728 }
729 }
730 }
731
732 fn widen_to_ints(&mut self) -> &mut Vec<i64> {
738 match self {
739 ListRepr::IntsI32(v) => {
740 let wide: Vec<i64> = v.drain(..).map(|n| n as i64).collect();
741 *self = ListRepr::Ints(wide);
742 match self {
743 ListRepr::Ints(v) => v,
744 _ => unreachable!(),
745 }
746 }
747 ListRepr::Ints(v) => v,
748 _ => unreachable!("widen_to_ints called on a non-Int buffer"),
749 }
750 }
751
752 pub fn set(&mut self, i: usize, value: RuntimeValue) {
755 match (&mut *self, &value) {
756 (ListRepr::Ints(v), RuntimeValue::Int(n)) => v[i] = *n,
757 (ListRepr::IntsI32(v), RuntimeValue::Int(n)) => {
758 if let Ok(narrow) = i32::try_from(*n) {
759 v[i] = narrow;
760 } else {
761 self.widen_to_ints()[i] = *n;
765 }
766 }
767 (ListRepr::Floats(v), RuntimeValue::Float(f)) => v[i] = *f,
768 (ListRepr::Bools(v), RuntimeValue::Bool(b)) => v[i] = *b,
769 (ListRepr::Boxed(v), _) => v[i] = value,
770 _ => self.make_boxed()[i] = value,
771 }
772 }
773
774 pub fn push(&mut self, value: RuntimeValue) {
775 match (&mut *self, &value) {
776 (ListRepr::Ints(v), RuntimeValue::Int(n)) => v.push(*n),
777 (ListRepr::IntsI32(v), RuntimeValue::Int(n)) => match i32::try_from(*n) {
778 Ok(narrow) => v.push(narrow),
779 Err(_) => self.widen_to_ints().push(*n),
780 },
781 (ListRepr::Floats(v), RuntimeValue::Float(f)) => v.push(*f),
782 (ListRepr::Bools(v), RuntimeValue::Bool(b)) => v.push(*b),
783 (ListRepr::Boxed(v), _) => v.push(value),
784 (ListRepr::Ints(v), RuntimeValue::Float(f)) if v.is_empty() => {
785 *self = ListRepr::Floats(vec![*f]);
786 }
787 (ListRepr::Ints(v), RuntimeValue::Bool(b)) if v.is_empty() => {
788 *self = ListRepr::Bools(vec![*b]);
789 }
790 (ListRepr::Floats(v), RuntimeValue::Int(n)) if v.is_empty() => {
791 *self = ListRepr::Ints(vec![*n]);
792 }
793 (ListRepr::Floats(v), RuntimeValue::Bool(b)) if v.is_empty() => {
794 *self = ListRepr::Bools(vec![*b]);
795 }
796 (ListRepr::Bools(v), RuntimeValue::Int(n)) if v.is_empty() => {
797 *self = ListRepr::Ints(vec![*n]);
798 }
799 (ListRepr::Bools(v), RuntimeValue::Float(f)) if v.is_empty() => {
800 *self = ListRepr::Floats(vec![*f]);
801 }
802 _ => self.make_boxed().push(value),
803 }
804 }
805
806 pub fn pop(&mut self) -> Option<RuntimeValue> {
807 match self {
808 ListRepr::Boxed(v) => v.pop(),
809 ListRepr::Ints(v) => v.pop().map(RuntimeValue::Int),
810 ListRepr::IntsI32(v) => v.pop().map(|n| RuntimeValue::Int(n as i64)),
811 ListRepr::Floats(v) => v.pop().map(RuntimeValue::Float),
812 ListRepr::Bools(v) => v.pop().map(RuntimeValue::Bool),
813 ListRepr::Strings { data, ends, cache } => {
814 let last = ends.len().checked_sub(1)?;
815 let s = Self::string_at(data, ends, last)?;
816 let start = if last == 0 { 0 } else { ends[last - 1] as usize };
817 data.truncate(start);
818 ends.pop();
819 let mut c = cache.borrow_mut();
820 if !c.is_empty() {
821 c.pop();
822 }
823 Some(RuntimeValue::Text(Rc::new(s)))
824 }
825 ListRepr::Structs { .. } => self.make_boxed().pop(),
827 ListRepr::Inductives { .. } => self.make_boxed().pop(),
828 ListRepr::WireStructs { .. } | ListRepr::WireColumn { .. } => self.make_boxed().pop(),
829 }
830 }
831
832 pub fn insert(&mut self, i: usize, value: RuntimeValue) {
833 match (&mut *self, &value) {
834 (ListRepr::Ints(v), RuntimeValue::Int(n)) => v.insert(i, *n),
835 (ListRepr::IntsI32(v), RuntimeValue::Int(n)) => match i32::try_from(*n) {
836 Ok(narrow) => v.insert(i, narrow),
837 Err(_) => self.widen_to_ints().insert(i, *n),
838 },
839 (ListRepr::Floats(v), RuntimeValue::Float(f)) => v.insert(i, *f),
840 (ListRepr::Bools(v), RuntimeValue::Bool(b)) => v.insert(i, *b),
841 (ListRepr::Boxed(v), _) => v.insert(i, value),
842 _ => self.make_boxed().insert(i, value),
843 }
844 }
845
846 pub fn remove_at(&mut self, i: usize) -> RuntimeValue {
847 match self {
848 ListRepr::Boxed(v) => v.remove(i),
849 ListRepr::Ints(v) => RuntimeValue::Int(v.remove(i)),
850 ListRepr::IntsI32(v) => RuntimeValue::Int(v.remove(i) as i64),
851 ListRepr::Floats(v) => RuntimeValue::Float(v.remove(i)),
852 ListRepr::Bools(v) => RuntimeValue::Bool(v.remove(i)),
853 ListRepr::Strings { .. } => self.make_boxed().remove(i),
855 ListRepr::Structs { .. } => self.make_boxed().remove(i),
856 ListRepr::Inductives { .. } => self.make_boxed().remove(i),
857 ListRepr::WireStructs { .. } | ListRepr::WireColumn { .. } => self.make_boxed().remove(i),
858 }
859 }
860
861 pub fn position(&self, needle: &RuntimeValue) -> Option<usize> {
864 match (self, needle) {
865 (ListRepr::Ints(v), RuntimeValue::Int(n)) => v.iter().position(|x| x == n),
866 (ListRepr::Ints(_), _) => None,
867 (ListRepr::IntsI32(v), RuntimeValue::Int(n)) => {
868 i32::try_from(*n).ok().and_then(|nn| v.iter().position(|x| *x == nn))
869 }
870 (ListRepr::IntsI32(_), _) => None,
871 (ListRepr::Floats(v), RuntimeValue::Float(f)) => {
872 v.iter().position(|x| (x - f).abs() < f64::EPSILON)
873 }
874 (ListRepr::Floats(_), _) => None,
875 (ListRepr::Bools(v), RuntimeValue::Bool(b)) => v.iter().position(|x| x == b),
876 (ListRepr::Bools(_), _) => None,
877 (ListRepr::Strings { data, ends, .. }, RuntimeValue::Text(t)) => {
878 (0..ends.len()).find(|&i| Self::string_at(data, ends, i).as_deref() == Some(t.as_str()))
879 }
880 (ListRepr::Strings { .. }, _) => None,
881 (ListRepr::Boxed(v), _) => {
882 v.iter().position(|x| crate::semantics::compare::values_equal(x, needle))
883 }
884 (ListRepr::Structs { .. }, _) => (0..self.len())
887 .find(|&i| self.get(i).is_some_and(|v| crate::semantics::compare::values_equal(&v, needle))),
888 (ListRepr::Inductives { .. }, _) => (0..self.len())
889 .find(|&i| self.get(i).is_some_and(|v| crate::semantics::compare::values_equal(&v, needle))),
890 (ListRepr::WireStructs { .. }, _) | (ListRepr::WireColumn { .. }, _) => (0..self.len())
892 .find(|&i| self.get(i).is_some_and(|v| crate::semantics::compare::values_equal(&v, needle))),
893 }
894 }
895
896 pub fn contains(&self, needle: &RuntimeValue) -> bool {
897 self.position(needle).is_some()
898 }
899
900 pub fn to_values(&self) -> Vec<RuntimeValue> {
902 match self {
903 ListRepr::Boxed(v) => v.clone(),
904 ListRepr::Ints(v) => v.iter().map(|&n| RuntimeValue::Int(n)).collect(),
905 ListRepr::IntsI32(v) => v.iter().map(|&n| RuntimeValue::Int(n as i64)).collect(),
906 ListRepr::Floats(v) => v.iter().map(|&f| RuntimeValue::Float(f)).collect(),
907 ListRepr::Bools(v) => v.iter().map(|&b| RuntimeValue::Bool(b)).collect(),
908 ListRepr::Strings { data, ends, .. } => (0..ends.len())
909 .filter_map(|i| Self::string_at(data, ends, i).map(|s| RuntimeValue::Text(Rc::new(s))))
910 .collect(),
911 ListRepr::Structs { type_name, field_names, columns } => {
912 let n = columns.first().map_or(0, |c| c.len());
913 (0..n).filter_map(|i| Self::struct_row(type_name, field_names, columns, i)).collect()
914 }
915 ListRepr::Inductives { inductive_type, ctor_dict, ctors, ranks, arg_cols } => (0..ctors.len())
916 .filter_map(|i| Self::inductive_row(inductive_type, ctor_dict, ctors, ranks, arg_cols, i))
917 .collect(),
918 ListRepr::WireStructs { bytes, type_name, field_names, len } => (0..*len)
919 .filter_map(|i| Self::wire_struct_row(bytes, type_name, field_names, *len, i))
920 .collect(),
921 ListRepr::WireColumn { bytes, floats, .. } => Self::wire_column_to_values(bytes, *floats),
922 }
923 }
924
925 pub fn slice(&self, start: usize, end: usize) -> ListRepr {
927 match self {
928 ListRepr::Boxed(v) => ListRepr::Boxed(v[start..=end].to_vec()),
929 ListRepr::Ints(v) => ListRepr::Ints(v[start..=end].to_vec()),
930 ListRepr::IntsI32(v) => ListRepr::IntsI32(v[start..=end].to_vec()),
931 ListRepr::Floats(v) => ListRepr::Floats(v[start..=end].to_vec()),
932 ListRepr::Bools(v) => ListRepr::Bools(v[start..=end].to_vec()),
933 ListRepr::Strings { data, ends, .. } => ListRepr::Boxed(
934 (start..=end)
935 .filter_map(|i| Self::string_at(data, ends, i).map(|s| RuntimeValue::Text(Rc::new(s))))
936 .collect(),
937 ),
938 ListRepr::Structs { type_name, field_names, columns } => ListRepr::Structs {
940 type_name: type_name.clone(),
941 field_names: field_names.clone(),
942 columns: columns.iter().map(|c| c.slice(start, end)).collect(),
943 },
944 ListRepr::Inductives { .. } => {
946 ListRepr::from_values((start..=end).filter_map(|i| self.get(i)).collect())
947 }
948 ListRepr::WireStructs { .. } | ListRepr::WireColumn { .. } => {
950 ListRepr::from_values((start..=end).filter_map(|i| self.get(i)).collect())
951 }
952 }
953 }
954
955 pub fn as_ints_mut(&mut self) -> Option<&mut Vec<i64>> {
957 match self {
958 ListRepr::Ints(v) => Some(v),
959 _ => None,
960 }
961 }
962
963 pub fn as_floats_mut(&mut self) -> Option<&mut Vec<f64>> {
964 match self {
965 ListRepr::Floats(v) => Some(v),
966 _ => None,
967 }
968 }
969}
970
971#[derive(Debug, Clone)]
972pub enum RuntimeValue {
973 Int(i64),
974 BigInt(Rc<logicaffeine_base::BigInt>),
980 Rational(Rc<logicaffeine_base::Rational>),
986 Decimal(Rc<logicaffeine_base::Decimal>),
993 Complex(Rc<logicaffeine_base::Complex>),
997 Modular(Rc<logicaffeine_base::Modular>),
1001 Float(f64),
1002 Bool(bool),
1003 Text(Rc<String>),
1004 Char(char),
1005 List(Rc<RefCell<ListRepr>>),
1006 Tuple(Rc<Vec<RuntimeValue>>),
1007 Set(Rc<RefCell<Vec<RuntimeValue>>>),
1008 Map(Rc<RefCell<MapStorage>>),
1009 Struct(Box<StructValue>),
1010 Inductive(Box<InductiveValue>),
1011 Function(Box<ClosureValue>),
1012 Nothing,
1013 Duration(i64),
1014 Date(i32),
1015 Moment(i64),
1016 Span { months: i32, days: i32 },
1017 Time(i64),
1018 Chan(ChanId),
1020 TaskHandle(TaskId),
1022 Peer(Rc<String>),
1025 Crdt(Rc<RefCell<crate::semantics::crdt::CrdtValue>>),
1031 Word(logicaffeine_base::WordVal),
1035 Lanes(Rc<logicaffeine_base::LanesVal>),
1040 Quantity(Rc<QuantityValue>),
1046 Money(Rc<logicaffeine_base::Money>),
1050 Uuid(Rc<logicaffeine_base::Uuid>),
1054}
1055
1056#[derive(Clone, Debug)]
1061pub struct QuantityValue {
1062 pub q: logicaffeine_base::Quantity,
1063 pub unit: logicaffeine_base::Unit,
1064}
1065
1066impl QuantityValue {
1067 pub fn display(&self) -> String {
1071 let magnitude = self
1072 .q
1073 .in_unit(&self.unit)
1074 .expect("a Quantity's display unit always shares its dimension");
1075 if self.unit.symbol.is_empty() {
1076 format!("{} {}", magnitude, self.q.dimension())
1077 } else {
1078 format!("{} {}", magnitude, self.unit.symbol)
1079 }
1080 }
1081}
1082
1083impl PartialEq for RuntimeValue {
1084 fn eq(&self, other: &Self) -> bool {
1090 crate::semantics::compare::values_equal(self, other)
1091 }
1092}
1093
1094impl Eq for RuntimeValue {}
1101
1102impl std::hash::Hash for RuntimeValue {
1103 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1110 use logicaffeine_base::numeric;
1111 match self {
1112 RuntimeValue::Int(n) => state.write_u64(numeric::numeric_hash_i64(*n)),
1114 RuntimeValue::BigInt(b) => state.write_u64(numeric::numeric_hash_bigint(b)),
1115 RuntimeValue::Float(f) => state.write_u64(numeric::numeric_hash_f64(*f)),
1116 RuntimeValue::Rational(r) => state.write_u64(numeric::numeric_hash_rational(r)),
1117 other => {
1119 std::mem::discriminant(other).hash(state);
1120 match other {
1121 RuntimeValue::Int(_)
1122 | RuntimeValue::BigInt(_)
1123 | RuntimeValue::Float(_)
1124 | RuntimeValue::Rational(_) => unreachable!("handled above"),
1125 RuntimeValue::Decimal(d) => d.hash(state),
1126 RuntimeValue::Complex(c) => c.hash(state),
1127 RuntimeValue::Modular(m) => m.hash(state),
1128 RuntimeValue::Bool(b) => b.hash(state),
1129 RuntimeValue::Text(s) => s.hash(state),
1130 RuntimeValue::Char(c) => c.hash(state),
1131 RuntimeValue::Nothing => {}
1132 RuntimeValue::Duration(d) => d.hash(state),
1133 RuntimeValue::Date(d) => d.hash(state),
1134 RuntimeValue::Moment(m) => m.hash(state),
1135 RuntimeValue::Span { months, days } => { months.hash(state); days.hash(state); }
1136 RuntimeValue::Time(t) => t.hash(state),
1137 RuntimeValue::Tuple(items) => {
1140 items.len().hash(state);
1141 for v in items.iter() {
1142 v.hash(state);
1143 }
1144 }
1145 RuntimeValue::Struct(s) => {
1149 s.type_name.hash(state);
1150 let mut fold: u64 = 0;
1151 for (k, v) in &s.fields {
1152 let mut h = rustc_hash::FxHasher::default();
1153 std::hash::Hash::hash(k, &mut h);
1154 std::hash::Hash::hash(v, &mut h);
1155 fold = fold.wrapping_add(std::hash::Hasher::finish(&h));
1156 }
1157 state.write_u64(fold);
1158 }
1159 RuntimeValue::List(items) => items.borrow().len().hash(state),
1163 RuntimeValue::Set(items) => items.borrow().len().hash(state),
1164 RuntimeValue::Map(m) => m.borrow().len().hash(state),
1165 RuntimeValue::Inductive(i) => { i.inductive_type.hash(state); i.constructor.hash(state); }
1166 RuntimeValue::Function(f) => f.body_index.hash(state),
1167 RuntimeValue::Chan(c) => c.0.hash(state),
1168 RuntimeValue::TaskHandle(t) => t.0.hash(state),
1169 RuntimeValue::Peer(topic) => topic.hash(state),
1170 RuntimeValue::Crdt(c) => c.borrow().len().hash(state),
1171 RuntimeValue::Word(w) => w.hash(state),
1172 RuntimeValue::Lanes(v) => v.hash(state),
1173 RuntimeValue::Quantity(qv) => {
1176 qv.q.magnitude_si().hash(state);
1177 qv.q.dimension().hash(state);
1178 }
1179 RuntimeValue::Money(m) => m.hash(state),
1181 RuntimeValue::Uuid(u) => u.hash(state),
1182 }
1183 }
1184 }
1185 }
1186}
1187
1188impl RuntimeValue {
1189 pub fn from_bigint(b: logicaffeine_base::BigInt) -> RuntimeValue {
1195 match b.to_i64() {
1196 Some(i) => RuntimeValue::Int(i),
1197 None => RuntimeValue::BigInt(Rc::new(b)),
1198 }
1199 }
1200
1201 pub fn from_rational(r: logicaffeine_base::Rational) -> RuntimeValue {
1207 match r.to_bigint() {
1208 Some(whole) => RuntimeValue::from_bigint(whole),
1209 None => RuntimeValue::Rational(Rc::new(r)),
1210 }
1211 }
1212
1213 pub fn type_name(&self) -> &str {
1217 match self {
1218 RuntimeValue::Int(_) => "Int",
1219 RuntimeValue::BigInt(_) => "Int",
1222 RuntimeValue::Rational(_) => "Rational",
1223 RuntimeValue::Decimal(_) => "Decimal",
1224 RuntimeValue::Complex(_) => "Complex",
1225 RuntimeValue::Modular(_) => "Modular",
1226 RuntimeValue::Float(_) => "Float",
1227 RuntimeValue::Bool(_) => "Bool",
1228 RuntimeValue::Text(_) => "Text",
1229 RuntimeValue::Char(_) => "Char",
1230 RuntimeValue::List(_) => "List",
1231 RuntimeValue::Tuple(_) => "Tuple",
1232 RuntimeValue::Set(_) => "Set",
1233 RuntimeValue::Map(_) => "Map",
1234 RuntimeValue::Struct(s) => &s.type_name,
1235 RuntimeValue::Inductive(ind) => ind.inductive_type.as_str(),
1236 RuntimeValue::Function(_) => "Function",
1237 RuntimeValue::Nothing => "Nothing",
1238 RuntimeValue::Duration(_) => "Duration",
1239 RuntimeValue::Date(_) => "Date",
1240 RuntimeValue::Moment(_) => "Moment",
1241 RuntimeValue::Span { .. } => "Span",
1242 RuntimeValue::Time(_) => "Time",
1243 RuntimeValue::Chan(_) => "Channel",
1244 RuntimeValue::TaskHandle(_) => "Task",
1245 RuntimeValue::Peer(_) => "PeerAgent",
1246 RuntimeValue::Crdt(c) => c.borrow().kind(),
1247 RuntimeValue::Word(w) => {
1248 if w.width() == 32 {
1249 "Word32"
1250 } else {
1251 "Word64"
1252 }
1253 }
1254 RuntimeValue::Lanes(v) => v.type_name(),
1255 RuntimeValue::Quantity(_) => "Quantity",
1256 RuntimeValue::Money(_) => "Money",
1257 RuntimeValue::Uuid(_) => "Uuid",
1258 }
1259 }
1260
1261 pub fn deep_clone(&self) -> RuntimeValue {
1268 match self {
1269 RuntimeValue::List(items) => {
1270 let cloned: Vec<RuntimeValue> =
1271 items.borrow().to_values().iter().map(|v| v.deep_clone()).collect();
1272 RuntimeValue::List(Rc::new(RefCell::new(ListRepr::from_values(cloned))))
1273 }
1274 RuntimeValue::Set(items) => {
1275 let cloned = items.borrow().iter().map(|v| v.deep_clone()).collect();
1276 RuntimeValue::Set(Rc::new(RefCell::new(cloned)))
1277 }
1278 RuntimeValue::Map(m) => {
1279 let cloned = m.borrow().iter().map(|(k, v)| (k.deep_clone(), v.deep_clone())).collect();
1280 RuntimeValue::Map(Rc::new(RefCell::new(cloned)))
1281 }
1282 RuntimeValue::Tuple(items) => {
1283 let cloned = items.iter().map(|v| v.deep_clone()).collect();
1284 RuntimeValue::Tuple(Rc::new(cloned))
1285 }
1286 RuntimeValue::Struct(s) => {
1287 let cloned_fields = s.fields.iter().map(|(k, v)| (k.clone(), v.deep_clone())).collect();
1288 RuntimeValue::Struct(Box::new(StructValue {
1289 type_name: s.type_name.clone(),
1290 fields: cloned_fields,
1291 }))
1292 }
1293 RuntimeValue::Inductive(ind) => {
1294 let cloned_args = ind.args.iter().map(|v| v.deep_clone()).collect();
1295 RuntimeValue::Inductive(Box::new(InductiveValue {
1296 inductive_type: ind.inductive_type.clone(),
1297 constructor: ind.constructor.clone(),
1298 args: cloned_args,
1299 }))
1300 }
1301 RuntimeValue::Function(f) => {
1302 let cloned_env = f.captured_env.iter()
1303 .map(|(k, v)| (k.clone(), v.deep_clone()))
1304 .collect();
1305 RuntimeValue::Function(Box::new(ClosureValue {
1306 body_index: f.body_index,
1307 captured_env: cloned_env,
1308 param_names: f.param_names.clone(),
1309 generated: f.generated.clone(),
1310 }))
1311 }
1312 RuntimeValue::Crdt(c) => {
1316 RuntimeValue::Crdt(Rc::new(RefCell::new(c.borrow().clone())))
1317 }
1318 other => other.clone(),
1319 }
1320 }
1321
1322 pub fn is_truthy(&self) -> bool {
1326 match self {
1327 RuntimeValue::Bool(b) => *b,
1328 RuntimeValue::Int(n) => *n != 0,
1329 RuntimeValue::Float(f) => *f != 0.0,
1330 RuntimeValue::BigInt(b) => !b.is_zero(),
1331 RuntimeValue::Rational(r) => !r.is_zero(),
1332 RuntimeValue::Decimal(d) => !d.is_zero(),
1333 RuntimeValue::Complex(c) => !c.is_zero(),
1334 RuntimeValue::Word(w) => w.to_u64() != 0,
1335 RuntimeValue::Text(s) => !s.is_empty(),
1336 RuntimeValue::List(l) => l.borrow().len() != 0,
1337 RuntimeValue::Set(s) => !s.borrow().is_empty(),
1338 RuntimeValue::Map(m) => !m.borrow().is_empty(),
1339 RuntimeValue::Nothing => false,
1340 _ => true,
1341 }
1342 }
1343
1344 pub fn to_display_string(&self) -> String {
1350 match self {
1351 RuntimeValue::Int(n) => n.to_string(),
1352 RuntimeValue::Word(w) => w.to_string(),
1353 RuntimeValue::Lanes(v) => {
1355 let parts: Vec<String> =
1356 (0..v.lanes()).map(|i| v.lane(i).to_string()).collect();
1357 format!("[{}]", parts.join(", "))
1358 }
1359 RuntimeValue::BigInt(b) => b.to_string(),
1360 RuntimeValue::Rational(r) => r.to_string(),
1361 RuntimeValue::Decimal(d) => d.to_string(),
1362 RuntimeValue::Complex(c) => c.to_string(),
1363 RuntimeValue::Modular(m) => m.to_string(),
1364 RuntimeValue::Quantity(qv) => qv.display(),
1365 RuntimeValue::Money(m) => m.to_string(),
1366 RuntimeValue::Uuid(u) => u.to_string(),
1367 RuntimeValue::Float(f) => logicaffeine_data::fmt::fmt_f64(*f),
1368 RuntimeValue::Bool(b) => if *b { "true" } else { "false" }.to_string(),
1369 RuntimeValue::Text(s) => s.as_str().to_string(),
1370 RuntimeValue::Char(c) => c.to_string(),
1371 RuntimeValue::List(items) => {
1372 let items = items.borrow();
1373 let parts: Vec<String> =
1374 items.to_values().iter().map(|v| v.to_display_string()).collect();
1375 format!("[{}]", parts.join(", "))
1376 }
1377 RuntimeValue::Tuple(items) => {
1378 let parts: Vec<String> = items.iter().map(|v| v.to_display_string()).collect();
1379 format!("({})", parts.join(", "))
1380 }
1381 RuntimeValue::Set(items) => {
1382 let items = items.borrow();
1383 let parts: Vec<String> = items.iter().map(|v| v.to_display_string()).collect();
1384 format!("{{{}}}", parts.join(", "))
1385 }
1386 RuntimeValue::Map(m) => {
1387 let m = m.borrow();
1388 let pairs: Vec<String> = m.iter()
1389 .map(|(k, v)| format!("{}: {}", k.to_display_string(), v.to_display_string()))
1390 .collect();
1391 format!("{{{}}}", pairs.join(", "))
1392 }
1393 RuntimeValue::Struct(s) => {
1394 if s.fields.is_empty() {
1395 s.type_name.clone()
1396 } else {
1397 let mut field_strs: Vec<(&str, String)> = s
1401 .fields
1402 .iter()
1403 .map(|(k, v)| (k.as_str(), v.to_display_string()))
1404 .collect();
1405 field_strs.sort_by(|a, b| a.0.cmp(b.0));
1406 let joined: Vec<String> = field_strs.iter().map(|(k, v)| format!("{k}: {v}")).collect();
1407 format!("{} {{ {} }}", s.type_name, joined.join(", "))
1408 }
1409 }
1410 RuntimeValue::Inductive(ind) => {
1411 if ind.args.is_empty() {
1412 ind.constructor.clone()
1413 } else {
1414 let arg_strs: Vec<String> = ind.args
1415 .iter()
1416 .map(|v| v.to_display_string())
1417 .collect();
1418 format!("{}({})", ind.constructor, arg_strs.join(", "))
1419 }
1420 }
1421 RuntimeValue::Function(_) => "<closure>".to_string(),
1422 RuntimeValue::Chan(_) => "<channel>".to_string(),
1423 RuntimeValue::TaskHandle(_) => "<task>".to_string(),
1424 RuntimeValue::Peer(topic) => format!("<peer {topic}>"),
1425 RuntimeValue::Crdt(c) => c.borrow().render(),
1426 RuntimeValue::Nothing => "nothing".to_string(),
1427 RuntimeValue::Duration(nanos) => {
1428 let abs_nanos = nanos.unsigned_abs();
1430 let sign = if *nanos < 0 { "-" } else { "" };
1431 if abs_nanos >= 3_600_000_000_000 {
1432 format!("{}{}h", sign, abs_nanos / 3_600_000_000_000)
1434 } else if abs_nanos >= 60_000_000_000 {
1435 format!("{}{}min", sign, abs_nanos / 60_000_000_000)
1437 } else if abs_nanos >= 1_000_000_000 {
1438 format!("{}{}s", sign, abs_nanos / 1_000_000_000)
1440 } else if abs_nanos >= 1_000_000 {
1441 format!("{}{}ms", sign, abs_nanos / 1_000_000)
1443 } else if abs_nanos >= 1_000 {
1444 format!("{}{}μs", sign, abs_nanos / 1_000)
1446 } else {
1447 format!("{}{}ns", sign, abs_nanos)
1449 }
1450 }
1451 RuntimeValue::Date(days) => {
1452 let z = *days as i64 + 719468; let era = if z >= 0 { z } else { z - 146096 } / 146097;
1456 let doe = z - era * 146097;
1457 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
1458 let y = yoe + era * 400;
1459 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
1460 let mp = (5 * doy + 2) / 153;
1461 let d = doy - (153 * mp + 2) / 5 + 1;
1462 let m = mp + if mp < 10 { 3 } else { -9 };
1463 let year = y + if m <= 2 { 1 } else { 0 };
1464 format!("{:04}-{:02}-{:02}", year, m, d)
1465 }
1466 RuntimeValue::Moment(nanos) => {
1467 let total_seconds = nanos.div_euclid(1_000_000_000);
1472 let days = total_seconds.div_euclid(86400) as i32;
1473 let day_seconds = total_seconds.rem_euclid(86400);
1474 let hours = day_seconds / 3600;
1475 let minutes = (day_seconds % 3600) / 60;
1476
1477 let z = days as i64 + 719468;
1479 let era = if z >= 0 { z } else { z - 146096 } / 146097;
1480 let doe = z - era * 146097;
1481 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
1482 let y = yoe + era * 400;
1483 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
1484 let mp = (5 * doy + 2) / 153;
1485 let d = doy - (153 * mp + 2) / 5 + 1;
1486 let m = mp + if mp < 10 { 3 } else { -9 };
1487 let year = y + if m <= 2 { 1 } else { 0 };
1488
1489 format!("{:04}-{:02}-{:02} {:02}:{:02}", year, m, d, hours, minutes)
1490 }
1491 RuntimeValue::Span { months, days } => {
1492 let mut parts = Vec::new();
1494
1495 let years = *months / 12;
1497 let remaining_months = *months % 12;
1498
1499 if years != 0 {
1500 parts.push(if years.abs() == 1 {
1501 format!("{} year", years)
1502 } else {
1503 format!("{} years", years)
1504 });
1505 }
1506
1507 if remaining_months != 0 {
1508 parts.push(if remaining_months.abs() == 1 {
1509 format!("{} month", remaining_months)
1510 } else {
1511 format!("{} months", remaining_months)
1512 });
1513 }
1514
1515 if *days != 0 || parts.is_empty() {
1516 parts.push(if days.abs() == 1 {
1517 format!("{} day", days)
1518 } else {
1519 format!("{} days", days)
1520 });
1521 }
1522
1523 parts.join(" and ")
1524 }
1525 RuntimeValue::Time(nanos) => {
1526 logicaffeine_base::temporal::format_time_of_day(*nanos)
1528 }
1529 }
1530 }
1531}
1532
1533pub enum ControlFlow {
1538 Continue,
1540 Return(RuntimeValue),
1542 Break,
1544}
1545
1546#[derive(Clone)]
1551pub struct FunctionDef<'a> {
1552 pub params: Vec<(Symbol, &'a TypeExpr<'a>)>,
1554 pub body: Block<'a>,
1556 pub return_type: Option<&'a TypeExpr<'a>>,
1558}
1559
1560struct Environment {
1574 globals: HashMap<Symbol, RuntimeValue>,
1576 main_block: HashMap<Symbol, RuntimeValue>,
1579 locals: HashMap<Symbol, RuntimeValue>,
1581 save_stack: Vec<Vec<(Symbol, Option<RuntimeValue>)>>,
1582 frame_stack: Vec<(HashMap<Symbol, RuntimeValue>, Vec<Vec<(Symbol, Option<RuntimeValue>)>>)>,
1586}
1587
1588impl Environment {
1589 fn new() -> Self {
1590 Environment {
1591 globals: HashMap::new(),
1592 main_block: HashMap::new(),
1593 locals: HashMap::new(),
1594 save_stack: Vec::new(),
1595 frame_stack: Vec::new(),
1596 }
1597 }
1598
1599 fn in_function(&self) -> bool {
1600 !self.frame_stack.is_empty()
1601 }
1602
1603 fn define_map(&mut self) -> &mut HashMap<Symbol, RuntimeValue> {
1606 if !self.frame_stack.is_empty() {
1607 &mut self.locals
1608 } else if !self.save_stack.is_empty() {
1609 &mut self.main_block
1610 } else {
1611 &mut self.globals
1612 }
1613 }
1614
1615 fn push_frame(&mut self) {
1618 self.frame_stack.push((
1619 std::mem::take(&mut self.locals),
1620 std::mem::take(&mut self.save_stack),
1621 ));
1622 }
1623
1624 fn pop_frame(&mut self) {
1626 let (locals, saves) = self.frame_stack.pop().unwrap_or_default();
1627 self.locals = locals;
1628 self.save_stack = saves;
1629 }
1630
1631 fn push_scope(&mut self) {
1632 self.save_stack.push(Vec::new());
1633 }
1634
1635 fn pop_scope(&mut self) {
1636 if let Some(saves) = self.save_stack.pop() {
1637 let map = if !self.frame_stack.is_empty() {
1638 &mut self.locals
1639 } else {
1640 &mut self.main_block
1643 };
1644 for (sym, old_val) in saves.into_iter().rev() {
1645 match old_val {
1646 Some(val) => { map.insert(sym, val); }
1647 None => { map.remove(&sym); }
1648 }
1649 }
1650 }
1651 }
1652
1653 fn define(&mut self, name: Symbol, value: RuntimeValue) {
1654 let map = self.define_map();
1655 let old = map.insert(name, value);
1656 if let Some(frame) = self.save_stack.last_mut() {
1657 frame.push((name, old));
1658 }
1659 }
1660
1661 fn lookup(&self, name: Symbol) -> Option<&RuntimeValue> {
1662 if self.in_function() {
1663 self.locals.get(&name).or_else(|| self.globals.get(&name))
1664 } else {
1665 self.main_block.get(&name).or_else(|| self.globals.get(&name))
1666 }
1667 }
1668
1669 fn assign(&mut self, name: Symbol, value: RuntimeValue) -> bool {
1670 if self.in_function() {
1671 if self.locals.contains_key(&name) {
1672 self.locals.insert(name, value);
1673 return true;
1674 }
1675 } else if self.main_block.contains_key(&name) {
1676 self.main_block.insert(name, value);
1677 return true;
1678 }
1679 if self.globals.contains_key(&name) {
1680 self.globals.insert(name, value);
1681 true
1682 } else {
1683 false
1684 }
1685 }
1686}
1687
1688#[derive(Clone)]
1692pub enum ClosureBodyRef<'a> {
1693 Expression(&'a Expr<'a>),
1694 Block(Block<'a>),
1695}
1696
1697const REDUNDANT_K: usize = 4;
1702const REDUNDANT_N: usize = 6;
1703
1704pub struct Interpreter<'a> {
1705 ctx: SharedCtx<'a>,
1710 task: TaskState,
1713 pub output: Vec<String>,
1715 yield_state: Option<crate::concurrency::bridge::Yield<'a>>,
1718 netbox: crate::concurrency::net_inbox::NetInbox,
1723}
1724
1725#[derive(Clone)]
1730struct SharedCtx<'a> {
1731 interner: &'a Interner,
1732 functions: HashMap<Symbol, FunctionDef<'a>>,
1733 struct_defs: HashMap<Symbol, Vec<(Symbol, Symbol, bool)>>,
1734 enum_defs: HashMap<Symbol, Vec<Symbol>>,
1738 vfs: Option<Arc<dyn Vfs>>,
1739 kernel_ctx: Option<Arc<crate::kernel::Context>>,
1740 policy_registry: Option<PolicyRegistry>,
1741 output_callback: Option<OutputCallback>,
1742 closure_bodies: Vec<ClosureBodyRef<'a>>,
1745 sym_show: Option<Symbol>,
1747 sym_length: Option<Symbol>,
1748 sym_format: Option<Symbol>,
1749 sym_parse_int: Option<Symbol>,
1750 sym_parse_float: Option<Symbol>,
1751 sym_abs: Option<Symbol>,
1752 sym_sqrt: Option<Symbol>,
1753 sym_min: Option<Symbol>,
1754 sym_max: Option<Symbol>,
1755 sym_floor: Option<Symbol>,
1756 sym_ceil: Option<Symbol>,
1757 sym_round: Option<Symbol>,
1758 sym_pow: Option<Symbol>,
1759 sym_copy: Option<Symbol>,
1760 sym_chr: Option<Symbol>,
1761 sym_count_ones: Option<Symbol>,
1762 sym_args: Option<Symbol>,
1763 program_args: Vec<String>,
1766}
1767
1768struct TaskState {
1773 env: Environment,
1775 call_depth: usize,
1777 tco_fn_sync: Option<Symbol>,
1783 pending_tail_call: Option<Vec<RuntimeValue>>,
1786 repeat_depth_sync: usize,
1791 tco_fn_async: Option<Symbol>,
1793 pending_tail_call_async: Option<Vec<RuntimeValue>>,
1795 repeat_depth_async: usize,
1797}
1798
1799impl TaskState {
1800 fn new() -> Self {
1801 TaskState {
1802 env: Environment::new(),
1803 call_depth: 0,
1804 tco_fn_sync: None,
1805 pending_tail_call: None,
1806 repeat_depth_sync: 0,
1807 tco_fn_async: None,
1808 pending_tail_call_async: None,
1809 repeat_depth_async: 0,
1810 }
1811 }
1812}
1813
1814impl<'a> Interpreter<'a> {
1815 pub fn new(interner: &'a Interner) -> Self {
1816 Interpreter {
1817 ctx: SharedCtx {
1818 interner,
1819 functions: HashMap::new(),
1820 struct_defs: HashMap::new(),
1821 enum_defs: HashMap::new(),
1822 vfs: None,
1823 kernel_ctx: None,
1824 policy_registry: None,
1825 output_callback: None,
1826 closure_bodies: Vec::new(),
1827 sym_show: interner.lookup("show"),
1828 sym_length: interner.lookup("length"),
1829 sym_format: interner.lookup("format"),
1830 sym_parse_int: interner.lookup("parseInt"),
1831 sym_parse_float: interner.lookup("parseFloat"),
1832 sym_abs: interner.lookup("abs"),
1833 sym_sqrt: interner.lookup("sqrt"),
1834 sym_min: interner.lookup("min"),
1835 sym_max: interner.lookup("max"),
1836 sym_floor: interner.lookup("floor"),
1837 sym_ceil: interner.lookup("ceil"),
1838 sym_round: interner.lookup("round"),
1839 sym_pow: interner.lookup("pow"),
1840 sym_copy: interner.lookup("copy"),
1841 sym_chr: interner.lookup("chr"),
1842 sym_count_ones: interner.lookup("count_ones"),
1843 sym_args: interner.lookup("args"),
1844 program_args: Vec::new(),
1845 },
1846 task: TaskState::new(),
1847 output: Vec::new(),
1848 yield_state: None,
1849 netbox: crate::concurrency::net_inbox::NetInbox::new(),
1850 }
1851 }
1852
1853 pub fn with_program_args(mut self, args: Vec<String>) -> Self {
1857 self.ctx.program_args = args;
1858 self
1859 }
1860
1861 pub fn with_vfs(mut self, vfs: Arc<dyn Vfs>) -> Self {
1863 self.ctx.vfs = Some(vfs);
1864 self
1865 }
1866
1867 pub fn with_kernel(mut self, ctx: Arc<crate::kernel::Context>) -> Self {
1872 self.ctx.kernel_ctx = Some(ctx);
1873 self
1874 }
1875
1876 pub fn with_policies(mut self, registry: PolicyRegistry) -> Self {
1878 self.ctx.policy_registry = Some(registry);
1879 self
1880 }
1881
1882 pub fn with_type_registry(mut self, registry: &crate::analysis::TypeRegistry) -> Self {
1886 use crate::analysis::registry::{TypeDef, FieldType};
1887 for (name_sym, type_def) in registry.iter_types() {
1888 if let TypeDef::Struct { fields, .. } = type_def {
1889 let field_defs: Vec<(Symbol, Symbol, bool)> = fields.iter().map(|f| {
1890 let type_sym = match &f.ty {
1891 FieldType::Primitive(s) | FieldType::Named(s) | FieldType::TypeParam(s) => *s,
1892 FieldType::Generic { base, .. } => *base,
1893 };
1894 (f.name, type_sym, f.is_public)
1895 }).collect();
1896 self.ctx.struct_defs.insert(*name_sym, field_defs);
1897 } else if let TypeDef::Enum { variants, .. } = type_def {
1898 let ctors: Vec<Symbol> = variants.iter().map(|v| v.name).collect();
1901 self.ctx.enum_defs.insert(*name_sym, ctors);
1902 }
1903 }
1904 self
1905 }
1906
1907 pub fn with_output_callback(mut self, callback: OutputCallback) -> Self {
1910 self.ctx.output_callback = Some(callback);
1911 self
1912 }
1913
1914 pub(crate) fn install_yield_state(&mut self, ys: crate::concurrency::bridge::Yield<'a>) {
1917 self.yield_state = Some(ys);
1918 }
1919
1920 fn emit_output(&mut self, line: String) {
1922 if let Some(ref callback) = self.ctx.output_callback {
1923 (callback.borrow_mut())(line.clone());
1924 }
1925 self.output.push(line);
1926 }
1927
1928 pub fn is_kernel_inductive(&self, name: &str) -> bool {
1930 self.ctx.kernel_ctx
1931 .as_ref()
1932 .map(|ctx| ctx.is_inductive(name))
1933 .unwrap_or(false)
1934 }
1935
1936 pub fn get_kernel_constructors(&self, name: &str) -> Vec<(String, usize)> {
1940 self.ctx.kernel_ctx
1941 .as_ref()
1942 .map(|ctx| {
1943 ctx.get_constructors(name)
1944 .iter()
1945 .map(|(ctor_name, ty)| {
1946 let arity = count_pi_args(ty);
1948 (ctor_name.to_string(), arity)
1949 })
1950 .collect()
1951 })
1952 .unwrap_or_default()
1953 }
1954
1955 pub async fn run(&mut self, stmts: &[Stmt<'a>]) -> Result<(), String> {
1958 logicaffeine_base::money::clear_ambient_rates();
1961 for stmt in stmts {
1962 match self.execute_stmt(stmt).await? {
1963 ControlFlow::Return(_) => break,
1964 ControlFlow::Break => break,
1965 ControlFlow::Continue => {}
1966 }
1967 }
1968 Ok(())
1969 }
1970
1971 #[cfg(not(target_arch = "wasm32"))]
1977 async fn activate_pnp_session(&mut self, bind: &crate::ast::SecurePad<'a>) -> Result<(), String> {
1978 let path = self.evaluate_expr(bind.pad).await?.to_display_string();
1979 let bytes = std::fs::read(&path)
1980 .map_err(|e| format!("one-time pad '{path}' could not be read: {e}"))?;
1981 let pool = crate::concurrency::pnp::PadPool::shared(bytes)
1982 .map_err(|e| format!("one-time pad '{path}' rejected (not truly random / too small): {e:?}"))?;
1983 let role = match bind.role {
1984 crate::ast::SecureRole::Initiator => crate::concurrency::pnp::Role::Initiator,
1985 crate::ast::SecureRole::Responder => crate::concurrency::pnp::Role::Responder,
1986 };
1987 let session: std::rc::Rc<dyn crate::concurrency::channel::ActiveSession> =
1988 std::rc::Rc::new(pool.session(role));
1989 crate::concurrency::channel::install_session(Some(session));
1990 Ok(())
1991 }
1992
1993 #[cfg(target_arch = "wasm32")]
1996 async fn activate_pnp_session(&mut self, _bind: &crate::ast::SecurePad<'a>) -> Result<(), String> {
1997 Ok(())
1998 }
1999
2000 #[async_recursion(?Send)]
2003 async fn execute_stmt(&mut self, stmt: &Stmt<'a>) -> Result<ControlFlow, String> {
2004 match stmt {
2005 Stmt::Let { var, value, .. } => {
2006 let val = self.evaluate_expr(value).await?;
2007 self.define(*var, val);
2008 Ok(ControlFlow::Continue)
2009 }
2010
2011 Stmt::Set { target, value } => {
2012 let val = self.evaluate_expr(value).await?;
2013 self.assign(*target, val)?;
2014 Ok(ControlFlow::Continue)
2015 }
2016
2017 Stmt::Call { function, args } => {
2018 self.call_function(*function, args).await?;
2019 Ok(ControlFlow::Continue)
2020 }
2021
2022 Stmt::If { cond, then_block, else_block } => {
2023 let condition = self.evaluate_expr(cond).await?;
2024 if condition.is_truthy() {
2025 let flow = self.execute_block(then_block).await?;
2026 if !matches!(flow, ControlFlow::Continue) {
2027 return Ok(flow);
2028 }
2029 } else if let Some(else_stmts) = else_block {
2030 let flow = self.execute_block(else_stmts).await?;
2031 if !matches!(flow, ControlFlow::Continue) {
2032 return Ok(flow);
2033 }
2034 }
2035 Ok(ControlFlow::Continue)
2036 }
2037
2038 Stmt::While { cond, body, .. } => {
2039 loop {
2040 let condition = self.evaluate_expr(cond).await?;
2041 if !condition.is_truthy() {
2042 break;
2043 }
2044 match self.execute_block(body).await? {
2045 ControlFlow::Break => break,
2046 ControlFlow::Return(v) => return Ok(ControlFlow::Return(v)),
2047 ControlFlow::Continue => {}
2048 }
2049 }
2050 Ok(ControlFlow::Continue)
2051 }
2052
2053 Stmt::Repeat { pattern, iterable, body } => {
2054 use crate::ast::stmt::Pattern;
2055
2056 let iter_val = self.evaluate_expr(iterable).await?;
2057 let items = crate::semantics::collections::iteration_snapshot(&iter_val)?;
2058
2059 self.push_scope();
2060 self.task.repeat_depth_async += 1;
2062 for item in items {
2063 match pattern {
2065 Pattern::Identifier(sym) => {
2066 self.define(*sym, item);
2067 }
2068 Pattern::Tuple(syms) => {
2069 if let RuntimeValue::Tuple(ref tuple_vals) = item {
2070 if syms.len() != tuple_vals.len() {
2071 self.task.repeat_depth_async -= 1;
2072 return Err(format!(
2073 "Cannot bind a {}-tuple to {} names",
2074 tuple_vals.len(),
2075 syms.len()
2076 ));
2077 }
2078 for (sym, val) in syms.iter().zip(tuple_vals.iter()) {
2079 self.define(*sym, val.clone());
2080 }
2081 } else {
2082 self.task.repeat_depth_async -= 1;
2083 return Err(format!("Expected tuple for pattern, got {}", item.type_name()));
2084 }
2085 }
2086 }
2087
2088 match self.execute_block(body).await? {
2089 ControlFlow::Break => break,
2090 ControlFlow::Return(v) => {
2091 self.task.repeat_depth_async -= 1;
2092 self.pop_scope();
2093 return Ok(ControlFlow::Return(v));
2094 }
2095 ControlFlow::Continue => {}
2096 }
2097 }
2098 self.task.repeat_depth_async -= 1;
2099 self.pop_scope();
2100 Ok(ControlFlow::Continue)
2101 }
2102
2103 Stmt::Return { value } => {
2104 if let Some(expr) = value {
2107 if let Some(call_args) = self.self_tail_call_args_async(*expr) {
2108 let mut vals = Vec::with_capacity(call_args.len());
2109 for a in call_args {
2110 vals.push(self.evaluate_expr(a).await?);
2111 }
2112 self.task.pending_tail_call_async = Some(vals);
2113 return Ok(ControlFlow::Return(RuntimeValue::Nothing));
2114 }
2115 }
2116 let ret_val = match value {
2117 Some(expr) => self.evaluate_expr(expr).await?,
2118 None => RuntimeValue::Nothing,
2119 };
2120 Ok(ControlFlow::Return(ret_val))
2121 }
2122
2123 Stmt::Break => Ok(ControlFlow::Break),
2124
2125 Stmt::FunctionDef { name, params, body, return_type, .. } => {
2126 let func = FunctionDef {
2127 params: params.clone(),
2128 body: *body,
2129 return_type: *return_type,
2130 };
2131 self.ctx.functions.insert(*name, func);
2132 Ok(ControlFlow::Continue)
2133 }
2134
2135 Stmt::StructDef { name, fields, .. } => {
2136 self.ctx.struct_defs.insert(*name, fields.clone());
2137 Ok(ControlFlow::Continue)
2138 }
2139
2140 Stmt::SetField { object, field, value } => {
2141 let new_val = self.evaluate_expr(value).await?;
2142 if let Expr::Identifier(obj_sym) = object {
2143 let mut obj_val = self.lookup(*obj_sym)?.clone();
2144 if let RuntimeValue::Struct(ref mut s) = obj_val {
2145 let field_name = self.ctx.interner.resolve(*field).to_string();
2146 s.fields.insert(field_name, new_val);
2147 self.assign(*obj_sym, obj_val)?;
2148 } else {
2149 return Err(format!("Cannot set field on non-struct value"));
2150 }
2151 } else {
2152 return Err("SetField target must be an identifier".to_string());
2153 }
2154 Ok(ControlFlow::Continue)
2155 }
2156
2157 Stmt::Push { value, collection } => {
2158 let val = self.evaluate_expr(value).await?;
2159 if let Expr::Identifier(coll_sym) = collection {
2160 self.ensure_collection_owned(*coll_sym);
2161 let coll_val = self.lookup(*coll_sym)?;
2162 crate::semantics::collections::list_push(&coll_val, val)?;
2163 } else if let Expr::FieldAccess { object, field } = collection {
2164 if let Expr::Identifier(obj_sym) = *object {
2165 let obj_val = self.lookup(*obj_sym)?;
2166 let field_name = self.ctx.interner.resolve(*field);
2167 crate::semantics::collections::push_to_struct_field(&obj_val, field_name, val)?;
2168 } else {
2169 return Err("Push to nested field access not supported".to_string());
2170 }
2171 } else {
2172 let coll_val = self.evaluate_expr(collection).await?;
2177 crate::semantics::collections::list_push(&coll_val, val)?;
2178 }
2179 Ok(ControlFlow::Continue)
2180 }
2181
2182 Stmt::Pop { collection, into } => {
2183 if let Expr::Identifier(coll_sym) = collection {
2184 self.ensure_collection_owned(*coll_sym);
2185 let coll_val = self.lookup(*coll_sym)?;
2186 let popped = crate::semantics::collections::list_pop(&coll_val)?;
2187 if let Some(into_var) = into {
2188 self.define(*into_var, popped);
2189 }
2190 } else {
2191 return Err("Pop collection must be an identifier".to_string());
2192 }
2193 Ok(ControlFlow::Continue)
2194 }
2195
2196 Stmt::Add { value, collection } => {
2197 let val = self.evaluate_expr(value).await?;
2198 if let Expr::Identifier(coll_sym) = collection {
2199 self.ensure_collection_owned(*coll_sym);
2200 }
2201 let coll_val = self.evaluate_expr(collection).await?;
2205 crate::semantics::collections::set_add(&coll_val, val)?;
2206 Ok(ControlFlow::Continue)
2207 }
2208
2209 Stmt::Remove { value, collection } => {
2210 let val = self.evaluate_expr(value).await?;
2211 if let Expr::Identifier(coll_sym) = collection {
2212 self.ensure_collection_owned(*coll_sym);
2213 }
2214 let coll_val = self.evaluate_expr(collection).await?;
2215 crate::semantics::collections::remove_from(&coll_val, &val)?;
2216 Ok(ControlFlow::Continue)
2217 }
2218
2219 Stmt::SetIndex { collection, index, value } => {
2220 let idx_val = self.evaluate_expr(index).await?;
2221 let new_val = self.evaluate_expr(value).await?;
2222 if let Expr::Identifier(coll_sym) = collection {
2223 if let RuntimeValue::Text(field) = &idx_val {
2227 let cur = self.lookup(*coll_sym)?.clone();
2228 if let RuntimeValue::Struct(mut s) = cur {
2229 s.fields.insert(field.to_string(), new_val);
2230 self.assign(*coll_sym, RuntimeValue::Struct(s))?;
2231 return Ok(ControlFlow::Continue);
2232 }
2233 }
2234 self.ensure_collection_owned(*coll_sym);
2235 let coll_val = self.lookup(*coll_sym)?;
2236 crate::semantics::collections::index_set(&coll_val, &idx_val, new_val)?;
2237 } else {
2238 let coll_val = self.evaluate_expr(collection).await?;
2242 crate::semantics::collections::index_set(&coll_val, &idx_val, new_val)?;
2243 }
2244 Ok(ControlFlow::Continue)
2245 }
2246
2247 Stmt::Splice { body } => {
2248 for s in body.iter() {
2252 let flow = self.execute_stmt(s).await?;
2253 if !matches!(flow, ControlFlow::Continue) {
2254 return Ok(flow);
2255 }
2256 }
2257 Ok(ControlFlow::Continue)
2258 }
2259
2260 Stmt::Inspect { target, arms, .. } => {
2261 let target_val = self.evaluate_expr(target).await?;
2262 self.execute_inspect(&target_val, arms).await
2263 }
2264
2265 Stmt::Zone { name, body, .. } => {
2266 self.push_scope();
2267 self.define(*name, RuntimeValue::Nothing);
2268 let result = self.execute_block(body).await;
2269 self.pop_scope();
2270 result?;
2271 Ok(ControlFlow::Continue)
2272 }
2273
2274 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
2275 for task in tasks.iter() {
2277 self.execute_stmt(task).await?;
2278 }
2279 Ok(ControlFlow::Continue)
2280 }
2281
2282 Stmt::Assert { .. } | Stmt::Trust { .. } => {
2283 Ok(ControlFlow::Continue)
2284 }
2285
2286 Stmt::RuntimeAssert { condition, .. } => {
2287 let val = self.evaluate_expr(condition).await?;
2288 if !val.is_truthy() {
2289 return Err("Assertion failed".to_string());
2290 }
2291 Ok(ControlFlow::Continue)
2292 }
2293
2294 Stmt::Give { object, recipient } => {
2295 let obj_val = self.evaluate_expr(object).await?;
2296 if let Expr::Identifier(sym) = recipient {
2297 self.call_function_with_values(*sym, vec![obj_val]).await?;
2298 }
2299 Ok(ControlFlow::Continue)
2300 }
2301
2302 Stmt::Show { object, recipient } => {
2303 let obj_val = self.evaluate_expr(object).await?;
2304 if let Expr::Identifier(sym) = recipient {
2305 let name = self.ctx.interner.resolve(*sym);
2306 if name == "show" {
2307 self.emit_output(obj_val.to_display_string());
2308 } else {
2309 self.call_function_with_values(*sym, vec![obj_val]).await?;
2310 }
2311 }
2312 Ok(ControlFlow::Continue)
2313 }
2314
2315 Stmt::ReadFrom { var, source } => {
2317 let content = match source {
2318 ReadSource::Console => {
2319 String::new()
2321 }
2322 ReadSource::File(path_expr) => {
2323 let path = self.evaluate_expr(path_expr).await?.to_display_string();
2324 match &self.ctx.vfs {
2325 Some(vfs) => {
2326 vfs.read_to_string(&path).await
2327 .map_err(|e| format!("Read error: {}", e))?
2328 }
2329 None => return Err("VFS not initialized. Use Interpreter::with_vfs()".to_string()),
2330 }
2331 }
2332 };
2333 self.define(*var, RuntimeValue::Text(Rc::new(content)));
2334 Ok(ControlFlow::Continue)
2335 }
2336
2337 Stmt::WriteFile { content, path } => {
2338 let content_val = self.evaluate_expr(content).await?.to_display_string();
2339 let path_val = self.evaluate_expr(path).await?.to_display_string();
2340 match &self.ctx.vfs {
2341 Some(vfs) => {
2342 vfs.write(&path_val, content_val.as_bytes()).await
2343 .map_err(|e| format!("Write error: {}", e))?;
2344 }
2345 None => return Err("VFS not initialized. Use Interpreter::with_vfs()".to_string()),
2346 }
2347 Ok(ControlFlow::Continue)
2348 }
2349
2350 Stmt::Spawn { name, .. } => {
2351 self.define(*name, RuntimeValue::Nothing);
2352 Ok(ControlFlow::Continue)
2353 }
2354
2355 Stmt::SendMessage { message, destination, compression, cached, unchecked, layout, shared, computed, indexed, deduped } => {
2359 use crate::concurrency::marshal::{
2360 default_integrity, message_to_wire_best, message_to_wire_cached, message_to_wire_with,
2361 with_compression_codec, with_dedup, with_integrity, with_numerics, with_structure,
2362 with_struct_view, with_type_registry, WireCodec, WireGoal, WireIntegrity, WireNumerics,
2363 WireSchemaCache, WireStructure,
2364 };
2365 use logicaffeine_language::ast::SendLayout;
2366 let dest = self.evaluate_expr(destination).await?;
2367 let topic = Self::peer_topic_of(&dest)?;
2368 let msg = self.evaluate_expr(message).await?;
2369 let msg = if *computed {
2376 match msg {
2377 RuntimeValue::Function(c) if c.generated.is_some() => RuntimeValue::Function(c),
2378 RuntimeValue::Function(c) => {
2379 if c.param_names.len() != 1 {
2380 return Err(
2381 "Send computed requires a single-argument pure function".to_string()
2382 );
2383 }
2384 let expr = match self.ctx.closure_bodies.get(c.body_index) {
2385 Some(ClosureBodyRef::Expression(e)) => *e,
2386 _ => {
2387 return Err(
2388 "Send computed requires a pure expression-bodied function".to_string()
2389 )
2390 }
2391 };
2392 match crate::concurrency::marshal::lower_expr_to_genexpr(expr, c.param_names[0]) {
2393 Some(gen) => RuntimeValue::Function(Box::new(ClosureValue {
2394 body_index: usize::MAX,
2395 captured_env: HashMap::default(),
2396 param_names: c.param_names.clone(),
2397 generated: Some(std::rc::Rc::new(gen)),
2398 })),
2399 None => {
2400 return Err(
2401 "Send computed: the function is not a pure arithmetic computation over its argument"
2402 .to_string(),
2403 )
2404 }
2405 }
2406 }
2407 _ => return Err("Send computed requires a function value".to_string()),
2408 }
2409 } else {
2410 msg
2411 };
2412 let from = self.netbox.inbox.as_ref().map(|t| t.to_string()).unwrap_or_default();
2414 self.netbox.my_profile.registry_epoch = self.build_wire_type_registry().epoch();
2417 let integrity = if *unchecked { WireIntegrity::Raw } else { default_integrity() };
2425 let numerics = match layout {
2426 Some(SendLayout::Fast) => WireNumerics::Fixed,
2427 Some(SendLayout::Packed) => WireNumerics::GroupVarint,
2428 Some(SendLayout::Smallest) => WireNumerics::Varint,
2431 Some(SendLayout::Redundant) => WireNumerics::Varint,
2433 Some(SendLayout::Compact) | None => WireNumerics::Varint,
2434 };
2435 let structure = match layout {
2439 Some(SendLayout::Smallest) => WireStructure::Auto,
2440 _ => WireStructure::Off,
2441 };
2442 let registry = if *shared {
2447 self.build_wire_type_registry()
2448 } else {
2449 crate::concurrency::marshal::WireTypeRegistry::new(Vec::new())
2450 };
2451 let plain = !*computed
2461 && !*unchecked
2462 && !*shared
2463 && !*indexed
2464 && !*cached
2465 && !*deduped
2466 && compression.is_none()
2467 && matches!(layout, None | Some(SendLayout::Compact));
2468 let bytes = if plain {
2469 self.netbox.encode_negotiated(&from, &msg, &topic, self.build_wire_type_registry())?
2472 } else {
2473 with_dedup(*deduped, || with_struct_view(*indexed, || with_type_registry(registry, || -> Result<Vec<u8>, String> {
2477 if matches!(layout, Some(SendLayout::Smallest)) && !*cached && !*deduped {
2486 return with_integrity(integrity, || {
2487 message_to_wire_best(&from, &msg, WireGoal::Smallest)
2488 });
2489 }
2490 if *cached {
2491 let cache = self
2492 .netbox
2493 .send_schema
2494 .entry(topic.clone())
2495 .or_insert_with(WireSchemaCache::content_addressed);
2496 let mut encode = || message_to_wire_cached(&from, &msg, WireCodec::Native, integrity, cache);
2497 with_structure(structure, || with_numerics(numerics, || match compression {
2498 Some(codec) => with_compression_codec(wire_compression_of(*codec), &mut encode),
2499 None => encode(),
2500 }))
2501 } else {
2502 let mut encode = || message_to_wire_with(&from, &msg, WireCodec::Native, integrity);
2503 with_structure(structure, || with_numerics(numerics, || match compression {
2504 Some(codec) => with_compression_codec(wire_compression_of(*codec), &mut encode),
2505 None => encode(),
2506 }))
2507 }
2508 })))?
2509 };
2510 let bytes = crate::concurrency::channel::seal_active_checked(bytes)
2515 .ok_or_else(|| "one-time pad exhausted — message not sent (PNP fail-closed)".to_string())?;
2516 if let Some(hs) = self.netbox.first_contact_handshake(&topic) {
2519 self.netbox
2520 .publish(&crate::concurrency::net_inbox::handshake_topic_for(&topic), hs)?;
2521 }
2522 if matches!(layout, Some(SendLayout::Redundant)) {
2523 let msg_id = self.netbox.next_msg_id();
2527 let shards = crate::concurrency::fec::frame_redundant(
2528 msg_id, &bytes, REDUNDANT_K, REDUNDANT_N,
2529 )
2530 .ok_or_else(|| "redundant framing failed".to_string())?;
2531 for shard in shards {
2532 self.netbox.publish(&topic, shard)?;
2533 }
2534 } else {
2535 self.netbox.publish(&topic, bytes)?;
2536 }
2537 Ok(ControlFlow::Continue)
2538 }
2539
2540 Stmt::AwaitMessage { source, into, view, stream } => {
2544 let src = self.evaluate_expr(source).await?;
2545 let want = Self::peer_topic_of(&src)?;
2546 if self.netbox.inbox.is_none() {
2547 return Err("Await requires a prior Listen to establish an inbox".to_string());
2548 }
2549 let msg = if *stream {
2553 self.await_stream_from(&want).await?
2554 } else {
2555 self.await_message_from(&want, *view).await?
2556 };
2557 self.define(*into, msg);
2558 Ok(ControlFlow::Continue)
2559 }
2560
2561 Stmt::StreamMessage { values, destination } => {
2564 let dest = self.evaluate_expr(destination).await?;
2565 let topic = Self::peer_topic_of(&dest)?;
2566 let list = self.evaluate_expr(values).await?;
2567 let items = match &list {
2568 RuntimeValue::List(rc) => rc.borrow().to_values(),
2569 other => vec![other.clone()],
2570 };
2571 let from = self.netbox.inbox.as_ref().map(|t| t.to_string()).unwrap_or_default();
2572 let registry = self.build_wire_type_registry();
2573 let blob = crate::concurrency::marshal::with_type_registry(registry, || {
2574 crate::concurrency::marshal::frame_stream_message(&from, &items)
2575 })?;
2576 self.netbox.publish(&topic, blob)?;
2579 Ok(ControlFlow::Continue)
2580 }
2581
2582 Stmt::MergeCrdt { source, target } => {
2583 let source_val = self.evaluate_expr(source).await?;
2584 let source_fields = match &source_val {
2585 RuntimeValue::Struct(s) => s.fields.clone(),
2586 _ => return Err("Merge source must be a struct".to_string()),
2587 };
2588
2589 if let Expr::Identifier(target_sym) = target {
2590 let mut target_val = self.lookup(*target_sym)?.clone();
2591
2592 if let RuntimeValue::Struct(ref mut s) = target_val {
2593 for (field_name, source_field_val) in source_fields {
2594 let current = s.fields.get(&field_name)
2595 .cloned()
2596 .unwrap_or(RuntimeValue::Int(0));
2597
2598 let merged =
2599 crate::semantics::arith::crdt_merge_field(¤t, source_field_val);
2600 s.fields.insert(field_name, merged);
2601 }
2602 self.assign(*target_sym, target_val)?;
2603 } else {
2604 return Err("Merge target must be a struct".to_string());
2605 }
2606 } else {
2607 return Err("Merge target must be an identifier".to_string());
2608 }
2609 Ok(ControlFlow::Continue)
2610 }
2611
2612 Stmt::IncreaseCrdt { object, field, amount } => {
2613 let amount_val = self.evaluate_expr(amount).await?;
2614 let amount_int = match amount_val {
2615 RuntimeValue::Int(n) => n,
2616 _ => return Err("CRDT increment amount must be an integer".to_string()),
2617 };
2618
2619 if let Expr::Identifier(obj_sym) = object {
2620 let mut obj_val = self.lookup(*obj_sym)?.clone();
2621
2622 if let RuntimeValue::Struct(ref mut s) = obj_val {
2623 let field_name = self.ctx.interner.resolve(*field).to_string();
2624 let current = s.fields.get(&field_name)
2625 .cloned()
2626 .unwrap_or(RuntimeValue::Int(0));
2627
2628 let new_val =
2629 crate::semantics::arith::crdt_counter_bump(current, amount_int, &field_name)?;
2630 s.fields.insert(field_name, new_val);
2631 self.assign(*obj_sym, obj_val)?;
2632 } else {
2633 return Err("Cannot increase field on non-struct value".to_string());
2634 }
2635 } else {
2636 return Err("IncreaseCrdt target must be an identifier".to_string());
2637 }
2638 Ok(ControlFlow::Continue)
2639 }
2640
2641 Stmt::DecreaseCrdt { object, field, amount } => {
2642 let amount_val = self.evaluate_expr(amount).await?;
2643 let amount_int = match amount_val {
2644 RuntimeValue::Int(n) => n,
2645 _ => return Err("CRDT decrement amount must be an integer".to_string()),
2646 };
2647
2648 if let Expr::Identifier(obj_sym) = object {
2649 let mut obj_val = self.lookup(*obj_sym)?.clone();
2650
2651 if let RuntimeValue::Struct(ref mut s) = obj_val {
2652 let field_name = self.ctx.interner.resolve(*field).to_string();
2653 let current = s.fields.get(&field_name)
2654 .cloned()
2655 .unwrap_or(RuntimeValue::Int(0));
2656
2657 let new_val = crate::semantics::arith::crdt_counter_bump(
2658 current,
2659 amount_int.wrapping_neg(),
2660 &field_name,
2661 )?;
2662 s.fields.insert(field_name, new_val);
2663 self.assign(*obj_sym, obj_val)?;
2664 } else {
2665 return Err("Cannot decrease field on non-struct value".to_string());
2666 }
2667 } else {
2668 return Err("DecreaseCrdt target must be an identifier".to_string());
2669 }
2670 Ok(ControlFlow::Continue)
2671 }
2672
2673 Stmt::AppendToSequence { sequence, value } => {
2674 let val = self.evaluate_expr(value).await?;
2675 let seq_val = self.evaluate_expr(sequence).await?;
2676 match &seq_val {
2677 RuntimeValue::Crdt(rc) => rc.borrow_mut().append(&val)?,
2678 RuntimeValue::List(_) => {
2679 crate::semantics::collections::list_push(&seq_val, val)?
2680 }
2681 _ => return Err(format!("Cannot append to {}", seq_val.type_name())),
2682 }
2683 Ok(ControlFlow::Continue)
2684 }
2685
2686 Stmt::ResolveConflict { object, field, value } => {
2687 let val = self.evaluate_expr(value).await?;
2688 let obj_val = self.evaluate_expr(object).await?;
2689 let field_name = self.ctx.interner.resolve(*field);
2690 if let RuntimeValue::Struct(s) = &obj_val {
2693 if let Some(RuntimeValue::Crdt(rc)) = s.fields.get(field_name) {
2694 rc.borrow_mut().resolve(&val)?;
2695 return Ok(ControlFlow::Continue);
2696 }
2697 }
2698 if let Expr::Identifier(obj_sym) = object {
2699 let mut owner = self.lookup(*obj_sym)?.clone();
2700 if let RuntimeValue::Struct(ref mut s) = owner {
2701 s.fields.insert(field_name.to_string(), val);
2702 self.assign(*obj_sym, owner)?;
2703 return Ok(ControlFlow::Continue);
2704 }
2705 }
2706 Err("Resolve target must be a struct field".to_string())
2707 }
2708
2709 Stmt::Check { subject, predicate, is_capability, object, source_text, .. } => {
2710 let registry = match &self.ctx.policy_registry {
2712 Some(r) => r,
2713 None => return Err("Security Check requires policies. Use compiled Rust or add ## Policy block.".to_string()),
2714 };
2715
2716 let subj_val = self.lookup(*subject)?.clone();
2717 let subj_type_name = match &subj_val {
2718 RuntimeValue::Struct(s) => s.type_name.clone(),
2719 _ => return Err(format!("Check subject must be a struct, got {}", subj_val.type_name())),
2720 };
2721
2722 let subj_type_sym = match self.ctx.interner.lookup(&subj_type_name) {
2724 Some(sym) => sym,
2725 None => return Err(format!("Unknown type '{}' in Check statement", subj_type_name)),
2726 };
2727
2728 let passed = if *is_capability {
2729 let obj_val = match object {
2731 Some(obj_sym) => Some(self.lookup(*obj_sym)?.clone()),
2732 None => None,
2733 };
2734
2735 let caps = registry.get_capabilities(subj_type_sym);
2736 let cap = caps
2737 .and_then(|caps| caps.iter().find(|c| c.action == *predicate));
2738
2739 match cap {
2740 Some(cap) => self.evaluate_policy_condition(&cap.condition, &subj_val, obj_val.as_ref()),
2741 None => {
2742 let pred_name = self.ctx.interner.resolve(*predicate);
2743 return Err(format!("No capability '{}' defined for type '{}'", pred_name, subj_type_name));
2744 }
2745 }
2746 } else {
2747 let preds = registry.get_predicates(subj_type_sym);
2749 let pred_def = preds
2750 .and_then(|preds| preds.iter().find(|p| p.predicate_name == *predicate));
2751
2752 match pred_def {
2753 Some(pred) => self.evaluate_policy_condition(&pred.condition, &subj_val, None),
2754 None => {
2755 let pred_name = self.ctx.interner.resolve(*predicate);
2756 return Err(format!("No predicate '{}' defined for type '{}'", pred_name, subj_type_name));
2757 }
2758 }
2759 };
2760
2761 if !passed {
2762 return Err(format!("Security Check Failed: {}", source_text));
2763 }
2764 Ok(ControlFlow::Continue)
2765 }
2766
2767 Stmt::ConnectTo { address, secure } => {
2773 let raw = self.evaluate_expr(address).await?.to_display_string();
2774 let url = logicaffeine_system::addr::multiaddr_to_ws_url(&raw)
2775 .map_err(|e| format!("Connect address '{raw}' is not a ws:// URL or supported multiaddr: {e}"))?;
2776 if let Some(bind) = secure {
2778 self.activate_pnp_session(bind).await?;
2779 }
2780 if !crate::concurrency::net_inbox::net_is_offline() {
2785 let net = logicaffeine_system::net::Net::connect(&url)
2786 .await
2787 .map_err(|e| format!("Connect to relay '{url}' failed: {e}"))?;
2788 self.netbox.net = Some(net);
2789 }
2790 Ok(ControlFlow::Continue)
2791 }
2792 Stmt::Listen { address, secure } => {
2798 let raw = self.evaluate_expr(address).await?.to_display_string();
2799 let topic = logicaffeine_system::addr::canonical_topic(&raw);
2800 let hs_topic = crate::concurrency::net_inbox::handshake_topic_for(&topic);
2801 if let Some(bind) = secure {
2802 self.activate_pnp_session(bind).await?;
2803 }
2804 if let Some(net) = self.netbox.net.as_mut() {
2808 net.subscribe(&topic).await?;
2809 net.subscribe(&hs_topic).await?;
2811 }
2812 self.netbox.inbox = Some(Rc::new(topic));
2813 Ok(ControlFlow::Continue)
2814 }
2815 Stmt::LetPeerAgent { var, address } => {
2819 let raw = self.evaluate_expr(address).await?.to_display_string();
2820 let topic = logicaffeine_system::addr::canonical_topic(&raw);
2821 self.define(*var, RuntimeValue::Peer(Rc::new(topic)));
2822 Ok(ControlFlow::Continue)
2823 }
2824 Stmt::Sleep { milliseconds } => {
2825 let val = self.evaluate_expr(milliseconds).await?;
2826
2827 if self.yield_state.is_some() {
2834 let ticks = match &val {
2835 RuntimeValue::Int(n) => (*n).max(0) as u64,
2836 RuntimeValue::Duration(d) => (*d).max(0) as u64,
2837 _ => return Err(format!("Sleep requires Duration or Int, got {}", val.type_name())),
2838 };
2839 if ticks > 0 {
2840 self.yield_request(BlockingRequest::Sleep(ticks)).await;
2841 }
2842 return Ok(ControlFlow::Continue);
2843 }
2844
2845 let nanos = match val {
2846 RuntimeValue::Duration(nanos) => nanos,
2847 RuntimeValue::Int(ms) => ms.wrapping_mul(1_000_000), _ => return Err(format!("Sleep requires Duration or Int, got {}", val.type_name())),
2849 };
2850
2851 if nanos > 0 {
2852 #[cfg(not(target_arch = "wasm32"))]
2853 {
2854 logicaffeine_system::tokio::time::sleep(
2856 std::time::Duration::from_nanos(nanos as u64)
2857 ).await;
2858 }
2859 #[cfg(target_arch = "wasm32")]
2860 {
2861 let millis = (nanos / 1_000_000) as u32;
2863 if millis > 0 {
2864 gloo_timers::future::TimeoutFuture::new(millis).await;
2865 }
2866 }
2867 }
2868 Ok(ControlFlow::Continue)
2869 }
2870 Stmt::Sync { var, topic } => {
2875 let topic_str = self.evaluate_expr(topic).await?.to_display_string();
2876 let current = self.lookup(*var)?.clone();
2877 let crdt_fields: Vec<(String, std::rc::Rc<std::cell::RefCell<crate::semantics::crdt::CrdtValue>>)> =
2885 match ¤t {
2886 RuntimeValue::Crdt(rc) => vec![(String::new(), rc.clone())],
2887 RuntimeValue::Struct(s) => s
2888 .fields
2889 .iter()
2890 .filter_map(|(k, v)| match v {
2891 RuntimeValue::Crdt(rc) => Some((k.clone(), rc.clone())),
2892 _ => None,
2893 })
2894 .collect(),
2895 _ => Vec::new(),
2896 };
2897 if !crdt_fields.is_empty() {
2898 let field_key = |name: &str| format!("{topic_str}\u{0}{name}");
2899 let mut frame: Vec<(String, Vec<u8>)> = Vec::new();
2901 for (name, rc) in &crdt_fields {
2902 let since = self.netbox.sync_versions.get(&field_key(name)).cloned().unwrap_or_default();
2903 if let Some(d) = rc.borrow().delta_since_bytes(&since) {
2904 frame.push((name.clone(), d));
2905 }
2906 }
2907 let payload = serde_json::to_vec(&frame).unwrap_or_default();
2908 if let Some(net) = self.netbox.net.as_mut() {
2912 net.subscribe(&topic_str).await?;
2913 if !frame.is_empty() {
2914 net.publish(&topic_str, payload)?;
2915 }
2916 let incoming = net.drain();
2917 for (_t, data) in incoming {
2919 let Ok(fields) = serde_json::from_slice::<Vec<(String, Vec<u8>)>>(&data) else {
2920 continue;
2921 };
2922 for (name, delta) in fields {
2923 if let Some((_, rc)) = crdt_fields.iter().find(|(n, _)| *n == name) {
2924 rc.borrow_mut().apply_delta_bytes(&delta);
2925 }
2926 }
2927 }
2928 }
2929 for (name, rc) in &crdt_fields {
2931 let v = rc.borrow().version();
2932 self.netbox.sync_versions.insert(field_key(name), v);
2933 }
2934 return Ok(ControlFlow::Continue);
2935 }
2936 let publish_bytes = crate::semantics::arith::crdt_to_wire(¤t);
2939 let merged = if let Some(net) = self.netbox.net.as_mut() {
2945 net.subscribe(&topic_str).await?;
2946 if let Some(bytes) = publish_bytes {
2947 net.publish(&topic_str, bytes)?;
2948 }
2949 let incoming = net.drain();
2952 let mut merged = current;
2953 for (_t, data) in incoming {
2954 merged = crate::semantics::arith::crdt_merge_wire(merged, &data);
2955 }
2956 merged
2957 } else {
2958 current
2959 };
2960 self.assign(*var, merged)?;
2961 Ok(ControlFlow::Continue)
2962 }
2963 Stmt::Mount { var, path } => {
2965 let path_val = self.evaluate_expr(path).await?.to_display_string();
2966 match &self.ctx.vfs {
2967 Some(vfs) => {
2968 let content = match vfs.read_to_string(&path_val).await {
2970 Ok(s) => s,
2971 Err(_) => String::new(),
2972 };
2973 self.define(*var, RuntimeValue::Text(Rc::new(content)));
2974 }
2975 None => return Err("VFS not initialized. Use Interpreter::with_vfs()".to_string()),
2976 }
2977 Ok(ControlFlow::Continue)
2978 }
2979
2980 Stmt::CreatePipe { var, capacity, .. } => {
2983 let cap = capacity.map(|c| c as usize);
2984 let resume = self.yield_request(BlockingRequest::NewChan(cap)).await;
2985 let ch = resume
2986 .into_chan()
2987 .ok_or_else(|| "scheduler did not create a channel".to_string())?;
2988 self.define(*var, RuntimeValue::Chan(ch));
2989 Ok(ControlFlow::Continue)
2990 }
2991 Stmt::SendPipe { value, pipe } => {
2992 let ch = self.eval_chan(pipe).await?;
2993 let val = self.evaluate_expr(value).await?;
2994 let payload = marshal::materialize(&val)
2995 .map_err(|e| format!("cannot send value through a channel: {:?}", e))?;
2996 self.yield_request(BlockingRequest::Send(ch, payload)).await;
2997 Ok(ControlFlow::Continue)
2998 }
2999 Stmt::ReceivePipe { var, pipe } => {
3000 let ch = self.eval_chan(pipe).await?;
3001 let resume = self.yield_request(BlockingRequest::Recv(ch)).await;
3002 let value = marshal::rebuild(resume.into_payload());
3003 self.define(*var, value);
3004 Ok(ControlFlow::Continue)
3005 }
3006 Stmt::LaunchTask { function, args } => {
3007 let mut arg_vals = Vec::with_capacity(args.len());
3008 for a in args.iter() {
3009 arg_vals.push(self.evaluate_expr(a).await?);
3010 }
3011 let child = self.spawn_child_task(*function, arg_vals);
3012 self.yield_request(BlockingRequest::Spawn(child)).await;
3013 Ok(ControlFlow::Continue)
3014 }
3015 Stmt::LaunchTaskWithHandle { handle, function, args } => {
3016 let mut arg_vals = Vec::with_capacity(args.len());
3017 for a in args.iter() {
3018 arg_vals.push(self.evaluate_expr(a).await?);
3019 }
3020 let child = self.spawn_child_task(*function, arg_vals);
3021 let resume = self.yield_request(BlockingRequest::Spawn(child)).await;
3022 let tid = resume
3023 .into_task()
3024 .ok_or_else(|| "scheduler did not return a task handle".to_string())?;
3025 self.define(*handle, RuntimeValue::TaskHandle(tid));
3026 Ok(ControlFlow::Continue)
3027 }
3028 Stmt::StopTask { handle } => {
3029 let tid = self.eval_task(handle).await?;
3030 self.yield_request(BlockingRequest::Abort(tid)).await;
3031 Ok(ControlFlow::Continue)
3032 }
3033 Stmt::TrySendPipe { value, pipe, result } => {
3034 let ch = self.eval_chan(pipe).await?;
3035 let val = self.evaluate_expr(value).await?;
3036 let payload = marshal::materialize(&val)
3037 .map_err(|e| format!("cannot send value through a channel: {:?}", e))?;
3038 let resume = self.yield_request(BlockingRequest::TrySend(ch, payload)).await;
3039 let ok = matches!(resume.into_payload(), RtPayload::Bool(true));
3040 if let Some(res) = result {
3041 self.define(*res, RuntimeValue::Bool(ok));
3042 }
3043 Ok(ControlFlow::Continue)
3044 }
3045 Stmt::TryReceivePipe { var, pipe } => {
3046 let ch = self.eval_chan(pipe).await?;
3047 let resume = self.yield_request(BlockingRequest::TryRecv(ch)).await;
3048 let value = marshal::rebuild(resume.into_payload());
3049 self.define(*var, value);
3050 Ok(ControlFlow::Continue)
3051 }
3052 Stmt::Select { branches } => {
3053 use crate::ast::stmt::SelectBranch;
3054 let mut arms = Vec::with_capacity(branches.len());
3057 for branch in branches.iter() {
3058 match branch {
3059 SelectBranch::Receive { pipe, .. } => {
3060 let ch = self.eval_chan(pipe).await?;
3061 arms.push(SelectArm::Recv(ch));
3062 }
3063 SelectBranch::Timeout { milliseconds, .. } => {
3064 let ticks = self.eval_select_timeout_ticks(milliseconds).await?;
3065 arms.push(SelectArm::Timeout(ticks));
3066 }
3067 }
3068 }
3069 let resume = self.yield_request(BlockingRequest::Select(arms)).await;
3070 let (arm, payload) = resume
3071 .into_select()
3072 .ok_or_else(|| "scheduler did not resolve the select".to_string())?;
3073 match &branches[arm] {
3074 SelectBranch::Receive { var, body, .. } => {
3075 self.define(*var, marshal::rebuild(payload));
3076 self.execute_block(*body).await
3077 }
3078 SelectBranch::Timeout { body, .. } => self.execute_block(*body).await,
3079 }
3080 }
3081
3082 Stmt::Escape { .. } => {
3084 Err(
3085 "Escape blocks contain raw Rust code and cannot be interpreted. \
3086 Use `largo build` or `largo run` to compile and run this program."
3087 .to_string()
3088 )
3089 }
3090
3091 Stmt::Require { .. } => {
3093 Ok(ControlFlow::Continue)
3094 }
3095
3096 Stmt::Theorem(_) | Stmt::Definition(_) | Stmt::Axiom(_) | Stmt::Theory(_) => Ok(ControlFlow::Continue),
3099 }
3100 }
3101
3102 #[async_recursion(?Send)]
3105 async fn execute_block(&mut self, block: Block<'a>) -> Result<ControlFlow, String> {
3106 self.push_scope();
3107 for stmt in block.iter() {
3108 match self.execute_stmt(stmt).await? {
3109 ControlFlow::Continue => {}
3110 flow => {
3111 self.pop_scope();
3112 return Ok(flow);
3113 }
3114 }
3115 }
3116 self.pop_scope();
3117 Ok(ControlFlow::Continue)
3118 }
3119
3120 fn inspect_unhandled(&self, _target: &RuntimeValue) -> String {
3126 "Inspect has no arm for the value and no Otherwise (matches must be exhaustive)".to_string()
3127 }
3128
3129 #[async_recursion(?Send)]
3133 async fn execute_inspect(&mut self, target: &RuntimeValue, arms: &[MatchArm<'a>]) -> Result<ControlFlow, String> {
3134 for arm in arms {
3135 if arm.variant.is_none() {
3137 let flow = self.execute_block(arm.body).await?;
3138 return Ok(flow);
3139 }
3140
3141 match target {
3142 RuntimeValue::Struct(s) => {
3143 if let Some(variant) = arm.variant {
3144 let variant_name = self.ctx.interner.resolve(variant);
3145 if s.type_name == variant_name {
3146 self.push_scope();
3147 for (field_name, binding_name) in &arm.bindings {
3148 let field_str = self.ctx.interner.resolve(*field_name);
3149 if let Some(val) = s.fields.get(field_str) {
3150 self.define(*binding_name, val.clone());
3151 }
3152 }
3153 let result = self.execute_block(arm.body).await;
3154 self.pop_scope();
3155 let flow = result?;
3156 return Ok(flow);
3157 }
3158 }
3159 }
3160
3161 RuntimeValue::Inductive(ind) => {
3162 if let Some(variant) = arm.variant {
3163 let variant_name = self.ctx.interner.resolve(variant);
3164 if ind.constructor == variant_name {
3165 self.push_scope();
3166 for (i, (_, binding_name)) in arm.bindings.iter().enumerate() {
3167 if i < ind.args.len() {
3168 self.define(*binding_name, ind.args[i].clone());
3169 }
3170 }
3171 let result = self.execute_block(arm.body).await;
3172 self.pop_scope();
3173 let flow = result?;
3174 return Ok(flow);
3175 }
3176 }
3177 }
3178
3179 _ => {}
3180 }
3181 }
3182 Err(self.inspect_unhandled(target))
3183 }
3184
3185 fn peer_topic_of(value: &RuntimeValue) -> Result<String, String> {
3189 match value {
3190 RuntimeValue::Peer(topic) => Ok((**topic).clone()),
3191 RuntimeValue::Text(s) => Ok(logicaffeine_system::addr::canonical_topic(s)),
3192 other => Err(format!(
3193 "Send/Await expects a PeerAgent or address string, got {}",
3194 other.type_name()
3195 )),
3196 }
3197 }
3198
3199 fn build_wire_type_registry(&self) -> crate::concurrency::marshal::WireTypeRegistry {
3203 let schemas: Vec<(String, Vec<String>)> = self
3204 .ctx
3205 .struct_defs
3206 .iter()
3207 .map(|(name_sym, fields)| {
3208 let type_name = self.ctx.interner.resolve(*name_sym).to_string();
3209 let field_names = fields
3210 .iter()
3211 .map(|(fname, _ty, _public)| self.ctx.interner.resolve(*fname).to_string())
3212 .collect();
3213 (type_name, field_names)
3214 })
3215 .collect();
3216 let enums: Vec<(String, Vec<String>)> = self
3217 .ctx
3218 .enum_defs
3219 .iter()
3220 .map(|(name_sym, ctors)| {
3221 let type_name = self.ctx.interner.resolve(*name_sym).to_string();
3222 let ctor_names = ctors.iter().map(|c| self.ctx.interner.resolve(*c).to_string()).collect();
3223 (type_name, ctor_names)
3224 })
3225 .collect();
3226 crate::concurrency::marshal::WireTypeRegistry::new(schemas).with_enums(enums)
3227 }
3228
3229 async fn await_message_from(&mut self, want: &str, view: bool) -> Result<RuntimeValue, String> {
3234 loop {
3235 if let Some(v) = self.netbox.try_take_message(want, view) {
3236 return Ok(v);
3237 }
3238 let registry = self.build_wire_type_registry();
3239 self.netbox.drain(registry);
3240 if let Some(v) = self.netbox.try_take_message(want, view) {
3241 return Ok(v);
3242 }
3243 Self::poll_tick().await;
3244 }
3245 }
3246
3247 async fn await_stream_from(&mut self, want: &str) -> Result<RuntimeValue, String> {
3250 loop {
3251 if let Some(v) = self.netbox.try_take_stream(want) {
3252 return Ok(v);
3253 }
3254 let registry = self.build_wire_type_registry();
3255 self.netbox.drain(registry);
3256 if let Some(v) = self.netbox.try_take_stream(want) {
3257 return Ok(v);
3258 }
3259 Self::poll_tick().await;
3260 }
3261 }
3262
3263 async fn poll_tick() {
3266 #[cfg(not(target_arch = "wasm32"))]
3267 logicaffeine_system::tokio::time::sleep(std::time::Duration::from_millis(2)).await;
3268 #[cfg(target_arch = "wasm32")]
3269 gloo_timers::future::TimeoutFuture::new(2).await;
3270 }
3271
3272 #[async_recursion(?Send)]
3275 async fn evaluate_expr(&mut self, expr: &Expr<'a>) -> Result<RuntimeValue, String> {
3276 match expr {
3277 Expr::Literal(lit) => self.evaluate_literal(lit),
3278
3279 Expr::Identifier(sym) => {
3280 let name = self.ctx.interner.resolve(*sym);
3281 match name {
3283 "today" => {
3284 return Ok(crate::semantics::temporal::today());
3285 }
3286 "now" => {
3287 return Ok(crate::semantics::temporal::now());
3288 }
3289 _ => {}
3290 }
3291 self.lookup(*sym).cloned()
3292 }
3293
3294 Expr::BinaryOp { op, left, right } => {
3295 match op {
3296 BinaryOpKind::And => {
3297 let left_val = self.evaluate_expr(left).await?;
3298 if !left_val.is_truthy() {
3299 return Ok(RuntimeValue::Bool(false));
3300 }
3301 let right_val = self.evaluate_expr(right).await?;
3302 Ok(RuntimeValue::Bool(right_val.is_truthy()))
3303 }
3304 BinaryOpKind::Or => {
3305 let left_val = self.evaluate_expr(left).await?;
3306 if left_val.is_truthy() {
3307 return Ok(RuntimeValue::Bool(true));
3308 }
3309 let right_val = self.evaluate_expr(right).await?;
3310 Ok(RuntimeValue::Bool(right_val.is_truthy()))
3311 }
3312 _ => {
3313 let left_val = self.evaluate_expr(left).await?;
3314 let right_val = self.evaluate_expr(right).await?;
3315 self.apply_binary_op(*op, left_val, right_val)
3316 }
3317 }
3318 }
3319
3320 Expr::Call { function, args } => {
3321 self.call_function(*function, args).await
3322 }
3323
3324 Expr::Index { collection, index } => {
3325 let coll_val = self.evaluate_expr(collection).await?;
3326 let idx_val = self.evaluate_expr(index).await?;
3327 crate::semantics::collections::index_get(&coll_val, &idx_val)
3328 }
3329
3330 Expr::Slice { collection, start, end } => {
3331 let coll_val = self.evaluate_expr(collection).await?;
3332 let start_val = self.evaluate_expr(start).await?;
3333 let end_val = self.evaluate_expr(end).await?;
3334 crate::semantics::collections::slice(&coll_val, &start_val, &end_val)
3335 }
3336
3337 Expr::Copy { expr: inner } => {
3338 let val = self.evaluate_expr(inner).await?;
3339 Ok(val.deep_clone())
3340 }
3341
3342 Expr::Give { value } => {
3343 self.evaluate_expr(value).await
3345 }
3346
3347 Expr::Length { collection } => {
3348 let coll_val = self.evaluate_expr(collection).await?;
3349 crate::semantics::collections::length_of(&coll_val)
3350 }
3351
3352 Expr::Contains { collection, value } => {
3353 let coll_val = self.evaluate_expr(collection).await?;
3354 let val = self.evaluate_expr(value).await?;
3355 crate::semantics::collections::contains(&coll_val, &val)
3356 }
3357
3358 Expr::Union { left, right } => {
3359 let left_val = self.evaluate_expr(left).await?;
3360 let right_val = self.evaluate_expr(right).await?;
3361 crate::semantics::collections::union(&left_val, &right_val)
3362 }
3363
3364 Expr::Intersection { left, right } => {
3365 let left_val = self.evaluate_expr(left).await?;
3366 let right_val = self.evaluate_expr(right).await?;
3367 crate::semantics::collections::intersection(&left_val, &right_val)
3368 }
3369
3370 Expr::List(items) => {
3371 let mut values = Vec::with_capacity(items.len());
3372 for e in items.iter() {
3373 values.push(self.evaluate_expr(e).await?);
3374 }
3375 Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::from_values(values)))))
3376 }
3377
3378 Expr::Tuple(items) => {
3379 let mut values = Vec::with_capacity(items.len());
3380 for e in items.iter() {
3381 values.push(self.evaluate_expr(e).await?);
3382 }
3383 Ok(RuntimeValue::Tuple(Rc::new(values)))
3384 }
3385
3386 Expr::Range { start, end } => {
3387 let start_val = self.evaluate_expr(start).await?;
3388 let end_val = self.evaluate_expr(end).await?;
3389 crate::semantics::collections::range(&start_val, &end_val)
3390 }
3391
3392 Expr::FieldAccess { object, field } => {
3393 let obj_val = self.evaluate_expr(object).await?;
3394 match &obj_val {
3395 RuntimeValue::Struct(s) => {
3396 let field_name = self.ctx.interner.resolve(*field);
3397 s.fields.get(field_name).cloned()
3398 .ok_or_else(|| format!("Field '{}' not found", field_name))
3399 }
3400 _ => Err(format!("Cannot access field on {}", obj_val.type_name())),
3401 }
3402 }
3403
3404 Expr::New { type_name, init_fields, .. } => {
3405 let name = self.ctx.interner.resolve(*type_name).to_string();
3406
3407 if name == "Seq" || name == "List" {
3408 return Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::Ints(Vec::new())))));
3409 }
3410
3411 if name == "Set" || name == "HashSet" {
3412 return Ok(RuntimeValue::Set(Rc::new(RefCell::new(vec![]))));
3413 }
3414
3415 if name == "Map" || name == "HashMap" {
3416 return Ok(RuntimeValue::Map(Rc::new(RefCell::new(MapStorage::default()))));
3417 }
3418
3419 let mut fields = HashMap::new();
3420 for (field_sym, field_expr) in init_fields {
3421 let field_name = self.ctx.interner.resolve(*field_sym).to_string();
3422 let field_val = self.evaluate_expr(field_expr).await?;
3423 fields.insert(field_name, field_val);
3424 }
3425
3426 if let Some(def) = self.ctx.struct_defs.get(type_name) {
3427 for (field_sym, type_sym, _) in def {
3428 let field_name = self.ctx.interner.resolve(*field_sym).to_string();
3429 if !fields.contains_key(&field_name) {
3430 let type_name_str = self.ctx.interner.resolve(*type_sym).to_string();
3431 let default = match type_name_str.as_str() {
3432 "Int" => RuntimeValue::Int(0),
3433 "Float" => RuntimeValue::Float(0.0),
3434 "Bool" => RuntimeValue::Bool(false),
3435 "Text" | "String" => RuntimeValue::Text(Rc::new(String::new())),
3436 "Char" => RuntimeValue::Char('\0'),
3437 "Byte" => RuntimeValue::Int(0),
3438 "Seq" | "List" => RuntimeValue::List(Rc::new(RefCell::new(ListRepr::Ints(Vec::new())))),
3439 "Set" | "HashSet" => RuntimeValue::Set(Rc::new(RefCell::new(vec![]))),
3440 "Map" | "HashMap" => RuntimeValue::Map(Rc::new(RefCell::new(MapStorage::default()))),
3441 "SharedSet" | "ORSet" | "SharedSet_AddWins" =>
3444 RuntimeValue::Crdt(Rc::new(RefCell::new(
3445 crate::semantics::crdt::CrdtValue::new_set(
3446 crate::semantics::crdt::next_replica_id())))),
3447 "SharedSet_RemoveWins" =>
3448 RuntimeValue::Crdt(Rc::new(RefCell::new(
3449 crate::semantics::crdt::CrdtValue::new_set_remove_wins(
3450 crate::semantics::crdt::next_replica_id())))),
3451 "SharedSequence" | "RGA" | "SharedSequence_YATA" | "CollaborativeSequence" =>
3452 RuntimeValue::Crdt(Rc::new(RefCell::new(
3453 crate::semantics::crdt::CrdtValue::new_seq(
3454 crate::semantics::crdt::next_replica_id())))),
3455 _ => RuntimeValue::Nothing,
3456 };
3457 fields.insert(field_name, default);
3458 }
3459 }
3460 }
3461
3462 Ok(RuntimeValue::Struct(Box::new(StructValue { type_name: name, fields })))
3463 }
3464
3465 Expr::NewVariant { enum_name, variant, fields } => {
3468 let inductive_type = self.ctx.interner.resolve(*enum_name).to_string();
3469 let constructor = self.ctx.interner.resolve(*variant).to_string();
3470
3471 let mut args = Vec::new();
3472 for (_, field_expr) in fields {
3473 let field_val = self.evaluate_expr(field_expr).await?;
3474 args.push(field_val);
3475 }
3476
3477 Ok(RuntimeValue::Inductive(Box::new(InductiveValue {
3478 inductive_type,
3479 constructor,
3480 args,
3481 })))
3482 }
3483
3484 Expr::ManifestOf { .. } => {
3485 Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::Ints(Vec::new())))))
3486 }
3487
3488 Expr::ChunkAt { .. } => {
3489 Ok(RuntimeValue::Nothing)
3490 }
3491
3492 Expr::WithCapacity { value, .. } => {
3493 self.evaluate_expr(value).await
3494 }
3495
3496 Expr::OptionSome { value } => {
3497 self.evaluate_expr(value).await
3498 }
3499
3500 Expr::OptionNone => {
3501 Ok(RuntimeValue::Nothing)
3502 }
3503
3504 Expr::Not { operand } => {
3505 let val = self.evaluate_expr(operand).await?;
3506 crate::semantics::arith::not_value(val)
3507 }
3508
3509 Expr::InterpolatedString(parts) => {
3510 let mut result = String::new();
3511 for part in parts {
3512 match part {
3513 crate::ast::stmt::StringPart::Literal(sym) => {
3514 result.push_str(self.ctx.interner.resolve(*sym));
3515 }
3516 crate::ast::stmt::StringPart::Expr { value, format_spec, debug } => {
3517 let val = self.evaluate_expr(value).await?;
3518 if *debug {
3519 let prefix = match value {
3520 Expr::Identifier(sym) => self.ctx.interner.resolve(*sym).to_string(),
3521 _ => "expr".to_string(),
3522 };
3523 result.push_str(&prefix);
3524 result.push('=');
3525 }
3526 if let Some(spec_sym) = format_spec {
3527 let spec = self.ctx.interner.resolve(*spec_sym);
3528 result.push_str(&apply_format_spec(&val, spec));
3529 } else {
3530 result.push_str(&val.to_display_string());
3531 }
3532 }
3533 }
3534 }
3535 Ok(RuntimeValue::Text(Rc::new(result)))
3536 }
3537
3538 Expr::Escape { .. } => {
3539 Err("Escape expressions contain raw Rust code and cannot be interpreted. \
3540 Use `largo build` or `largo run` to compile and run this program.".to_string())
3541 }
3542
3543 Expr::Closure { params, body, .. } => {
3544 let free_vars = self.collect_free_vars_in_closure(params, body);
3545 let mut captured_env = HashMap::new();
3546 for sym in &free_vars {
3547 if let Some(val) = self.task.env.lookup(*sym) {
3548 captured_env.insert(*sym, val.deep_clone());
3549 }
3550 }
3551
3552 let body_index = self.ctx.closure_bodies.len();
3553 match body {
3554 ClosureBody::Expression(expr) => {
3555 self.ctx.closure_bodies.push(ClosureBodyRef::Expression(expr));
3556 }
3557 ClosureBody::Block(block) => {
3558 self.ctx.closure_bodies.push(ClosureBodyRef::Block(block));
3559 }
3560 }
3561
3562 let param_names: Vec<Symbol> = params.iter().map(|(name, _)| *name).collect();
3563
3564 Ok(RuntimeValue::Function(Box::new(ClosureValue {
3565 body_index,
3566 captured_env,
3567 param_names,
3568 generated: None,
3569 })))
3570 }
3571
3572 Expr::CallExpr { callee, args } => {
3573 let callee_val = self.evaluate_expr(callee).await?;
3574 if let RuntimeValue::Function(closure) = callee_val {
3575 let mut arg_values = Vec::with_capacity(args.len());
3576 for arg in args.iter() {
3577 arg_values.push(self.evaluate_expr(arg).await?);
3578 }
3579 self.call_closure_value(&closure, arg_values).await
3580 } else {
3581 Err(format!("Cannot call value of type {}", callee_val.type_name()))
3582 }
3583 }
3584 }
3585 }
3586
3587 fn evaluate_literal(&self, lit: &Literal) -> Result<RuntimeValue, String> {
3589 match lit {
3590 Literal::Number(n) => Ok(RuntimeValue::Int(*n)),
3591 Literal::Float(f) => Ok(RuntimeValue::Float(*f)),
3592 Literal::Text(sym) => Ok(RuntimeValue::Text(Rc::new(self.ctx.interner.resolve(*sym).to_string()))),
3593 Literal::Boolean(b) => Ok(RuntimeValue::Bool(*b)),
3594 Literal::Nothing => Ok(RuntimeValue::Nothing),
3595 Literal::Char(c) => Ok(RuntimeValue::Char(*c)),
3596 Literal::Duration(nanos) => Ok(RuntimeValue::Duration(*nanos)),
3597 Literal::Date(days) => Ok(RuntimeValue::Date(*days)),
3598 Literal::Moment(nanos) => Ok(RuntimeValue::Moment(*nanos)),
3599 Literal::Span { months, days } => Ok(RuntimeValue::Span { months: *months, days: *days }),
3600 Literal::Time(nanos) => Ok(RuntimeValue::Time(*nanos)),
3601 }
3602 }
3603
3604 fn apply_binary_op(&self, op: BinaryOpKind, left: RuntimeValue, right: RuntimeValue) -> Result<RuntimeValue, String> {
3606 crate::semantics::arith::binary_op(op, left, right)
3607 }
3608
3609 pub fn values_equal_pub(&self, left: &RuntimeValue, right: &RuntimeValue) -> bool {
3610 self.values_equal(left, right)
3611 }
3612
3613 fn values_equal(&self, left: &RuntimeValue, right: &RuntimeValue) -> bool {
3614 crate::semantics::compare::values_equal(left, right)
3615 }
3616
3617 #[async_recursion(?Send)]
3619 async fn call_function(&mut self, function: Symbol, args: &[&'async_recursion Expr<'a>]) -> Result<RuntimeValue, String> {
3620 let func_sym = Some(function);
3622 if func_sym == self.ctx.sym_show {
3623 for arg in args {
3624 let val = self.evaluate_expr(arg).await?;
3625 self.emit_output(val.to_display_string());
3626 }
3627 return Ok(RuntimeValue::Nothing);
3628 } else if func_sym == self.ctx.sym_args {
3629 let items: Vec<RuntimeValue> = self
3633 .ctx
3634 .program_args
3635 .iter()
3636 .map(|s| RuntimeValue::Text(Rc::new(s.clone())))
3637 .collect();
3638 return Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::from_values(items)))));
3639 } else if let Some(id) = self.builtin_id(function) {
3640 crate::semantics::builtins::check_arity(id, args.len())?;
3642 let vals = if id == crate::semantics::builtins::BuiltinId::Format {
3644 match args.first() {
3645 Some(a) => vec![self.evaluate_expr(a).await?],
3646 None => Vec::new(),
3647 }
3648 } else {
3649 let mut v = Vec::with_capacity(args.len());
3650 for arg in args {
3651 v.push(self.evaluate_expr(arg).await?);
3652 }
3653 v
3654 };
3655 return crate::semantics::builtins::call_builtin(id, vals);
3656 }
3657
3658 if let Some(func) = self.ctx.functions.get(&function) {
3660 let param_count = func.params.len();
3661 let body = func.body;
3662
3663 if args.len() != param_count {
3664 return Err(format!(
3665 "Function {} expects {} arguments, got {}",
3666 self.ctx.interner.resolve(function),
3667 param_count,
3668 args.len()
3669 ));
3670 }
3671
3672 let mut arg_values = Vec::with_capacity(param_count);
3674 for arg in args {
3675 arg_values.push(self.evaluate_expr(arg).await?);
3676 }
3677
3678 if self.task.call_depth >= crate::semantics::MAX_CALL_DEPTH {
3681 return Err(crate::semantics::CALL_DEPTH_ERR.to_string());
3682 }
3683 self.task.call_depth += 1;
3684 self.task.env.push_frame();
3685 for i in 0..param_count {
3686 let param_name = self.ctx.functions[&function].params[i].0;
3687 self.task.env.define(param_name, std::mem::replace(&mut arg_values[i], RuntimeValue::Nothing));
3688 }
3689
3690 let prev_tco = self.task.tco_fn_async.replace(function);
3694 let prev_repeat = std::mem::replace(&mut self.task.repeat_depth_async, 0);
3695 let mut return_value = RuntimeValue::Nothing;
3696 let mut body_err = None;
3697 'tco: loop {
3698 self.task.pending_tail_call_async = None;
3699 let mut idx = 0;
3700 while idx < body.len() {
3701 if idx + 1 < body.len() {
3702 if let Some(call_args) = crate::tail_call::tail_pair_args(
3703 &body[idx],
3704 &body[idx + 1],
3705 function,
3706 param_count,
3707 ) {
3708 let mut vals = Vec::with_capacity(call_args.len());
3709 let mut perr = None;
3710 for a in call_args {
3711 match self.evaluate_expr(a).await {
3712 Ok(v) => vals.push(v),
3713 Err(e) => {
3714 perr = Some(e);
3715 break;
3716 }
3717 }
3718 }
3719 match perr {
3720 Some(e) => body_err = Some(e),
3721 None => self.task.pending_tail_call_async = Some(vals),
3722 }
3723 break;
3724 }
3725 }
3726 match self.execute_stmt(&body[idx]).await {
3727 Ok(ControlFlow::Return(val)) => {
3728 return_value = val;
3729 break;
3730 }
3731 Ok(ControlFlow::Break) => break,
3732 Ok(ControlFlow::Continue) => {}
3733 Err(e) => {
3734 body_err = Some(e);
3735 break;
3736 }
3737 }
3738 idx += 1;
3739 }
3740 if body_err.is_some() {
3741 break 'tco;
3742 }
3743 match self.task.pending_tail_call_async.take() {
3744 Some(new_args) => {
3745 self.task.env.pop_frame();
3746 self.task.env.push_frame();
3747 for (i, v) in new_args.into_iter().enumerate() {
3748 let param_name = self.ctx.functions[&function].params[i].0;
3749 self.task.env.define(param_name, v);
3750 }
3751 continue 'tco;
3752 }
3753 None => break 'tco,
3754 }
3755 }
3756 self.task.repeat_depth_async = prev_repeat;
3757 self.task.tco_fn_async = prev_tco;
3758
3759 self.task.env.pop_frame();
3760 self.task.call_depth -= 1;
3761 match body_err {
3762 Some(e) => Err(e),
3763 None => Ok(return_value),
3764 }
3765 } else {
3766 let maybe_closure = self.task.env.lookup(function)
3768 .and_then(|v| if let RuntimeValue::Function(c) = v { Some((**c).clone()) } else { None });
3769
3770 if let Some(closure) = maybe_closure {
3771 let mut arg_values = Vec::with_capacity(args.len());
3772 for arg in args {
3773 arg_values.push(self.evaluate_expr(arg).await?);
3774 }
3775 self.call_closure_value(&closure, arg_values).await
3776 } else {
3777 Err(format!("Unknown function: {}", self.ctx.interner.resolve(function)))
3778 }
3779 }
3780 }
3781
3782 fn yield_request(&self, req: BlockingRequest<'a>) -> YieldFuture<'a> {
3787 let ys = self
3788 .yield_state
3789 .clone()
3790 .expect("concurrency op executed outside a scheduler context");
3791 ys.borrow_mut().request = Some(req);
3792 YieldFuture::new(ys)
3793 }
3794
3795 async fn eval_chan(&mut self, expr: &Expr<'a>) -> Result<ChanId, String> {
3797 match self.evaluate_expr(expr).await? {
3798 RuntimeValue::Chan(id) => Ok(id),
3799 other => Err(format!("expected a channel, found {}", other.type_name())),
3800 }
3801 }
3802
3803 async fn eval_task(&mut self, expr: &Expr<'a>) -> Result<TaskId, String> {
3805 match self.evaluate_expr(expr).await? {
3806 RuntimeValue::TaskHandle(id) => Ok(id),
3807 other => Err(format!("expected a task handle, found {}", other.type_name())),
3808 }
3809 }
3810
3811 async fn eval_select_timeout_ticks(&mut self, expr: &Expr<'a>) -> Result<u64, String> {
3816 let ticks = match self.evaluate_expr(expr).await? {
3817 RuntimeValue::Int(n) => n.max(0) as u64,
3818 RuntimeValue::Duration(d) => d.max(0) as u64,
3819 RuntimeValue::Span { months, days } => {
3820 (((months as i64) * 30 + days as i64) * 86_400).max(0) as u64
3821 }
3822 other => {
3823 return Err(format!(
3824 "select timeout must be a number or duration, found {}",
3825 other.type_name()
3826 ))
3827 }
3828 };
3829 Ok(ticks)
3830 }
3831
3832 fn spawn_child_task(
3835 &self,
3836 function: Symbol,
3837 args: Vec<RuntimeValue>,
3838 ) -> Box<dyn logicaffeine_runtime::Task<'a> + 'a> {
3839 let ys: Yield<'a> = Rc::new(RefCell::new(YieldState::new()));
3840 let mut child = Interpreter {
3841 ctx: self.ctx.clone(),
3842 task: TaskState::new(),
3843 output: Vec::new(),
3844 yield_state: Some(ys.clone()),
3845 netbox: crate::concurrency::net_inbox::NetInbox::new(),
3846 };
3847 let fut = Box::pin(async move {
3848 child.call_function_with_values(function, args).await.map(|_| ())
3849 });
3850 Box::new(InterpreterTask::new(fut, ys, None))
3851 }
3852
3853 #[async_recursion(?Send)]
3854 async fn call_function_with_values(&mut self, function: Symbol, mut args: Vec<RuntimeValue>) -> Result<RuntimeValue, String> {
3855 if Some(function) == self.ctx.sym_show {
3857 for val in args {
3858 self.emit_output(val.to_display_string());
3859 }
3860 return Ok(RuntimeValue::Nothing);
3861 }
3862
3863 if let Some(func) = self.ctx.functions.get(&function) {
3864 let param_count = func.params.len();
3865 let body = func.body;
3866
3867 if args.len() != param_count {
3868 return Err(format!(
3869 "Function {} expects {} arguments, got {}",
3870 self.ctx.interner.resolve(function), param_count, args.len()
3871 ));
3872 }
3873
3874 if self.task.call_depth >= crate::semantics::MAX_CALL_DEPTH {
3875 return Err(crate::semantics::CALL_DEPTH_ERR.to_string());
3876 }
3877 self.task.call_depth += 1;
3878 self.task.env.push_frame();
3879 for i in 0..param_count {
3880 let param_name = self.ctx.functions[&function].params[i].0;
3881 self.task.env.define(param_name, std::mem::replace(&mut args[i], RuntimeValue::Nothing));
3882 }
3883
3884 let prev_tco = self.task.tco_fn_async.replace(function);
3887 let prev_repeat = std::mem::replace(&mut self.task.repeat_depth_async, 0);
3888 let mut return_value = RuntimeValue::Nothing;
3889 let mut body_err = None;
3890 'tco: loop {
3891 self.task.pending_tail_call_async = None;
3892 let mut idx = 0;
3893 while idx < body.len() {
3894 if idx + 1 < body.len() {
3895 if let Some(call_args) = crate::tail_call::tail_pair_args(
3896 &body[idx],
3897 &body[idx + 1],
3898 function,
3899 param_count,
3900 ) {
3901 let mut vals = Vec::with_capacity(call_args.len());
3902 let mut perr = None;
3903 for a in call_args {
3904 match self.evaluate_expr(a).await {
3905 Ok(v) => vals.push(v),
3906 Err(e) => {
3907 perr = Some(e);
3908 break;
3909 }
3910 }
3911 }
3912 match perr {
3913 Some(e) => body_err = Some(e),
3914 None => self.task.pending_tail_call_async = Some(vals),
3915 }
3916 break;
3917 }
3918 }
3919 match self.execute_stmt(&body[idx]).await {
3920 Ok(ControlFlow::Return(val)) => {
3921 return_value = val;
3922 break;
3923 }
3924 Ok(ControlFlow::Break) => break,
3925 Ok(ControlFlow::Continue) => {}
3926 Err(e) => {
3927 body_err = Some(e);
3928 break;
3929 }
3930 }
3931 idx += 1;
3932 }
3933 if body_err.is_some() {
3934 break 'tco;
3935 }
3936 match self.task.pending_tail_call_async.take() {
3937 Some(new_args) => {
3938 self.task.env.pop_frame();
3939 self.task.env.push_frame();
3940 for (i, v) in new_args.into_iter().enumerate() {
3941 let param_name = self.ctx.functions[&function].params[i].0;
3942 self.task.env.define(param_name, v);
3943 }
3944 continue 'tco;
3945 }
3946 None => break 'tco,
3947 }
3948 }
3949 self.task.repeat_depth_async = prev_repeat;
3950 self.task.tco_fn_async = prev_tco;
3951
3952 self.task.env.pop_frame();
3953 self.task.call_depth -= 1;
3954 match body_err {
3955 Some(e) => Err(e),
3956 None => Ok(return_value),
3957 }
3958 } else {
3959 let maybe_closure = self.task.env.lookup(function)
3960 .and_then(|v| if let RuntimeValue::Function(c) = v { Some((**c).clone()) } else { None });
3961
3962 if let Some(closure) = maybe_closure {
3963 self.call_closure_value(&closure, args).await
3964 } else {
3965 Err(format!("Unknown function: {}", self.ctx.interner.resolve(function)))
3966 }
3967 }
3968 }
3969
3970 fn builtin_id(&self, f: Symbol) -> Option<crate::semantics::builtins::BuiltinId> {
3972 use crate::semantics::builtins::BuiltinId as B;
3973 let s = Some(f);
3974 if s == self.ctx.sym_length {
3975 Some(B::Length)
3976 } else if s == self.ctx.sym_format {
3977 Some(B::Format)
3978 } else if s == self.ctx.sym_parse_int {
3979 Some(B::ParseInt)
3980 } else if s == self.ctx.sym_parse_float {
3981 Some(B::ParseFloat)
3982 } else if s == self.ctx.sym_chr {
3983 Some(B::Chr)
3984 } else if s == self.ctx.sym_abs {
3985 Some(B::Abs)
3986 } else if s == self.ctx.sym_sqrt {
3987 Some(B::Sqrt)
3988 } else if s == self.ctx.sym_min {
3989 Some(B::Min)
3990 } else if s == self.ctx.sym_max {
3991 Some(B::Max)
3992 } else if s == self.ctx.sym_floor {
3993 Some(B::Floor)
3994 } else if s == self.ctx.sym_ceil {
3995 Some(B::Ceil)
3996 } else if s == self.ctx.sym_round {
3997 Some(B::Round)
3998 } else if s == self.ctx.sym_pow {
3999 Some(B::Pow)
4000 } else if s == self.ctx.sym_copy {
4001 Some(B::Copy)
4002 } else if s == self.ctx.sym_count_ones {
4003 Some(B::CountOnes)
4004 } else {
4005 crate::semantics::builtins::builtin_from_name(self.ctx.interner.resolve(f))
4010 }
4011 }
4012
4013 fn push_scope(&mut self) {
4016 self.task.env.push_scope();
4017 }
4018
4019 fn pop_scope(&mut self) {
4020 self.task.env.pop_scope();
4021 }
4022
4023 fn define(&mut self, name: Symbol, value: RuntimeValue) {
4024 self.task.env.define(name, value);
4025 }
4026
4027 fn assign(&mut self, name: Symbol, value: RuntimeValue) -> Result<(), String> {
4028 if self.task.env.assign(name, value) {
4029 Ok(())
4030 } else {
4031 Err(format!("Undefined variable: {}", self.ctx.interner.resolve(name)))
4032 }
4033 }
4034
4035 fn lookup(&self, name: Symbol) -> Result<&RuntimeValue, String> {
4036 self.task.env.lookup(name)
4037 .ok_or_else(|| format!("Undefined variable: {}", self.ctx.interner.resolve(name)))
4038 }
4039
4040 fn is_mutable_param(&self, sym: Symbol) -> bool {
4045 let Some(fn_sym) = self.task.tco_fn_sync.or(self.task.tco_fn_async) else {
4046 return false;
4047 };
4048 let Some(fdef) = self.ctx.functions.get(&fn_sym) else {
4049 return false;
4050 };
4051 fdef.params
4052 .iter()
4053 .any(|(p, ty)| *p == sym && matches!(ty, crate::ast::stmt::TypeExpr::Mutable { .. }))
4054 }
4055
4056 fn ensure_collection_owned(&mut self, sym: Symbol) {
4065 if !crate::semantics::collections::value_semantics_enabled() {
4072 return;
4073 }
4074 if self.is_mutable_param(sym) {
4075 return;
4076 }
4077 let shared = match self.task.env.lookup(sym) {
4078 Some(RuntimeValue::List(rc)) => Rc::strong_count(rc) > 1,
4079 Some(RuntimeValue::Map(rc)) => Rc::strong_count(rc) > 1,
4080 Some(RuntimeValue::Set(rc)) => Rc::strong_count(rc) > 1,
4081 _ => false,
4082 };
4083 if shared {
4084 let owned = self.task.env.lookup(sym).map(|v| v.deep_clone());
4085 if let Some(owned) = owned {
4086 self.task.env.assign(sym, owned);
4087 }
4088 }
4089 }
4090
4091 fn evaluate_policy_condition(
4093 &self,
4094 condition: &PolicyCondition,
4095 subject: &RuntimeValue,
4096 object: Option<&RuntimeValue>,
4097 ) -> bool {
4098 crate::semantics::policy::evaluate_policy_condition(
4099 self.ctx.policy_registry.as_ref(),
4100 self.ctx.interner,
4101 condition,
4102 subject,
4103 object,
4104 )
4105 }
4106
4107 pub fn global_bindings(&self) -> Vec<(String, String, String)> {
4111 let mut rows: Vec<(String, String, String)> = self
4112 .task
4113 .env
4114 .globals
4115 .iter()
4116 .map(|(sym, value)| {
4117 (
4118 self.ctx.interner.resolve(*sym).to_string(),
4119 value.type_name().to_string(),
4120 value.to_display_string(),
4121 )
4122 })
4123 .collect();
4124 rows.sort();
4125 rows
4126 }
4127
4128 pub fn run_sync(&mut self, stmts: &[Stmt<'a>]) -> Result<(), String> {
4135 logicaffeine_base::money::clear_ambient_rates();
4137 for stmt in stmts {
4138 match self.execute_stmt_sync(stmt)? {
4139 ControlFlow::Return(_) => break,
4140 ControlFlow::Break => break,
4141 ControlFlow::Continue => {}
4142 }
4143 }
4144 Ok(())
4145 }
4146
4147 fn execute_stmt_sync(&mut self, stmt: &Stmt<'a>) -> Result<ControlFlow, String> {
4148 match stmt {
4149 Stmt::Let { var, value, .. } => {
4150 let val = self.evaluate_expr_sync(value)?;
4151 self.define(*var, val);
4152 Ok(ControlFlow::Continue)
4153 }
4154
4155 Stmt::Set { target, value } => {
4156 let val = self.evaluate_expr_sync(value)?;
4157 self.assign(*target, val)?;
4158 Ok(ControlFlow::Continue)
4159 }
4160
4161 Stmt::Call { function, args } => {
4162 self.call_function_sync(*function, args)?;
4163 Ok(ControlFlow::Continue)
4164 }
4165
4166 Stmt::If { cond, then_block, else_block } => {
4167 let condition = self.evaluate_expr_sync(cond)?;
4168 if condition.is_truthy() {
4169 let flow = self.execute_block_sync(then_block)?;
4170 if !matches!(flow, ControlFlow::Continue) {
4171 return Ok(flow);
4172 }
4173 } else if let Some(else_stmts) = else_block {
4174 let flow = self.execute_block_sync(else_stmts)?;
4175 if !matches!(flow, ControlFlow::Continue) {
4176 return Ok(flow);
4177 }
4178 }
4179 Ok(ControlFlow::Continue)
4180 }
4181
4182 Stmt::While { cond, body, .. } => {
4183 loop {
4184 let condition = self.evaluate_expr_sync(cond)?;
4185 if !condition.is_truthy() {
4186 break;
4187 }
4188 match self.execute_block_sync(body)? {
4189 ControlFlow::Break => break,
4190 ControlFlow::Return(v) => return Ok(ControlFlow::Return(v)),
4191 ControlFlow::Continue => {}
4192 }
4193 }
4194 Ok(ControlFlow::Continue)
4195 }
4196
4197 Stmt::Repeat { pattern, iterable, body } => {
4198 use crate::ast::stmt::Pattern;
4199
4200 let iter_val = self.evaluate_expr_sync(iterable)?;
4201 let items = crate::semantics::collections::iteration_snapshot(&iter_val)?;
4202
4203 self.push_scope();
4204 self.task.repeat_depth_sync += 1;
4208 for item in items {
4209 match pattern {
4210 Pattern::Identifier(sym) => {
4211 self.define(*sym, item);
4212 }
4213 Pattern::Tuple(syms) => {
4214 if let RuntimeValue::Tuple(ref tuple_vals) = item {
4215 if syms.len() != tuple_vals.len() {
4216 self.task.repeat_depth_sync -= 1;
4217 return Err(format!(
4218 "Cannot bind a {}-tuple to {} names",
4219 tuple_vals.len(),
4220 syms.len()
4221 ));
4222 }
4223 for (sym, val) in syms.iter().zip(tuple_vals.iter()) {
4224 self.define(*sym, val.clone());
4225 }
4226 } else {
4227 self.task.repeat_depth_sync -= 1;
4228 return Err(format!("Expected tuple for pattern, got {}", item.type_name()));
4229 }
4230 }
4231 }
4232
4233 match self.execute_block_sync(body)? {
4234 ControlFlow::Break => break,
4235 ControlFlow::Return(v) => {
4236 self.task.repeat_depth_sync -= 1;
4237 self.pop_scope();
4238 return Ok(ControlFlow::Return(v));
4239 }
4240 ControlFlow::Continue => {}
4241 }
4242 }
4243 self.task.repeat_depth_sync -= 1;
4244 self.pop_scope();
4245 Ok(ControlFlow::Continue)
4246 }
4247
4248 Stmt::Return { value } => {
4249 if let Some(expr) = value {
4255 if let Some(call_args) = self.self_tail_call_args_sync(*expr) {
4256 let mut vals = Vec::with_capacity(call_args.len());
4257 for a in call_args {
4258 vals.push(self.evaluate_expr_sync(a)?);
4259 }
4260 self.task.pending_tail_call = Some(vals);
4261 return Ok(ControlFlow::Return(RuntimeValue::Nothing));
4262 }
4263 }
4264 let ret_val = match value {
4265 Some(expr) => self.evaluate_expr_sync(expr)?,
4266 None => RuntimeValue::Nothing,
4267 };
4268 Ok(ControlFlow::Return(ret_val))
4269 }
4270
4271 Stmt::Break => Ok(ControlFlow::Break),
4272
4273 Stmt::FunctionDef { name, params, body, return_type, .. } => {
4274 let func = FunctionDef {
4275 params: params.clone(),
4276 body: *body,
4277 return_type: *return_type,
4278 };
4279 self.ctx.functions.insert(*name, func);
4280 Ok(ControlFlow::Continue)
4281 }
4282
4283 Stmt::StructDef { name, fields, .. } => {
4284 self.ctx.struct_defs.insert(*name, fields.clone());
4285 Ok(ControlFlow::Continue)
4286 }
4287
4288 Stmt::SetField { object, field, value } => {
4289 let new_val = self.evaluate_expr_sync(value)?;
4290 if let Expr::Identifier(obj_sym) = object {
4291 let mut obj_val = self.lookup(*obj_sym)?.clone();
4292 if let RuntimeValue::Struct(ref mut s) = obj_val {
4293 let field_name = self.ctx.interner.resolve(*field).to_string();
4294 s.fields.insert(field_name, new_val);
4295 self.assign(*obj_sym, obj_val)?;
4296 } else {
4297 return Err(format!("Cannot set field on non-struct value"));
4298 }
4299 } else {
4300 return Err("SetField target must be an identifier".to_string());
4301 }
4302 Ok(ControlFlow::Continue)
4303 }
4304
4305 Stmt::Push { value, collection } => {
4306 let val = self.evaluate_expr_sync(value)?;
4307 if let Expr::Identifier(coll_sym) = collection {
4308 self.ensure_collection_owned(*coll_sym);
4309 let coll_val = self.lookup(*coll_sym)?;
4310 crate::semantics::collections::list_push(&coll_val, val)?;
4311 } else if let Expr::FieldAccess { object, field } = collection {
4312 if let Expr::Identifier(obj_sym) = *object {
4313 let obj_val = self.lookup(*obj_sym)?;
4314 let field_name = self.ctx.interner.resolve(*field);
4315 crate::semantics::collections::push_to_struct_field(&obj_val, field_name, val)?;
4316 } else {
4317 return Err("Push to nested field access not supported".to_string());
4318 }
4319 } else {
4320 let coll_val = self.evaluate_expr_sync(collection)?;
4323 crate::semantics::collections::list_push(&coll_val, val)?;
4324 }
4325 Ok(ControlFlow::Continue)
4326 }
4327
4328 Stmt::Pop { collection, into } => {
4329 if let Expr::Identifier(coll_sym) = collection {
4330 self.ensure_collection_owned(*coll_sym);
4331 let coll_val = self.lookup(*coll_sym)?;
4332 let popped = crate::semantics::collections::list_pop(&coll_val)?;
4333 if let Some(into_var) = into {
4334 self.define(*into_var, popped);
4335 }
4336 } else {
4337 return Err("Pop collection must be an identifier".to_string());
4338 }
4339 Ok(ControlFlow::Continue)
4340 }
4341
4342 Stmt::Add { value, collection } => {
4343 let val = self.evaluate_expr_sync(value)?;
4344 if let Expr::Identifier(coll_sym) = collection {
4345 self.ensure_collection_owned(*coll_sym);
4346 }
4347 let coll_val = self.evaluate_expr_sync(collection)?;
4350 crate::semantics::collections::set_add(&coll_val, val)?;
4351 Ok(ControlFlow::Continue)
4352 }
4353
4354 Stmt::Remove { value, collection } => {
4355 let val = self.evaluate_expr_sync(value)?;
4356 if let Expr::Identifier(coll_sym) = collection {
4357 self.ensure_collection_owned(*coll_sym);
4358 }
4359 let coll_val = self.evaluate_expr_sync(collection)?;
4360 crate::semantics::collections::remove_from(&coll_val, &val)?;
4361 Ok(ControlFlow::Continue)
4362 }
4363
4364 Stmt::SetIndex { collection, index, value } => {
4365 let idx_val = self.evaluate_expr_sync(index)?;
4366 let new_val = self.evaluate_expr_sync(value)?;
4367 if let Expr::Identifier(coll_sym) = collection {
4368 if let RuntimeValue::Text(field) = &idx_val {
4371 let cur = self.lookup(*coll_sym)?.clone();
4372 if let RuntimeValue::Struct(mut s) = cur {
4373 s.fields.insert(field.to_string(), new_val);
4374 self.assign(*coll_sym, RuntimeValue::Struct(s))?;
4375 return Ok(ControlFlow::Continue);
4376 }
4377 }
4378 self.ensure_collection_owned(*coll_sym);
4379 let coll_val = self.lookup(*coll_sym)?;
4380 crate::semantics::collections::index_set(&coll_val, &idx_val, new_val)?;
4381 } else {
4382 let coll_val = self.evaluate_expr_sync(collection)?;
4385 crate::semantics::collections::index_set(&coll_val, &idx_val, new_val)?;
4386 }
4387 Ok(ControlFlow::Continue)
4388 }
4389
4390 Stmt::Splice { body } => {
4391 for s in body.iter() {
4393 let flow = self.execute_stmt_sync(s)?;
4394 if !matches!(flow, ControlFlow::Continue) {
4395 return Ok(flow);
4396 }
4397 }
4398 Ok(ControlFlow::Continue)
4399 }
4400
4401 Stmt::Inspect { target, arms, .. } => {
4402 let target_val = self.evaluate_expr_sync(target)?;
4403 self.execute_inspect_sync(&target_val, arms)
4404 }
4405
4406 Stmt::Zone { name, body, .. } => {
4407 self.push_scope();
4408 self.define(*name, RuntimeValue::Nothing);
4409 let result = self.execute_block_sync(body);
4410 self.pop_scope();
4411 result?;
4412 Ok(ControlFlow::Continue)
4413 }
4414
4415 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
4416 for task in tasks.iter() {
4417 self.execute_stmt_sync(task)?;
4418 }
4419 Ok(ControlFlow::Continue)
4420 }
4421
4422 Stmt::Assert { .. } | Stmt::Trust { .. } => {
4423 Ok(ControlFlow::Continue)
4424 }
4425
4426 Stmt::RuntimeAssert { condition, .. } => {
4427 let val = self.evaluate_expr_sync(condition)?;
4428 if !val.is_truthy() {
4429 return Err("Assertion failed".to_string());
4430 }
4431 Ok(ControlFlow::Continue)
4432 }
4433
4434 Stmt::Give { object, recipient } => {
4435 let obj_val = self.evaluate_expr_sync(object)?;
4436 if let Expr::Identifier(sym) = recipient {
4437 self.call_function_with_values_sync(*sym, vec![obj_val])?;
4438 }
4439 Ok(ControlFlow::Continue)
4440 }
4441
4442 Stmt::Show { object, recipient } => {
4443 let obj_val = self.evaluate_expr_sync(object)?;
4444 if let Expr::Identifier(sym) = recipient {
4445 let name = self.ctx.interner.resolve(*sym);
4446 if name == "show" {
4447 self.emit_output(obj_val.to_display_string());
4448 } else {
4449 self.call_function_with_values_sync(*sym, vec![obj_val])?;
4450 }
4451 }
4452 Ok(ControlFlow::Continue)
4453 }
4454
4455 Stmt::ReadFrom { var, source } => {
4457 match source {
4458 ReadSource::Console => {
4459 self.define(*var, RuntimeValue::Text(Rc::new(String::new())));
4460 Ok(ControlFlow::Continue)
4461 }
4462 ReadSource::File(_) => {
4463 Err("File read requires async execution path".to_string())
4464 }
4465 }
4466 }
4467
4468 Stmt::WriteFile { .. } => {
4469 Err("File write requires async execution path".to_string())
4470 }
4471
4472 Stmt::Spawn { name, .. } => {
4473 self.define(*name, RuntimeValue::Nothing);
4474 Ok(ControlFlow::Continue)
4475 }
4476
4477 Stmt::SendMessage { .. } => {
4478 Err("Send (peer messaging) requires the async execution path".to_string())
4479 }
4480 Stmt::StreamMessage { .. } => {
4481 Err("Stream (batch peer messaging) requires the async execution path".to_string())
4482 }
4483
4484 Stmt::AwaitMessage { .. } => {
4485 Err("Await (peer messaging) requires the async execution path".to_string())
4486 }
4487
4488 Stmt::MergeCrdt { source, target } => {
4489 let source_val = self.evaluate_expr_sync(source)?;
4490 let source_fields = match &source_val {
4491 RuntimeValue::Struct(s) => s.fields.clone(),
4492 _ => return Err("Merge source must be a struct".to_string()),
4493 };
4494
4495 if let Expr::Identifier(target_sym) = target {
4496 let mut target_val = self.lookup(*target_sym)?.clone();
4497
4498 if let RuntimeValue::Struct(ref mut s) = target_val {
4499 for (field_name, source_field_val) in source_fields {
4500 let current = s.fields.get(&field_name)
4501 .cloned()
4502 .unwrap_or(RuntimeValue::Int(0));
4503
4504 let merged =
4505 crate::semantics::arith::crdt_merge_field(¤t, source_field_val);
4506 s.fields.insert(field_name, merged);
4507 }
4508 self.assign(*target_sym, target_val)?;
4509 } else {
4510 return Err("Merge target must be a struct".to_string());
4511 }
4512 } else {
4513 return Err("Merge target must be an identifier".to_string());
4514 }
4515 Ok(ControlFlow::Continue)
4516 }
4517
4518 Stmt::IncreaseCrdt { object, field, amount } => {
4519 let amount_val = self.evaluate_expr_sync(amount)?;
4520 let amount_int = match amount_val {
4521 RuntimeValue::Int(n) => n,
4522 _ => return Err("CRDT increment amount must be an integer".to_string()),
4523 };
4524
4525 if let Expr::Identifier(obj_sym) = object {
4526 let mut obj_val = self.lookup(*obj_sym)?.clone();
4527
4528 if let RuntimeValue::Struct(ref mut s) = obj_val {
4529 let field_name = self.ctx.interner.resolve(*field).to_string();
4530 let current = s.fields.get(&field_name)
4531 .cloned()
4532 .unwrap_or(RuntimeValue::Int(0));
4533
4534 let new_val =
4535 crate::semantics::arith::crdt_counter_bump(current, amount_int, &field_name)?;
4536 s.fields.insert(field_name, new_val);
4537 self.assign(*obj_sym, obj_val)?;
4538 } else {
4539 return Err("Cannot increase field on non-struct value".to_string());
4540 }
4541 } else {
4542 return Err("IncreaseCrdt target must be an identifier".to_string());
4543 }
4544 Ok(ControlFlow::Continue)
4545 }
4546
4547 Stmt::DecreaseCrdt { object, field, amount } => {
4548 let amount_val = self.evaluate_expr_sync(amount)?;
4549 let amount_int = match amount_val {
4550 RuntimeValue::Int(n) => n,
4551 _ => return Err("CRDT decrement amount must be an integer".to_string()),
4552 };
4553
4554 if let Expr::Identifier(obj_sym) = object {
4555 let mut obj_val = self.lookup(*obj_sym)?.clone();
4556
4557 if let RuntimeValue::Struct(ref mut s) = obj_val {
4558 let field_name = self.ctx.interner.resolve(*field).to_string();
4559 let current = s.fields.get(&field_name)
4560 .cloned()
4561 .unwrap_or(RuntimeValue::Int(0));
4562
4563 let new_val = crate::semantics::arith::crdt_counter_bump(
4564 current,
4565 amount_int.wrapping_neg(),
4566 &field_name,
4567 )?;
4568 s.fields.insert(field_name, new_val);
4569 self.assign(*obj_sym, obj_val)?;
4570 } else {
4571 return Err("Cannot decrease field on non-struct value".to_string());
4572 }
4573 } else {
4574 return Err("DecreaseCrdt target must be an identifier".to_string());
4575 }
4576 Ok(ControlFlow::Continue)
4577 }
4578
4579 Stmt::AppendToSequence { sequence, value } => {
4580 let val = self.evaluate_expr_sync(value)?;
4581 let seq_val = self.evaluate_expr_sync(sequence)?;
4582 match &seq_val {
4583 RuntimeValue::Crdt(rc) => rc.borrow_mut().append(&val)?,
4584 RuntimeValue::List(_) => {
4585 crate::semantics::collections::list_push(&seq_val, val)?
4586 }
4587 _ => return Err(format!("Cannot append to {}", seq_val.type_name())),
4588 }
4589 Ok(ControlFlow::Continue)
4590 }
4591
4592 Stmt::ResolveConflict { object, field, value } => {
4593 let val = self.evaluate_expr_sync(value)?;
4594 let obj_val = self.evaluate_expr_sync(object)?;
4595 let field_name = self.ctx.interner.resolve(*field);
4596 if let RuntimeValue::Struct(s) = &obj_val {
4597 if let Some(RuntimeValue::Crdt(rc)) = s.fields.get(field_name) {
4598 rc.borrow_mut().resolve(&val)?;
4599 return Ok(ControlFlow::Continue);
4600 }
4601 }
4602 if let Expr::Identifier(obj_sym) = object {
4603 let mut owner = self.lookup(*obj_sym)?.clone();
4604 if let RuntimeValue::Struct(ref mut s) = owner {
4605 s.fields.insert(field_name.to_string(), val);
4606 self.assign(*obj_sym, owner)?;
4607 return Ok(ControlFlow::Continue);
4608 }
4609 }
4610 Err("Resolve target must be a struct field".to_string())
4611 }
4612
4613 Stmt::Check { subject, predicate, is_capability, object, source_text, .. } => {
4614 let registry = match &self.ctx.policy_registry {
4615 Some(r) => r,
4616 None => return Err("Security Check requires policies. Use compiled Rust or add ## Policy block.".to_string()),
4617 };
4618
4619 let subj_val = self.lookup(*subject)?.clone();
4620 let obj_val = if *is_capability {
4623 match object {
4624 Some(obj_sym) => Some(self.lookup(*obj_sym)?.clone()),
4625 None => None,
4626 }
4627 } else {
4628 None
4629 };
4630 crate::semantics::policy::check_policy(
4631 registry,
4632 self.ctx.interner,
4633 &subj_val,
4634 *predicate,
4635 *is_capability,
4636 obj_val.as_ref(),
4637 source_text,
4638 )?;
4639 Ok(ControlFlow::Continue)
4640 }
4641
4642 Stmt::Listen { .. } | Stmt::ConnectTo { .. } => {
4643 Err("Networking (Connect/Listen) requires the async execution path".to_string())
4644 }
4645 Stmt::LetPeerAgent { var, address } => {
4648 let raw = self.evaluate_expr_sync(address)?.to_display_string();
4649 let topic = logicaffeine_system::addr::canonical_topic(&raw);
4650 self.define(*var, RuntimeValue::Peer(Rc::new(topic)));
4651 Ok(ControlFlow::Continue)
4652 }
4653 Stmt::Sleep { .. } => {
4654 Err("Sleep requires async execution path".to_string())
4655 }
4656 Stmt::Sync { .. } => {
4657 Err("Sync requires the async execution path".to_string())
4658 }
4659 Stmt::Mount { .. } => {
4660 Err("Mount requires async execution path".to_string())
4661 }
4662
4663 Stmt::LaunchTask { .. } |
4664 Stmt::LaunchTaskWithHandle { .. } |
4665 Stmt::CreatePipe { .. } |
4666 Stmt::SendPipe { .. } |
4667 Stmt::ReceivePipe { .. } |
4668 Stmt::TrySendPipe { .. } |
4669 Stmt::TryReceivePipe { .. } |
4670 Stmt::StopTask { .. } |
4671 Stmt::Select { .. } => {
4672 Err("Go-like concurrency (Launch, Pipe, Select) is only supported in compiled mode".to_string())
4673 }
4674
4675 Stmt::Escape { .. } => {
4676 Err(
4677 "Escape blocks contain raw Rust code and cannot be interpreted. \
4678 Use `largo build` or `largo run` to compile and run this program."
4679 .to_string()
4680 )
4681 }
4682
4683 Stmt::Require { .. } => {
4684 Ok(ControlFlow::Continue)
4685 }
4686
4687 Stmt::Theorem(_) | Stmt::Definition(_) | Stmt::Axiom(_) | Stmt::Theory(_) => {
4688 Ok(ControlFlow::Continue)
4689 }
4690 }
4691 }
4692
4693 fn execute_block_sync(&mut self, block: Block<'a>) -> Result<ControlFlow, String> {
4694 self.push_scope();
4695 for stmt in block.iter() {
4696 match self.execute_stmt_sync(stmt)? {
4697 ControlFlow::Continue => {}
4698 flow => {
4699 self.pop_scope();
4700 return Ok(flow);
4701 }
4702 }
4703 }
4704 self.pop_scope();
4705 Ok(ControlFlow::Continue)
4706 }
4707
4708 fn execute_inspect_sync(&mut self, target: &RuntimeValue, arms: &[MatchArm<'a>]) -> Result<ControlFlow, String> {
4709 for arm in arms {
4710 if arm.variant.is_none() {
4711 let flow = self.execute_block_sync(arm.body)?;
4712 return Ok(flow);
4713 }
4714
4715 match target {
4716 RuntimeValue::Struct(s) => {
4717 if let Some(variant) = arm.variant {
4718 let variant_name = self.ctx.interner.resolve(variant);
4719 if s.type_name == variant_name {
4720 self.push_scope();
4721 for (field_name, binding_name) in &arm.bindings {
4722 let field_str = self.ctx.interner.resolve(*field_name);
4723 if let Some(val) = s.fields.get(field_str) {
4724 self.define(*binding_name, val.clone());
4725 }
4726 }
4727 let result = self.execute_block_sync(arm.body);
4728 self.pop_scope();
4729 let flow = result?;
4730 return Ok(flow);
4731 }
4732 }
4733 }
4734
4735 RuntimeValue::Inductive(ind) => {
4736 if let Some(variant) = arm.variant {
4737 let variant_name = self.ctx.interner.resolve(variant);
4738 if ind.constructor == variant_name {
4739 self.push_scope();
4740 for (i, (_, binding_name)) in arm.bindings.iter().enumerate() {
4741 if i < ind.args.len() {
4742 self.define(*binding_name, ind.args[i].clone());
4743 }
4744 }
4745 let result = self.execute_block_sync(arm.body);
4746 self.pop_scope();
4747 let flow = result?;
4748 return Ok(flow);
4749 }
4750 }
4751 }
4752
4753 _ => {}
4754 }
4755 }
4756 Err(self.inspect_unhandled(target))
4757 }
4758
4759 fn evaluate_expr_sync(&mut self, expr: &Expr<'a>) -> Result<RuntimeValue, String> {
4760 match expr {
4761 Expr::Literal(lit) => self.evaluate_literal(lit),
4762
4763 Expr::Identifier(sym) => {
4764 let name = self.ctx.interner.resolve(*sym);
4765 match name {
4767 "today" => {
4768 return Ok(crate::semantics::temporal::today());
4769 }
4770 "now" => {
4771 return Ok(crate::semantics::temporal::now());
4772 }
4773 _ => {}
4774 }
4775 self.lookup(*sym).cloned()
4776 }
4777
4778 Expr::BinaryOp { op, left, right } => {
4779 match op {
4780 BinaryOpKind::And => {
4781 let left_val = self.evaluate_expr_sync(left)?;
4782 if !left_val.is_truthy() {
4783 return Ok(RuntimeValue::Bool(false));
4784 }
4785 let right_val = self.evaluate_expr_sync(right)?;
4786 Ok(RuntimeValue::Bool(right_val.is_truthy()))
4787 }
4788 BinaryOpKind::Or => {
4789 let left_val = self.evaluate_expr_sync(left)?;
4790 if left_val.is_truthy() {
4791 return Ok(RuntimeValue::Bool(true));
4792 }
4793 let right_val = self.evaluate_expr_sync(right)?;
4794 Ok(RuntimeValue::Bool(right_val.is_truthy()))
4795 }
4796 _ => {
4797 let left_val = self.evaluate_expr_sync(left)?;
4798 let right_val = self.evaluate_expr_sync(right)?;
4799 self.apply_binary_op(*op, left_val, right_val)
4800 }
4801 }
4802 }
4803
4804 Expr::Call { function, args } => {
4805 self.call_function_sync(*function, args)
4806 }
4807
4808 Expr::Index { collection, index } => {
4809 let coll_val = self.evaluate_expr_sync(collection)?;
4810 let idx_val = self.evaluate_expr_sync(index)?;
4811 crate::semantics::collections::index_get(&coll_val, &idx_val)
4812 }
4813
4814 Expr::Slice { collection, start, end } => {
4815 let coll_val = self.evaluate_expr_sync(collection)?;
4816 let start_val = self.evaluate_expr_sync(start)?;
4817 let end_val = self.evaluate_expr_sync(end)?;
4818 crate::semantics::collections::slice(&coll_val, &start_val, &end_val)
4819 }
4820
4821 Expr::Copy { expr: inner } => {
4822 let val = self.evaluate_expr_sync(inner)?;
4823 Ok(val.deep_clone())
4824 }
4825
4826 Expr::Give { value } => {
4827 self.evaluate_expr_sync(value)
4828 }
4829
4830 Expr::Length { collection } => {
4831 let coll_val = self.evaluate_expr_sync(collection)?;
4832 crate::semantics::collections::length_of(&coll_val)
4833 }
4834
4835 Expr::Contains { collection, value } => {
4836 let coll_val = self.evaluate_expr_sync(collection)?;
4837 let val = self.evaluate_expr_sync(value)?;
4838 crate::semantics::collections::contains(&coll_val, &val)
4839 }
4840
4841 Expr::Union { left, right } => {
4842 let left_val = self.evaluate_expr_sync(left)?;
4843 let right_val = self.evaluate_expr_sync(right)?;
4844 crate::semantics::collections::union(&left_val, &right_val)
4845 }
4846
4847 Expr::Intersection { left, right } => {
4848 let left_val = self.evaluate_expr_sync(left)?;
4849 let right_val = self.evaluate_expr_sync(right)?;
4850 crate::semantics::collections::intersection(&left_val, &right_val)
4851 }
4852
4853 Expr::List(items) => {
4854 let mut values = Vec::with_capacity(items.len());
4855 for e in items.iter() {
4856 values.push(self.evaluate_expr_sync(e)?);
4857 }
4858 Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::from_values(values)))))
4859 }
4860
4861 Expr::Tuple(items) => {
4862 let mut values = Vec::with_capacity(items.len());
4863 for e in items.iter() {
4864 values.push(self.evaluate_expr_sync(e)?);
4865 }
4866 Ok(RuntimeValue::Tuple(Rc::new(values)))
4867 }
4868
4869 Expr::Range { start, end } => {
4870 let start_val = self.evaluate_expr_sync(start)?;
4871 let end_val = self.evaluate_expr_sync(end)?;
4872 crate::semantics::collections::range(&start_val, &end_val)
4873 }
4874
4875 Expr::FieldAccess { object, field } => {
4876 let obj_val = self.evaluate_expr_sync(object)?;
4877 match &obj_val {
4878 RuntimeValue::Struct(s) => {
4879 let field_name = self.ctx.interner.resolve(*field);
4880 s.fields.get(field_name).cloned()
4881 .ok_or_else(|| format!("Field '{}' not found", field_name))
4882 }
4883 _ => Err(format!("Cannot access field on {}", obj_val.type_name())),
4884 }
4885 }
4886
4887 Expr::New { type_name, init_fields, .. } => {
4888 let name = self.ctx.interner.resolve(*type_name).to_string();
4889
4890 if name == "Seq" || name == "List" {
4891 return Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::Ints(Vec::new())))));
4892 }
4893
4894 if name == "Set" || name == "HashSet" {
4895 return Ok(RuntimeValue::Set(Rc::new(RefCell::new(vec![]))));
4896 }
4897
4898 if name == "Map" || name == "HashMap" {
4899 return Ok(RuntimeValue::Map(Rc::new(RefCell::new(MapStorage::default()))));
4900 }
4901
4902 let mut fields = HashMap::new();
4903 for (field_sym, field_expr) in init_fields {
4904 let field_name = self.ctx.interner.resolve(*field_sym).to_string();
4905 let field_val = self.evaluate_expr_sync(field_expr)?;
4906 fields.insert(field_name, field_val);
4907 }
4908
4909 if let Some(def) = self.ctx.struct_defs.get(type_name) {
4910 for (field_sym, type_sym, _) in def {
4911 let field_name = self.ctx.interner.resolve(*field_sym).to_string();
4912 if !fields.contains_key(&field_name) {
4913 let type_name_str = self.ctx.interner.resolve(*type_sym).to_string();
4914 let default = match type_name_str.as_str() {
4915 "Int" => RuntimeValue::Int(0),
4916 "Float" => RuntimeValue::Float(0.0),
4917 "Bool" => RuntimeValue::Bool(false),
4918 "Text" | "String" => RuntimeValue::Text(Rc::new(String::new())),
4919 "Char" => RuntimeValue::Char('\0'),
4920 "Byte" => RuntimeValue::Int(0),
4921 "Seq" | "List" => RuntimeValue::List(Rc::new(RefCell::new(ListRepr::Ints(Vec::new())))),
4922 "Set" | "HashSet" => RuntimeValue::Set(Rc::new(RefCell::new(vec![]))),
4923 "Map" | "HashMap" => RuntimeValue::Map(Rc::new(RefCell::new(MapStorage::default()))),
4924 "SharedSet" | "ORSet" | "SharedSet_AddWins" =>
4927 RuntimeValue::Crdt(Rc::new(RefCell::new(
4928 crate::semantics::crdt::CrdtValue::new_set(
4929 crate::semantics::crdt::next_replica_id())))),
4930 "SharedSet_RemoveWins" =>
4931 RuntimeValue::Crdt(Rc::new(RefCell::new(
4932 crate::semantics::crdt::CrdtValue::new_set_remove_wins(
4933 crate::semantics::crdt::next_replica_id())))),
4934 "SharedSequence" | "RGA" | "SharedSequence_YATA" | "CollaborativeSequence" =>
4935 RuntimeValue::Crdt(Rc::new(RefCell::new(
4936 crate::semantics::crdt::CrdtValue::new_seq(
4937 crate::semantics::crdt::next_replica_id())))),
4938 _ => RuntimeValue::Nothing,
4939 };
4940 fields.insert(field_name, default);
4941 }
4942 }
4943 }
4944
4945 Ok(RuntimeValue::Struct(Box::new(StructValue { type_name: name, fields })))
4946 }
4947
4948 Expr::NewVariant { enum_name, variant, fields } => {
4949 let inductive_type = self.ctx.interner.resolve(*enum_name).to_string();
4950 let constructor = self.ctx.interner.resolve(*variant).to_string();
4951
4952 let mut args = Vec::new();
4953 for (_, field_expr) in fields {
4954 let field_val = self.evaluate_expr_sync(field_expr)?;
4955 args.push(field_val);
4956 }
4957
4958 Ok(RuntimeValue::Inductive(Box::new(InductiveValue {
4959 inductive_type,
4960 constructor,
4961 args,
4962 })))
4963 }
4964
4965 Expr::ManifestOf { .. } => {
4966 Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::Ints(Vec::new())))))
4967 }
4968
4969 Expr::ChunkAt { .. } => {
4970 Ok(RuntimeValue::Nothing)
4971 }
4972
4973 Expr::WithCapacity { value, .. } => {
4974 self.evaluate_expr_sync(value)
4975 }
4976
4977 Expr::OptionSome { value } => {
4978 self.evaluate_expr_sync(value)
4979 }
4980
4981 Expr::OptionNone => {
4982 Ok(RuntimeValue::Nothing)
4983 }
4984
4985 Expr::Not { operand } => {
4986 let val = self.evaluate_expr_sync(operand)?;
4987 crate::semantics::arith::not_value(val)
4988 }
4989
4990 Expr::InterpolatedString(parts) => {
4991 let mut result = String::new();
4992 for part in parts {
4993 match part {
4994 crate::ast::stmt::StringPart::Literal(sym) => {
4995 result.push_str(self.ctx.interner.resolve(*sym));
4996 }
4997 crate::ast::stmt::StringPart::Expr { value, format_spec, debug } => {
4998 let val = self.evaluate_expr_sync(value)?;
4999 if *debug {
5000 let prefix = match value {
5001 Expr::Identifier(sym) => self.ctx.interner.resolve(*sym).to_string(),
5002 _ => "expr".to_string(),
5003 };
5004 result.push_str(&prefix);
5005 result.push('=');
5006 }
5007 if let Some(spec_sym) = format_spec {
5008 let spec = self.ctx.interner.resolve(*spec_sym);
5009 result.push_str(&apply_format_spec(&val, spec));
5010 } else {
5011 result.push_str(&val.to_display_string());
5012 }
5013 }
5014 }
5015 }
5016 Ok(RuntimeValue::Text(Rc::new(result)))
5017 }
5018
5019 Expr::Escape { .. } => {
5020 Err("Escape expressions contain raw Rust code and cannot be interpreted. \
5021 Use `largo build` or `largo run` to compile and run this program.".to_string())
5022 }
5023
5024 Expr::Closure { params, body, .. } => {
5025 let free_vars = self.collect_free_vars_in_closure(params, body);
5026 let mut captured_env = HashMap::new();
5027 for sym in &free_vars {
5028 if let Some(val) = self.task.env.lookup(*sym) {
5029 captured_env.insert(*sym, val.deep_clone());
5030 }
5031 }
5032
5033 let body_index = self.ctx.closure_bodies.len();
5034 match body {
5035 ClosureBody::Expression(expr) => {
5036 self.ctx.closure_bodies.push(ClosureBodyRef::Expression(expr));
5037 }
5038 ClosureBody::Block(block) => {
5039 self.ctx.closure_bodies.push(ClosureBodyRef::Block(block));
5040 }
5041 }
5042
5043 let param_names: Vec<Symbol> = params.iter().map(|(name, _)| *name).collect();
5044
5045 Ok(RuntimeValue::Function(Box::new(ClosureValue {
5046 body_index,
5047 captured_env,
5048 param_names,
5049 generated: None,
5050 })))
5051 }
5052
5053 Expr::CallExpr { callee, args } => {
5054 let callee_val = self.evaluate_expr_sync(callee)?;
5055 if let RuntimeValue::Function(closure) = callee_val {
5056 let mut arg_values = Vec::with_capacity(args.len());
5057 for arg in args.iter() {
5058 arg_values.push(self.evaluate_expr_sync(arg)?);
5059 }
5060 self.call_closure_value_sync(&closure, arg_values)
5061 } else {
5062 Err(format!("Cannot call value of type {}", callee_val.type_name()))
5063 }
5064 }
5065 }
5066 }
5067
5068 fn self_tail_call_args_sync(&self, expr: &'a Expr<'a>) -> Option<&'a [&'a Expr<'a>]> {
5073 if self.task.repeat_depth_sync != 0 {
5074 return None;
5075 }
5076 let cur = self.task.tco_fn_sync?;
5077 let param_count = self.ctx.functions.get(&cur)?.params.len();
5078 crate::tail_call::direct_self_tail_args(expr, cur, param_count)
5079 }
5080
5081 fn self_tail_call_args_async(&self, expr: &'a Expr<'a>) -> Option<&'a [&'a Expr<'a>]> {
5083 if self.task.repeat_depth_async != 0 {
5084 return None;
5085 }
5086 let cur = self.task.tco_fn_async?;
5087 let param_count = self.ctx.functions.get(&cur)?.params.len();
5088 crate::tail_call::direct_self_tail_args(expr, cur, param_count)
5089 }
5090
5091 fn call_function_sync(&mut self, function: Symbol, args: &[&Expr<'a>]) -> Result<RuntimeValue, String> {
5092 let func_sym = Some(function);
5094 if func_sym == self.ctx.sym_show {
5095 for arg in args {
5096 let val = self.evaluate_expr_sync(arg)?;
5097 self.emit_output(val.to_display_string());
5098 }
5099 return Ok(RuntimeValue::Nothing);
5100 } else if func_sym == self.ctx.sym_args {
5101 let items: Vec<RuntimeValue> = self
5105 .ctx
5106 .program_args
5107 .iter()
5108 .map(|s| RuntimeValue::Text(Rc::new(s.clone())))
5109 .collect();
5110 return Ok(RuntimeValue::List(Rc::new(RefCell::new(ListRepr::from_values(items)))));
5111 } else if let Some(id) = self.builtin_id(function) {
5112 crate::semantics::builtins::check_arity(id, args.len())?;
5114 let vals = if id == crate::semantics::builtins::BuiltinId::Format {
5116 match args.first() {
5117 Some(a) => vec![self.evaluate_expr_sync(a)?],
5118 None => Vec::new(),
5119 }
5120 } else {
5121 let mut v = Vec::with_capacity(args.len());
5122 for arg in args {
5123 v.push(self.evaluate_expr_sync(arg)?);
5124 }
5125 v
5126 };
5127 return crate::semantics::builtins::call_builtin(id, vals);
5128 }
5129
5130 if let Some(func) = self.ctx.functions.get(&function) {
5132 let param_count = func.params.len();
5133 let body = func.body;
5134
5135 if args.len() != param_count {
5136 return Err(format!(
5137 "Function {} expects {} arguments, got {}",
5138 self.ctx.interner.resolve(function),
5139 param_count,
5140 args.len()
5141 ));
5142 }
5143
5144 let mut arg_values = Vec::with_capacity(param_count);
5145 for arg in args {
5146 arg_values.push(self.evaluate_expr_sync(arg)?);
5147 }
5148
5149 if self.task.call_depth >= crate::semantics::MAX_CALL_DEPTH {
5150 return Err(crate::semantics::CALL_DEPTH_ERR.to_string());
5151 }
5152 self.task.call_depth += 1;
5153 self.task.env.push_frame();
5154 for i in 0..param_count {
5155 let param_name = self.ctx.functions[&function].params[i].0;
5156 self.task.env.define(param_name, std::mem::replace(&mut arg_values[i], RuntimeValue::Nothing));
5157 }
5158
5159 let prev_tco = self.task.tco_fn_sync.replace(function);
5164 let prev_repeat = std::mem::replace(&mut self.task.repeat_depth_sync, 0);
5165 let mut return_value = RuntimeValue::Nothing;
5166 let mut body_err = None;
5167 'tco: loop {
5168 self.task.pending_tail_call = None;
5169 let mut idx = 0;
5170 while idx < body.len() {
5171 if idx + 1 < body.len() {
5175 if let Some(call_args) = crate::tail_call::tail_pair_args(
5176 &body[idx],
5177 &body[idx + 1],
5178 function,
5179 param_count,
5180 ) {
5181 let mut vals = Vec::with_capacity(call_args.len());
5182 let mut perr = None;
5183 for a in call_args {
5184 match self.evaluate_expr_sync(a) {
5185 Ok(v) => vals.push(v),
5186 Err(e) => {
5187 perr = Some(e);
5188 break;
5189 }
5190 }
5191 }
5192 match perr {
5193 Some(e) => body_err = Some(e),
5194 None => self.task.pending_tail_call = Some(vals),
5195 }
5196 break;
5197 }
5198 }
5199 match self.execute_stmt_sync(&body[idx]) {
5200 Ok(ControlFlow::Return(val)) => {
5201 return_value = val;
5202 break;
5203 }
5204 Ok(ControlFlow::Break) => break,
5205 Ok(ControlFlow::Continue) => {}
5206 Err(e) => {
5207 body_err = Some(e);
5208 break;
5209 }
5210 }
5211 idx += 1;
5212 }
5213 if body_err.is_some() {
5214 break 'tco;
5215 }
5216 match self.task.pending_tail_call.take() {
5217 Some(new_args) => {
5218 self.task.env.pop_frame();
5221 self.task.env.push_frame();
5222 for (i, v) in new_args.into_iter().enumerate() {
5223 let param_name = self.ctx.functions[&function].params[i].0;
5224 self.task.env.define(param_name, v);
5225 }
5226 continue 'tco;
5227 }
5228 None => break 'tco,
5229 }
5230 }
5231 self.task.repeat_depth_sync = prev_repeat;
5232 self.task.tco_fn_sync = prev_tco;
5233
5234 self.task.env.pop_frame();
5235 self.task.call_depth -= 1;
5236 match body_err {
5237 Some(e) => Err(e),
5238 None => Ok(return_value),
5239 }
5240 } else {
5241 let maybe_closure = self.task.env.lookup(function)
5243 .and_then(|v| if let RuntimeValue::Function(c) = v { Some((**c).clone()) } else { None });
5244
5245 if let Some(closure) = maybe_closure {
5246 let mut arg_values = Vec::with_capacity(args.len());
5247 for arg in args {
5248 arg_values.push(self.evaluate_expr_sync(arg)?);
5249 }
5250 self.call_closure_value_sync(&closure, arg_values)
5251 } else {
5252 Err(format!("Unknown function: {}", self.ctx.interner.resolve(function)))
5253 }
5254 }
5255 }
5256
5257 fn call_function_with_values_sync(&mut self, function: Symbol, mut args: Vec<RuntimeValue>) -> Result<RuntimeValue, String> {
5258 if Some(function) == self.ctx.sym_show {
5260 for val in args {
5261 self.emit_output(val.to_display_string());
5262 }
5263 return Ok(RuntimeValue::Nothing);
5264 }
5265
5266 if let Some(func) = self.ctx.functions.get(&function) {
5267 let param_count = func.params.len();
5268 let body = func.body;
5269
5270 if args.len() != param_count {
5271 return Err(format!(
5272 "Function {} expects {} arguments, got {}",
5273 self.ctx.interner.resolve(function), param_count, args.len()
5274 ));
5275 }
5276
5277 if self.task.call_depth >= crate::semantics::MAX_CALL_DEPTH {
5278 return Err(crate::semantics::CALL_DEPTH_ERR.to_string());
5279 }
5280 self.task.call_depth += 1;
5281 self.task.env.push_frame();
5282 for i in 0..param_count {
5283 let param_name = self.ctx.functions[&function].params[i].0;
5284 self.task.env.define(param_name, std::mem::replace(&mut args[i], RuntimeValue::Nothing));
5285 }
5286
5287 let prev_tco = self.task.tco_fn_sync.replace(function);
5292 let prev_repeat = std::mem::replace(&mut self.task.repeat_depth_sync, 0);
5293 let mut return_value = RuntimeValue::Nothing;
5294 let mut body_err = None;
5295 'tco: loop {
5296 self.task.pending_tail_call = None;
5297 let mut idx = 0;
5298 while idx < body.len() {
5299 if idx + 1 < body.len() {
5303 if let Some(call_args) = crate::tail_call::tail_pair_args(
5304 &body[idx],
5305 &body[idx + 1],
5306 function,
5307 param_count,
5308 ) {
5309 let mut vals = Vec::with_capacity(call_args.len());
5310 let mut perr = None;
5311 for a in call_args {
5312 match self.evaluate_expr_sync(a) {
5313 Ok(v) => vals.push(v),
5314 Err(e) => {
5315 perr = Some(e);
5316 break;
5317 }
5318 }
5319 }
5320 match perr {
5321 Some(e) => body_err = Some(e),
5322 None => self.task.pending_tail_call = Some(vals),
5323 }
5324 break;
5325 }
5326 }
5327 match self.execute_stmt_sync(&body[idx]) {
5328 Ok(ControlFlow::Return(val)) => {
5329 return_value = val;
5330 break;
5331 }
5332 Ok(ControlFlow::Break) => break,
5333 Ok(ControlFlow::Continue) => {}
5334 Err(e) => {
5335 body_err = Some(e);
5336 break;
5337 }
5338 }
5339 idx += 1;
5340 }
5341 if body_err.is_some() {
5342 break 'tco;
5343 }
5344 match self.task.pending_tail_call.take() {
5345 Some(new_args) => {
5346 self.task.env.pop_frame();
5349 self.task.env.push_frame();
5350 for (i, v) in new_args.into_iter().enumerate() {
5351 let param_name = self.ctx.functions[&function].params[i].0;
5352 self.task.env.define(param_name, v);
5353 }
5354 continue 'tco;
5355 }
5356 None => break 'tco,
5357 }
5358 }
5359 self.task.repeat_depth_sync = prev_repeat;
5360 self.task.tco_fn_sync = prev_tco;
5361
5362 self.task.env.pop_frame();
5363 self.task.call_depth -= 1;
5364 match body_err {
5365 Some(e) => Err(e),
5366 None => Ok(return_value),
5367 }
5368 } else {
5369 let maybe_closure = self.task.env.lookup(function)
5370 .and_then(|v| if let RuntimeValue::Function(c) = v { Some((**c).clone()) } else { None });
5371
5372 if let Some(closure) = maybe_closure {
5373 self.call_closure_value_sync(&closure, args)
5374 } else {
5375 Err(format!("Unknown function: {}", self.ctx.interner.resolve(function)))
5376 }
5377 }
5378 }
5379
5380 pub(crate) fn collect_free_vars_in_closure(
5389 &self,
5390 params: &[(Symbol, &TypeExpr<'a>)],
5391 body: &ClosureBody<'a>,
5392 ) -> Vec<Symbol> {
5393 Self::free_vars_in_closure(params, body)
5394 }
5395
5396 pub(crate) fn free_vars_in_closure(
5399 params: &[(Symbol, &TypeExpr<'a>)],
5400 body: &ClosureBody<'a>,
5401 ) -> Vec<Symbol> {
5402 let param_set: std::collections::HashSet<Symbol> = params.iter().map(|(s, _)| *s).collect();
5403 let mut out = Vec::new();
5404 let mut seen = std::collections::HashSet::new();
5405
5406 match body {
5407 ClosureBody::Expression(expr) => {
5408 Self::collect_symbols_from_expr(expr, ¶m_set, &mut out, &mut seen);
5409 }
5410 ClosureBody::Block(block) => {
5411 Self::collect_symbols_from_block(block, ¶m_set, &mut out, &mut seen);
5412 }
5413 }
5414
5415 out
5416 }
5417
5418 fn collect_symbols_from_expr(
5419 expr: &Expr<'a>,
5420 exclude: &std::collections::HashSet<Symbol>,
5421 out: &mut Vec<Symbol>,
5422 seen: &mut std::collections::HashSet<Symbol>,
5423 ) {
5424 match expr {
5425 Expr::Identifier(sym) => {
5426 if !exclude.contains(sym) && seen.insert(*sym) {
5427 out.push(*sym);
5428 }
5429 }
5430 Expr::Literal(_) | Expr::OptionNone | Expr::Escape { .. } => {}
5431 Expr::BinaryOp { left, right, .. } => {
5432 Self::collect_symbols_from_expr(left, exclude, out, seen);
5433 Self::collect_symbols_from_expr(right, exclude, out, seen);
5434 }
5435 Expr::Call { function, args } => {
5436 if !exclude.contains(function) && seen.insert(*function) {
5437 out.push(*function);
5438 }
5439 for arg in args {
5440 Self::collect_symbols_from_expr(arg, exclude, out, seen);
5441 }
5442 }
5443 Expr::FieldAccess { object, .. } => {
5444 Self::collect_symbols_from_expr(object, exclude, out, seen);
5445 }
5446 Expr::Index { collection, index } => {
5447 Self::collect_symbols_from_expr(collection, exclude, out, seen);
5448 Self::collect_symbols_from_expr(index, exclude, out, seen);
5449 }
5450 Expr::Slice { collection, start, end } => {
5451 Self::collect_symbols_from_expr(collection, exclude, out, seen);
5452 Self::collect_symbols_from_expr(start, exclude, out, seen);
5453 Self::collect_symbols_from_expr(end, exclude, out, seen);
5454 }
5455 Expr::Copy { expr: e } | Expr::Give { value: e } | Expr::Length { collection: e }
5456 | Expr::Not { operand: e } => {
5457 Self::collect_symbols_from_expr(e, exclude, out, seen);
5458 }
5459 Expr::List(items) | Expr::Tuple(items) => {
5460 for item in items {
5461 Self::collect_symbols_from_expr(item, exclude, out, seen);
5462 }
5463 }
5464 Expr::Range { start, end } => {
5465 Self::collect_symbols_from_expr(start, exclude, out, seen);
5466 Self::collect_symbols_from_expr(end, exclude, out, seen);
5467 }
5468 Expr::New { init_fields, .. } => {
5469 for (_, e) in init_fields {
5470 Self::collect_symbols_from_expr(e, exclude, out, seen);
5471 }
5472 }
5473 Expr::NewVariant { fields, .. } => {
5474 for (_, e) in fields {
5475 Self::collect_symbols_from_expr(e, exclude, out, seen);
5476 }
5477 }
5478 Expr::Contains { collection, value } | Expr::Union { left: collection, right: value }
5479 | Expr::Intersection { left: collection, right: value } => {
5480 Self::collect_symbols_from_expr(collection, exclude, out, seen);
5481 Self::collect_symbols_from_expr(value, exclude, out, seen);
5482 }
5483 Expr::ManifestOf { zone } | Expr::OptionSome { value: zone } => {
5484 Self::collect_symbols_from_expr(zone, exclude, out, seen);
5485 }
5486 Expr::ChunkAt { index, zone } | Expr::WithCapacity { value: index, capacity: zone } => {
5487 Self::collect_symbols_from_expr(index, exclude, out, seen);
5488 Self::collect_symbols_from_expr(zone, exclude, out, seen);
5489 }
5490 Expr::Closure { params: inner_params, body: inner_body, .. } => {
5491 let mut inner_exclude = exclude.clone();
5493 for (s, _) in inner_params {
5494 inner_exclude.insert(*s);
5495 }
5496 match inner_body {
5497 ClosureBody::Expression(e) => {
5498 Self::collect_symbols_from_expr(e, &inner_exclude, out, seen);
5499 }
5500 ClosureBody::Block(b) => {
5501 Self::collect_symbols_from_block(b, &inner_exclude, out, seen);
5502 }
5503 }
5504 }
5505 Expr::CallExpr { callee, args } => {
5506 Self::collect_symbols_from_expr(callee, exclude, out, seen);
5507 for arg in args {
5508 Self::collect_symbols_from_expr(arg, exclude, out, seen);
5509 }
5510 }
5511 Expr::InterpolatedString(parts) => {
5512 for part in parts {
5513 if let crate::ast::stmt::StringPart::Expr { value, .. } = part {
5514 Self::collect_symbols_from_expr(value, exclude, out, seen);
5515 }
5516 }
5517 }
5518 }
5519 }
5520
5521 fn collect_symbols_from_block(
5522 stmts: &[Stmt<'a>],
5523 exclude: &std::collections::HashSet<Symbol>,
5524 out: &mut Vec<Symbol>,
5525 seen: &mut std::collections::HashSet<Symbol>,
5526 ) {
5527 use crate::ast::stmt::Pattern;
5528 let mut bound = exclude.clone();
5536 for stmt in stmts {
5537 match stmt {
5538 Stmt::Let { var, value, .. } => {
5539 Self::collect_symbols_from_expr(value, &bound, out, seen);
5540 bound.insert(*var);
5541 }
5542 Stmt::Set { value, .. } => {
5543 Self::collect_symbols_from_expr(value, &bound, out, seen);
5544 }
5545 Stmt::Call { function, args } => {
5546 if !bound.contains(function) && seen.insert(*function) {
5547 out.push(*function);
5548 }
5549 for arg in args {
5550 Self::collect_symbols_from_expr(arg, &bound, out, seen);
5551 }
5552 }
5553 Stmt::Return { value: Some(e) } => {
5554 Self::collect_symbols_from_expr(e, &bound, out, seen);
5555 }
5556 Stmt::If { cond, then_block, else_block } => {
5557 Self::collect_symbols_from_expr(cond, &bound, out, seen);
5558 Self::collect_symbols_from_block(then_block, &bound, out, seen);
5559 if let Some(eb) = else_block {
5560 Self::collect_symbols_from_block(eb, &bound, out, seen);
5561 }
5562 }
5563 Stmt::While { cond, body, .. } => {
5564 Self::collect_symbols_from_expr(cond, &bound, out, seen);
5565 Self::collect_symbols_from_block(body, &bound, out, seen);
5566 }
5567 Stmt::Repeat { pattern, iterable, body } => {
5568 Self::collect_symbols_from_expr(iterable, &bound, out, seen);
5569 let mut body_bound = bound.clone();
5570 match pattern {
5571 Pattern::Identifier(s) => {
5572 body_bound.insert(*s);
5573 }
5574 Pattern::Tuple(syms) => {
5575 for s in syms {
5576 body_bound.insert(*s);
5577 }
5578 }
5579 }
5580 Self::collect_symbols_from_block(body, &body_bound, out, seen);
5581 }
5582 Stmt::Show { object, .. } | Stmt::Give { object, .. } => {
5583 Self::collect_symbols_from_expr(object, &bound, out, seen);
5584 }
5585 Stmt::Push { value, collection } | Stmt::Add { value, collection }
5586 | Stmt::Remove { value, collection } => {
5587 Self::collect_symbols_from_expr(value, &bound, out, seen);
5588 Self::collect_symbols_from_expr(collection, &bound, out, seen);
5589 }
5590 Stmt::SetIndex { collection, index, value } => {
5591 Self::collect_symbols_from_expr(collection, &bound, out, seen);
5592 Self::collect_symbols_from_expr(index, &bound, out, seen);
5593 Self::collect_symbols_from_expr(value, &bound, out, seen);
5594 }
5595 Stmt::SetField { object, value, .. } => {
5596 Self::collect_symbols_from_expr(object, &bound, out, seen);
5597 Self::collect_symbols_from_expr(value, &bound, out, seen);
5598 }
5599 Stmt::RuntimeAssert { condition, .. } => {
5600 Self::collect_symbols_from_expr(condition, &bound, out, seen);
5601 }
5602 Stmt::Zone { body, .. } => {
5603 Self::collect_symbols_from_block(body, &bound, out, seen);
5604 }
5605 Stmt::Inspect { target, arms, .. } => {
5606 Self::collect_symbols_from_expr(target, &bound, out, seen);
5607 for arm in arms {
5608 Self::collect_symbols_from_block(arm.body, &bound, out, seen);
5609 }
5610 }
5611 Stmt::Pop { collection, .. } => {
5612 Self::collect_symbols_from_expr(collection, &bound, out, seen);
5613 }
5614 _ => {}
5615 }
5616 }
5617 }
5618
5619 #[async_recursion(?Send)]
5621 async fn call_closure_value(
5622 &mut self,
5623 closure: &ClosureValue,
5624 mut arg_values: Vec<RuntimeValue>,
5625 ) -> Result<RuntimeValue, String> {
5626 if arg_values.len() != closure.param_names.len() {
5627 return Err(format!(
5628 "Closure expects {} arguments, got {}",
5629 closure.param_names.len(),
5630 arg_values.len()
5631 ));
5632 }
5633
5634 if let Some(expr) = &closure.generated {
5638 let i = match arg_values.first() {
5639 Some(RuntimeValue::Int(n)) => *n,
5640 _ => 0,
5641 };
5642 return Ok(RuntimeValue::Int(crate::concurrency::marshal::gen_eval(expr, i)));
5643 }
5644
5645 let body_index = closure.body_index;
5647 let is_block = matches!(self.ctx.closure_bodies.get(body_index), Some(ClosureBodyRef::Block(_)));
5648
5649 if self.task.call_depth >= crate::semantics::MAX_CALL_DEPTH {
5652 return Err(crate::semantics::CALL_DEPTH_ERR.to_string());
5653 }
5654 self.task.call_depth += 1;
5655 self.task.env.push_frame();
5656
5657 for (sym, val) in &closure.captured_env {
5659 self.task.env.define(*sym, val.deep_clone());
5660 }
5661
5662 for (i, param_sym) in closure.param_names.iter().enumerate() {
5664 self.task.env.define(*param_sym, std::mem::replace(&mut arg_values[i], RuntimeValue::Nothing));
5665 }
5666
5667 let result = if is_block {
5668 let block = match &self.ctx.closure_bodies[body_index] {
5669 ClosureBodyRef::Block(b) => *b,
5670 _ => unreachable!(),
5671 };
5672 let mut outcome = Ok(RuntimeValue::Nothing);
5673 for stmt in block.iter() {
5674 match self.execute_stmt(stmt).await {
5675 Ok(ControlFlow::Return(val)) => {
5676 outcome = Ok(val);
5677 break;
5678 }
5679 Ok(ControlFlow::Break) => break,
5680 Ok(ControlFlow::Continue) => {}
5681 Err(e) => {
5682 outcome = Err(e);
5683 break;
5684 }
5685 }
5686 }
5687 outcome
5688 } else {
5689 let expr = match &self.ctx.closure_bodies[body_index] {
5690 ClosureBodyRef::Expression(e) => *e,
5691 _ => unreachable!(),
5692 };
5693 self.evaluate_expr(expr).await
5694 };
5695
5696 self.task.env.pop_frame();
5697 self.task.call_depth -= 1;
5698 result
5699 }
5700
5701 fn call_closure_value_sync(
5703 &mut self,
5704 closure: &ClosureValue,
5705 mut arg_values: Vec<RuntimeValue>,
5706 ) -> Result<RuntimeValue, String> {
5707 if arg_values.len() != closure.param_names.len() {
5708 return Err(format!(
5709 "Closure expects {} arguments, got {}",
5710 closure.param_names.len(),
5711 arg_values.len()
5712 ));
5713 }
5714
5715 if let Some(expr) = &closure.generated {
5717 let i = match arg_values.first() {
5718 Some(RuntimeValue::Int(n)) => *n,
5719 _ => 0,
5720 };
5721 return Ok(RuntimeValue::Int(crate::concurrency::marshal::gen_eval(expr, i)));
5722 }
5723
5724 let body_index = closure.body_index;
5725 let is_block = matches!(self.ctx.closure_bodies.get(body_index), Some(ClosureBodyRef::Block(_)));
5726
5727 if self.task.call_depth >= crate::semantics::MAX_CALL_DEPTH {
5729 return Err(crate::semantics::CALL_DEPTH_ERR.to_string());
5730 }
5731 self.task.call_depth += 1;
5732 self.task.env.push_frame();
5733
5734 for (sym, val) in &closure.captured_env {
5735 self.task.env.define(*sym, val.deep_clone());
5736 }
5737
5738 for (i, param_sym) in closure.param_names.iter().enumerate() {
5739 self.task.env.define(*param_sym, std::mem::replace(&mut arg_values[i], RuntimeValue::Nothing));
5740 }
5741
5742 let result = if is_block {
5743 let block = match &self.ctx.closure_bodies[body_index] {
5744 ClosureBodyRef::Block(b) => *b,
5745 _ => unreachable!(),
5746 };
5747 let mut outcome = Ok(RuntimeValue::Nothing);
5748 for stmt in block.iter() {
5749 match self.execute_stmt_sync(stmt) {
5750 Ok(ControlFlow::Return(val)) => {
5751 outcome = Ok(val);
5752 break;
5753 }
5754 Ok(ControlFlow::Break) => break,
5755 Ok(ControlFlow::Continue) => {}
5756 Err(e) => {
5757 outcome = Err(e);
5758 break;
5759 }
5760 }
5761 }
5762 outcome
5763 } else {
5764 let expr = match &self.ctx.closure_bodies[body_index] {
5765 ClosureBodyRef::Expression(e) => *e,
5766 _ => unreachable!(),
5767 };
5768 self.evaluate_expr_sync(expr)
5769 };
5770
5771 self.task.env.pop_frame();
5772 self.task.call_depth -= 1;
5773 result
5774 }
5775}
5776
5777fn apply_format_spec(val: &RuntimeValue, spec: &str) -> String {
5782 crate::semantics::format::apply_format_spec(val, spec)
5783}
5784
5785pub fn needs_async(stmts: &[Stmt]) -> bool {
5786 stmts.iter().any(|s| stmt_needs_async(s))
5787}
5788
5789fn stmt_needs_async(stmt: &Stmt) -> bool {
5790 match stmt {
5791 Stmt::ReadFrom { source, .. } => {
5792 matches!(source, ReadSource::File(_))
5793 }
5794 Stmt::WriteFile { .. } | Stmt::Sleep { .. } | Stmt::Mount { .. } => true,
5795 Stmt::Sync { .. } | Stmt::Listen { .. } | Stmt::ConnectTo { .. } => true,
5797 Stmt::SendMessage { .. } | Stmt::AwaitMessage { .. } | Stmt::StreamMessage { .. } => true,
5799 Stmt::If { then_block, else_block, .. } => {
5800 needs_async(then_block)
5801 || else_block.as_ref().map_or(false, |b| needs_async(b))
5802 }
5803 Stmt::While { body, .. } | Stmt::Repeat { body, .. } => needs_async(body),
5804 Stmt::FunctionDef { body, .. } => needs_async(body),
5805 Stmt::Zone { body, .. } => needs_async(body),
5806 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => needs_async(tasks),
5807 Stmt::Inspect { arms, .. } => arms.iter().any(|arm| needs_async(arm.body)),
5808 _ => false,
5809 }
5810}
5811
5812fn count_pi_args(term: &crate::kernel::Term) -> usize {
5816 use crate::kernel::Term;
5817 match term {
5818 Term::Pi { body_type, .. } => 1 + count_pi_args(body_type),
5819 _ => 0,
5820 }
5821}
5822
5823#[derive(Debug, Clone)]
5829pub struct InterpreterResult {
5830 pub lines: Vec<String>,
5832 pub error: Option<String>,
5834}
5835
5836#[cfg(test)]
5837mod ints_i32_repr_tests {
5838 use super::*;
5839
5840 fn i32_buf(vals: &[i64]) -> ListRepr {
5841 let mut r = ListRepr::IntsI32(Vec::new());
5842 for &v in vals {
5843 r.push(RuntimeValue::Int(v));
5844 }
5845 r
5846 }
5847
5848 #[test]
5849 fn push_and_get_sign_extend() {
5850 let r = i32_buf(&[-1, 0, 7, i32::MIN as i64, i32::MAX as i64]);
5851 assert!(matches!(r, ListRepr::IntsI32(_)), "stays half-width when every value fits i32");
5852 assert_eq!(r.get(0), Some(RuntimeValue::Int(-1)), "negative sign-extends losslessly");
5853 assert_eq!(r.get(3), Some(RuntimeValue::Int(i32::MIN as i64)));
5854 assert_eq!(r.get(4), Some(RuntimeValue::Int(i32::MAX as i64)));
5855 assert_eq!(r.len(), 5);
5856 }
5857
5858 #[test]
5859 fn push_out_of_range_widens_and_preserves_values() {
5860 let mut r = i32_buf(&[1, -2, 100]);
5863 r.push(RuntimeValue::Int(i32::MAX as i64 + 1));
5864 assert!(matches!(r, ListRepr::Ints(_)), "an out-of-range push widens to full-width Ints");
5865 assert_eq!(r.get(0), Some(RuntimeValue::Int(1)));
5866 assert_eq!(r.get(1), Some(RuntimeValue::Int(-2)));
5867 assert_eq!(r.get(2), Some(RuntimeValue::Int(100)));
5868 assert_eq!(r.get(3), Some(RuntimeValue::Int(i32::MAX as i64 + 1)));
5869 }
5870
5871 #[test]
5872 fn set_out_of_range_widens() {
5873 let mut r = i32_buf(&[5, 5, 5]);
5874 r.set(1, RuntimeValue::Int(i64::MIN));
5875 assert!(matches!(r, ListRepr::Ints(_)), "an out-of-range in-place store widens");
5876 assert_eq!(r.get(0), Some(RuntimeValue::Int(5)));
5877 assert_eq!(r.get(1), Some(RuntimeValue::Int(i64::MIN)));
5878 assert_eq!(r.get(2), Some(RuntimeValue::Int(5)));
5879 }
5880
5881 #[test]
5882 fn set_in_range_truncates_losslessly() {
5883 let mut r = i32_buf(&[0, 0, 0]);
5884 r.set(2, RuntimeValue::Int(-12345));
5885 assert!(matches!(r, ListRepr::IntsI32(_)), "in-range store stays half-width");
5886 assert_eq!(r.get(2), Some(RuntimeValue::Int(-12345)));
5887 }
5888
5889 #[test]
5890 fn non_int_push_promotes_to_boxed() {
5891 let mut r = i32_buf(&[1, 2]);
5894 r.push(RuntimeValue::Float(3.5));
5895 assert!(matches!(r, ListRepr::Boxed(_)));
5896 assert_eq!(r.get(2), Some(RuntimeValue::Float(3.5)));
5897 assert_eq!(r.get(0), Some(RuntimeValue::Int(1)));
5898 }
5899
5900 #[test]
5901 fn clone_round_trips_to_values() {
5902 let r = i32_buf(&[-7, 42, i32::MIN as i64]);
5903 let snap = r.clone();
5904 assert_eq!(snap.to_values(), r.to_values());
5905 assert_eq!(
5906 r.to_values(),
5907 vec![
5908 RuntimeValue::Int(-7),
5909 RuntimeValue::Int(42),
5910 RuntimeValue::Int(i32::MIN as i64)
5911 ]
5912 );
5913 }
5914
5915 #[test]
5916 fn pop_truncate_position_match_full_width() {
5917 let mut r = i32_buf(&[10, 20, 30]);
5918 assert_eq!(r.position(&RuntimeValue::Int(20)), Some(1));
5919 assert_eq!(r.position(&RuntimeValue::Int(99)), None);
5920 assert_eq!(r.pop(), Some(RuntimeValue::Int(30)));
5921 r.truncate(1);
5922 assert_eq!(r.len(), 1);
5923 assert_eq!(r.get(0), Some(RuntimeValue::Int(10)));
5924 }
5925}
5926
5927#[cfg(test)]
5928mod structs_repr_tests {
5929 use super::*;
5930 use std::collections::HashMap;
5931
5932 fn point(x: i64, y: i64) -> RuntimeValue {
5933 let mut f = HashMap::new();
5934 f.insert("x".to_string(), RuntimeValue::Int(x));
5935 f.insert("y".to_string(), RuntimeValue::Int(y));
5936 RuntimeValue::Struct(Box::new(StructValue { type_name: "Point".to_string(), fields: f }))
5937 }
5938 fn int_field(v: &RuntimeValue, name: &str) -> i64 {
5939 match v {
5940 RuntimeValue::Struct(sv) => match sv.fields.get(name) {
5941 Some(RuntimeValue::Int(n)) => *n,
5942 other => panic!("field {name} not an int: {other:?}"),
5943 },
5944 other => panic!("not a struct: {other:?}"),
5945 }
5946 }
5947
5948 #[test]
5949 fn from_values_homogeneous_structs_is_columnar() {
5950 let r = ListRepr::from_values(vec![point(0, 0), point(1, 2), point(2, 4)]);
5951 assert!(matches!(r, ListRepr::Structs { .. }), "a homogeneous struct list de-boxes to columns");
5952 assert_eq!(r.len(), 3);
5953 assert!(!r.is_empty());
5954 }
5955
5956 #[test]
5957 fn structs_get_reconstructs_exact() {
5958 let r = ListRepr::from_values(vec![point(0, 0), point(1, 2), point(2, 4)]);
5959 let s = r.get(1).unwrap();
5960 assert_eq!(int_field(&s, "x"), 1);
5961 assert_eq!(int_field(&s, "y"), 2);
5962 assert!(r.get(3).is_none(), "out-of-range index is None");
5963 }
5964
5965 #[test]
5966 fn structs_to_values_reconstructs_all_rows() {
5967 let r = ListRepr::from_values(vec![point(5, 6), point(7, 8)]);
5968 let vs = r.to_values();
5969 assert_eq!(vs.len(), 2);
5970 assert_eq!(int_field(&vs[0], "x"), 5);
5971 assert_eq!(int_field(&vs[1], "y"), 8);
5972 }
5973
5974 #[test]
5975 fn structs_truncate_is_columnwise() {
5976 let mut r = ListRepr::from_values(vec![point(0, 0), point(1, 1), point(2, 2)]);
5977 r.truncate(2);
5978 assert!(matches!(r, ListRepr::Structs { .. }), "truncate keeps it columnar");
5979 assert_eq!(r.len(), 2);
5980 assert_eq!(int_field(&r.get(1).unwrap(), "x"), 1);
5981 }
5982
5983 #[test]
5984 fn columnar_field_read_is_direct() {
5985 let r = ListRepr::from_values(vec![point(10, 20), point(30, 40)]);
5986 assert_eq!(r.get_field(0, "x"), Some(RuntimeValue::Int(10)));
5988 assert_eq!(r.get_field(1, "y"), Some(RuntimeValue::Int(40)));
5989 assert_eq!(r.get_field(0, "z"), None, "missing field");
5990 assert!(r.get_field(5, "x").is_none(), "out of range");
5991 match r.column("x") {
5993 Some(ListRepr::Ints(v)) => assert_eq!(v, &vec![10, 30]),
5994 other => panic!("expected an Ints column, got {other:?}"),
5995 }
5996 let boxed = ListRepr::Boxed(vec![point(1, 2)]);
5998 assert!(boxed.get_field(0, "x").is_none());
5999 assert!(boxed.column("x").is_none());
6000 }
6001
6002 #[test]
6003 fn structs_mutation_decolumnarizes_and_stays_correct() {
6004 let mut r = ListRepr::from_values(vec![point(0, 0), point(1, 1), point(2, 2)]);
6007 r.set(1, RuntimeValue::Int(99));
6008 assert!(matches!(r, ListRepr::Boxed(_)), "a mutating set de-columnarizes");
6009 assert_eq!(int_field(&r.get(0).unwrap(), "x"), 0);
6010 assert_eq!(r.get(1), Some(RuntimeValue::Int(99)));
6011 assert_eq!(int_field(&r.get(2).unwrap(), "y"), 2);
6012 }
6013
6014 #[test]
6015 fn structs_push_stays_correct() {
6016 let mut r = ListRepr::from_values(vec![point(0, 0)]);
6017 r.push(point(1, 1));
6018 assert_eq!(r.len(), 2);
6019 assert_eq!(int_field(&r.get(0).unwrap(), "x"), 0);
6020 assert_eq!(int_field(&r.get(1).unwrap(), "x"), 1);
6021 }
6022
6023 #[test]
6024 fn heterogeneous_structs_stay_boxed() {
6025 let mut only_x = HashMap::new();
6027 only_x.insert("x".to_string(), RuntimeValue::Int(9));
6028 let odd = RuntimeValue::Struct(Box::new(StructValue { type_name: "Point".to_string(), fields: only_x }));
6029 let r = ListRepr::from_values(vec![point(0, 0), odd]);
6030 assert!(matches!(r, ListRepr::Boxed(_)), "ragged field sets stay boxed");
6031
6032 let mut cf = HashMap::new();
6034 cf.insert("x".to_string(), RuntimeValue::Int(1));
6035 cf.insert("y".to_string(), RuntimeValue::Int(2));
6036 let q = RuntimeValue::Struct(Box::new(StructValue { type_name: "Other".to_string(), fields: cf }));
6037 let r2 = ListRepr::from_values(vec![point(0, 0), q]);
6038 assert!(matches!(r2, ListRepr::Boxed(_)), "mixed type names stay boxed");
6039 }
6040
6041 #[test]
6042 fn columnar_field_scan_is_faster_than_boxed_and_iteration_is_not_slower() {
6043 use std::time::Instant;
6047 const N: usize = 5000;
6048 const ITERS: u32 = 300;
6049
6050 let rows: Vec<RuntimeValue> = (0..N as i64).map(|i| point(i, i * 2)).collect();
6051 let columnar = ListRepr::from_values(rows.clone());
6052 assert!(matches!(columnar, ListRepr::Structs { .. }), "the columnar baseline must be Structs");
6053 let boxed = ListRepr::Boxed(rows);
6054
6055 let scan_columnar = || -> i64 {
6059 match columnar.column("x") {
6060 Some(ListRepr::Ints(v)) => v.iter().copied().sum(),
6061 _ => unreachable!(),
6062 }
6063 };
6064 let scan_boxed = || -> i64 {
6065 let mut s = 0i64;
6066 for i in 0..boxed.len() {
6067 if let Some(RuntimeValue::Struct(sv)) = boxed.get(i) {
6068 if let Some(RuntimeValue::Int(x)) = sv.fields.get("x") {
6069 s += *x;
6070 }
6071 }
6072 }
6073 s
6074 };
6075 assert_eq!(scan_columnar(), scan_boxed(), "the two scans must agree");
6076
6077 let t = Instant::now();
6078 for _ in 0..ITERS {
6079 std::hint::black_box(scan_columnar());
6080 }
6081 let col_ns = t.elapsed().as_nanos().max(1);
6082 let t = Instant::now();
6083 for _ in 0..ITERS {
6084 std::hint::black_box(scan_boxed());
6085 }
6086 let box_ns = t.elapsed().as_nanos().max(1);
6087 println!(
6088 "\n[E3] field scan (sum x over {N}, ×{ITERS}): columnar {col_ns} ns vs boxed {box_ns} ns ({:.1}× faster)",
6089 box_ns as f64 / col_ns as f64
6090 );
6091 assert!(col_ns * 2 <= box_ns, "columnar field scan should be ≥2× faster: columnar {col_ns} vs boxed {box_ns}");
6092
6093 let iter_repr = |r: &ListRepr| {
6097 let mut acc = 0i64;
6098 for i in 0..r.len() {
6099 if let Some(RuntimeValue::Struct(sv)) = r.get(i) {
6100 if let (Some(RuntimeValue::Int(x)), Some(RuntimeValue::Int(y))) =
6101 (sv.fields.get("x"), sv.fields.get("y"))
6102 {
6103 acc += x + y;
6104 }
6105 }
6106 }
6107 acc
6108 };
6109 assert_eq!(iter_repr(&columnar), iter_repr(&boxed), "full-row iteration must agree");
6110 let t = Instant::now();
6111 for _ in 0..ITERS {
6112 std::hint::black_box(iter_repr(&columnar));
6113 }
6114 let col_it = t.elapsed().as_nanos().max(1);
6115 let t = Instant::now();
6116 for _ in 0..ITERS {
6117 std::hint::black_box(iter_repr(&boxed));
6118 }
6119 let box_it = t.elapsed().as_nanos().max(1);
6120 println!(
6121 "[E3] full-row iter (×{ITERS}): columnar {col_it} ns vs boxed {box_it} ns ({:.2}× of boxed)",
6122 col_it as f64 / box_it as f64
6123 );
6124 assert!(col_it <= box_it * 3, "full-row iteration must not be catastrophically slower: {col_it} vs {box_it}");
6125 }
6126
6127 #[test]
6128 fn zero_field_structs_stay_boxed_and_keep_count() {
6129 let unit = || RuntimeValue::Struct(Box::new(StructValue { type_name: "Unit".to_string(), fields: HashMap::new() }));
6132 let r = ListRepr::from_values(vec![unit(), unit(), unit()]);
6133 assert!(matches!(r, ListRepr::Boxed(_)), "zero-field struct list stays boxed");
6134 assert_eq!(r.len(), 3, "row count is preserved");
6135 }
6136}
6137
6138#[cfg(test)]
6139mod enums_repr_tests {
6140 use super::*;
6141
6142 fn nullary(ty: &str, ctor: &str) -> RuntimeValue {
6143 RuntimeValue::Inductive(Box::new(InductiveValue { inductive_type: ty.into(), constructor: ctor.into(), args: vec![] }))
6144 }
6145 fn with_args(ty: &str, ctor: &str, args: Vec<RuntimeValue>) -> RuntimeValue {
6146 RuntimeValue::Inductive(Box::new(InductiveValue { inductive_type: ty.into(), constructor: ctor.into(), args }))
6147 }
6148 fn ctor_of(v: &RuntimeValue) -> String {
6149 match v {
6150 RuntimeValue::Inductive(i) => i.constructor.clone(),
6151 other => panic!("not an inductive: {other:?}"),
6152 }
6153 }
6154 fn int_arg(v: &RuntimeValue, j: usize) -> i64 {
6155 match v {
6156 RuntimeValue::Inductive(i) => match &i.args[j] {
6157 RuntimeValue::Int(n) => *n,
6158 other => panic!("arg {j} not an int: {other:?}"),
6159 },
6160 other => panic!("not an inductive: {other:?}"),
6161 }
6162 }
6163
6164 #[test]
6165 fn from_values_nullary_enums_is_columnar() {
6166 let r = ListRepr::from_values(vec![nullary("Color", "Red"), nullary("Color", "Green"), nullary("Color", "Red")]);
6167 assert!(matches!(r, ListRepr::Inductives { .. }), "a nullary enum list de-boxes to columns");
6168 assert_eq!(r.len(), 3);
6169 assert_eq!(ctor_of(&r.get(0).unwrap()), "Red");
6170 assert_eq!(ctor_of(&r.get(1).unwrap()), "Green");
6171 assert_eq!(ctor_of(&r.get(2).unwrap()), "Red");
6172 assert!(r.get(3).is_none());
6173 }
6174
6175 #[test]
6176 fn from_values_uniform_arg_enums_is_columnar() {
6177 let r = ListRepr::from_values(vec![
6178 with_args("Boxed", "B", vec![RuntimeValue::Int(1)]),
6179 with_args("Boxed", "B", vec![RuntimeValue::Int(2)]),
6180 ]);
6181 assert!(matches!(r, ListRepr::Inductives { .. }), "a uniform-arg enum list packs columnar");
6182 assert_eq!(int_arg(&r.get(0).unwrap(), 0), 1);
6183 assert_eq!(int_arg(&r.get(1).unwrap(), 0), 2);
6184 }
6185
6186 #[test]
6187 fn from_values_mixed_arity_enums_is_columnar() {
6188 let rows = vec![
6191 with_args("Option", "Some", vec![RuntimeValue::Int(1)]),
6192 nullary("Option", "None"),
6193 with_args("Option", "Some", vec![RuntimeValue::Int(2)]),
6194 nullary("Option", "None"),
6195 with_args("Option", "Some", vec![RuntimeValue::Int(3)]),
6196 ];
6197 let r = ListRepr::from_values(rows);
6198 assert!(matches!(r, ListRepr::Inductives { .. }), "a mixed-arity enum list packs columnar");
6199 assert_eq!(r.len(), 5);
6200 assert_eq!(ctor_of(&r.get(0).unwrap()), "Some");
6201 assert_eq!(int_arg(&r.get(0).unwrap(), 0), 1);
6202 assert_eq!(ctor_of(&r.get(1).unwrap()), "None");
6203 assert_eq!(ctor_of(&r.get(2).unwrap()), "Some");
6204 assert_eq!(int_arg(&r.get(2).unwrap(), 0), 2);
6205 assert_eq!(ctor_of(&r.get(3).unwrap()), "None");
6206 assert_eq!(ctor_of(&r.get(4).unwrap()), "Some");
6207 assert_eq!(int_arg(&r.get(4).unwrap(), 0), 3);
6208 }
6209
6210 #[test]
6211 fn enums_to_values_reconstructs_all_rows() {
6212 let rows = vec![with_args("Option", "Some", vec![RuntimeValue::Int(7)]), nullary("Option", "None")];
6213 let vs = ListRepr::from_values(rows).to_values();
6214 assert_eq!(vs.len(), 2);
6215 assert_eq!(ctor_of(&vs[0]), "Some");
6216 assert_eq!(int_arg(&vs[0], 0), 7);
6217 assert_eq!(ctor_of(&vs[1]), "None");
6218 }
6219
6220 #[test]
6221 fn enums_mutation_decolumnarizes_and_stays_correct() {
6222 let mut r = ListRepr::from_values(vec![nullary("Color", "Red"), nullary("Color", "Green")]);
6223 r.set(1, RuntimeValue::Int(99));
6224 assert!(matches!(r, ListRepr::Boxed(_)), "a mutating set de-columnarizes");
6225 assert_eq!(ctor_of(&r.get(0).unwrap()), "Red");
6226 assert_eq!(r.get(1), Some(RuntimeValue::Int(99)));
6227 }
6228
6229 #[test]
6230 fn heterogeneous_enums_stay_boxed() {
6231 let r = ListRepr::from_values(vec![nullary("Color", "Red"), nullary("Suit", "Spade")]);
6232 assert!(matches!(r, ListRepr::Boxed(_)), "mixed inductive types stay boxed");
6233 }
6234}
6235
6236#[cfg(test)]
6237mod float_comparison_tests {
6238 use super::*;
6239
6240 fn expect_bool(r: Result<RuntimeValue, String>, want: bool, label: &str) {
6241 match r {
6242 Ok(RuntimeValue::Bool(b)) => assert_eq!(b, want, "{label}"),
6243 other => panic!("{label}: expected Bool, got {:?}", other.map(|v| v.type_name().to_string())),
6244 }
6245 }
6246
6247 #[test]
6248 fn float_relational_uses_ieee_semantics() {
6249 use RuntimeValue::{Float, Int};
6250 let interner = Interner::new();
6251 let interp = Interpreter::new(&interner);
6252 let nan = f64::NAN;
6253
6254 expect_bool(interp.apply_binary_op(BinaryOpKind::Lt, Float(-0.0), Float(0.0)), false, "-0.0 < 0.0");
6256 expect_bool(interp.apply_binary_op(BinaryOpKind::Gt, Float(0.0), Float(-0.0)), false, "0.0 > -0.0");
6257 expect_bool(interp.apply_binary_op(BinaryOpKind::LtEq, Float(-0.0), Float(0.0)), true, "-0.0 <= 0.0");
6258 expect_bool(interp.apply_binary_op(BinaryOpKind::GtEq, Float(-0.0), Float(0.0)), true, "-0.0 >= 0.0");
6259
6260 expect_bool(interp.apply_binary_op(BinaryOpKind::Lt, Float(nan), Float(1.0)), false, "NaN < 1");
6262 expect_bool(interp.apply_binary_op(BinaryOpKind::Gt, Float(nan), Float(1.0)), false, "NaN > 1");
6263 expect_bool(interp.apply_binary_op(BinaryOpKind::LtEq, Float(nan), Float(nan)), false, "NaN <= NaN");
6264 expect_bool(interp.apply_binary_op(BinaryOpKind::GtEq, Float(1.0), Float(nan)), false, "1 >= NaN");
6265
6266 expect_bool(interp.apply_binary_op(BinaryOpKind::Lt, Float(1.5), Float(2.5)), true, "1.5 < 2.5");
6268 expect_bool(interp.apply_binary_op(BinaryOpKind::Lt, Int(2), Float(2.5)), true, "2 < 2.5");
6269 expect_bool(interp.apply_binary_op(BinaryOpKind::GtEq, Float(2.5), Int(2)), true, "2.5 >= 2");
6270 expect_bool(interp.apply_binary_op(BinaryOpKind::Lt, Int(3), Int(5)), true, "3 < 5");
6271 expect_bool(interp.apply_binary_op(BinaryOpKind::GtEq, Int(5), Int(5)), true, "5 >= 5");
6272 }
6273}