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::{
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<Statement>),
Sync(Vec<Statement>),
Async(VecDeque<Statement>),
Sync(VecDeque<Statement>),
}
impl BlockExpression {
fn type_evaluation(&self, context: &Context) -> Result<TypeEvaluation, AstError> {
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))

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 {
statements: statements.into(),
}

View File

@ -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)
))
]))

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)))
}
}

View File

@ -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<Option<Value>,
});
}
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<Option<Value>,
///
/// 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<Option<Value>, RuntimeError> {
let mut previous_evaluation = None;
pub fn run(&self, mut tree: AbstractSyntaxTree) -> Result<Option<Value>, 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<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(
&self,
statement: Statement,
collect_garbage: bool,
) -> Result<Option<Evaluation>, RuntimeError> {
) -> Result<Evaluation, RuntimeError> {
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<Evaluation, RuntimeError> {
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'";