pub mod abstract_tree;
pub mod built_in_functions;
pub mod context;
pub mod error;
pub mod lexer;
pub mod parser;
pub mod value;
use std::{cell::RefCell, ops::Range, rc::Rc, vec};
use abstract_tree::Type;
use ariadne::{Color, Fmt, Label, Report, ReportKind};
use context::Context;
use error::{Error, RuntimeError, TypeConflict, ValidationError};
use lexer::lex;
use parser::parse;
pub use value::Value;
pub fn interpret<'src>(source_id: &str, source: &str) -> Result, InterpreterError> {
let mut interpreter = Interpreter::new(Context::new());
interpreter.run(Rc::new(source_id.to_string()), Rc::from(source))
}
pub fn interpret_without_std(
source_id: &str,
source: &str,
) -> Result , InterpreterError> {
let mut interpreter = Interpreter::new(Context::new());
interpreter.run(Rc::new(source_id.to_string()), Rc::from(source))
}
pub struct Interpreter {
context: Context,
sources: Rc, Rc)>>>,
}
impl Interpreter {
pub fn new(context: Context) -> Self {
Interpreter {
context,
sources: Rc::new(RefCell::new(Vec::new())),
}
}
pub fn run(
&mut self,
source_id: Rc,
source: Rc,
) -> Result, InterpreterError> {
let tokens = lex(source.as_ref()).map_err(|errors| InterpreterError {
source_id: source_id.clone(),
errors,
})?;
let abstract_tree = parse(&tokens).map_err(|errors| InterpreterError {
source_id: source_id.clone(),
errors,
})?;
let value_option = abstract_tree
.run(&self.context)
.map_err(|errors| InterpreterError {
source_id: source_id.clone(),
errors,
})?;
self.sources.borrow_mut().push((source_id, source.into()));
Ok(value_option)
}
pub fn run_all, Rc)>>(
&mut self,
sources: T,
) -> Result, InterpreterError> {
let mut previous_value_option = None;
for (source_id, source) in sources {
previous_value_option = self.run(source_id.clone(), source)?;
}
Ok(previous_value_option)
}
pub fn load_std(&mut self) -> Result , InterpreterError> {
self.run_all([
(
Rc::new("std/io.ds".to_string()),
Rc::from(include_str!("../../std/io.ds")),
),
(
Rc::new("std/thread.ds".to_string()),
Rc::from(include_str!("../../std/thread.ds")),
),
])
}
pub fn sources(&self) -> vec::IntoIter<(Rc, Rc)> {
self.sources.borrow().clone().into_iter()
}
}
#[derive(Debug, PartialEq)]
pub struct InterpreterError {
source_id: Rc,
errors: Vec,
}
impl InterpreterError {
pub fn errors(&self) -> &Vec {
&self.errors
}
}
impl InterpreterError {
pub fn build_reports<'a>(self) -> Vec, Range)>> {
let mut reports = Vec::new();
for error in self.errors {
let (mut builder, validation_error, error_position) = match error {
Error::Lex {
expected,
span,
reason,
} => {
let description = if expected.is_empty() {
"Invalid character.".to_string()
} else {
format!("Expected {expected}.")
};
(
Report::build(
ReportKind::Custom("Lexing Error", Color::Yellow),
self.source_id.clone(),
span.1,
)
.with_message(description)
.with_label(
Label::new((self.source_id.clone(), span.0..span.1))
.with_message(reason)
.with_color(Color::Red),
),
None,
span.into(),
)
}
Error::Parse {
expected,
span,
reason,
} => {
let description = if expected.is_empty() {
"Invalid token.".to_string()
} else {
format!("Expected {expected}.")
};
(
Report::build(
ReportKind::Custom("Parsing Error", Color::Yellow),
self.source_id.clone(),
span.1,
)
.with_message(description)
.with_label(
Label::new((self.source_id.clone(), span.0..span.1))
.with_message(reason)
.with_color(Color::Red),
),
None,
span.into(),
)
}
Error::Runtime { error, position } => (
Report::build(
ReportKind::Custom("Runtime Error", Color::Red),
self.source_id.clone(),
position.1,
)
.with_message("An error occured that forced the program to exit.")
.with_note(
"There may be unexpected side-effects because the program could not finish.",
)
.with_help(
"This is the interpreter's fault. Please submit a bug with this error message.",
),
if let RuntimeError::ValidationFailure(validation_error) = error {
Some(validation_error)
} else {
None
},
position,
),
Error::Validation { error, position } => (
Report::build(
ReportKind::Custom("Validation Error", Color::Magenta),
self.source_id.clone(),
position.1,
)
.with_message("The syntax is valid but this code is not sound.")
.with_note("This error was detected by the interpreter before running the code."),
Some(error),
position,
),
};
let type_color = Color::Green;
let identifier_color = Color::Blue;
if let Some(validation_error) = validation_error {
match validation_error {
ValidationError::ExpectedBoolean { actual, position } => {
builder.add_label(
Label::new((self.source_id.clone(), position.0..position.1))
.with_message(format!(
"Expected {} but got {}.",
"boolean".fg(type_color),
actual.fg(type_color)
)),
);
}
ValidationError::ExpectedIntegerOrFloat(position) => {
builder.add_label(
Label::new((self.source_id.clone(), position.0..position.1))
.with_message(format!(
"Expected {} or {}.",
"integer".fg(type_color),
"float".fg(type_color)
)),
);
}
ValidationError::RwLockPoison(_) => todo!(),
ValidationError::TypeCheck {
conflict,
actual_position,
expected_position: expected_postion,
} => {
let TypeConflict { actual, expected } = conflict;
builder.add_labels([
Label::new((
self.source_id.clone(),
expected_postion.0..expected_postion.1,
))
.with_message(format!(
"Type {} established here.",
expected.fg(type_color)
)),
Label::new((
self.source_id.clone(),
actual_position.0..actual_position.1,
))
.with_message(format!("Got type {} here.", actual.fg(type_color))),
]);
}
ValidationError::VariableNotFound(identifier) => builder.add_label(
Label::new((self.source_id.clone(), error_position.0..error_position.1))
.with_message(format!(
"Variable {} does not exist in this context.",
identifier.fg(identifier_color)
)),
),
ValidationError::CannotIndex { r#type, position } => builder.add_label(
Label::new((self.source_id.clone(), position.0..position.1)).with_message(
format!("Cannot index into a {}.", r#type.fg(type_color)),
),
),
ValidationError::CannotIndexWith {
collection_type,
collection_position,
index_type,
index_position,
} => {
builder = builder.with_message(format!(
"Cannot index into {} with {}.",
collection_type.clone().fg(type_color),
index_type.clone().fg(type_color)
));
builder.add_labels([
Label::new((
self.source_id.clone(),
collection_position.0..collection_position.1,
))
.with_message(format!(
"This has type {}.",
collection_type.fg(type_color),
)),
Label::new((
self.source_id.clone(),
index_position.0..index_position.1,
))
.with_message(format!("This has type {}.", index_type.fg(type_color),)),
])
}
ValidationError::InterpreterExpectedReturn(_) => todo!(),
ValidationError::ExpectedFunction { .. } => todo!(),
ValidationError::ExpectedValue(_) => todo!(),
ValidationError::PropertyNotFound { .. } => todo!(),
ValidationError::WrongArguments { .. } => todo!(),
ValidationError::ExpectedIntegerFloatOrString { actual, position } => {
builder = builder.with_message(format!(
"Expected an {}, {} or {}.",
Type::Integer.fg(type_color),
Type::Float.fg(type_color),
Type::String.fg(type_color)
));
builder.add_labels([Label::new((
self.source_id.clone(),
position.0..position.1,
))
.with_message(format!("This has type {}.", actual.fg(type_color),))])
}
}
}
let report = builder.finish();
reports.push(report);
}
reports
}
}