diff options
Diffstat (limited to 'src/model/vm.rs')
| -rw-r--r-- | src/model/vm.rs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/src/model/vm.rs b/src/model/vm.rs new file mode 100644 index 00000000..a1b1ba81 --- /dev/null +++ b/src/model/vm.rs @@ -0,0 +1,86 @@ +use std::path::PathBuf; + +use comemo::Tracked; + +use super::{Route, Scopes, Value}; +use crate::diag::{SourceError, StrResult}; +use crate::syntax::{SourceId, Span}; +use crate::util::PathExt; +use crate::World; + +/// A virtual machine. +pub struct Vm<'a> { + /// The core context. + pub world: Tracked<'a, dyn World>, + /// The route of source ids the machine took to reach its current location. + pub route: Tracked<'a, Route>, + /// The current location. + pub location: Option<SourceId>, + /// The stack of scopes. + pub scopes: Scopes<'a>, + /// A control flow event that is currently happening. + pub flow: Option<Flow>, +} + +impl<'a> Vm<'a> { + /// Create a new virtual machine. + pub fn new( + world: Tracked<'a, dyn World>, + route: Tracked<'a, Route>, + location: Option<SourceId>, + scopes: Scopes<'a>, + ) -> Self { + Self { + world, + route, + location, + scopes, + flow: None, + } + } + + /// Resolve a user-entered path to be relative to the compilation + /// environment's root. + pub fn locate(&self, path: &str) -> StrResult<PathBuf> { + if let Some(id) = self.location { + if let Some(path) = path.strip_prefix('/') { + return Ok(self.world.config().root.join(path).normalize()); + } + + if let Some(dir) = self.world.source(id).path().parent() { + return Ok(dir.join(path).normalize()); + } + } + + return Err("cannot access file system from here".into()); + } +} + +/// A control flow event that occurred during evaluation. +#[derive(Debug, Clone, PartialEq)] +pub enum Flow { + /// Stop iteration in a loop. + Break(Span), + /// Skip the remainder of the current iteration in a loop. + Continue(Span), + /// Stop execution of a function early, optionally returning an explicit + /// value. + Return(Span, Option<Value>), +} + +impl Flow { + /// Return an error stating that this control flow is forbidden. + pub fn forbidden(&self) -> SourceError { + match *self { + Self::Break(span) => { + error!(span, "cannot break outside of loop") + } + Self::Continue(span) => { + error!(span, "cannot continue outside of loop") + } + Self::Return(span, _) => { + error!(span, "cannot return outside of function") + } + } + } +} |
