Skip to content

Commit

Permalink
Handle printing in the frontend
Browse files Browse the repository at this point in the history
closes #108
  • Loading branch information
sharkdp authored and David Peter committed Sep 5, 2023
1 parent 89ece08 commit 9f7b548
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 55 deletions.
45 changes: 42 additions & 3 deletions numbat-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use highlighter::NumbatHighlighter;
use numbat::diagnostic::ErrorDiagnostic;
use numbat::pretty_print::PrettyPrint;
use numbat::resolver::{CodeSource, FileSystemImporter, ResolverError};
use numbat::{markup, NameResolutionError, RuntimeError};
use numbat::{markup, InterpreterSettings, NameResolutionError, RuntimeError};
use numbat::{Context, ExitStatus, InterpreterResult, NumbatError};

use anyhow::{bail, Context as AnyhowContext, Result};
Expand Down Expand Up @@ -149,6 +149,12 @@ impl Cli {

if load_prelude {
let ctx = self.context.clone();
let mut no_print_settings = InterpreterSettings {
print_fn: Box::new(
move |_: &str| { // ignore any print statements when loading this module asynchronously
},
),
};
thread::spawn(move || {
numbat::Context::fetch_exchange_rates();

Expand All @@ -158,7 +164,11 @@ impl Cli {
// a short delay (the limiting factor is the HTTP request).
ctx.lock()
.unwrap()
.interpret("use units::currencies", CodeSource::Internal)
.interpret_with_settings(
&mut no_print_settings,
"use units::currencies",
CodeSource::Internal,
)
.ok();
});
}
Expand Down Expand Up @@ -331,7 +341,20 @@ impl Cli {
execution_mode: ExecutionMode,
pretty_print_mode: PrettyPrintMode,
) -> ControlFlow {
let result = { self.context.lock().unwrap().interpret(input, code_source) };
let to_be_printed: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
let to_be_printed_c = to_be_printed.clone();
let mut settings = InterpreterSettings {
print_fn: Box::new(move |s: &str| {
to_be_printed_c.lock().unwrap().push(s.to_string());
}),
};

let result = {
self.context
.lock()
.unwrap()
.interpret_with_settings(&mut settings, input, code_source)
};

let pretty_print = match pretty_print_mode {
PrettyPrintMode::Always => true,
Expand All @@ -352,6 +375,22 @@ impl Cli {
println!();
}

let to_be_printed = to_be_printed.lock().unwrap();
for s in to_be_printed.iter() {
print!(
"{}{}",
if execution_mode == ExecutionMode::Interactive {
" "
} else {
""
},
s
);
}
if !to_be_printed.is_empty() && execution_mode == ExecutionMode::Interactive {
println!();
}

match interpreter_result {
InterpreterResult::Quantity(quantity) => {
let q_markup = markup::whitespace(" ")
Expand Down
26 changes: 18 additions & 8 deletions numbat/src/bytecode_interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use std::collections::HashMap;

use crate::ast::ProcedureKind;
use crate::interpreter::{Interpreter, InterpreterResult, Result, RuntimeError};
use crate::interpreter::{
Interpreter, InterpreterResult, InterpreterSettings, Result, RuntimeError,
};
use crate::prefix::Prefix;
use crate::typed_ast::{BinaryOperator, Expression, Statement, UnaryOperator};
use crate::unit::Unit;
use crate::unit_registry::UnitRegistry;
use crate::vm::{Constant, Op, Vm};
use crate::vm::{Constant, ExecutionContext, Op, Vm};
use crate::{decorator, ffi};

pub struct BytecodeInterpreter {
Expand Down Expand Up @@ -209,12 +211,16 @@ impl BytecodeInterpreter {
Ok(())
}

fn run(&mut self) -> Result<InterpreterResult> {
self.vm.disassemble();
fn run(&mut self, settings: &mut InterpreterSettings) -> Result<InterpreterResult> {
let mut ctx = ExecutionContext {
print_fn: &mut settings.print_fn,
};

self.vm.disassemble(&mut ctx);

let result = self.vm.run();
let result = self.vm.run(&mut ctx);

self.vm.debug();
self.vm.debug(&mut ctx);

result
}
Expand All @@ -233,7 +239,11 @@ impl Interpreter for BytecodeInterpreter {
}
}

fn interpret_statements(&mut self, statements: &[Statement]) -> Result<InterpreterResult> {
fn interpret_statements(
&mut self,
settings: &mut InterpreterSettings,
statements: &[Statement],
) -> Result<InterpreterResult> {
if statements.is_empty() {
return Err(RuntimeError::NoStatements);
};
Expand All @@ -242,7 +252,7 @@ impl Interpreter for BytecodeInterpreter {
self.compile_statement(statement)?;
}

self.run()
self.run(settings)
}

fn get_unit_registry(&self) -> &UnitRegistry {
Expand Down
9 changes: 5 additions & 4 deletions numbat/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::OnceLock;

use crate::currency::ExchangeRatesCache;
use crate::interpreter::RuntimeError;
use crate::vm::ExecutionContext;
use crate::{ast::ProcedureKind, quantity::Quantity};

type ControlFlow = std::ops::ControlFlow<RuntimeError>;
Expand All @@ -14,7 +15,7 @@ type BoxedFunction = Box<dyn Fn(&[Quantity]) -> Quantity + Send + Sync>;

pub(crate) enum Callable {
Function(BoxedFunction),
Procedure(fn(&[Quantity]) -> ControlFlow),
Procedure(fn(&mut ExecutionContext, &[Quantity]) -> ControlFlow),
}

pub(crate) struct ForeignFunction {
Expand Down Expand Up @@ -284,15 +285,15 @@ pub(crate) fn functions() -> &'static HashMap<String, ForeignFunction> {
})
}

fn print(args: &[Quantity]) -> ControlFlow {
fn print(ctx: &mut ExecutionContext, args: &[Quantity]) -> ControlFlow {
assert!(args.len() == 1);

println!("{}", args[0]);
(ctx.print_fn)(&format!("{}\n", args[0]));

ControlFlow::Continue(())
}

fn assert_eq(args: &[Quantity]) -> ControlFlow {
fn assert_eq(_: &mut ExecutionContext, args: &[Quantity]) -> ControlFlow {
assert!(args.len() == 2 || args.len() == 3);

if args.len() == 2 {
Expand Down
25 changes: 23 additions & 2 deletions numbat/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,30 @@ impl InterpreterResult {

pub type Result<T> = std::result::Result<T, RuntimeError>;

pub type PrintFunction = dyn FnMut(&str) -> () + Send;

pub struct InterpreterSettings {
pub print_fn: Box<PrintFunction>,
}

impl Default for InterpreterSettings {
fn default() -> Self {
Self {
print_fn: Box::new(move |s: &str| {
print!("{}", s);
}),
}
}
}

pub trait Interpreter {
fn new() -> Self;

fn interpret_statements(&mut self, statements: &[Statement]) -> Result<InterpreterResult>;
fn interpret_statements(
&mut self,
settings: &mut InterpreterSettings,
statements: &[Statement],
) -> Result<InterpreterResult>;
fn get_unit_registry(&self) -> &UnitRegistry;
}

Expand Down Expand Up @@ -105,7 +125,8 @@ mod tests {
let statements_typechecked = crate::typechecker::TypeChecker::default()
.check_statements(statements_transformed)
.expect("No type check errors for inputs in this test suite");
BytecodeInterpreter::new().interpret_statements(&statements_typechecked)
BytecodeInterpreter::new()
.interpret_statements(&mut InterpreterSettings::default(), &statements_typechecked)
}

fn assert_evaluates_to(input: &str, expected: Quantity) {
Expand Down
14 changes: 13 additions & 1 deletion numbat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use ast::Statement;
pub use diagnostic::Diagnostic;
pub use interpreter::ExitStatus;
pub use interpreter::InterpreterResult;
pub use interpreter::InterpreterSettings;
pub use interpreter::RuntimeError;
pub use name_resolution::NameResolutionError;
pub use parser::ParseError;
Expand Down Expand Up @@ -138,6 +139,15 @@ impl Context {
&mut self,
code: &str,
code_source: CodeSource,
) -> Result<(Vec<Statement>, InterpreterResult)> {
self.interpret_with_settings(&mut InterpreterSettings::default(), code, code_source)
}

pub fn interpret_with_settings(
&mut self,
settings: &mut InterpreterSettings,
code: &str,
code_source: CodeSource,
) -> Result<(Vec<Statement>, InterpreterResult)> {
let statements = self
.resolver
Expand Down Expand Up @@ -174,7 +184,9 @@ impl Context {

let typed_statements = result?;

let result = self.interpreter.interpret_statements(&typed_statements);
let result = self
.interpreter
.interpret_statements(settings, &typed_statements);

if result.is_err() {
// Similar to above: we need to reset the state of the typechecker and the prefix transformer
Expand Down
Loading

0 comments on commit 9f7b548

Please sign in to comment.