This commit is contained in:
Jeff 2024-03-24 17:34:36 -04:00
parent 2871fd125a
commit 4b460c0e68
4 changed files with 89 additions and 67 deletions

View File

@ -69,6 +69,24 @@ impl AbstractNode for MapIndex {
}; };
} }
if let (
Expression::Value(ValueNode::Structure { fields, .. }),
Expression::Identifier(identifier),
) = (&self.left.node, &self.right.node)
{
return if let Some(type_result) = fields.iter().find_map(|(property, expression)| {
if property == identifier {
Some(expression.node.expected_type(context))
} else {
None
}
}) {
type_result
} else {
Ok(Type::None)
};
}
Err(ValidationError::CannotIndexWith { Err(ValidationError::CannotIndexWith {
collection_type: self.left.node.expected_type(context)?, collection_type: self.left.node.expected_type(context)?,
collection_position: self.left.position, collection_position: self.left.position,

View File

@ -6,7 +6,11 @@ pub mod lexer;
pub mod parser; pub mod parser;
pub mod value; pub mod value;
use std::{cell::RefCell, ops::Range, rc::Rc, vec}; use std::{
ops::Range,
sync::{Arc, RwLock},
vec,
};
use abstract_tree::Type; use abstract_tree::Type;
use ariadne::{Color, Fmt, Label, Report, ReportKind}; use ariadne::{Color, Fmt, Label, Report, ReportKind};
@ -14,42 +18,48 @@ use context::Context;
use error::{Error, RuntimeError, TypeConflict, ValidationError}; use error::{Error, RuntimeError, TypeConflict, ValidationError};
use lexer::lex; use lexer::lex;
use parser::parse; use parser::parse;
use rayon::prelude::*;
pub use value::Value; pub use value::Value;
pub fn interpret<'src>(source_id: &str, source: &str) -> Result<Option<Value>, InterpreterError> { pub fn interpret<'src>(source_id: &str, source: &str) -> Result<Option<Value>, InterpreterError> {
let mut interpreter = Interpreter::new(Context::new()); let mut interpreter = Interpreter::new(Context::new());
interpreter.load_std()?; interpreter.load_std()?;
interpreter.run(Rc::from(source_id), Rc::from(source)) interpreter.run(Arc::from(source_id), Arc::from(source))
} }
pub fn interpret_without_std( pub fn interpret_without_std(
source_id: &str, source_id: &str,
source: &str, source: &str,
) -> Result<Option<Value>, InterpreterError> { ) -> Result<Option<Value>, InterpreterError> {
let mut interpreter = Interpreter::new(Context::new()); let interpreter = Interpreter::new(Context::new());
interpreter.run(Rc::from(source_id.to_string()), Rc::from(source)) interpreter.run(Arc::from(source_id.to_string()), Arc::from(source))
} }
pub struct Interpreter { pub struct Interpreter {
context: Context, context: Context,
sources: Rc<RefCell<Vec<(Rc<str>, Rc<str>)>>>, sources: Arc<RwLock<Vec<(Arc<str>, Arc<str>)>>>,
} }
impl Interpreter { impl Interpreter {
pub fn new(context: Context) -> Self { pub fn new(context: Context) -> Self {
Interpreter { Interpreter {
context, context,
sources: Rc::new(RefCell::new(Vec::new())), sources: Arc::new(RwLock::new(Vec::new())),
} }
} }
pub fn run( pub fn run(
&mut self, &self,
source_id: Rc<str>, source_id: Arc<str>,
source: Rc<str>, source: Arc<str>,
) -> Result<Option<Value>, InterpreterError> { ) -> Result<Option<Value>, InterpreterError> {
self.sources
.write()
.unwrap()
.push((source_id.clone(), source.clone()));
let tokens = lex(source.as_ref()).map_err(|errors| InterpreterError { let tokens = lex(source.as_ref()).map_err(|errors| InterpreterError {
source_id: source_id.clone(), source_id: source_id.clone(),
errors, errors,
@ -60,50 +70,42 @@ impl Interpreter {
})?; })?;
let value_option = abstract_tree let value_option = abstract_tree
.run(&self.context) .run(&self.context)
.map_err(|errors| InterpreterError { .map_err(|errors| InterpreterError { source_id, errors })?;
source_id: source_id.clone(),
errors,
})?;
self.sources.borrow_mut().push((source_id, source.into()));
Ok(value_option) Ok(value_option)
} }
pub fn run_all<T: IntoIterator<Item = (Rc<str>, Rc<str>)>>( pub fn load_std(&mut self) -> Result<(), InterpreterError> {
&mut self, let std_sources = [
sources: T, (
) -> Result<Option<Value>, InterpreterError> { Arc::from("std/io.ds"),
let mut previous_value_option = None; Arc::from(include_str!("../../std/io.ds")),
),
(
Arc::from("std/thread.ds"),
Arc::from(include_str!("../../std/thread.ds")),
),
];
for (source_id, source) in sources { let error = std_sources
previous_value_option = self.run(source_id.clone(), source)?; .into_par_iter()
.find_map_any(|(source_id, source)| self.run(source_id, source).err());
if let Some(error) = error {
Err(error)
} else {
Ok(())
} }
Ok(previous_value_option)
} }
pub fn load_std(&mut self) -> Result<Option<Value>, InterpreterError> { pub fn sources(&self) -> vec::IntoIter<(Arc<str>, Arc<str>)> {
self.run_all([ self.sources.read().unwrap().clone().into_iter()
(
Rc::from("std/io.ds"),
Rc::from(include_str!("../../std/io.ds")),
),
(
Rc::from("std/thread.ds"),
Rc::from(include_str!("../../std/thread.ds")),
),
])
}
pub fn sources(&self) -> vec::IntoIter<(Rc<str>, Rc<str>)> {
self.sources.borrow().clone().into_iter()
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct InterpreterError { pub struct InterpreterError {
source_id: Rc<str>, source_id: Arc<str>,
errors: Vec<Error>, errors: Vec<Error>,
} }
@ -114,7 +116,7 @@ impl InterpreterError {
} }
impl InterpreterError { impl InterpreterError {
pub fn build_reports<'a>(self) -> Vec<Report<'a, (Rc<str>, Range<usize>)>> { pub fn build_reports<'a>(self) -> Vec<Report<'a, (Arc<str>, Range<usize>)>> {
let mut reports = Vec::new(); let mut reports = Vec::new();
for error in self.errors { for error in self.errors {
@ -173,6 +175,17 @@ impl InterpreterError {
span.into(), span.into(),
) )
} }
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 would cause an error.")
.with_note("This error was detected by the interpreter before running the code."),
Some(error),
position,
),
Error::Runtime { error, position } => ( Error::Runtime { error, position } => (
Report::build( Report::build(
ReportKind::Custom("Runtime Error", Color::Red), ReportKind::Custom("Runtime Error", Color::Red),
@ -185,6 +198,9 @@ impl InterpreterError {
) )
.with_help( .with_help(
"This is the interpreter's fault. Please submit a bug with this error message.", "This is the interpreter's fault. Please submit a bug with this error message.",
)
.with_label(
Label::new((self.source_id.clone(), position.0..position.1)).with_message("Runtime error occured here.")
), ),
if let RuntimeError::ValidationFailure(validation_error) = error { if let RuntimeError::ValidationFailure(validation_error) = error {
Some(validation_error) Some(validation_error)
@ -193,17 +209,6 @@ impl InterpreterError {
}, },
position, 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 type_color = Color::Green;
@ -235,14 +240,14 @@ impl InterpreterError {
ValidationError::TypeCheck { ValidationError::TypeCheck {
conflict, conflict,
actual_position, actual_position,
expected_position: expected_postion, expected_position,
} => { } => {
let TypeConflict { actual, expected } = conflict; let TypeConflict { actual, expected } = conflict;
builder.add_labels([ builder.add_labels([
Label::new(( Label::new((
self.source_id.clone(), self.source_id.clone(),
expected_postion.0..expected_postion.1, expected_position.0..expected_position.1,
)) ))
.with_message(format!( .with_message(format!(
"Type {} established here.", "Type {} established here.",
@ -308,11 +313,10 @@ impl InterpreterError {
Type::String.fg(type_color) Type::String.fg(type_color)
)); ));
builder.add_labels([Label::new(( builder.add_label(
self.source_id.clone(), Label::new((self.source_id.clone(), position.0..position.1))
position.0..position.1, .with_message(format!("This has type {}.", actual.fg(type_color),)),
)) )
.with_message(format!("This has type {}.", actual.fg(type_color),))])
} }
} }
} }

View File

@ -3,7 +3,7 @@ use std::{
io::{self, stderr}, io::{self, stderr},
path::PathBuf, path::PathBuf,
process::Command, process::Command,
rc::Rc, sync::Arc,
}; };
use ariadne::sources; use ariadne::sources;
@ -19,7 +19,7 @@ use reedline::{
}; };
pub fn run_shell(context: Context) -> Result<(), io::Error> { pub fn run_shell(context: Context) -> Result<(), io::Error> {
let mut interpreter = Interpreter::new(context.clone()); let interpreter = Interpreter::new(context.clone());
let mut keybindings = default_emacs_keybindings(); let mut keybindings = default_emacs_keybindings();
keybindings.add_binding( keybindings.add_binding(
@ -80,7 +80,7 @@ pub fn run_shell(context: Context) -> Result<(), io::Error> {
continue; continue;
} }
let run_result = interpreter.run(Rc::from("input"), Rc::from(buffer.as_str())); let run_result = interpreter.run(Arc::from("input"), Arc::from(buffer.as_str()));
match run_result { match run_result {
Ok(Some(value)) => { Ok(Some(value)) => {

View File

@ -9,7 +9,7 @@ use colored::Colorize;
use std::{ use std::{
fs::read_to_string, fs::read_to_string,
io::{stderr, Write}, io::{stderr, Write},
rc::Rc, sync::Arc,
}; };
use dust_lang::{context::Context, Interpreter}; use dust_lang::{context::Context, Interpreter};
@ -46,12 +46,12 @@ fn main() {
interpreter.load_std().unwrap(); interpreter.load_std().unwrap();
let (source_id, source) = if let Some(path) = args.path { let (source_id, source): (Arc<str>, Arc<str>) = if let Some(path) = args.path {
let source = read_to_string(&path).unwrap(); let source = read_to_string(&path).unwrap();
(Rc::from(path), source) (Arc::from(path), Arc::from(source.as_str()))
} else if let Some(command) = args.command { } else if let Some(command) = args.command {
(Rc::from("input"), command) (Arc::from("command"), Arc::from(command.as_str()))
} else { } else {
match run_shell(context) { match run_shell(context) {
Ok(_) => {} Ok(_) => {}
@ -61,7 +61,7 @@ fn main() {
return; return;
}; };
let run_result = interpreter.run(source_id, Rc::from(source)); let run_result = interpreter.run(source_id.clone(), source.clone());
match run_result { match run_result {
Ok(value) => { Ok(value) => {