use std::collections::HashMap;
use std::sync::Arc;
use crate::error::IonError;
use crate::host_types::{HostEnumDef, HostStructDef, IonType, IonTypeDef};
use crate::interpreter::{Interpreter, Limits};
use crate::lexer::Lexer;
use crate::module::Module;
use crate::parser::Parser;
use crate::stdlib::OutputHandler;
use crate::value::Value;
pub struct Engine {
interpreter: Interpreter,
output: Arc<dyn OutputHandler>,
}
impl Engine {
pub fn new() -> Self {
let output = crate::stdlib::missing_output_handler();
Self {
interpreter: Interpreter::with_output(Arc::clone(&output)),
output,
}
}
pub fn with_output<H>(output: H) -> Self
where
H: OutputHandler + 'static,
{
Self::with_output_handler(Arc::new(output))
}
pub fn with_output_handler(output: Arc<dyn OutputHandler>) -> Self {
Self {
interpreter: Interpreter::with_output(Arc::clone(&output)),
output,
}
}
pub fn eval(&mut self, source: &str) -> Result<Value, IonError> {
let mut lexer = Lexer::new(source);
let tokens = lexer.tokenize()?;
let mut parser = Parser::new(tokens);
let program = parser.parse_program()?;
self.interpreter.eval_program(&program)
}
pub fn set(&mut self, name: &str, value: Value) {
self.interpreter.env.define(name.to_string(), value, false);
}
pub fn get(&self, name: &str) -> Option<Value> {
self.interpreter.env.get(name).cloned()
}
pub fn get_all(&self) -> HashMap<String, Value> {
self.interpreter.env.top_level()
}
pub fn set_limits(&mut self, limits: Limits) {
self.interpreter.limits = limits;
}
pub fn set_output<H>(&mut self, output: H)
where
H: OutputHandler + 'static,
{
self.set_output_handler(Arc::new(output));
}
pub fn set_output_handler(&mut self, output: Arc<dyn OutputHandler>) {
self.output = Arc::clone(&output);
let io = crate::stdlib::io_module_with_output(output);
self.interpreter
.env
.define(io.name.clone(), io.to_value(), false);
}
pub fn register_fn(&mut self, name: &str, func: fn(&[Value]) -> Result<Value, String>) {
self.interpreter.env.define(
name.to_string(),
Value::BuiltinFn(name.to_string(), func),
false,
);
}
pub fn register_closure<F>(&mut self, name: &str, func: F)
where
F: Fn(&[Value]) -> Result<Value, String> + Send + Sync + 'static,
{
self.interpreter.env.define(
name.to_string(),
Value::BuiltinClosure(name.to_string(), crate::value::BuiltinClosureFn::new(func)),
false,
);
}
pub fn register_struct(&mut self, def: HostStructDef) {
self.interpreter.types.register_struct(def);
}
pub fn register_enum(&mut self, def: HostEnumDef) {
self.interpreter.types.register_enum(def);
}
pub fn register_module(&mut self, module: Module) {
let name = module.name.clone();
let value = module.to_value();
self.interpreter.env.define(name, value, false);
}
pub fn register_type<T: IonType>(&mut self) {
match T::ion_type_def() {
IonTypeDef::Struct(def) => self.interpreter.types.register_struct(def),
IonTypeDef::Enum(def) => self.interpreter.types.register_enum(def),
}
}
pub fn set_typed<T: IonType>(&mut self, name: &str, value: &T) {
self.interpreter
.env
.define(name.to_string(), value.to_ion(), false);
}
pub fn get_typed<T: IonType>(&self, name: &str) -> Result<T, String> {
let val = self.interpreter.env.get(name).ok_or_else(|| {
format!(
"{}{}{}",
ion_str!("variable '"),
name,
ion_str!("' not found")
)
})?;
T::from_ion(val)
}
#[cfg(feature = "vm")]
pub fn vm_eval(&mut self, source: &str) -> Result<Value, IonError> {
let mut lexer = Lexer::new(source);
let tokens = lexer.tokenize()?;
let mut parser = Parser::new(tokens);
let program = parser.parse_program()?;
let compiler = crate::compiler::Compiler::new();
match compiler.compile_program(&program) {
Ok((chunk, fn_chunks)) => {
let mut vm = crate::vm::Vm::with_env_and_output(
std::mem::take(&mut self.interpreter.env),
Arc::clone(&self.output),
);
vm.preload_fn_chunks(fn_chunks);
vm.set_types(self.interpreter.types.clone());
let result = vm.execute(&chunk);
self.interpreter.env = std::mem::take(vm.env_mut());
result
}
Err(_) => {
self.interpreter.eval_program(&program)
}
}
}
}
impl Default for Engine {
fn default() -> Self {
Self::new()
}
}