From 561a290b16f15aaf0d3b8214d31409945eeabaf6 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 24 Aug 2024 10:13:16 -0400 Subject: [PATCH] Implement block scopes; Refactor async block execution --- dust-lang/src/ast/expression.rs | 8 +- dust-lang/src/ast/mod.rs | 2 +- dust-lang/src/parser.rs | 87 +++++++------- dust-lang/src/value.rs | 4 +- dust-lang/src/vm.rs | 195 +++++++++++++++++++++----------- 5 files changed, 182 insertions(+), 114 deletions(-) diff --git a/dust-lang/src/ast/expression.rs b/dust-lang/src/ast/expression.rs index 0e6366d..a651488 100644 --- a/dust-lang/src/ast/expression.rs +++ b/dust-lang/src/ast/expression.rs @@ -1,6 +1,6 @@ use std::{ cmp::Ordering, - collections::HashMap, + collections::{HashMap, VecDeque}, fmt::{self, Display, Formatter}, }; @@ -1131,15 +1131,15 @@ impl Display for ElseExpression { #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum BlockExpression { - Async(Vec), - Sync(Vec), + Async(VecDeque), + Sync(VecDeque), } impl BlockExpression { fn type_evaluation(&self, context: &Context) -> Result { match self { BlockExpression::Async(statements) | BlockExpression::Sync(statements) => { - if let Some(statement) = statements.last() { + if let Some(statement) = statements.back() { statement.type_evaluation(context) } else { Ok(TypeEvaluation::Return(None)) diff --git a/dust-lang/src/ast/mod.rs b/dust-lang/src/ast/mod.rs index 377e03a..b980fd9 100644 --- a/dust-lang/src/ast/mod.rs +++ b/dust-lang/src/ast/mod.rs @@ -30,7 +30,7 @@ impl AbstractSyntaxTree { } } - pub fn with_statements(statements: [Statement; LEN]) -> Self { + pub fn with_statements>>(statements: T) -> Self { Self { statements: statements.into(), } diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 1a94178..a23f833 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -4,6 +4,7 @@ //! - `parse` convenience function //! - `Parser` struct, which parses the input a statement at a time use std::{ + collections::VecDeque, fmt::{self, Display, Formatter}, num::{ParseFloatError, ParseIntError}, str::ParseBoolError, @@ -994,7 +995,7 @@ impl<'src> Parser<'src> { }); } - let mut statements = Vec::new(); + let mut statements = VecDeque::new(); loop { if let Token::RightCurlyBrace = self.current_token { @@ -1011,7 +1012,7 @@ impl<'src> Parser<'src> { let statement = self.parse_statement()?; - statements.push(statement); + statements.push_back(statement); } } @@ -1177,10 +1178,9 @@ mod tests { Ok(AbstractSyntaxTree::with_statements([ Statement::Expression(Expression::infinite_loop( Node::new( - BlockExpression::Sync(vec![Statement::ExpressionNullified(Node::new( - Expression::r#break(None, (7, 12)), - (7, 13) - ))]), + BlockExpression::Sync(VecDeque::from([Statement::ExpressionNullified( + Node::new(Expression::r#break(None, (7, 12)), (7, 13)) + )])), (5, 15) ), (0, 15) @@ -1262,14 +1262,14 @@ mod tests { (21, 27), ), Node::new( - BlockExpression::Sync(vec![Statement::Expression( + BlockExpression::Sync(VecDeque::from([Statement::Expression( Expression::compound_assignment( Expression::identifier(Identifier::new("x"), (30, 31)), Node::new(MathOperator::Add, (32, 34)), Expression::literal(1, (35, 36)), (30, 36), ), - )]), + )])), (28, 38), ), (15, 38), @@ -1328,7 +1328,7 @@ mod tests { parse(source), Ok(AbstractSyntaxTree { statements: [Statement::Expression(Expression::block( - BlockExpression::Async(vec![ + BlockExpression::Async(VecDeque::from([ Statement::ExpressionNullified(Node::new( Expression::operator( OperatorExpression::Assignment { @@ -1346,7 +1346,7 @@ mod tests { }, (16, 23) )) - ]), + ])), (0, 25) ))] .into() @@ -1645,9 +1645,9 @@ mod tests { IfExpression::If { condition: Expression::identifier(Identifier::new("x"), (3, 4)), if_block: Node::new( - BlockExpression::Sync(vec![Statement::Expression( + BlockExpression::Sync(VecDeque::from([Statement::Expression( Expression::identifier(Identifier::new("y"), (7, 8)) - )]), + )])), (5, 10) ) }, @@ -1668,15 +1668,15 @@ mod tests { IfExpression::IfElse { condition: Expression::identifier(Identifier::new("x"), (3, 4)), if_block: Node::new( - BlockExpression::Sync(vec![Statement::Expression( + BlockExpression::Sync(VecDeque::from([Statement::Expression( Expression::identifier(Identifier::new("y"), (7, 8)) - )]), + )])), (5, 10) ), r#else: ElseExpression::Block(Node::new( - BlockExpression::Sync(vec![Statement::Expression( + BlockExpression::Sync(VecDeque::from([Statement::Expression( Expression::identifier(Identifier::new("z"), (18, 19)) - )]), + )])), (16, 21) )) }, @@ -1697,24 +1697,24 @@ mod tests { IfExpression::IfElse { condition: Expression::identifier(Identifier::new("x"), (3, 4)), if_block: Node::new( - BlockExpression::Sync(vec![Statement::Expression( + BlockExpression::Sync(VecDeque::from([Statement::Expression( Expression::identifier(Identifier::new("y"), (7, 8)) - )]), + )])), (5, 10) ), r#else: ElseExpression::If(Node::new( Box::new(IfExpression::IfElse { condition: Expression::identifier(Identifier::new("z"), (19, 20)), if_block: Node::new( - BlockExpression::Sync(vec![Statement::Expression( + BlockExpression::Sync(VecDeque::from([Statement::Expression( Expression::identifier(Identifier::new("a"), (23, 24)) - )]), + )])), (21, 26) ), r#else: ElseExpression::Block(Node::new( - BlockExpression::Sync(vec![Statement::Expression( + BlockExpression::Sync(VecDeque::from([Statement::Expression( Expression::identifier(Identifier::new("b"), (34, 35)) - )]), + )])), (32, 37) )), }), @@ -1744,14 +1744,19 @@ mod tests { (6, 12) ), Node::new( - BlockExpression::Sync(vec![Statement::Expression(Expression::operator( - OperatorExpression::CompoundAssignment { - assignee: Expression::identifier(Identifier::new("x"), (15, 16)), - operator: Node::new(MathOperator::Add, (17, 19)), - modifier: Expression::literal(1, (20, 21)), - }, - (15, 21) - ))]), + BlockExpression::Sync(VecDeque::from([Statement::Expression( + Expression::operator( + OperatorExpression::CompoundAssignment { + assignee: Expression::identifier( + Identifier::new("x"), + (15, 16) + ), + operator: Node::new(MathOperator::Add, (17, 19)), + modifier: Expression::literal(1, (20, 21)), + }, + (15, 21) + ) + )])), (13, 23) ), (0, 23) @@ -1806,14 +1811,16 @@ mod tests { parse(source), Ok(AbstractSyntaxTree::with_statements([ Statement::Expression(Expression::block( - BlockExpression::Sync(vec![Statement::Expression(Expression::operator( - OperatorExpression::Math { - left: Expression::literal(40, (2, 4)), - operator: Node::new(MathOperator::Add, (5, 6)), - right: Expression::literal(2, (7, 8)), - }, - (2, 8) - ))]), + BlockExpression::Sync(VecDeque::from([Statement::Expression( + Expression::operator( + OperatorExpression::Math { + left: Expression::literal(40, (2, 4)), + operator: Node::new(MathOperator::Add, (5, 6)), + right: Expression::literal(2, (7, 8)), + }, + (2, 8) + ) + )])), (0, 10) )) ])) @@ -1828,7 +1835,7 @@ mod tests { parse(source), Ok(AbstractSyntaxTree::with_statements([ Statement::Expression(Expression::block( - BlockExpression::Sync(vec![ + BlockExpression::Sync(VecDeque::from([ Statement::ExpressionNullified(Node::new( Expression::assignment( Expression::identifier("foo", (2, 5)), @@ -1850,7 +1857,7 @@ mod tests { Expression::literal("42", (28, 32)), (22, 32) )) - ]), + ])), (0, 34) )) ])) diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index d1fea48..f65c8e8 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -1398,9 +1398,9 @@ impl Function { } } - let mut vm = Vm::new(body, new_context); + let mut vm = Vm::new(new_context); - vm.run() + vm.run(body) .map_err(|error| FunctionCallError::Runtime(Box::new(error))) } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 05777ba..f06ed2b 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -10,7 +10,7 @@ use std::{ sync::{Arc, Mutex}, }; -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; +use rayon::prelude::*; use crate::{ ast::{ @@ -69,12 +69,13 @@ pub fn run_with_context(source: &str, context: Context) -> Result, }); } - let mut vm = Vm::new(abstract_syntax_tree, context); + let vm = Vm::new(context); - vm.run().map_err(|runtime_error| DustError::Runtime { - runtime_error, - source, - }) + vm.run(abstract_syntax_tree) + .map_err(|runtime_error| DustError::Runtime { + runtime_error, + source, + }) } /// Dust virtual machine. @@ -85,37 +86,71 @@ pub fn run_with_context(source: &str, context: Context) -> Result, /// /// See the `run_with_context` function for an example of how to use the Analyzer and the VM. pub struct Vm { - abstract_tree: AbstractSyntaxTree, context: Context, } impl Vm { - pub fn new(abstract_tree: AbstractSyntaxTree, context: Context) -> Self { - Self { - abstract_tree, - context, - } + pub fn new(context: Context) -> Self { + Self { context } } - pub fn run(&mut self) -> Result, RuntimeError> { - let mut previous_evaluation = None; + pub fn run(&self, mut tree: AbstractSyntaxTree) -> Result, RuntimeError> { + let mut previous_evaluation = Evaluation::Return(None); - while let Some(statement) = self.abstract_tree.statements.pop_front() { + while let Some(statement) = tree.statements.pop_front() { previous_evaluation = self.run_statement(statement, true)?; } match previous_evaluation { - Some(Evaluation::Break(value_option)) => Ok(value_option), - Some(Evaluation::Return(value_option)) => Ok(value_option), + Evaluation::Break(value_option) => Ok(value_option), + Evaluation::Return(value_option) => Ok(value_option), _ => Ok(None), } } + fn run_async(&self, tree: AbstractSyntaxTree) -> Result, RuntimeError> { + let final_result = Arc::new(Mutex::new(Evaluation::Return(None))); + let statements_length = tree.statements.len(); + + let error_option = + tree.statements + .into_par_iter() + .enumerate() + .find_map_any(|(i, statement)| { + let evaluation_result = self.run_statement(statement, false); + + match evaluation_result { + Ok(evaluation_option) => { + if i == statements_length - 1 { + let mut final_result = final_result.lock().unwrap(); + + *final_result = evaluation_option; + } + + None + } + Err(error) => Some(error), + } + }); + + if let Some(error) = error_option { + Err(error) + } else { + let final_result = final_result.lock().unwrap(); + + match &*final_result { + Evaluation::Break(value_option) => Ok(value_option.clone()), + Evaluation::Return(value_option) => Ok(value_option.clone()), + _ => Ok(None), + } + } + } + fn run_statement( &self, statement: Statement, collect_garbage: bool, - ) -> Result, RuntimeError> { + ) -> Result { log::trace!( "Running statement at {:?}: {}", statement.position(), @@ -125,21 +160,21 @@ impl Vm { let position = statement.position(); let result = match statement { Statement::Expression(expression) => { - Ok(Some(self.run_expression(expression, collect_garbage)?)) + Ok(self.run_expression(expression, collect_garbage)?) } Statement::ExpressionNullified(expression) => { let evaluation = self.run_expression(expression.inner, collect_garbage)?; if let Evaluation::Break(_) = evaluation { - Ok(Some(evaluation)) + Ok(evaluation) } else { - Ok(None) + Ok(Evaluation::Return(None)) } } Statement::Let(let_statement) => { self.run_let_statement(let_statement.inner, collect_garbage)?; - Ok(None) + Ok(Evaluation::Return(None)) } Statement::StructDefinition(struct_definition) => { let (name, struct_type) = match struct_definition.inner { @@ -181,7 +216,7 @@ impl Vm { position: struct_definition.position, })?; - Ok(None) + Ok(Evaluation::Return(None)) } }; @@ -295,9 +330,15 @@ impl Vm { Expression::TupleAccess(_) => todo!(), }; - evaluation_result.map_err(|error| RuntimeError::Expression { - error: Box::new(error), - position, + evaluation_result.map_err(|error| { + if error.position() == position { + error + } else { + RuntimeError::Expression { + error: Box::new(error), + position, + } + } }) } @@ -657,7 +698,7 @@ impl Vm { for statement in statements.clone() { let evaluation = self.run_statement(statement, false)?; - if let Some(Evaluation::Break(value_option)) = evaluation { + if let Evaluation::Break(value_option) = evaluation { break 'outer Ok(Evaluation::Return(value_option)); } } @@ -958,54 +999,25 @@ impl Vm { block: BlockExpression, collect_garbage: bool, ) -> Result { + let block_context = self.context.create_child(); + let vm = Vm::new(block_context); + match block { - BlockExpression::Async(statements) => { - let final_result = Arc::new(Mutex::new(None)); - let statements_length = statements.len(); - let error_option = - statements - .into_par_iter() - .enumerate() - .find_map_any(|(i, statement)| { - let evaluation_result = self.run_statement(statement, false); - - match evaluation_result { - Ok(evaluation_option) => { - if i == statements_length - 1 { - let mut final_result = final_result.lock().unwrap(); - - *final_result = evaluation_option; - } - - None - } - Err(error) => Some(error), - } - }); - - if let Some(error) = error_option { - Err(error) - } else if let Some(evaluation) = final_result.lock().unwrap().take() { - Ok(evaluation) - } else { - Ok(Evaluation::Return(None)) - } - } + BlockExpression::Async(statements) => vm + .run_async(AbstractSyntaxTree::with_statements(statements)) + .map(Evaluation::Return), BlockExpression::Sync(statements) => { - let mut previous_evaluation = None; + let mut evaluation = Evaluation::Return(None); for statement in statements { - previous_evaluation = self.run_statement(statement, collect_garbage)?; + evaluation = vm.run_statement(statement, collect_garbage)?; - if let Some(Evaluation::Break(value_option)) = previous_evaluation { - return Ok(Evaluation::Break(value_option)); + if let Evaluation::Break(_) = evaluation { + return Ok(evaluation); } } - match previous_evaluation { - Some(evaluation) => Ok(evaluation), - None => Ok(Evaluation::Return(None)), - } + Ok(evaluation) } } } @@ -1386,6 +1398,55 @@ mod tests { use super::*; + #[test] + fn block_scope_captures_parent() { + let source = "let x = 42; { x }"; + + assert_eq!(run(source), Ok(Some(Value::integer(42)))); + } + + #[test] + fn block_scope_does_not_capture_child() { + let source = "{ let x = 42; } x"; + + assert_eq!( + run(source), + Err(DustError::Runtime { + runtime_error: RuntimeError::UnassociatedIdentifier { + identifier: Identifier::new("x"), + position: (16, 17) + }, + source + }) + ); + } + + #[test] + fn block_scope_does_not_capture_sibling() { + let source = "{ let x = 42; } { x }"; + + assert_eq!( + run(source), + Err(DustError::Runtime { + runtime_error: RuntimeError::Expression { + error: Box::new(RuntimeError::UnassociatedIdentifier { + identifier: Identifier::new("x"), + position: (18, 19) + }), + position: (16, 21) + }, + source + }) + ); + } + + #[test] + fn block_scope_does_not_pollute_parent() { + let source = "let x = 42; { let x = \"foo\"; let x = \"bar\"; } x"; + + assert_eq!(run(source), Ok(Some(Value::integer(42)))); + } + #[test] fn character() { let input = "'a'";