1use crate::ast::{ImportDef, ImportKind, Item};
11use crate::bytecode::{CaptureSrc, Const, FnProto, Instr, Pat};
12use crate::compiler::{compile_program, module_global_name, ProgramCtx};
13use crate::parser::parse;
14use crate::value::shared::{HostBound, Lock, Sh, ShWeak};
15use crate::value::{AtomCell, Closure, Value, Variant, VariantPayload};
16use std::collections::{BTreeMap, HashMap, HashSet};
17use std::fmt;
18use std::path::PathBuf;
19use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
20use std::sync::Arc;
21use std::time::Duration;
22
23#[derive(Debug, Clone, PartialEq)]
24pub struct Fault {
25 pub msg: String,
26 pub at: Option<String>,
28}
29
30impl Fault {
31 pub fn new(msg: impl Into<String>) -> Fault {
32 Fault {
33 msg: msg.into(),
34 at: None,
35 }
36 }
37}
38
39impl fmt::Display for Fault {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match &self.at {
42 Some(at) => write!(f, "fault at {}: {}", at, self.msg),
43 None => write!(f, "fault: {}", self.msg),
44 }
45 }
46}
47
48#[derive(Debug)]
49pub enum FunctError {
50 Parse(String),
51 Compile(String),
52 Fault(Fault),
53}
54
55impl fmt::Display for FunctError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 FunctError::Parse(m) => write!(f, "parse error: {}", m),
59 FunctError::Compile(m) => write!(f, "compile error: {}", m),
60 FunctError::Fault(fa) => write!(f, "{}", fa),
61 }
62 }
63}
64
65impl std::error::Error for FunctError {}
66
67#[derive(Clone)]
68pub struct Frame {
69 pub fn_id: u32,
70 pub proto: Sh<FnProto>,
73 pub ip: u32,
74 pub locals: Vec<Value>,
75 pub upvals: Vec<Value>,
76}
77
78#[derive(Clone)]
79pub enum Status {
80 Running,
81 Done(Value),
82 Faulted(Fault),
83}
84
85#[derive(Clone)]
87pub struct VmState {
88 pub frames: Vec<Frame>,
89 pub stack: Vec<Value>,
90 pub status: Status,
91}
92
93impl VmState {
94 pub fn current_line(&self) -> Option<u32> {
95 let f = self.frames.last()?;
96 Some(f.proto.line_at(f.ip as usize))
97 }
98
99 pub fn depth(&self) -> usize {
100 self.frames.len()
101 }
102
103 pub fn is_running(&self) -> bool {
104 matches!(self.status, Status::Running)
105 }
106}
107
108#[derive(Debug, Clone, PartialEq)]
109pub enum StepResult {
110 Running,
111 Done(Value),
112 Faulted(Fault),
113}
114
115#[derive(Debug, Clone, PartialEq)]
116pub enum RunResult {
117 Done(Value),
118 Faulted(Fault),
119 Paused(Cause),
120}
121
122#[derive(Debug, Clone, PartialEq)]
123pub enum Cause {
124 FuelExhausted,
126 DeadlineReached,
129 Breakpoint(u32),
130 NextLine(u32),
131}
132
133#[derive(Debug, Clone)]
134pub enum StopWhen {
135 Never,
137 Fuel(u64),
139 Deadline(Duration),
143 Epoch,
147 Budget {
150 fuel: Option<u64>,
151 deadline: Option<Duration>,
152 },
153 Breakpoints(HashSet<u32>),
154 NextLine,
155}
156
157pub(crate) type NativeImpl =
158 Sh<dyn Fn(&mut Funct, Vec<Value>) -> Result<Value, Fault> + Send + Sync>;
159
160pub trait HostFn: Fn(&mut Funct, Vec<Value>) -> Result<Value, Fault> + HostBound {}
162impl<F: Fn(&mut Funct, Vec<Value>) -> Result<Value, Fault> + HostBound> HostFn for F {}
163
164pub(crate) struct NativeEntry {
165 pub name: String,
166 pub f: NativeImpl,
167}
168
169pub(crate) type Getter = Sh<dyn Fn(&mut Funct, &Value) -> Result<Value, Fault> + Send + Sync>;
170
171struct LoadResult {
172 main: Option<u32>,
173 exports: Vec<(String, u32)>,
174}
175
176#[derive(Debug, Clone)]
178pub(crate) enum ModuleExports {
179 File(Vec<(String, u32)>),
181 Host(u32),
183}
184
185pub struct Funct {
187 pub(crate) fns: Vec<Sh<FnProto>>,
188 pub(crate) ctx: ProgramCtx,
189 pub(crate) globals: Vec<Option<Value>>,
190 pub(crate) natives: Vec<NativeEntry>,
191 pub(crate) native_ids: HashMap<String, u32>,
192 pub(crate) getters: HashMap<(String, String), Getter>,
194 pub(crate) atom_counter: u64,
195 pub(crate) atoms: Vec<ShWeak<AtomCell>>,
196 pub(crate) modules: HashMap<String, ModuleExports>,
198 pub(crate) module_root: PathBuf,
200 loading: Vec<String>,
202 pub(crate) test_fns: Vec<String>,
204 epoch: Arc<AtomicU64>,
208 epoch_deadline: u64,
210 ticker: Option<Ticker>,
212}
213
214struct Ticker {
218 running: Arc<AtomicBool>,
219 tick: Duration,
220 handle: Option<std::thread::JoinHandle<()>>,
221}
222
223impl Drop for Ticker {
224 fn drop(&mut self) {
225 self.running.store(false, Ordering::Relaxed);
226 if let Some(h) = self.handle.take() {
227 let _ = h.join();
228 }
229 }
230}
231
232impl Funct {
233 pub fn new() -> Funct {
235 let mut vm = Funct::bare();
236 crate::prelude::install(&mut vm);
237 let n = vm.ctx.global_names.len() as u32;
240 vm.ctx.shared.extend(0..n);
241 vm
242 }
243
244 pub fn bare() -> Funct {
246 Funct {
247 fns: Vec::new(),
248 ctx: ProgramCtx {
249 fn_ids: HashMap::new(),
250 fn_count: 0,
251 global_ids: HashMap::new(),
252 global_names: Vec::new(),
253 shared: HashSet::new(),
254 },
255 globals: Vec::new(),
256 natives: Vec::new(),
257 native_ids: HashMap::new(),
258 getters: HashMap::new(),
259 atom_counter: 0,
260 atoms: Vec::new(),
261 modules: HashMap::new(),
262 module_root: PathBuf::from("."),
263 loading: Vec::new(),
264 test_fns: Vec::new(),
265 epoch: Arc::new(AtomicU64::new(0)),
266 epoch_deadline: u64::MAX,
267 ticker: None,
268 }
269 }
270
271 pub fn set_module_root(&mut self, root: impl Into<PathBuf>) {
273 self.module_root = root.into();
274 }
275
276 pub fn epoch(&self) -> Arc<AtomicU64> {
283 self.epoch.clone()
284 }
285
286 pub fn epoch_now(&self) -> u64 {
288 self.epoch.load(Ordering::Relaxed)
289 }
290
291 pub fn bump_epoch(&self) -> u64 {
294 self.epoch.fetch_add(1, Ordering::Relaxed) + 1
295 }
296
297 pub fn set_deadline(&mut self, epoch: u64) {
301 self.epoch_deadline = epoch;
302 }
303
304 pub fn clear_deadline(&mut self) {
306 self.epoch_deadline = u64::MAX;
307 }
308
309 fn ticker_target(&mut self, after: Duration) -> u64 {
312 const TICK: Duration = Duration::from_millis(1);
313 if self.ticker.is_none() {
314 let running = Arc::new(AtomicBool::new(true));
315 let epoch = self.epoch.clone();
316 let flag = running.clone();
317 let handle = std::thread::Builder::new()
318 .name("funct-epoch-ticker".into())
319 .spawn(move || {
320 while flag.load(Ordering::Relaxed) {
321 std::thread::sleep(TICK);
322 epoch.fetch_add(1, Ordering::Relaxed);
323 }
324 })
325 .ok();
326 self.ticker = Some(Ticker {
327 running,
328 tick: TICK,
329 handle,
330 });
331 }
332 let tick = self.ticker.as_ref().map(|t| t.tick).unwrap_or(TICK);
333 let ticks = after.as_nanos().div_ceil(tick.as_nanos().max(1)).max(1) as u64;
336 self.epoch_now().saturating_add(ticks)
337 }
338
339 pub fn eval(&mut self, src: &str) -> Result<Value, FunctError> {
344 let main = self.load(src)?;
345 match main {
346 Some(fn_id) => {
347 let mut st = self.state_for(fn_id, vec![])?;
348 match self.run(&mut st, StopWhen::Never) {
349 RunResult::Done(v) => Ok(v),
350 RunResult::Faulted(f) => Err(FunctError::Fault(f)),
351 RunResult::Paused(_) => unreachable!("StopWhen::Never paused"),
352 }
353 }
354 None => Ok(Value::Unit),
355 }
356 }
357
358 pub fn load(&mut self, src: &str) -> Result<Option<u32>, FunctError> {
361 self.load_with_prefix(src, None).map(|r| r.main)
362 }
363
364 fn load_with_prefix(
365 &mut self,
366 src: &str,
367 prefix: Option<&str>,
368 ) -> Result<LoadResult, FunctError> {
369 let prog = parse(src).map_err(FunctError::Parse)?;
370 for item in &prog.items {
373 match item {
374 Item::Import(imp) => self.process_import(imp, prefix)?,
375 Item::Extern { name, .. } => self.process_extern(name),
376 Item::ExternLet { name, .. } => {
377 let g = self.ctx.ensure_global(name);
380 self.ctx.shared.insert(g);
381 self.sync_globals();
382 }
383 _ => {}
384 }
385 }
386 let compiled =
387 compile_program(&mut self.ctx, &prog, prefix).map_err(FunctError::Compile)?;
388 for (id, proto) in compiled.protos {
389 let id = id as usize;
390 if self.fns.len() <= id {
391 self.fns.resize_with(id + 1, || {
392 Sh::new(FnProto {
393 name: "<hole>".into(),
394 arity: 0,
395 num_locals: 0,
396 num_upvals: 0,
397 code: vec![],
398 consts: vec![],
399 pats: vec![],
400 lines: vec![],
401 closure_captures: vec![],
402 })
403 });
404 }
405 self.fns[id] = Sh::new(proto);
406 }
407 self.sync_globals();
408 for (gslot, fn_id) in compiled.fn_globals {
409 self.globals[gslot as usize] = Some(Value::Closure(Sh::new(Closure {
410 fn_id,
411 upvals: vec![],
412 })));
413 }
414 if prefix.is_none() {
417 for t in &compiled.tests {
418 if !self.test_fns.contains(t) {
419 self.test_fns.push(t.clone());
420 }
421 }
422 }
423 Ok(LoadResult {
424 main: compiled.main,
425 exports: compiled.exports,
426 })
427 }
428
429 fn process_extern(&mut self, name: &str) {
434 if let Some(&g) = self.ctx.global_ids.get(name) {
435 if self
436 .globals
437 .get(g as usize)
438 .map(|v| v.is_some())
439 .unwrap_or(false)
440 {
441 return; }
443 }
444 let fn_name = name.to_string();
445 self.register_raw(name, move |_vm, _args| {
446 Err(Fault::new(format!(
447 "host function `{}` was declared `extern` but this host does not provide it",
448 fn_name
449 )))
450 });
451 }
452
453 pub fn test_names(&self) -> Vec<String> {
455 self.test_fns.clone()
456 }
457
458 pub fn load_module(&mut self, path: &str) -> Result<Vec<String>, FunctError> {
462 self.ensure_module(path)?;
463 Ok(self.module_export_names(path))
464 }
465
466 pub fn reload_module(&mut self, path: &str) -> Result<Vec<String>, FunctError> {
471 if let Some(ModuleExports::Host(_)) = self.modules.get(path) {
472 return Err(FunctError::Fault(Fault::new(format!(
473 "`{}` is a host module registered from Rust; re-register it instead",
474 path
475 ))));
476 }
477 self.modules.remove(path);
478 self.load_module(path)
479 }
480
481 pub(crate) fn register_host_module(&mut self, name: &str, gid: u32) {
483 self.modules
484 .insert(name.to_string(), ModuleExports::Host(gid));
485 }
486
487 fn module_export_names(&self, path: &str) -> Vec<String> {
488 match self.modules.get(path) {
489 Some(ModuleExports::File(list)) => list.iter().map(|(n, _)| n.clone()).collect(),
490 Some(ModuleExports::Host(gid)) => match self.globals.get(*gid as usize) {
491 Some(Some(Value::Record(r))) => r.keys().cloned().collect(),
492 _ => vec![],
493 },
494 None => vec![],
495 }
496 }
497
498 fn ensure_module(&mut self, path: &str) -> Result<(), FunctError> {
499 if self.modules.contains_key(path) {
500 return Ok(());
501 }
502 if path
503 .split('/')
504 .any(|seg| seg.is_empty() || seg == "." || seg == "..")
505 {
506 return Err(FunctError::Fault(Fault::new(format!(
507 "invalid module path `{}`: segments must be plain names (no `..`, `.` or empty)",
508 path
509 ))));
510 }
511 if let Some(i) = self.loading.iter().position(|p| p == path) {
512 let mut chain: Vec<&str> = self.loading[i..].iter().map(|s| s.as_str()).collect();
513 chain.push(path);
514 return Err(FunctError::Fault(Fault::new(format!(
515 "circular module imports: {}",
516 chain.join(" -> ")
517 ))));
518 }
519 let file = self.module_root.join(format!("{}.ft", path));
520 let src = std::fs::read_to_string(&file).map_err(|e| {
521 FunctError::Fault(Fault::new(format!(
522 "cannot load module `{}`: {} ({})",
523 path,
524 file.display(),
525 e
526 )))
527 })?;
528 self.loading.push(path.to_string());
529 let result = (|| {
530 let loaded = self.load_with_prefix(&src, Some(path))?;
531 if let Some(fn_id) = loaded.main {
533 let mut st = self.state_for(fn_id, vec![])?;
534 match self.run(&mut st, StopWhen::Never) {
535 RunResult::Done(_) => {}
536 RunResult::Faulted(f) => return Err(FunctError::Fault(f)),
537 RunResult::Paused(_) => unreachable!(),
538 }
539 }
540 Ok(loaded.exports)
541 })();
542 self.loading.pop();
543 let exports = result?;
544 self.modules
545 .insert(path.to_string(), ModuleExports::File(exports));
546 Ok(())
547 }
548
549 fn export_value(&self, path: &str, name: &str) -> Result<Value, FunctError> {
551 let loud_missing = |available: Vec<String>| {
552 FunctError::Fault(Fault::new(format!(
553 "module `{}` has no export `{}` (exports: {})",
554 path,
555 name,
556 if available.is_empty() {
557 "none".to_string()
558 } else {
559 available.join(", ")
560 }
561 )))
562 };
563 match self.modules.get(path) {
564 Some(ModuleExports::File(list)) => match list.iter().find(|(n, _)| n == name) {
565 Some((_, gid)) => self
566 .globals
567 .get(*gid as usize)
568 .cloned()
569 .flatten()
570 .ok_or_else(|| {
571 FunctError::Fault(Fault::new(format!(
572 "export `{}` of module `{}` is uninitialized",
573 name, path
574 )))
575 }),
576 None => Err(loud_missing(list.iter().map(|(n, _)| n.clone()).collect())),
577 },
578 Some(ModuleExports::Host(gid)) => match self.globals.get(*gid as usize) {
579 Some(Some(Value::Record(r))) => r
580 .get(name)
581 .cloned()
582 .ok_or_else(|| loud_missing(r.keys().cloned().collect())),
583 _ => Err(FunctError::Fault(Fault::new(format!(
584 "host module `{}` record is missing",
585 path
586 )))),
587 },
588 None => Err(FunctError::Fault(Fault::new(format!(
589 "module `{}` not loaded",
590 path
591 )))),
592 }
593 }
594
595 fn process_import(&mut self, imp: &ImportDef, prefix: Option<&str>) -> Result<(), FunctError> {
596 self.ensure_module(&imp.path)?;
597 match &imp.kind {
598 ImportKind::Named(names) => {
599 for (name, alias) in names {
600 let v = self.export_value(&imp.path, name)?;
601 let bind = alias.as_deref().unwrap_or(name);
602 let full = module_global_name(prefix, bind);
603 let g = self.ctx.ensure_global(&full);
604 self.sync_globals();
605 self.globals[g as usize] = Some(v);
606 }
607 }
608 ImportKind::Qualified(alias) => {
609 let bind = match alias {
610 Some(a) => a.clone(),
611 None => {
612 let last = imp.path.rsplit('/').next().unwrap_or(&imp.path);
613 let valid = !last.is_empty()
614 && last
615 .chars()
616 .next()
617 .map(|c| c.is_ascii_lowercase() || c == '_')
618 .unwrap_or(false)
619 && last.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
620 if !valid {
621 return Err(FunctError::Fault(Fault::new(format!(
622 "`{}` is not a usable module alias; add one: import \"{}\" as <name>",
623 last, imp.path
624 ))));
625 }
626 last.to_string()
627 }
628 };
629 let v = match self.modules.get(&imp.path).cloned() {
630 Some(ModuleExports::Host(gid)) => self
631 .globals
632 .get(gid as usize)
633 .cloned()
634 .flatten()
635 .ok_or_else(|| {
636 FunctError::Fault(Fault::new(format!(
637 "host module `{}` record is missing",
638 imp.path
639 )))
640 })?,
641 Some(ModuleExports::File(list)) => {
642 let mut map = BTreeMap::new();
649 for (name, gid) in &list {
650 if let Some(Some(v)) = self.globals.get(*gid as usize) {
651 map.insert(name.clone(), v.clone());
652 }
653 }
654 Value::record(map)
655 }
656 None => unreachable!("ensure_module just loaded it"),
657 };
658 let full = module_global_name(prefix, &bind);
659 let g = self.ctx.ensure_global(&full);
660 self.sync_globals();
661 self.globals[g as usize] = Some(v);
662 }
663 }
664 Ok(())
665 }
666
667 pub fn eval_resumable(&mut self, src: &str) -> Result<VmState, FunctError> {
670 let main = self.load(src)?;
671 match main {
672 Some(fn_id) => self.state_for(fn_id, vec![]),
673 None => Ok(VmState {
674 frames: vec![],
675 stack: vec![],
676 status: Status::Done(Value::Unit),
677 }),
678 }
679 }
680
681 pub(crate) fn sync_globals(&mut self) {
682 if self.globals.len() < self.ctx.global_names.len() {
683 self.globals.resize(self.ctx.global_names.len(), None);
684 }
685 }
686
687 fn state_for(&mut self, fn_id: u32, args: Vec<Value>) -> Result<VmState, FunctError> {
688 let proto = self
689 .fns
690 .get(fn_id as usize)
691 .cloned()
692 .ok_or_else(|| FunctError::Fault(Fault::new(format!("unknown fn id {}", fn_id))))?;
693 let frame = make_frame(fn_id, proto, args).map_err(FunctError::Fault)?;
694 Ok(VmState {
695 frames: vec![frame],
696 stack: vec![],
697 status: Status::Running,
698 })
699 }
700
701 pub fn global(&self, name: &str) -> Option<Value> {
702 let id = *self.ctx.global_ids.get(name)?;
703 self.globals.get(id as usize)?.clone()
704 }
705
706 pub fn set_global(&mut self, name: &str, v: Value) {
710 let g = self.ctx.ensure_global(name);
711 self.ctx.shared.insert(g);
712 self.sync_globals();
713 self.globals[g as usize] = Some(v);
714 }
715
716 pub fn start(&mut self, name: &str, args: Vec<Value>) -> Result<VmState, FunctError> {
718 let f = self
719 .global(name)
720 .ok_or_else(|| FunctError::Fault(Fault::new(format!("unknown function `{}`", name))))?;
721 self.start_value(&f, args)
722 }
723
724 pub fn start_value(&mut self, f: &Value, args: Vec<Value>) -> Result<VmState, FunctError> {
725 match f {
726 Value::Closure(c) => {
727 let proto = self.fns[c.fn_id as usize].clone();
728 let mut frame = make_frame(c.fn_id, proto, args).map_err(FunctError::Fault)?;
729 frame.upvals = c.upvals.clone();
730 Ok(VmState {
731 frames: vec![frame],
732 stack: vec![],
733 status: Status::Running,
734 })
735 }
736 Value::NativeFn(id) => {
737 let f = self.natives[*id as usize].f.clone();
739 match f(self, args) {
740 Ok(v) => Ok(VmState {
741 frames: vec![],
742 stack: vec![],
743 status: Status::Done(v),
744 }),
745 Err(e) => Err(FunctError::Fault(e)),
746 }
747 }
748 other => Err(FunctError::Fault(Fault::new(format!(
749 "value of type {} is not callable",
750 other.type_name()
751 )))),
752 }
753 }
754
755 pub fn call_value(&mut self, f: &Value, args: Vec<Value>) -> Result<Value, Fault> {
757 let mut st = self.start_value(f, args).map_err(|e| match e {
758 FunctError::Fault(f) => f,
759 other => Fault::new(other.to_string()),
760 })?;
761 match self.run(&mut st, StopWhen::Never) {
762 RunResult::Done(v) => Ok(v),
763 RunResult::Faulted(f) => Err(f),
764 RunResult::Paused(_) => unreachable!(),
765 }
766 }
767
768 pub fn call(&mut self, name: &str, args: Vec<Value>) -> Result<Value, FunctError> {
770 let f = self
771 .global(name)
772 .ok_or_else(|| FunctError::Fault(Fault::new(format!("unknown function `{}`", name))))?;
773 self.call_value(&f, args).map_err(FunctError::Fault)
774 }
775
776 pub fn run(&mut self, st: &mut VmState, stop: StopWhen) -> RunResult {
777 match &stop {
781 StopWhen::Never => return self.run_fast(st, None),
782 StopWhen::Fuel(n) => return self.run_fast(st, Some(*n)),
783 StopWhen::Epoch => {
784 let dl = self.epoch_deadline;
785 return self.run_with_deadline(st, None, dl);
786 }
787 StopWhen::Deadline(d) => {
788 let dl = self.ticker_target(*d);
789 return self.run_with_deadline(st, None, dl);
790 }
791 StopWhen::Budget { fuel, deadline } => {
792 return match deadline {
793 None => self.run_fast(st, *fuel),
795 Some(d) => {
796 let dl = self.ticker_target(*d);
797 self.run_with_deadline(st, *fuel, dl)
798 }
799 };
800 }
801 _ => {}
802 }
803 let mut fuel: Option<u64> = None;
804 let mut last_line = st.current_line().unwrap_or(0);
807 let start_line = st.current_line().unwrap_or(0);
808 let start_depth = st.depth();
809 loop {
810 match &st.status {
811 Status::Done(v) => return RunResult::Done(v.clone()),
812 Status::Faulted(f) => return RunResult::Faulted(f.clone()),
813 Status::Running => {}
814 }
815 if let Some(0) = fuel {
816 return RunResult::Paused(Cause::FuelExhausted);
817 }
818 if let Some(line) = st.current_line() {
819 match &stop {
820 StopWhen::Breakpoints(bps) => {
821 if line != last_line && bps.contains(&line) {
822 return RunResult::Paused(Cause::Breakpoint(line));
823 }
824 }
825 StopWhen::NextLine => {
826 if line != start_line && st.depth() <= start_depth {
827 return RunResult::Paused(Cause::NextLine(line));
828 }
829 }
830 _ => {}
831 }
832 last_line = line;
833 }
834 match self.step(st) {
835 StepResult::Running => {}
836 StepResult::Done(v) => return RunResult::Done(v),
837 StepResult::Faulted(f) => return RunResult::Faulted(f),
838 }
839 if let Some(n) = fuel.as_mut() {
840 *n -= 1;
841 }
842 }
843 }
844
845 fn run_fast(&mut self, st: &mut VmState, mut fuel: Option<u64>) -> RunResult {
849 loop {
850 match &st.status {
853 Status::Done(v) => return RunResult::Done(v.clone()),
854 Status::Faulted(f) => return RunResult::Faulted(f.clone()),
855 Status::Running => {}
856 }
857 if let Some(0) = fuel {
858 return RunResult::Paused(Cause::FuelExhausted);
859 }
860 match self.step_inner(st) {
861 Ok(None) => {}
862 Ok(Some(v)) => {
863 st.status = Status::Done(v.clone());
864 return RunResult::Done(v);
865 }
866 Err(mut fault) => {
867 if fault.at.is_none() {
868 if let Some(f) = st.frames.last() {
869 let ip = (f.ip as usize).saturating_sub(1);
870 fault.at = Some(format!("{}:{}", f.proto.name, f.proto.line_at(ip)));
871 }
872 }
873 st.status = Status::Faulted(fault.clone());
874 return RunResult::Faulted(fault);
875 }
876 }
877 if let Some(n) = fuel.as_mut() {
878 *n -= 1;
879 }
880 }
881 }
882
883 fn run_with_deadline(
889 &mut self,
890 st: &mut VmState,
891 mut fuel: Option<u64>,
892 deadline: u64,
893 ) -> RunResult {
894 const EPOCH_POLL: u32 = 256;
895 let mut since_poll: u32 = 0;
896 loop {
897 match &st.status {
898 Status::Done(v) => return RunResult::Done(v.clone()),
899 Status::Faulted(f) => return RunResult::Faulted(f.clone()),
900 Status::Running => {}
901 }
902 if let Some(0) = fuel {
903 return RunResult::Paused(Cause::FuelExhausted);
904 }
905 since_poll += 1;
906 if since_poll >= EPOCH_POLL {
907 since_poll = 0;
908 if self.epoch.load(Ordering::Relaxed) >= deadline {
909 return RunResult::Paused(Cause::DeadlineReached);
910 }
911 }
912 match self.step_inner(st) {
913 Ok(None) => {}
914 Ok(Some(v)) => {
915 st.status = Status::Done(v.clone());
916 return RunResult::Done(v);
917 }
918 Err(mut fault) => {
919 if fault.at.is_none() {
920 if let Some(f) = st.frames.last() {
921 let ip = (f.ip as usize).saturating_sub(1);
922 fault.at = Some(format!("{}:{}", f.proto.name, f.proto.line_at(ip)));
923 }
924 }
925 st.status = Status::Faulted(fault.clone());
926 return RunResult::Faulted(fault);
927 }
928 }
929 if let Some(n) = fuel.as_mut() {
930 *n -= 1;
931 }
932 }
933 }
934
935 pub fn step(&mut self, st: &mut VmState) -> StepResult {
939 match &st.status {
940 Status::Done(v) => return StepResult::Done(v.clone()),
941 Status::Faulted(f) => return StepResult::Faulted(f.clone()),
942 Status::Running => {}
943 }
944 match self.step_inner(st) {
945 Ok(Some(v)) => {
946 st.status = Status::Done(v.clone());
947 StepResult::Done(v)
948 }
949 Ok(None) => StepResult::Running,
950 Err(mut fault) => {
951 if fault.at.is_none() {
952 if let Some(f) = st.frames.last() {
953 let ip = (f.ip as usize).saturating_sub(1);
954 fault.at = Some(format!("{}:{}", f.proto.name, f.proto.line_at(ip)));
955 }
956 }
957 st.status = Status::Faulted(fault.clone());
958 StepResult::Faulted(fault)
959 }
960 }
961 }
962
963 fn step_inner(&mut self, st: &mut VmState) -> Result<Option<Value>, Fault> {
964 let frame = st
965 .frames
966 .last_mut()
967 .ok_or_else(|| Fault::new("vm has no frames (already finished?)"))?;
968 let ip = frame.ip as usize;
969 let instr = *frame.proto.code.get(ip).ok_or_else(|| {
972 Fault::new(format!("ip {} out of bounds in {}", ip, frame.proto.name))
973 })?;
974 frame.ip += 1;
975
976 macro_rules! pop {
977 () => {
978 st.stack
979 .pop()
980 .ok_or_else(|| Fault::new("operand stack underflow"))?
981 };
982 }
983
984 match instr {
985 Instr::Nop => {}
986 Instr::Const(k) => {
987 let frame = st.frames.last().unwrap();
988 let v = match &frame.proto.consts[k as usize] {
989 Const::Int(i) => Value::Int(*i),
990 Const::Float(f) => Value::Float(*f),
991 Const::Str(s) => Value::str(s.clone()),
992 c => return Err(Fault::new(format!("cannot load const {:?}", c))),
993 };
994 st.stack.push(v);
995 }
996 Instr::Unit => st.stack.push(Value::Unit),
997 Instr::True => st.stack.push(Value::Bool(true)),
998 Instr::False => st.stack.push(Value::Bool(false)),
999 Instr::LoadLocal(n) => {
1000 let v = st.frames.last().unwrap().locals[n as usize].clone();
1001 st.stack.push(v);
1002 }
1003 Instr::StoreLocal(n) => {
1004 let v = pop!();
1005 st.frames.last_mut().unwrap().locals[n as usize] = v;
1006 }
1007 Instr::LoadUpval(n) => {
1008 let v = st.frames.last().unwrap().upvals[n as usize].clone();
1009 st.stack.push(v);
1010 }
1011 Instr::LoadGlobal(g) => {
1012 let v = self
1013 .globals
1014 .get(g as usize)
1015 .cloned()
1016 .flatten()
1017 .ok_or_else(|| {
1018 let name = self
1019 .ctx
1020 .global_names
1021 .get(g as usize)
1022 .cloned()
1023 .unwrap_or_else(|| format!("#{}", g));
1024 Fault::new(format!("global `{}` used before it was initialized", name))
1025 })?;
1026 st.stack.push(v);
1027 }
1028 Instr::StoreGlobal(g) => {
1029 let v = pop!();
1030 self.sync_globals();
1031 if (g as usize) >= self.globals.len() {
1032 self.globals.resize(g as usize + 1, None);
1033 }
1034 self.globals[g as usize] = Some(v);
1035 }
1036 Instr::NewCell => {
1037 let v = pop!();
1038 st.stack.push(Value::Cell(Sh::new(Lock::new(v))));
1039 }
1040 Instr::CellGet => {
1041 let c = pop!();
1042 match c {
1043 Value::Cell(c) => st.stack.push(c.read().clone()),
1044 other => return Err(Fault::new(format!("CellGet on {}", other.type_name()))),
1045 }
1046 }
1047 Instr::CellSet => {
1048 let v = pop!();
1049 let c = pop!();
1050 match c {
1051 Value::Cell(c) => *c.write() = v,
1052 other => return Err(Fault::new(format!("CellSet on {}", other.type_name()))),
1053 }
1054 }
1055 Instr::MakeList(n) => {
1056 let items = pop_n(&mut st.stack, n as usize)?;
1057 st.stack.push(Value::list(items));
1058 }
1059 Instr::MakeTuple(n) => {
1060 let items = pop_n(&mut st.stack, n as usize)?;
1061 st.stack.push(Value::tuple(items));
1062 }
1063 Instr::MakeRecord(names_k) => {
1064 let names = self.names_const(st, names_k)?;
1065 let vals = pop_n(&mut st.stack, names.len())?;
1066 let mut map = BTreeMap::new();
1067 for (n, v) in names.into_iter().zip(vals) {
1068 map.insert(n, v);
1069 }
1070 st.stack.push(Value::record(map));
1071 }
1072 Instr::RecordUpdate(names_k) => {
1073 let names = self.names_const(st, names_k)?;
1074 let vals = pop_n(&mut st.stack, names.len())?;
1075 let base = pop!();
1076 let mut map = match base {
1077 Value::Record(r) => r.clone(),
1078 other => {
1079 return Err(Fault::new(format!(
1080 "record update `{{ ..base }}` needs a record, got {}",
1081 other.type_name()
1082 )))
1083 }
1084 };
1085 for (n, v) in names.into_iter().zip(vals) {
1086 map.insert(n, v);
1087 }
1088 st.stack.push(Value::Record(map));
1089 }
1090 Instr::GetField(name_k) => {
1091 let name = self.name_const(st, name_k)?;
1092 let recv = pop!();
1093 let v = self.get_field(&recv, &name)?;
1094 st.stack.push(v);
1095 }
1096 Instr::Index => {
1097 let idx = pop!();
1098 let recv = pop!();
1099 st.stack.push(index_value(&recv, &idx)?);
1100 }
1101 Instr::MakeVariantUnit { tag } => {
1102 let tag = self.name_const(st, tag)?;
1103 st.stack.push(Value::Variant(Sh::new(Variant {
1104 tag,
1105 payload: VariantPayload::Unit,
1106 })));
1107 }
1108 Instr::MakeVariantPos { tag, count } => {
1109 let tag = self.name_const(st, tag)?;
1110 let items = pop_n(&mut st.stack, count as usize)?;
1111 st.stack.push(Value::Variant(Sh::new(Variant {
1112 tag,
1113 payload: VariantPayload::Positional(items),
1114 })));
1115 }
1116 Instr::MakeVariantNamed { tag, names } => {
1117 let tag = self.name_const(st, tag)?;
1118 let names = self.names_const(st, names)?;
1119 let vals = pop_n(&mut st.stack, names.len())?;
1120 let mut map = BTreeMap::new();
1121 for (n, v) in names.into_iter().zip(vals) {
1122 map.insert(n, v);
1123 }
1124 st.stack.push(Value::Variant(Sh::new(Variant {
1125 tag,
1126 payload: VariantPayload::Named(map),
1127 })));
1128 }
1129 Instr::Add => bin_op(&mut st.stack, BinKind::Add)?,
1130 Instr::Sub => bin_op(&mut st.stack, BinKind::Sub)?,
1131 Instr::Mul => bin_op(&mut st.stack, BinKind::Mul)?,
1132 Instr::Div => bin_op(&mut st.stack, BinKind::Div)?,
1133 Instr::Mod => bin_op(&mut st.stack, BinKind::Mod)?,
1134 Instr::Pow => bin_op(&mut st.stack, BinKind::Pow)?,
1135 Instr::Eq => {
1136 let b = pop!();
1137 let a = pop!();
1138 st.stack.push(Value::Bool(a == b));
1139 }
1140 Instr::Ne => {
1141 let b = pop!();
1142 let a = pop!();
1143 st.stack.push(Value::Bool(a != b));
1144 }
1145 Instr::Lt => cmp_op(&mut st.stack, |o| o == std::cmp::Ordering::Less)?,
1146 Instr::Le => cmp_op(&mut st.stack, |o| o != std::cmp::Ordering::Greater)?,
1147 Instr::Gt => cmp_op(&mut st.stack, |o| o == std::cmp::Ordering::Greater)?,
1148 Instr::Ge => cmp_op(&mut st.stack, |o| o != std::cmp::Ordering::Less)?,
1149 Instr::Neg => {
1150 let v = pop!();
1151 st.stack.push(match v {
1152 Value::Int(i) => Value::Int(
1153 i.checked_neg()
1154 .ok_or_else(|| Fault::new("integer overflow in negation"))?,
1155 ),
1156 Value::Float(f) => Value::Float(-f),
1157 other => {
1158 return Err(Fault::new(format!("cannot negate {}", other.type_name())))
1159 }
1160 });
1161 }
1162 Instr::Not => {
1163 let v = pop!();
1164 match v {
1165 Value::Bool(b) => st.stack.push(Value::Bool(!b)),
1166 other => {
1167 return Err(Fault::new(format!(
1168 "`not` needs a Bool, got {}",
1169 other.type_name()
1170 )))
1171 }
1172 }
1173 }
1174 Instr::MakeRange { inclusive } => {
1175 let hi = pop!();
1176 let lo = pop!();
1177 match (lo, hi) {
1178 (Value::Int(a), Value::Int(b)) => st.stack.push(Value::Range(a, b, inclusive)),
1179 (a, b) => {
1180 return Err(Fault::new(format!(
1181 "range bounds must be Int, got {} and {}",
1182 a.type_name(),
1183 b.type_name()
1184 )))
1185 }
1186 }
1187 }
1188 Instr::Jump(t) => st.frames.last_mut().unwrap().ip = t,
1189 Instr::JumpIfFalse(t) => {
1190 let v = pop!();
1191 match v {
1192 Value::Bool(false) => st.frames.last_mut().unwrap().ip = t,
1193 Value::Bool(true) => {}
1194 other => {
1195 return Err(Fault::new(format!(
1196 "condition must be a Bool, got {}",
1197 other.type_name()
1198 )))
1199 }
1200 }
1201 }
1202 Instr::JumpIfFalsePeek(t) => match st.stack.last() {
1203 Some(Value::Bool(false)) => st.frames.last_mut().unwrap().ip = t,
1204 Some(Value::Bool(true)) => {}
1205 Some(other) => {
1206 return Err(Fault::new(format!(
1207 "`and`/`or` operands must be Bool, got {}",
1208 other.type_name()
1209 )))
1210 }
1211 None => return Err(Fault::new("operand stack underflow")),
1212 },
1213 Instr::JumpIfTruePeek(t) => match st.stack.last() {
1214 Some(Value::Bool(true)) => st.frames.last_mut().unwrap().ip = t,
1215 Some(Value::Bool(false)) => {}
1216 Some(other) => {
1217 return Err(Fault::new(format!(
1218 "`and`/`or` operands must be Bool, got {}",
1219 other.type_name()
1220 )))
1221 }
1222 None => return Err(Fault::new("operand stack underflow")),
1223 },
1224 Instr::MatchPat { pat, fail } => {
1225 let subject = st
1226 .stack
1227 .last()
1228 .ok_or_else(|| Fault::new("operand stack underflow"))?
1229 .clone();
1230 let frame = st.frames.last_mut().unwrap();
1231 let pat = frame.proto.pats[pat as usize].clone();
1232 if !match_pat(&pat, &subject, &mut frame.locals) {
1233 frame.ip = fail;
1234 }
1235 }
1236 Instr::MakeClosure { fn_id, captures } => {
1237 let frame = st.frames.last().unwrap();
1238 let caps = &frame.proto.closure_captures[captures as usize];
1239 let upvals: Vec<Value> = caps
1240 .iter()
1241 .map(|c| match c {
1242 CaptureSrc::Local(s) => frame.locals[*s as usize].clone(),
1243 CaptureSrc::Upval(i) => frame.upvals[*i as usize].clone(),
1244 })
1245 .collect();
1246 st.stack
1247 .push(Value::Closure(Sh::new(Closure { fn_id, upvals })));
1248 }
1249 Instr::Call(argc) => {
1250 let args = pop_n(&mut st.stack, argc as usize)?;
1251 let callee = pop!();
1252 self.do_call(st, callee, args, false)?;
1253 }
1254 Instr::TailCall(argc) => {
1255 let args = pop_n(&mut st.stack, argc as usize)?;
1256 let callee = pop!();
1257 self.do_call(st, callee, args, true)?;
1258 }
1259 Instr::Invoke { name, global, argc } => {
1260 let name = self.name_const(st, name)?;
1261 let args = pop_n(&mut st.stack, argc as usize)?;
1262 let recv = pop!();
1263 let field_callable = match &recv {
1265 Value::Record(r) => r.get(&name).cloned(),
1266 _ => None,
1267 };
1268 match field_callable {
1269 Some(f @ (Value::Closure(_) | Value::NativeFn(_))) => {
1270 self.do_call(st, f, args, false)?;
1271 }
1272 Some(other) => {
1273 return Err(Fault::new(format!(
1274 "field `{}` is not callable (it is {})",
1275 name,
1276 other.type_name()
1277 )))
1278 }
1279 None => {
1280 let f = global
1283 .and_then(|g| self.globals.get(g as usize).cloned().flatten())
1284 .or_else(|| self.global(&name))
1285 .ok_or_else(|| {
1286 Fault::new(format!(
1287 "no function `{}` for method call on {}",
1288 name,
1289 recv.type_name()
1290 ))
1291 })?;
1292 let mut full_args = Vec::with_capacity(args.len() + 1);
1293 full_args.push(recv);
1294 full_args.extend(args);
1295 self.do_call(st, f, full_args, false)?;
1296 }
1297 }
1298 }
1299 Instr::Ret => {
1300 let ret = pop!();
1301 st.frames.pop();
1302 if st.frames.is_empty() {
1303 return Ok(Some(ret));
1304 }
1305 st.stack.push(ret);
1306 }
1307 Instr::Deref => {
1308 let v = pop!();
1309 match v {
1310 Value::Atom(a) => st.stack.push(a.value.read().clone()),
1311 other => {
1312 return Err(Fault::new(format!(
1313 "`@` deref needs an Atom, got {}",
1314 other.type_name()
1315 )))
1316 }
1317 }
1318 }
1319 Instr::Try => {
1320 let v = pop!();
1321 match &v {
1322 Value::Variant(var) => match (var.tag.as_str(), &var.payload) {
1323 ("Ok", VariantPayload::Positional(p))
1324 | ("Some", VariantPayload::Positional(p))
1325 if p.len() == 1 =>
1326 {
1327 st.stack.push(p[0].clone());
1328 }
1329 ("Err", _) | ("None", _) => {
1330 st.frames.pop();
1332 if st.frames.is_empty() {
1333 return Ok(Some(v));
1334 }
1335 st.stack.push(v);
1336 }
1337 _ => {
1338 return Err(Fault::new(format!(
1339 "`?` needs Ok/Err/Some/None, got {}",
1340 v.type_name()
1341 )))
1342 }
1343 },
1344 other => {
1345 return Err(Fault::new(format!(
1346 "`?` needs a Result/Option, got {}",
1347 other.type_name()
1348 )))
1349 }
1350 }
1351 }
1352 Instr::Fault(msg_k) => {
1353 let msg = self.name_const(st, msg_k)?;
1354 return Err(Fault::new(msg));
1355 }
1356 Instr::Pop => {
1357 pop!();
1358 }
1359 Instr::Dup => {
1360 let v = st
1361 .stack
1362 .last()
1363 .cloned()
1364 .ok_or_else(|| Fault::new("stack underflow"))?;
1365 st.stack.push(v);
1366 }
1367 Instr::IterNext { iter, idx, end } => {
1368 let frame = st.frames.last_mut().unwrap();
1369 let i = match &frame.locals[idx as usize] {
1370 Value::Int(i) => *i,
1371 other => {
1372 return Err(Fault::new(format!(
1373 "loop index corrupted: {}",
1374 other.type_name()
1375 )))
1376 }
1377 };
1378 let item: Option<Value> = match &frame.locals[iter as usize] {
1379 Value::List(items) | Value::Tuple(items) => items.get(i as usize).cloned(),
1380 Value::Range(a, b, inc) => {
1381 let v = a + i;
1382 let ok = if *inc { v <= *b } else { v < *b };
1383 if ok {
1384 Some(Value::Int(v))
1385 } else {
1386 None
1387 }
1388 }
1389 Value::Str(s) => s.chars().nth(i as usize).map(|c| Value::str(c.to_string())),
1390 other => {
1391 return Err(Fault::new(format!(
1392 "cannot iterate over {}",
1393 other.type_name()
1394 )))
1395 }
1396 };
1397 match item {
1398 Some(v) => {
1399 frame.locals[idx as usize] = Value::Int(i + 1);
1400 st.stack.push(v);
1401 }
1402 None => frame.ip = end,
1403 }
1404 }
1405 }
1406 Ok(None)
1407 }
1408
1409 fn do_call(
1410 &mut self,
1411 st: &mut VmState,
1412 callee: Value,
1413 args: Vec<Value>,
1414 tail: bool,
1415 ) -> Result<(), Fault> {
1416 match callee {
1417 Value::Closure(c) => {
1418 let proto = self
1419 .fns
1420 .get(c.fn_id as usize)
1421 .cloned()
1422 .ok_or_else(|| Fault::new(format!("unknown fn id {}", c.fn_id)))?;
1423 let mut frame = make_frame(c.fn_id, proto, args)?;
1424 frame.upvals = c.upvals.clone();
1425 if tail {
1426 st.frames.pop();
1427 }
1428 st.frames.push(frame);
1429 Ok(())
1430 }
1431 Value::NativeFn(id) => {
1432 let f = self
1433 .natives
1434 .get(id as usize)
1435 .ok_or_else(|| Fault::new(format!("unknown native fn id {}", id)))?
1436 .f
1437 .clone();
1438 let result = f(self, args)?;
1439 if tail {
1440 st.frames.pop();
1442 if st.frames.is_empty() {
1443 st.status = Status::Done(result);
1444 return Ok(());
1445 }
1446 }
1447 st.stack.push(result);
1448 Ok(())
1449 }
1450 other => Err(Fault::new(format!(
1451 "value of type {} is not callable",
1452 other.type_name()
1453 ))),
1454 }
1455 }
1456
1457 pub(crate) fn get_field(&mut self, recv: &Value, name: &str) -> Result<Value, Fault> {
1458 match recv {
1459 Value::Record(r) => r
1460 .get(name)
1461 .cloned()
1462 .ok_or_else(|| Fault::new(format!("record has no field `{}`", name))),
1463 Value::Variant(v) => match &v.payload {
1464 VariantPayload::Named(fields) => fields.get(name).cloned().ok_or_else(|| {
1465 Fault::new(format!("variant {} has no field `{}`", v.tag, name))
1466 }),
1467 _ => Err(Fault::new(format!("variant {} has no named fields", v.tag))),
1468 },
1469 Value::Atom(a) if name == "value" => Ok(a.value.read().clone()),
1470 Value::Native(n) => {
1471 let key = (n.type_name.to_string(), name.to_string());
1472 match self.getters.get(&key).cloned() {
1473 Some(g) => g(self, recv),
1474 None => Err(Fault::new(format!(
1475 "native type {} has no registered field `{}`",
1476 n.type_name, name
1477 ))),
1478 }
1479 }
1480 other => Err(Fault::new(format!("{} has no fields", other.type_name()))),
1481 }
1482 }
1483
1484 fn name_const(&self, st: &VmState, k: u32) -> Result<String, Fault> {
1485 let frame = st.frames.last().ok_or_else(|| Fault::new("no frame"))?;
1486 match &frame.proto.consts[k as usize] {
1487 Const::Name(s) | Const::Str(s) => Ok(s.clone()),
1488 c => Err(Fault::new(format!("expected name const, got {:?}", c))),
1489 }
1490 }
1491
1492 fn names_const(&self, st: &VmState, k: u32) -> Result<Vec<String>, Fault> {
1493 let frame = st.frames.last().ok_or_else(|| Fault::new("no frame"))?;
1494 match &frame.proto.consts[k as usize] {
1495 Const::Names(s) => Ok(s.clone()),
1496 c => Err(Fault::new(format!("expected names const, got {:?}", c))),
1497 }
1498 }
1499
1500 pub fn make_atom(&mut self, v: Value) -> Value {
1503 let id = self.atom_counter;
1504 self.atom_counter += 1;
1505 let cell = Sh::new(AtomCell {
1506 id,
1507 value: Lock::new(v),
1508 watchers: Lock::new(Vec::new()),
1509 });
1510 self.atoms.push(Sh::downgrade(&cell));
1511 Value::Atom(cell)
1512 }
1513
1514 pub(crate) fn fire_watchers(
1515 &mut self,
1516 atom: &Sh<AtomCell>,
1517 old: Value,
1518 new: Value,
1519 ) -> Result<(), Fault> {
1520 let watchers: Vec<Value> = atom
1521 .watchers
1522 .read()
1523 .iter()
1524 .map(|(_, f)| f.clone())
1525 .collect();
1526 for w in watchers {
1527 self.call_value(&w, vec![old.clone(), new.clone()])?;
1528 }
1529 Ok(())
1530 }
1531
1532 pub fn live_atoms(&mut self) -> Vec<Sh<AtomCell>> {
1533 self.atoms.retain(|w| w.strong_count() > 0);
1534 self.atoms.iter().filter_map(|w| w.upgrade()).collect()
1535 }
1536}
1537
1538impl Default for Funct {
1539 fn default() -> Self {
1540 Funct::new()
1541 }
1542}
1543
1544fn make_frame(fn_id: u32, proto: Sh<FnProto>, args: Vec<Value>) -> Result<Frame, Fault> {
1545 if args.len() != proto.arity as usize {
1546 return Err(Fault::new(format!(
1547 "{} expects {} argument(s), got {}",
1548 proto.name,
1549 proto.arity,
1550 args.len()
1551 )));
1552 }
1553 let mut locals = args;
1554 locals.resize(proto.num_locals as usize, Value::Unit);
1555 Ok(Frame {
1556 fn_id,
1557 proto,
1558 ip: 0,
1559 locals,
1560 upvals: vec![],
1561 })
1562}
1563
1564fn pop_n(stack: &mut Vec<Value>, n: usize) -> Result<Vec<Value>, Fault> {
1565 if stack.len() < n {
1566 return Err(Fault::new("operand stack underflow"));
1567 }
1568 Ok(stack.split_off(stack.len() - n))
1569}
1570
1571enum BinKind {
1572 Add,
1573 Sub,
1574 Mul,
1575 Div,
1576 Mod,
1577 Pow,
1578}
1579
1580fn bin_op(stack: &mut Vec<Value>, kind: BinKind) -> Result<(), Fault> {
1581 let b = stack
1582 .pop()
1583 .ok_or_else(|| Fault::new("operand stack underflow"))?;
1584 let a = stack
1585 .pop()
1586 .ok_or_else(|| Fault::new("operand stack underflow"))?;
1587 use Value::*;
1588 let v = match (&kind, &a, &b) {
1589 (BinKind::Add, Str(x), Str(y)) => Value::str(format!("{}{}", x, y)),
1591 (BinKind::Add, List(x), List(y)) => {
1593 let mut items = (**x).clone();
1594 items.extend(y.iter().cloned());
1595 Value::list_v(items)
1596 }
1597 (_, Int(x), Int(y)) => match kind {
1598 BinKind::Add => Int(x
1599 .checked_add(*y)
1600 .ok_or_else(|| Fault::new("integer overflow in +"))?),
1601 BinKind::Sub => Int(x
1602 .checked_sub(*y)
1603 .ok_or_else(|| Fault::new("integer overflow in -"))?),
1604 BinKind::Mul => Int(x
1605 .checked_mul(*y)
1606 .ok_or_else(|| Fault::new("integer overflow in *"))?),
1607 BinKind::Div => {
1608 if *y == 0 {
1609 return Err(Fault::new("division by zero"));
1610 }
1611 Int(x / y)
1612 }
1613 BinKind::Mod => {
1614 if *y == 0 {
1615 return Err(Fault::new("modulo by zero"));
1616 }
1617 Int(x % y)
1618 }
1619 BinKind::Pow => {
1620 if *y >= 0 {
1621 let e: u32 = (*y)
1622 .try_into()
1623 .map_err(|_| Fault::new("exponent too large"))?;
1624 Int(x
1625 .checked_pow(e)
1626 .ok_or_else(|| Fault::new("integer overflow in **"))?)
1627 } else {
1628 Float((*x as f64).powi(*y as i32))
1629 }
1630 }
1631 },
1632 (_, a, b) => {
1633 let (x, y) = match (as_f64(a), as_f64(b)) {
1634 (Some(x), Some(y)) => (x, y),
1635 _ => {
1636 let op = match kind {
1637 BinKind::Add => "+",
1638 BinKind::Sub => "-",
1639 BinKind::Mul => "*",
1640 BinKind::Div => "/",
1641 BinKind::Mod => "%",
1642 BinKind::Pow => "**",
1643 };
1644 return Err(Fault::new(format!(
1645 "cannot apply `{}` to {} and {}",
1646 op,
1647 a.type_name(),
1648 b.type_name()
1649 )));
1650 }
1651 };
1652 match kind {
1653 BinKind::Add => Float(x + y),
1654 BinKind::Sub => Float(x - y),
1655 BinKind::Mul => Float(x * y),
1656 BinKind::Div => {
1657 if y == 0.0 {
1658 return Err(Fault::new("division by zero"));
1659 }
1660 Float(x / y)
1661 }
1662 BinKind::Mod => {
1663 if y == 0.0 {
1664 return Err(Fault::new("modulo by zero"));
1665 }
1666 Float(x % y)
1667 }
1668 BinKind::Pow => Float(x.powf(y)),
1669 }
1670 }
1671 };
1672 stack.push(v);
1673 Ok(())
1674}
1675
1676fn as_f64(v: &Value) -> Option<f64> {
1677 match v {
1678 Value::Int(i) => Some(*i as f64),
1679 Value::Float(f) => Some(*f),
1680 _ => None,
1681 }
1682}
1683
1684fn cmp_op(stack: &mut Vec<Value>, test: fn(std::cmp::Ordering) -> bool) -> Result<(), Fault> {
1685 let b = stack
1686 .pop()
1687 .ok_or_else(|| Fault::new("operand stack underflow"))?;
1688 let a = stack
1689 .pop()
1690 .ok_or_else(|| Fault::new("operand stack underflow"))?;
1691 let ord = match (&a, &b) {
1692 (Value::Str(x), Value::Str(y)) => x.cmp(y),
1693 (x, y) => match (as_f64(x), as_f64(y)) {
1694 (Some(x), Some(y)) => x
1695 .partial_cmp(&y)
1696 .ok_or_else(|| Fault::new("cannot compare NaN"))?,
1697 _ => {
1698 return Err(Fault::new(format!(
1699 "cannot compare {} and {}",
1700 a.type_name(),
1701 b.type_name()
1702 )))
1703 }
1704 },
1705 };
1706 stack.push(Value::Bool(test(ord)));
1707 Ok(())
1708}
1709
1710fn index_value(recv: &Value, idx: &Value) -> Result<Value, Fault> {
1711 match (recv, idx) {
1712 (Value::List(items), Value::Int(i)) | (Value::Tuple(items), Value::Int(i)) => {
1713 let i = *i;
1714 if i < 0 || i as usize >= items.len() {
1715 return Err(Fault::new(format!(
1716 "index {} out of bounds (length {})",
1717 i,
1718 items.len()
1719 )));
1720 }
1721 Ok(items[i as usize].clone())
1722 }
1723 (Value::Record(r), Value::Str(k)) => r
1724 .get(&**k)
1725 .cloned()
1726 .ok_or_else(|| Fault::new(format!("record has no field `{}`", k))),
1727 (Value::Str(s), Value::Int(i)) => {
1728 let c = s
1729 .chars()
1730 .nth(*i as usize)
1731 .ok_or_else(|| Fault::new(format!("string index {} out of bounds", i)))?;
1732 Ok(Value::str(c.to_string()))
1733 }
1734 (r, i) => Err(Fault::new(format!(
1735 "cannot index {} with {}",
1736 r.type_name(),
1737 i.type_name()
1738 ))),
1739 }
1740}
1741
1742pub fn match_pat(pat: &Pat, v: &Value, locals: &mut Vec<Value>) -> bool {
1744 match pat {
1745 Pat::Wildcard => true,
1746 Pat::Bind(slot) => {
1747 locals[*slot as usize] = v.clone();
1748 true
1749 }
1750 Pat::LitInt(i) => v == &Value::Int(*i),
1751 Pat::LitFloat(f) => v == &Value::Float(*f),
1752 Pat::LitStr(s) => matches!(v, Value::Str(x) if &**x == s.as_str()),
1753 Pat::LitBool(b) => v == &Value::Bool(*b),
1754 Pat::LitUnit => matches!(v, Value::Unit),
1755 Pat::VariantPos { tag, items } => match v {
1756 Value::Variant(var) if var.tag == *tag => match &var.payload {
1757 VariantPayload::Unit => items.is_empty(),
1758 VariantPayload::Positional(vals) => {
1759 vals.len() == items.len()
1760 && items.iter().zip(vals).all(|(p, x)| match_pat(p, x, locals))
1761 }
1762 VariantPayload::Named(_) => false,
1763 },
1764 _ => false,
1765 },
1766 Pat::VariantNamed { tag, fields, rest } => match v {
1767 Value::Variant(var) if var.tag == *tag => match &var.payload {
1768 VariantPayload::Named(vals) => match_fields(fields, *rest, vals, locals),
1769 _ => false,
1770 },
1771 _ => false,
1772 },
1773 Pat::Record { fields, rest } => match v {
1774 Value::Record(vals) => match_fields(fields, *rest, vals, locals),
1775 _ => false,
1776 },
1777 Pat::Tuple(items) => match v {
1778 Value::Tuple(vals) => {
1779 vals.len() == items.len()
1780 && items
1781 .iter()
1782 .zip(vals.iter())
1783 .all(|(p, x)| match_pat(p, x, locals))
1784 }
1785 _ => false,
1786 },
1787 Pat::List { items, rest } => match v {
1788 Value::List(vals) => match rest {
1789 None => {
1790 vals.len() == items.len()
1791 && items
1792 .iter()
1793 .zip(vals.iter())
1794 .all(|(p, x)| match_pat(p, x, locals))
1795 }
1796 Some(rest_bind) => {
1797 if vals.len() < items.len() {
1798 return false;
1799 }
1800 if !items
1801 .iter()
1802 .zip(vals.iter())
1803 .all(|(p, x)| match_pat(p, x, locals))
1804 {
1805 return false;
1806 }
1807 if let Some(slot) = rest_bind {
1808 let rest_items: Vec<Value> =
1809 vals.iter().skip(items.len()).cloned().collect();
1810 locals[*slot as usize] = Value::list(rest_items);
1811 }
1812 true
1813 }
1814 },
1815 _ => false,
1816 },
1817 Pat::Range { lo, hi, inclusive } => match v {
1818 Value::Int(i) => {
1819 if *inclusive {
1820 i >= lo && i <= hi
1821 } else {
1822 i >= lo && i < hi
1823 }
1824 }
1825 _ => false,
1826 },
1827 Pat::Or(alts) => alts.iter().any(|p| match_pat(p, v, locals)),
1828 Pat::As(inner, slot) => {
1829 if match_pat(inner, v, locals) {
1830 locals[*slot as usize] = v.clone();
1831 true
1832 } else {
1833 false
1834 }
1835 }
1836 }
1837}
1838
1839trait FieldMap {
1842 fn flen(&self) -> usize;
1843 fn fget(&self, k: &str) -> Option<&Value>;
1844}
1845impl FieldMap for BTreeMap<String, Value> {
1846 fn flen(&self) -> usize {
1847 self.len()
1848 }
1849 fn fget(&self, k: &str) -> Option<&Value> {
1850 self.get(k)
1851 }
1852}
1853impl FieldMap for crate::value::FMap {
1854 fn flen(&self) -> usize {
1855 self.len()
1856 }
1857 fn fget(&self, k: &str) -> Option<&Value> {
1858 self.get(k)
1859 }
1860}
1861
1862fn match_fields(
1863 fields: &[(String, Pat)],
1864 rest: bool,
1865 vals: &impl FieldMap,
1866 locals: &mut Vec<Value>,
1867) -> bool {
1868 if !rest && vals.flen() != fields.len() {
1869 return false;
1870 }
1871 for (name, p) in fields {
1872 match vals.fget(name) {
1873 Some(x) => {
1874 if !match_pat(p, x, locals) {
1875 return false;
1876 }
1877 }
1878 None => return false,
1879 }
1880 }
1881 true
1882}