1
0

Implement block scopes; Refactor async block execution

This commit is contained in:
Jeff 2024-08-24 10:13:16 -04:00
parent 511cc10e98
commit 561a290b16
5 changed files with 182 additions and 114 deletions

View File

@ -1,6 +1,6 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::HashMap, collections::{HashMap, VecDeque},
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
}; };
@ -1131,15 +1131,15 @@ impl Display for ElseExpression {
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BlockExpression { pub enum BlockExpression {
Async(Vec<Statement>), Async(VecDeque<Statement>),
Sync(Vec<Statement>), Sync(VecDeque<Statement>),
} }
impl BlockExpression { impl BlockExpression {
fn type_evaluation(&self, context: &Context) -> Result<TypeEvaluation, AstError> { fn type_evaluation(&self, context: &Context) -> Result<TypeEvaluation, AstError> {
match self { match self {
BlockExpression::Async(statements) | BlockExpression::Sync(statements) => { BlockExpression::Async(statements) | BlockExpression::Sync(statements) => {
if let Some(statement) = statements.last() { if let Some(statement) = statements.back() {
statement.type_evaluation(context) statement.type_evaluation(context)
} else { } else {
Ok(TypeEvaluation::Return(None)) Ok(TypeEvaluation::Return(None))

View File

@ -30,7 +30,7 @@ impl AbstractSyntaxTree {
} }
} }
pub fn with_statements<const LEN: usize>(statements: [Statement; LEN]) -> Self { pub fn with_statements<T: Into<VecDeque<Statement>>>(statements: T) -> Self {
Self { Self {
statements: statements.into(), statements: statements.into(),
} }

View File

@ -4,6 +4,7 @@
//! - `parse` convenience function //! - `parse` convenience function
//! - `Parser` struct, which parses the input a statement at a time //! - `Parser` struct, which parses the input a statement at a time
use std::{ use std::{
collections::VecDeque,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
num::{ParseFloatError, ParseIntError}, num::{ParseFloatError, ParseIntError},
str::ParseBoolError, str::ParseBoolError,
@ -994,7 +995,7 @@ impl<'src> Parser<'src> {
}); });
} }
let mut statements = Vec::new(); let mut statements = VecDeque::new();
loop { loop {
if let Token::RightCurlyBrace = self.current_token { if let Token::RightCurlyBrace = self.current_token {
@ -1011,7 +1012,7 @@ impl<'src> Parser<'src> {
let statement = self.parse_statement()?; let statement = self.parse_statement()?;
statements.push(statement); statements.push_back(statement);
} }
} }
@ -1177,10 +1178,9 @@ mod tests {
Ok(AbstractSyntaxTree::with_statements([ Ok(AbstractSyntaxTree::with_statements([
Statement::Expression(Expression::infinite_loop( Statement::Expression(Expression::infinite_loop(
Node::new( Node::new(
BlockExpression::Sync(vec![Statement::ExpressionNullified(Node::new( BlockExpression::Sync(VecDeque::from([Statement::ExpressionNullified(
Expression::r#break(None, (7, 12)), Node::new(Expression::r#break(None, (7, 12)), (7, 13))
(7, 13) )])),
))]),
(5, 15) (5, 15)
), ),
(0, 15) (0, 15)
@ -1262,14 +1262,14 @@ mod tests {
(21, 27), (21, 27),
), ),
Node::new( Node::new(
BlockExpression::Sync(vec![Statement::Expression( BlockExpression::Sync(VecDeque::from([Statement::Expression(
Expression::compound_assignment( Expression::compound_assignment(
Expression::identifier(Identifier::new("x"), (30, 31)), Expression::identifier(Identifier::new("x"), (30, 31)),
Node::new(MathOperator::Add, (32, 34)), Node::new(MathOperator::Add, (32, 34)),
Expression::literal(1, (35, 36)), Expression::literal(1, (35, 36)),
(30, 36), (30, 36),
), ),
)]), )])),
(28, 38), (28, 38),
), ),
(15, 38), (15, 38),
@ -1328,7 +1328,7 @@ mod tests {
parse(source), parse(source),
Ok(AbstractSyntaxTree { Ok(AbstractSyntaxTree {
statements: [Statement::Expression(Expression::block( statements: [Statement::Expression(Expression::block(
BlockExpression::Async(vec![ BlockExpression::Async(VecDeque::from([
Statement::ExpressionNullified(Node::new( Statement::ExpressionNullified(Node::new(
Expression::operator( Expression::operator(
OperatorExpression::Assignment { OperatorExpression::Assignment {
@ -1346,7 +1346,7 @@ mod tests {
}, },
(16, 23) (16, 23)
)) ))
]), ])),
(0, 25) (0, 25)
))] ))]
.into() .into()
@ -1645,9 +1645,9 @@ mod tests {
IfExpression::If { IfExpression::If {
condition: Expression::identifier(Identifier::new("x"), (3, 4)), condition: Expression::identifier(Identifier::new("x"), (3, 4)),
if_block: Node::new( if_block: Node::new(
BlockExpression::Sync(vec![Statement::Expression( BlockExpression::Sync(VecDeque::from([Statement::Expression(
Expression::identifier(Identifier::new("y"), (7, 8)) Expression::identifier(Identifier::new("y"), (7, 8))
)]), )])),
(5, 10) (5, 10)
) )
}, },
@ -1668,15 +1668,15 @@ mod tests {
IfExpression::IfElse { IfExpression::IfElse {
condition: Expression::identifier(Identifier::new("x"), (3, 4)), condition: Expression::identifier(Identifier::new("x"), (3, 4)),
if_block: Node::new( if_block: Node::new(
BlockExpression::Sync(vec![Statement::Expression( BlockExpression::Sync(VecDeque::from([Statement::Expression(
Expression::identifier(Identifier::new("y"), (7, 8)) Expression::identifier(Identifier::new("y"), (7, 8))
)]), )])),
(5, 10) (5, 10)
), ),
r#else: ElseExpression::Block(Node::new( r#else: ElseExpression::Block(Node::new(
BlockExpression::Sync(vec![Statement::Expression( BlockExpression::Sync(VecDeque::from([Statement::Expression(
Expression::identifier(Identifier::new("z"), (18, 19)) Expression::identifier(Identifier::new("z"), (18, 19))
)]), )])),
(16, 21) (16, 21)
)) ))
}, },
@ -1697,24 +1697,24 @@ mod tests {
IfExpression::IfElse { IfExpression::IfElse {
condition: Expression::identifier(Identifier::new("x"), (3, 4)), condition: Expression::identifier(Identifier::new("x"), (3, 4)),
if_block: Node::new( if_block: Node::new(
BlockExpression::Sync(vec![Statement::Expression( BlockExpression::Sync(VecDeque::from([Statement::Expression(
Expression::identifier(Identifier::new("y"), (7, 8)) Expression::identifier(Identifier::new("y"), (7, 8))
)]), )])),
(5, 10) (5, 10)
), ),
r#else: ElseExpression::If(Node::new( r#else: ElseExpression::If(Node::new(
Box::new(IfExpression::IfElse { Box::new(IfExpression::IfElse {
condition: Expression::identifier(Identifier::new("z"), (19, 20)), condition: Expression::identifier(Identifier::new("z"), (19, 20)),
if_block: Node::new( if_block: Node::new(
BlockExpression::Sync(vec![Statement::Expression( BlockExpression::Sync(VecDeque::from([Statement::Expression(
Expression::identifier(Identifier::new("a"), (23, 24)) Expression::identifier(Identifier::new("a"), (23, 24))
)]), )])),
(21, 26) (21, 26)
), ),
r#else: ElseExpression::Block(Node::new( r#else: ElseExpression::Block(Node::new(
BlockExpression::Sync(vec![Statement::Expression( BlockExpression::Sync(VecDeque::from([Statement::Expression(
Expression::identifier(Identifier::new("b"), (34, 35)) Expression::identifier(Identifier::new("b"), (34, 35))
)]), )])),
(32, 37) (32, 37)
)), )),
}), }),
@ -1744,14 +1744,19 @@ mod tests {
(6, 12) (6, 12)
), ),
Node::new( Node::new(
BlockExpression::Sync(vec![Statement::Expression(Expression::operator( BlockExpression::Sync(VecDeque::from([Statement::Expression(
OperatorExpression::CompoundAssignment { Expression::operator(
assignee: Expression::identifier(Identifier::new("x"), (15, 16)), OperatorExpression::CompoundAssignment {
operator: Node::new(MathOperator::Add, (17, 19)), assignee: Expression::identifier(
modifier: Expression::literal(1, (20, 21)), Identifier::new("x"),
}, (15, 16)
(15, 21) ),
))]), operator: Node::new(MathOperator::Add, (17, 19)),
modifier: Expression::literal(1, (20, 21)),
},
(15, 21)
)
)])),
(13, 23) (13, 23)
), ),
(0, 23) (0, 23)
@ -1806,14 +1811,16 @@ mod tests {
parse(source), parse(source),
Ok(AbstractSyntaxTree::with_statements([ Ok(AbstractSyntaxTree::with_statements([
Statement::Expression(Expression::block( Statement::Expression(Expression::block(
BlockExpression::Sync(vec![Statement::Expression(Expression::operator( BlockExpression::Sync(VecDeque::from([Statement::Expression(
OperatorExpression::Math { Expression::operator(
left: Expression::literal(40, (2, 4)), OperatorExpression::Math {
operator: Node::new(MathOperator::Add, (5, 6)), left: Expression::literal(40, (2, 4)),
right: Expression::literal(2, (7, 8)), operator: Node::new(MathOperator::Add, (5, 6)),
}, right: Expression::literal(2, (7, 8)),
(2, 8) },
))]), (2, 8)
)
)])),
(0, 10) (0, 10)
)) ))
])) ]))
@ -1828,7 +1835,7 @@ mod tests {
parse(source), parse(source),
Ok(AbstractSyntaxTree::with_statements([ Ok(AbstractSyntaxTree::with_statements([
Statement::Expression(Expression::block( Statement::Expression(Expression::block(
BlockExpression::Sync(vec![ BlockExpression::Sync(VecDeque::from([
Statement::ExpressionNullified(Node::new( Statement::ExpressionNullified(Node::new(
Expression::assignment( Expression::assignment(
Expression::identifier("foo", (2, 5)), Expression::identifier("foo", (2, 5)),
@ -1850,7 +1857,7 @@ mod tests {
Expression::literal("42", (28, 32)), Expression::literal("42", (28, 32)),
(22, 32) (22, 32)
)) ))
]), ])),
(0, 34) (0, 34)
)) ))
])) ]))

View File

@ -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))) .map_err(|error| FunctionCallError::Runtime(Box::new(error)))
} }
} }

View File

@ -10,7 +10,7 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use rayon::prelude::*;
use crate::{ use crate::{
ast::{ ast::{
@ -69,12 +69,13 @@ pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>,
}); });
} }
let mut vm = Vm::new(abstract_syntax_tree, context); let vm = Vm::new(context);
vm.run().map_err(|runtime_error| DustError::Runtime { vm.run(abstract_syntax_tree)
runtime_error, .map_err(|runtime_error| DustError::Runtime {
source, runtime_error,
}) source,
})
} }
/// Dust virtual machine. /// Dust virtual machine.
@ -85,37 +86,71 @@ pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>,
/// ///
/// See the `run_with_context` function for an example of how to use the Analyzer and the VM. /// See the `run_with_context` function for an example of how to use the Analyzer and the VM.
pub struct Vm { pub struct Vm {
abstract_tree: AbstractSyntaxTree,
context: Context, context: Context,
} }
impl Vm { impl Vm {
pub fn new(abstract_tree: AbstractSyntaxTree, context: Context) -> Self { pub fn new(context: Context) -> Self {
Self { Self { context }
abstract_tree,
context,
}
} }
pub fn run(&mut self) -> Result<Option<Value>, RuntimeError> { pub fn run(&self, mut tree: AbstractSyntaxTree) -> Result<Option<Value>, RuntimeError> {
let mut previous_evaluation = None; 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)?; previous_evaluation = self.run_statement(statement, true)?;
} }
match previous_evaluation { match previous_evaluation {
Some(Evaluation::Break(value_option)) => Ok(value_option), Evaluation::Break(value_option) => Ok(value_option),
Some(Evaluation::Return(value_option)) => Ok(value_option), Evaluation::Return(value_option) => Ok(value_option),
_ => Ok(None), _ => Ok(None),
} }
} }
fn run_async(&self, tree: AbstractSyntaxTree) -> Result<Option<Value>, 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( fn run_statement(
&self, &self,
statement: Statement, statement: Statement,
collect_garbage: bool, collect_garbage: bool,
) -> Result<Option<Evaluation>, RuntimeError> { ) -> Result<Evaluation, RuntimeError> {
log::trace!( log::trace!(
"Running statement at {:?}: {}", "Running statement at {:?}: {}",
statement.position(), statement.position(),
@ -125,21 +160,21 @@ impl Vm {
let position = statement.position(); let position = statement.position();
let result = match statement { let result = match statement {
Statement::Expression(expression) => { Statement::Expression(expression) => {
Ok(Some(self.run_expression(expression, collect_garbage)?)) Ok(self.run_expression(expression, collect_garbage)?)
} }
Statement::ExpressionNullified(expression) => { Statement::ExpressionNullified(expression) => {
let evaluation = self.run_expression(expression.inner, collect_garbage)?; let evaluation = self.run_expression(expression.inner, collect_garbage)?;
if let Evaluation::Break(_) = evaluation { if let Evaluation::Break(_) = evaluation {
Ok(Some(evaluation)) Ok(evaluation)
} else { } else {
Ok(None) Ok(Evaluation::Return(None))
} }
} }
Statement::Let(let_statement) => { Statement::Let(let_statement) => {
self.run_let_statement(let_statement.inner, collect_garbage)?; self.run_let_statement(let_statement.inner, collect_garbage)?;
Ok(None) Ok(Evaluation::Return(None))
} }
Statement::StructDefinition(struct_definition) => { Statement::StructDefinition(struct_definition) => {
let (name, struct_type) = match struct_definition.inner { let (name, struct_type) = match struct_definition.inner {
@ -181,7 +216,7 @@ impl Vm {
position: struct_definition.position, position: struct_definition.position,
})?; })?;
Ok(None) Ok(Evaluation::Return(None))
} }
}; };
@ -295,9 +330,15 @@ impl Vm {
Expression::TupleAccess(_) => todo!(), Expression::TupleAccess(_) => todo!(),
}; };
evaluation_result.map_err(|error| RuntimeError::Expression { evaluation_result.map_err(|error| {
error: Box::new(error), if error.position() == position {
position, error
} else {
RuntimeError::Expression {
error: Box::new(error),
position,
}
}
}) })
} }
@ -657,7 +698,7 @@ impl Vm {
for statement in statements.clone() { for statement in statements.clone() {
let evaluation = self.run_statement(statement, false)?; 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)); break 'outer Ok(Evaluation::Return(value_option));
} }
} }
@ -958,54 +999,25 @@ impl Vm {
block: BlockExpression, block: BlockExpression,
collect_garbage: bool, collect_garbage: bool,
) -> Result<Evaluation, RuntimeError> { ) -> Result<Evaluation, RuntimeError> {
let block_context = self.context.create_child();
let vm = Vm::new(block_context);
match block { match block {
BlockExpression::Async(statements) => { BlockExpression::Async(statements) => vm
let final_result = Arc::new(Mutex::new(None)); .run_async(AbstractSyntaxTree::with_statements(statements))
let statements_length = statements.len(); .map(Evaluation::Return),
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::Sync(statements) => { BlockExpression::Sync(statements) => {
let mut previous_evaluation = None; let mut evaluation = Evaluation::Return(None);
for statement in statements { 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 { if let Evaluation::Break(_) = evaluation {
return Ok(Evaluation::Break(value_option)); return Ok(evaluation);
} }
} }
match previous_evaluation { Ok(evaluation)
Some(evaluation) => Ok(evaluation),
None => Ok(Evaluation::Return(None)),
}
} }
} }
} }
@ -1386,6 +1398,55 @@ mod tests {
use super::*; 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] #[test]
fn character() { fn character() {
let input = "'a'"; let input = "'a'";