Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ndc_bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn main() -> anyhow::Result<()> {

let string = fs::read_to_string(path)?;

let mut interpreter = Interpreter::new();
let mut interpreter = Interpreter::one_shot();
interpreter.configure(ndc_stdlib::register);

#[cfg(feature = "trace")]
Expand Down
79 changes: 78 additions & 1 deletion ndc_interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub struct Interpreter {
/// `None` until the first `eval` call; kept alive afterwards so that
/// variables declared on one line are visible on subsequent lines.
repl_state: Option<(Vm, Compiler)>,
resumable: bool,
#[cfg(feature = "trace")]
tracer: Option<Box<dyn tracer::VmTracer>>,
}
Expand Down Expand Up @@ -58,11 +59,24 @@ impl Interpreter {
),
source_db: SourceDb::new(),
repl_state: None,
resumable: true,
#[cfg(feature = "trace")]
tracer: None,
}
}

/// Create an interpreter for executing a complete program once.
///
/// Unlike the default resumable interpreter used by the REPL, this uses
/// the optimizing compiler and does not retain state after execution.
#[must_use]
pub fn one_shot() -> Self {
Self {
resumable: false,
..Self::from_capturing(false)
}
}

pub fn configure<F: FnOnce(&mut FunctionRegistry<Rc<NativeFunction>>)>(&mut self, f: F) {
f(&mut self.function_registry);
let functions = self
Expand Down Expand Up @@ -190,7 +204,7 @@ impl Interpreter {
let analyser_checkpoint = self.analyser.checkpoint();
let (expressions, _, mut timings) = self.parse_and_analyse(input, source_id)?;
let vm_result = self.interpret_vm(input, expressions.into_iter());
if vm_result.is_err() {
if vm_result.is_err() || !self.resumable {
self.analyser.restore(analyser_checkpoint);
}
let (value, vm_timings) = vm_result?;
Expand Down Expand Up @@ -259,6 +273,28 @@ impl Interpreter {
.collect();

let mut timings = ExecutionTimings::default();
if !self.resumable {
let code = measure(&mut timings, Phase::Compiling, || {
self.compile_one_shot(expressions)
})?;
let num_locals = code.num_locals();
let output = if self.capturing {
OutputSink::Buffer(Vec::new())
} else {
OutputSink::Stdout
};
let mut vm = Vm::new(code, globals).with_output(output);
#[cfg(feature = "trace")]
{
vm = vm.with_source(input);
if let Some(tracer) = self.tracer.take() {
vm = vm.with_tracer(tracer);
}
}
measure(&mut timings, Phase::Running, || vm.run())?;
return Ok((vm.last_value(num_locals), timings));
}

let result = match self.repl_state.take() {
None => {
let output = if self.capturing {
Expand Down Expand Up @@ -311,6 +347,13 @@ impl Interpreter {

Ok((result, timings))
}

fn compile_one_shot(
&self,
expressions: impl Iterator<Item = ExpressionLocation>,
) -> Result<CompiledFunction, ndc_vm::CompileError> {
Compiler::compile(expressions, Rc::clone(&self.struct_registry))
}
}

impl Default for Interpreter {
Expand Down Expand Up @@ -343,3 +386,37 @@ pub enum InterpreterError {
#[error("{0}")]
Vm(#[from] ndc_vm::VmError),
}

#[cfg(test)]
mod tests {
use super::*;
use ndc_vm::chunk::OpCode;

#[test]
fn one_shot_compilation_runs_the_optimizer() {
let mut interpreter = Interpreter::one_shot();
let source_id = interpreter.source_db.add("<test>", "1;");
let (expressions, _, _) = interpreter
.parse_and_analyse("1;", source_id)
.expect("analysis should succeed");

let compiled = interpreter
.compile_one_shot(expressions.into_iter())
.expect("compilation should succeed");

assert_eq!(compiled.opcodes(), [OpCode::Halt]);
}

#[test]
fn one_shot_execution_does_not_retain_declarations() {
let mut interpreter = Interpreter::one_shot();
interpreter
.eval("let value = 1;")
.expect("first program should succeed");

assert!(
interpreter.eval("value;").is_err(),
"a one-shot interpreter must treat each program as isolated",
);
}
}
5 changes: 5 additions & 0 deletions ndc_vm/src/value/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ pub enum UpvalueCell {
}

impl CompiledFunction {
#[must_use]
pub fn num_locals(&self) -> usize {
self.num_locals
}

pub fn opcodes(&self) -> &[OpCode] {
self.body.opcodes()
}
Expand Down
Loading