summaryrefslogtreecommitdiff
path: root/src/eval/mod.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-05-25 13:50:33 +0200
committerLaurenz <laurmaedje@gmail.com>2022-05-25 13:59:06 +0200
commitc010cbc17dcbb2f0d6005d21530143bf57cb5871 (patch)
tree937fe79f0c121bcc025480181287fd4a3d0c0f4f /src/eval/mod.rs
parent6935cf8dfefff3d6cf234f077a7d61661fd5ca57 (diff)
Move route from context to VM
Diffstat (limited to 'src/eval/mod.rs')
-rw-r--r--src/eval/mod.rs96
1 files changed, 78 insertions, 18 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 2b853586..702a76b2 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -43,13 +43,63 @@ use crate::syntax::{Span, Spanned};
use crate::util::EcoString;
use crate::Context;
-/// Evaluate an expression.
-pub trait Eval {
- /// The output of evaluating the expression.
- type Output;
+/// Evaluate a source file and return the resulting module.
+///
+/// Returns either a module containing a scope with top-level bindings and
+/// layoutable contents or diagnostics in the form of a vector of error
+/// messages with file and span information.
+pub fn evaluate(
+ ctx: &mut Context,
+ id: SourceId,
+ mut route: Vec<SourceId>,
+) -> TypResult<Module> {
+ // Prevent cyclic evaluation.
+ if route.contains(&id) {
+ let path = ctx.sources.get(id).path().display();
+ panic!("Tried to cyclicly evaluate {}", path);
+ }
- /// Evaluate the expression to the output value.
- fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output>;
+ // Check whether the module was already evaluated.
+ if let Some(module) = ctx.modules.get(&id) {
+ if module.valid(&ctx.sources) {
+ return Ok(module.clone());
+ } else {
+ ctx.modules.remove(&id);
+ }
+ }
+
+ route.push(id);
+
+ // Parse the file.
+ let source = ctx.sources.get(id);
+ let ast = source.ast()?;
+
+ // Save the old dependencies.
+ let prev_deps = std::mem::replace(&mut ctx.deps, vec![(id, source.rev())]);
+
+ // Evaluate the module.
+ let std = ctx.config.std.clone();
+ let scopes = Scopes::new(Some(&std));
+ let mut vm = Machine::new(ctx, route, scopes);
+ let result = ast.eval(&mut vm);
+ let scope = vm.scopes.top;
+ let flow = vm.flow;
+
+ // Restore the and dependencies.
+ let deps = std::mem::replace(&mut ctx.deps, prev_deps);
+
+ // Handle control flow.
+ if let Some(flow) = flow {
+ return Err(flow.forbidden());
+ }
+
+ // Assemble the module.
+ let module = Module { scope, content: result?, deps };
+
+ // Save the evaluated module.
+ ctx.modules.insert(id, module.clone());
+
+ Ok(module)
}
/// An evaluated module, ready for importing or layouting.
@@ -70,6 +120,15 @@ impl Module {
}
}
+/// Evaluate an expression.
+pub trait Eval {
+ /// The output of evaluating the expression.
+ type Output;
+
+ /// Evaluate the expression to the output value.
+ fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output>;
+}
+
impl Eval for Markup {
type Output = Content;
@@ -553,7 +612,7 @@ impl Eval for FuncCall {
Value::Dict(dict) => dict.get(&args.into_key()?).at(self.span())?.clone(),
Value::Func(func) => {
let point = || Tracepoint::Call(func.name().map(ToString::to_string));
- func.call(vm.ctx, args).trace(point, self.span())?
+ func.call(vm, args).trace(point, self.span())?
}
v => bail!(
@@ -581,7 +640,7 @@ impl Eval for MethodCall {
} else {
let value = self.receiver().eval(vm)?;
let args = self.args().eval(vm)?;
- methods::call(vm.ctx, value, &method, args, span).trace(point, span)?
+ methods::call(vm, value, &method, args, span).trace(point, span)?
})
}
}
@@ -672,7 +731,7 @@ impl Eval for ClosureExpr {
// Define the actual function.
Ok(Value::Func(Func::from_closure(Closure {
- location: vm.ctx.route.last().copied(),
+ location: vm.route.last().copied(),
name,
captured,
params,
@@ -731,7 +790,7 @@ impl Eval for ShowExpr {
let body = self.body();
let span = body.span();
let func = Func::from_closure(Closure {
- location: vm.ctx.route.last().copied(),
+ location: vm.route.last().copied(),
name: None,
captured,
params,
@@ -875,7 +934,7 @@ impl Eval for ImportExpr {
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> {
let span = self.path().span();
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
- let module = import(vm.ctx, &path, span)?;
+ let module = import(vm, &path, span)?;
match self.imports() {
Imports::Wildcard => {
@@ -904,16 +963,16 @@ impl Eval for IncludeExpr {
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> {
let span = self.path().span();
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
- let module = import(vm.ctx, &path, span)?;
+ let module = import(vm, &path, span)?;
Ok(module.content.clone())
}
}
/// Process an import of a module relative to the current location.
-fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> {
+fn import(vm: &mut Machine, path: &str, span: Span) -> TypResult<Module> {
// Load the source file.
- let full = ctx.locate(&path).at(span)?;
- let id = ctx.sources.load(&full).map_err(|err| match err.kind() {
+ let full = vm.locate(&path).at(span)?;
+ let id = vm.ctx.sources.load(&full).map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => {
error!(span, "file not found (searched at {})", full.display())
}
@@ -921,13 +980,14 @@ fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> {
})?;
// Prevent cyclic importing.
- if ctx.route.contains(&id) {
+ if vm.route.contains(&id) {
bail!(span, "cyclic import");
}
// Evaluate the file.
- let module = ctx.evaluate(id).trace(|| Tracepoint::Import, span)?;
- ctx.deps.extend(module.deps.iter().cloned());
+ let route = vm.route.clone();
+ let module = evaluate(vm.ctx, id, route).trace(|| Tracepoint::Import, span)?;
+ vm.ctx.deps.extend(module.deps.iter().cloned());
Ok(module)
}