From e29e092875734c17cd9ffecd3f16998999541550 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 20 Mar 2024 17:05:37 -0400 Subject: [PATCH] Begin implementing async blocks --- Cargo.lock | 46 ++++++++++++++++++ dust-lang/Cargo.toml | 1 + dust-lang/src/abstract_tree/async_block.rs | 56 ++++++++++++++++++++++ dust-lang/src/abstract_tree/mod.rs | 2 + dust-lang/src/abstract_tree/statement.rs | 8 +++- dust-lang/src/error.rs | 6 +++ dust-lang/src/lexer.rs | 3 ++ dust-lang/src/lib.rs | 2 - dust-lang/src/parser.rs | 42 ++++++++++++++++ 9 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 dust-lang/src/abstract_tree/async_block.rs diff --git a/Cargo.lock b/Cargo.lock index 49038dc..dafa4bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,6 +288,31 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + [[package]] name = "crossterm" version = "0.27.0" @@ -351,6 +376,7 @@ dependencies = [ "env_logger", "log", "rand", + "rayon", "stanza", ] @@ -883,6 +909,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.4.1" diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index d58e075..a2e6e0a 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -16,4 +16,5 @@ colored = "2.1.0" env_logger = "0.11.3" log = "0.4.21" rand = "0.8.5" +rayon = "1.9.0" stanza = "0.5.1" diff --git a/dust-lang/src/abstract_tree/async_block.rs b/dust-lang/src/abstract_tree/async_block.rs new file mode 100644 index 0000000..7d297a1 --- /dev/null +++ b/dust-lang/src/abstract_tree/async_block.rs @@ -0,0 +1,56 @@ +use rayon::prelude::*; + +use crate::{ + context::Context, + error::{RuntimeError, ValidationError}, +}; + +use super::{AbstractNode, Action, Statement, Type, WithPosition}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct AsyncBlock { + statements: Vec>, +} + +impl AsyncBlock { + pub fn new(statements: Vec>) -> Self { + Self { statements } + } +} + +impl AbstractNode for AsyncBlock { + fn expected_type(&self, _context: &Context) -> Result { + self.statements.last().unwrap().node.expected_type(_context) + } + + fn validate(&self, _context: &Context) -> Result<(), ValidationError> { + for statement in &self.statements { + statement.node.validate(_context)?; + } + + Ok(()) + } + + fn run(self, _context: &Context) -> Result { + let statement_count = self.statements.len(); + + self.statements + .into_par_iter() + .enumerate() + .find_map_any(|(index, statement)| { + let result = statement.node.run(_context); + + match result { + Ok(action) => { + if index == statement_count - 1 { + Some(Ok(action)) + } else { + None + } + } + Err(runtime_error) => Some(Err(runtime_error)), + } + }) + .unwrap() + } +} diff --git a/dust-lang/src/abstract_tree/mod.rs b/dust-lang/src/abstract_tree/mod.rs index bf884f8..1412355 100644 --- a/dust-lang/src/abstract_tree/mod.rs +++ b/dust-lang/src/abstract_tree/mod.rs @@ -1,4 +1,5 @@ pub mod assignment; +pub mod async_block; pub mod block; pub mod expression; pub mod function_call; @@ -21,6 +22,7 @@ use chumsky::span::{SimpleSpan, Span}; pub use self::{ assignment::{Assignment, AssignmentOperator}, + async_block::AsyncBlock, block::Block, expression::Expression, function_call::FunctionCall, diff --git a/dust-lang/src/abstract_tree/statement.rs b/dust-lang/src/abstract_tree/statement.rs index b141d28..d873569 100644 --- a/dust-lang/src/abstract_tree/statement.rs +++ b/dust-lang/src/abstract_tree/statement.rs @@ -4,13 +4,14 @@ use crate::{ }; use super::{ - AbstractNode, Action, Assignment, Block, Expression, IfElse, Loop, StructureDefinition, Type, - While, + AbstractNode, Action, Assignment, AsyncBlock, Block, Expression, IfElse, Loop, + StructureDefinition, Type, While, }; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Statement { Assignment(Assignment), + AsyncBlock(AsyncBlock), Block(Block), Break, Expression(Expression), @@ -33,6 +34,7 @@ impl AbstractNode for Statement { fn expected_type(&self, _context: &Context) -> Result { match self { Statement::Assignment(assignment) => assignment.expected_type(_context), + Statement::AsyncBlock(async_block) => async_block.expected_type(_context), Statement::Block(block) => block.expected_type(_context), Statement::Break => Ok(Type::None), Statement::Expression(expression) => expression.expected_type(_context), @@ -48,6 +50,7 @@ impl AbstractNode for Statement { fn validate(&self, _context: &Context) -> Result<(), ValidationError> { match self { Statement::Assignment(assignment) => assignment.validate(_context), + Statement::AsyncBlock(async_block) => async_block.validate(_context), Statement::Block(block) => block.validate(_context), Statement::Break => Ok(()), Statement::Expression(expression) => expression.validate(_context), @@ -63,6 +66,7 @@ impl AbstractNode for Statement { fn run(self, _context: &Context) -> Result { match self { Statement::Assignment(assignment) => assignment.run(_context), + Statement::AsyncBlock(async_block) => async_block.run(_context), Statement::Block(block) => block.run(_context), Statement::Break => Ok(Action::Break), Statement::Expression(expression) => expression.run(_context), diff --git a/dust-lang/src/error.rs b/dust-lang/src/error.rs index b8f85c9..99f3c4f 100644 --- a/dust-lang/src/error.rs +++ b/dust-lang/src/error.rs @@ -247,6 +247,12 @@ impl From for RuntimeError { } } +impl From> for RuntimeError { + fn from(_: PoisonError) -> Self { + RuntimeError::RwLockPoison(RwLockPoisonError) + } +} + impl From for RuntimeError { fn from(error: ValidationError) -> Self { RuntimeError::ValidationFailure(error) diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 427abc6..5a7da6c 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -19,6 +19,7 @@ pub enum Token<'src> { #[derive(Copy, Clone, Debug, PartialEq)] pub enum Keyword { Any, + Async, Bool, Break, Else, @@ -39,6 +40,7 @@ impl Display for Keyword { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Keyword::Any => write!(f, "any"), + Keyword::Async => write!(f, "async"), Keyword::Bool => write!(f, "bool"), Keyword::Break => write!(f, "break"), Keyword::Else => write!(f, "else"), @@ -255,6 +257,7 @@ pub fn lexer<'src>() -> impl Parser< let keyword = choice(( just("any").to(Keyword::Any), + just("async").to(Keyword::Async), just("bool").to(Keyword::Bool), just("break").to(Keyword::Break), just("else").to(Keyword::Else), diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 8349dd3..01b346a 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -1,5 +1,3 @@ -extern crate chumsky; - pub mod abstract_tree; pub mod context; pub mod error; diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 4967733..251f99d 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -395,6 +395,21 @@ pub fn parser<'src>() -> impl Parser< Statement::Expression(node).with_position(position) }); + let async_block = just(Token::Keyword(Keyword::Async)) + .ignore_then( + positioned_statement + .clone() + .repeated() + .collect() + .delimited_by( + just(Token::Control(Control::CurlyOpen)), + just(Token::Control(Control::CurlyClose)), + ), + ) + .map_with(|statements, state| { + Statement::AsyncBlock(AsyncBlock::new(statements)).with_position(state.span()) + }); + let r#break = just(Token::Keyword(Keyword::Break)) .map_with(|_, state| Statement::Break.with_position(state.span())); @@ -485,6 +500,7 @@ pub fn parser<'src>() -> impl Parser< }); choice(( + async_block, structure_definition, if_else, assignment, @@ -506,6 +522,32 @@ mod tests { use super::*; + #[test] + fn async_block() { + assert_eq!( + parse( + &lex(" + async { + 1 + 2 + 3 + } + ") + .unwrap() + ) + .unwrap()[0] + .node, + Statement::AsyncBlock(AsyncBlock::new(vec![ + Statement::Expression(Expression::Value(ValueNode::Integer(1))) + .with_position((53, 54)), + Statement::Expression(Expression::Value(ValueNode::Integer(2))) + .with_position((79, 80)), + Statement::Expression(Expression::Value(ValueNode::Integer(3))) + .with_position((105, 106)), + ])) + ) + } + #[test] fn structure_instance() { assert_eq!(