use crate::basic_io::BasicIO;
use crate::chunk::Chunk;
use crate::compiler::Compiler;
use crate::error::*;
use crate::error_handler::ErrorHandler;
use crate::expr::ExprValue;
use crate::function::{Function, FunctionDefinition, FunctionType};
use crate::native_function::{NativeFunction, NativeFunctionDefinition};
use crate::opcodes::OpCode;
use crate::parser::Parser;
use crate::resolver::Resolver;
use crate::scanner::Scanner;
use crate::std::Std;
use crate::tokens::TokenType;
use crate::vm::VirtualMachine;
use std::collections::HashMap;
pub struct XBasicBuilder<T: 'static> {
stdio: T,
native_functions: HashMap<String, NativeFunction<T>>,
compute_limit: usize,
}
impl<T> XBasicBuilder<T>
where
T: BasicIO,
{
pub fn new(stdio: T) -> Self {
let mut xbb = Self {
stdio,
native_functions: HashMap::new(),
compute_limit: 0, };
Std::attach(&mut xbb);
xbb
}
pub fn define_function<F: 'static>(
&mut self,
name: String,
arity: u8,
function: F,
) -> Result<(), DefineFunctionError>
where
F: Fn(Vec<ExprValue>, &mut T) -> ExprValue,
{
if self.native_functions.len() < 256 {
if self.native_functions.contains_key(&name) {
return Err(DefineFunctionError::NameInUse);
}
self.native_functions
.insert(name.clone(), NativeFunction::new(name, arity, function));
Ok(())
} else {
Err(DefineFunctionError::FunctionLimitReached)
}
}
pub fn compute_limit(&mut self, limit: usize) {
self.compute_limit = limit;
}
pub fn build(self) -> XBasic<T> {
let native_function_definitions = self
.native_functions
.iter()
.map(|(_, v)| NativeFunctionDefinition::new(v.name.clone(), v.arity))
.collect();
XBasic {
error_handler: ErrorHandler::new(),
functions: Vec::new(),
native_functions: native_function_definitions,
compiler: Compiler::new(),
vm: VirtualMachine::new(
self.stdio,
self.native_functions.into_iter().map(|(_, v)| v).collect(),
self.compute_limit,
),
}
}
}
pub struct XBasic<T: 'static> {
pub error_handler: ErrorHandler,
functions: Vec<Function>,
native_functions: Vec<NativeFunctionDefinition>,
compiler: Compiler,
vm: VirtualMachine<T>,
}
impl<T> XBasic<T>
where
T: BasicIO,
{
pub fn new(stdio: T) -> Self {
let xbb = XBasicBuilder::new(stdio);
xbb.build()
}
pub fn run(&mut self, source: &str) -> Result<(), RunError> {
let mut sc = Scanner::new(source, &mut self.error_handler);
let tokens = sc.scan_tokens();
let tokens = match tokens {
Ok(x) => x,
Err(_) => return Err(RunError::Scan),
};
let mut only_whitespace = true;
for token in &tokens {
if token.token_type != TokenType::Newline && token.token_type != TokenType::Eof {
only_whitespace = false;
break;
}
}
if only_whitespace {
return Ok(());
}
let mut function_definitions = {
let mut resolver = Resolver::new(tokens.clone(), &mut self.error_handler);
match resolver.resolve(
&self
.functions
.iter()
.map(|a| a.definition())
.collect::<Vec<_>>(),
) {
Some(x) => x,
None => return Err(RunError::ResolveFunctions),
}
};
for function in &self.native_functions {
let mut unique = true;
for other in &function_definitions {
if function.name == other.name {
unique = false;
break;
}
}
if unique {
function_definitions.push(FunctionDefinition::new(
function.name.to_string(),
function.arity,
0,
FunctionType::Native,
));
}
}
let mut parser = Parser::new(tokens, function_definitions, &mut self.error_handler);
let stmts = match parser.parse() {
Some((stmts, functions)) => {
for func in functions {
self.functions.push(func);
}
stmts
}
None => return Err(RunError::Parse),
};
let native_function_ids: HashMap<String, usize> = self
.native_functions
.iter()
.enumerate()
.map(|(i, f)| (f.name.clone(), i))
.collect();
match self.compiler.compile(
stmts,
&mut self.functions,
native_function_ids,
&mut self.error_handler,
) {
Some(chunk) => {
self.vm.run(
chunk,
self.functions
.iter()
.cloned()
.map(|func| func.chunk.unwrap())
.collect(),
&mut self.error_handler,
);
}
None => return Err(RunError::Compile),
}
if self.error_handler.had_runtime_error {
return Err(RunError::Runtime);
}
Ok(())
}
pub fn clear_errors(&mut self) {
self.error_handler.reset();
self.compiler.clear_errors();
self.vm.clear_errors();
}
pub fn call_function(
&mut self,
name: &str,
arguments: &[ExprValue],
) -> Result<ExprValue, CallFunctionError> {
for (i, function) in self.functions.iter().enumerate() {
if function.name == name {
if function.parameters.len() != arguments.len() {
return Err(CallFunctionError::ArityMismatch);
}
let mut instructions = Vec::new();
for i in 0..arguments.len() {
instructions.push(OpCode::Literal8 as u8);
instructions.push(i as u8);
}
instructions.push(OpCode::Call as u8);
instructions.push(i as u8);
let lines = instructions.iter().map(|_| 0).collect();
let chunk = Chunk::new(instructions, lines, arguments.to_vec(), 0, 0);
if !self.vm.run(
chunk,
self.functions
.iter()
.cloned()
.map(|func| func.chunk.unwrap())
.collect(),
&mut self.error_handler,
) {
return Err(CallFunctionError::Runtime);
}
return Ok(self.vm.pop());
}
}
Err(CallFunctionError::NotFound)
}
pub fn get_io(&self) -> &T {
&self.vm.stdio
}
pub fn get_io_mut(&mut self) -> &mut T {
&mut self.vm.stdio
}
}