Implement block scopes; Refactor async block execution
This commit is contained in:
parent
511cc10e98
commit
561a290b16
@ -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))
|
||||
|
@ -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(),
|
||||
}
|
||||
|
@ -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)
|
||||
))
|
||||
]))
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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'";
|
||||
|
Loading…
Reference in New Issue
Block a user