new error handling!
This commit is contained in:
154
src/lib.rs
154
src/lib.rs
@@ -2,17 +2,19 @@ mod tokenizer;
|
||||
mod parser;
|
||||
mod executor;
|
||||
mod function;
|
||||
mod error;
|
||||
|
||||
use executor::{Executor, RuntimeError};
|
||||
use executor::Executor;
|
||||
use parser::{ParseTree, Parser};
|
||||
use tokenizer::Tokenizer;
|
||||
use function::{FunctionType, Function};
|
||||
use error::Error;
|
||||
|
||||
use core::str;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::io::BufRead;
|
||||
use std::fmt;
|
||||
use std::iter::Peekable;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -147,16 +149,15 @@ impl Object {
|
||||
}
|
||||
|
||||
/// evaluate the tree inside an object if it isn't evaluated yet, returns the value
|
||||
pub fn eval(&mut self) -> Result<Value, RuntimeError> {
|
||||
pub fn eval(&mut self) -> Result<Value, Error> {
|
||||
match self.value.clone() {
|
||||
Cache::Cached(v) => Ok(v),
|
||||
Cache::Uncached(tree) => {
|
||||
let mut t = vec![Ok(tree.clone())].into_iter();
|
||||
|
||||
let mut exec = Executor::new(&mut t, &mut self.globals)
|
||||
let mut exec = Executor::new()
|
||||
.add_globals(self.globals.clone())
|
||||
.locals(self.locals.clone());
|
||||
|
||||
let v = exec.exec(Box::new(tree))?;
|
||||
let v = exec.exec(tree)?;
|
||||
|
||||
self.value = Cache::Cached(v.clone());
|
||||
|
||||
@@ -174,20 +175,111 @@ impl Object {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Runtime<'a, R: BufRead> {
|
||||
tokenizer: Peekable<Tokenizer<R>>,
|
||||
global_types: HashMap<String, Type>,
|
||||
globals: HashMap<String, Arc<Mutex<Object>>>,
|
||||
parser: Option<Parser<'a, Tokenizer<R>>>,
|
||||
/// A custom type used in the tokenizer to automatically keep track of which character we are on
|
||||
pub(crate) struct CodeIter<R: BufRead> {
|
||||
reader: R,
|
||||
code: String,
|
||||
|
||||
// position in code
|
||||
pos: usize,
|
||||
|
||||
// the current line number
|
||||
line: usize,
|
||||
|
||||
// column in the current line
|
||||
column: usize,
|
||||
}
|
||||
|
||||
impl<'a, R: BufRead> Runtime<'a, R> {
|
||||
pub fn new(reader: R, name: &str) -> Self {
|
||||
impl<R: BufRead> CodeIter<R> {
|
||||
fn new(reader: R) -> Self {
|
||||
Self {
|
||||
tokenizer: Tokenizer::new(reader, name).peekable(),
|
||||
reader,
|
||||
code: String::new(),
|
||||
pos: 0,
|
||||
line: 0,
|
||||
column: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn getpos(&self) -> (usize, usize) {
|
||||
(self.line, self.column)
|
||||
}
|
||||
|
||||
fn code(&self) -> String {
|
||||
self.code.clone()
|
||||
}
|
||||
|
||||
// Peekable is useless here because I cannot access the inner object otherwise
|
||||
pub(crate) fn peek(&mut self) -> Option<char> {
|
||||
if let Some(c) = self.code.chars().nth(self.pos) {
|
||||
Some(c)
|
||||
} else {
|
||||
match self.reader.read_line(&mut self.code) {
|
||||
Ok(0) => return None,
|
||||
Ok(_) => (),
|
||||
Err(_e) => panic!("aaaa"),
|
||||
};
|
||||
|
||||
self.peek()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next_if(&mut self, func: impl FnOnce(&char) -> bool) -> Option<char> {
|
||||
let c = self.peek()?;
|
||||
|
||||
if (func)(&c) {
|
||||
self.next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: BufRead> Iterator for CodeIter<R> {
|
||||
type Item = char;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(c) = self.code.chars().nth(self.pos) {
|
||||
match c {
|
||||
'\n' => {
|
||||
self.line += 1;
|
||||
self.column = 0;
|
||||
self.pos += 1;
|
||||
|
||||
None
|
||||
},
|
||||
c => {
|
||||
self.column += 1;
|
||||
self.pos += 1;
|
||||
Some(c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self.reader.read_line(&mut self.code) {
|
||||
Ok(0) => return None,
|
||||
Ok(_) => (),
|
||||
Err(_e) => panic!("aaaa"),
|
||||
};
|
||||
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Runtime<R: BufRead> {
|
||||
reader: Arc<Mutex<CodeIter<R>>>,
|
||||
filename: String,
|
||||
global_types: HashMap<String, Type>,
|
||||
globals: HashMap<String, Arc<Mutex<Object>>>,
|
||||
}
|
||||
|
||||
impl<R: BufRead> Runtime<R> {
|
||||
pub fn new(reader: R, filename: &str) -> Self {
|
||||
Self {
|
||||
reader: Arc::new(Mutex::new(CodeIter::new(reader))),
|
||||
filename: filename.to_string(),
|
||||
global_types: HashMap::new(),
|
||||
globals: HashMap::new(),
|
||||
parser: None,
|
||||
}.add_global("version'", Value::String(
|
||||
format!("{} ({}/{})",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
@@ -197,6 +289,12 @@ impl<'a, R: BufRead> Runtime<'a, R> {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn code(&self) -> String {
|
||||
let reader = self.reader.lock().unwrap();
|
||||
let code = reader.code();
|
||||
code
|
||||
}
|
||||
|
||||
pub fn add_global(mut self, name: &str, value: Value) -> Self {
|
||||
self.global_types.insert(name.to_string(), value.get_type());
|
||||
self.globals.insert(name.to_string(),
|
||||
@@ -207,9 +305,27 @@ impl<'a, R: BufRead> Runtime<'a, R> {
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn values(&'a mut self) -> impl Iterator<Item = Result<Value, RuntimeError>> + 'a {
|
||||
self.parser = Some(Parser::new(&mut self.tokenizer, &mut self.global_types));
|
||||
Executor::new(self.parser.as_mut().unwrap(), &mut self.globals)
|
||||
impl<R: BufRead> Iterator for Runtime<R> {
|
||||
type Item = Result<Value, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let tokenizer = Tokenizer::new(self.reader.clone());
|
||||
|
||||
let tree = Parser::new()
|
||||
.add_globals(self.global_types.clone())
|
||||
.parse(&mut tokenizer.peekable());
|
||||
|
||||
let tree = match tree.map_err(|e| e
|
||||
.code(self.code())
|
||||
.file(self.filename.clone()))
|
||||
{
|
||||
Ok(Some(tree)) => tree,
|
||||
Ok(None) => return None,
|
||||
Err(e) => return Some(Err(e))
|
||||
};
|
||||
|
||||
Some(Executor::new().add_globals(self.globals.clone()).exec(tree))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user