Add async blocks
This commit is contained in:
parent
d32633272c
commit
17286896a8
@ -55,7 +55,8 @@ pub enum Statement {
|
||||
value: Box<Node<Statement>>,
|
||||
},
|
||||
|
||||
// A sequence of statements
|
||||
// Statement blocks, delimited by curly braces
|
||||
AsyncBlock(Vec<Node<Statement>>),
|
||||
Block(Vec<Node<Statement>>),
|
||||
|
||||
// Logic, math and comparison expressions with two operands
|
||||
@ -139,8 +140,9 @@ pub enum Statement {
|
||||
impl Statement {
|
||||
pub fn expected_type(&self, context: &Context) -> Option<Type> {
|
||||
match self {
|
||||
Statement::AsyncBlock(_) => None,
|
||||
Statement::Assignment { .. } => None,
|
||||
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(context),
|
||||
Statement::Block(statements) => statements.last().unwrap().inner.expected_type(context),
|
||||
Statement::BinaryOperation {
|
||||
left,
|
||||
operator,
|
||||
@ -247,6 +249,19 @@ impl Display for Statement {
|
||||
} => {
|
||||
write!(f, "{identifier} {operator} {value}")
|
||||
}
|
||||
Statement::AsyncBlock(statements) => {
|
||||
write!(f, "async {{ ")?;
|
||||
|
||||
for (i, statement) in statements.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
|
||||
write!(f, "{statement}")?;
|
||||
}
|
||||
|
||||
write!(f, " }}")
|
||||
}
|
||||
Statement::Block(statements) => {
|
||||
write!(f, "{{ ")?;
|
||||
|
||||
|
@ -93,6 +93,13 @@ impl<'a> Analyzer<'a> {
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Statement::AsyncBlock(statements) => {
|
||||
for statement in statements {
|
||||
self.analyze_statement(statement)?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Statement::BinaryOperation {
|
||||
left,
|
||||
operator,
|
||||
|
@ -419,6 +419,7 @@ impl Lexer {
|
||||
let token = match string {
|
||||
"Infinity" => Token::Float("Infinity"),
|
||||
"NaN" => Token::Float("NaN"),
|
||||
"async" => Token::Async,
|
||||
"bool" => Token::Bool,
|
||||
"else" => Token::Else,
|
||||
"false" => Token::Boolean("false"),
|
||||
@ -507,27 +508,28 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn all_keywords() {
|
||||
let input = "bool else false float if int is_even is_odd length read_line struct to_string true while write_line";
|
||||
let input = "async bool else false float if int is_even is_odd length read_line struct to_string true while write_line";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::Bool, (0, 4)),
|
||||
(Token::Else, (5, 9)),
|
||||
(Token::Boolean("false"), (10, 15)),
|
||||
(Token::FloatKeyword, (16, 21)),
|
||||
(Token::If, (22, 24)),
|
||||
(Token::Int, (25, 28)),
|
||||
(Token::IsEven, (29, 36)),
|
||||
(Token::IsOdd, (37, 43)),
|
||||
(Token::Length, (44, 50)),
|
||||
(Token::ReadLine, (51, 60)),
|
||||
(Token::Struct, (61, 67)),
|
||||
(Token::ToString, (68, 77)),
|
||||
(Token::Boolean("true"), (78, 82)),
|
||||
(Token::While, (83, 88)),
|
||||
(Token::WriteLine, (89, 99)),
|
||||
(Token::Eof, (99, 99)),
|
||||
(Token::Async, (0, 5)),
|
||||
(Token::Bool, (6, 10)),
|
||||
(Token::Else, (11, 15)),
|
||||
(Token::Boolean("false"), (16, 21)),
|
||||
(Token::FloatKeyword, (22, 27)),
|
||||
(Token::If, (28, 30)),
|
||||
(Token::Int, (31, 34)),
|
||||
(Token::IsEven, (35, 42)),
|
||||
(Token::IsOdd, (43, 49)),
|
||||
(Token::Length, (50, 56)),
|
||||
(Token::ReadLine, (57, 66)),
|
||||
(Token::Struct, (67, 73)),
|
||||
(Token::ToString, (74, 83)),
|
||||
(Token::Boolean("true"), (84, 88)),
|
||||
(Token::While, (89, 94)),
|
||||
(Token::WriteLine, (95, 105)),
|
||||
(Token::Eof, (105, 105)),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
@ -229,6 +229,37 @@ impl<'src> Parser<'src> {
|
||||
|
||||
fn parse_primary(&mut self) -> Result<Node<Statement>, ParseError> {
|
||||
match self.current {
|
||||
(Token::Async, position) => {
|
||||
self.next_token()?;
|
||||
|
||||
if let Token::LeftCurlyBrace = self.current.0 {
|
||||
self.next_token()?;
|
||||
} else {
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
actual: self.current.0.to_owned(),
|
||||
position: self.current.1,
|
||||
});
|
||||
}
|
||||
|
||||
let mut statements = Vec::new();
|
||||
|
||||
loop {
|
||||
if let Token::RightCurlyBrace = self.current.0 {
|
||||
let right_end = self.current.1 .1;
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::AsyncBlock(statements),
|
||||
(position.0, right_end),
|
||||
));
|
||||
}
|
||||
|
||||
let statement = self.parse_statement(0)?;
|
||||
|
||||
statements.push(statement);
|
||||
}
|
||||
}
|
||||
(Token::Boolean(text), position) => {
|
||||
self.next_token()?;
|
||||
|
||||
@ -1180,6 +1211,48 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn async_block() {
|
||||
let input = "async { x = 42; y = 4.0 }";
|
||||
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::AsyncBlock(vec![
|
||||
Node::new(
|
||||
Statement::Nil(Box::new(Node::new(
|
||||
Statement::Assignment {
|
||||
identifier: Node::new(Identifier::new("x"), (8, 9)),
|
||||
operator: Node::new(AssignmentOperator::Assign, (10, 11)),
|
||||
value: Box::new(Node::new(
|
||||
Statement::Constant(Value::integer(42)),
|
||||
(12, 14)
|
||||
)),
|
||||
},
|
||||
(8, 14)
|
||||
))),
|
||||
(8, 15)
|
||||
),
|
||||
Node::new(
|
||||
Statement::Assignment {
|
||||
identifier: Node::new(Identifier::new("y"), (16, 17)),
|
||||
operator: Node::new(AssignmentOperator::Assign, (18, 19)),
|
||||
value: Box::new(Node::new(
|
||||
Statement::Constant(Value::float(4.0)),
|
||||
(20, 23)
|
||||
)),
|
||||
},
|
||||
(16, 23)
|
||||
)
|
||||
]),
|
||||
(0, 25)
|
||||
)]
|
||||
.into()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_struct_access() {
|
||||
let input = "Foo(42, 'bar').0";
|
||||
|
@ -17,6 +17,7 @@ pub enum Token<'src> {
|
||||
String(&'src str),
|
||||
|
||||
// Keywords
|
||||
Async,
|
||||
Bool,
|
||||
Else,
|
||||
FloatKeyword,
|
||||
@ -65,6 +66,7 @@ pub enum Token<'src> {
|
||||
impl<'src> Token<'src> {
|
||||
pub fn to_owned(&self) -> TokenOwned {
|
||||
match self {
|
||||
Token::Async => TokenOwned::Async,
|
||||
Token::Bang => TokenOwned::Bang,
|
||||
Token::Bool => TokenOwned::Bool,
|
||||
Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()),
|
||||
@ -123,6 +125,7 @@ impl<'src> Token<'src> {
|
||||
Token::Integer(integer_text) => integer_text,
|
||||
Token::String(text) => text,
|
||||
|
||||
Token::Async => "async",
|
||||
Token::Bang => "!",
|
||||
Token::Bool => "bool",
|
||||
Token::Colon => ":",
|
||||
@ -170,6 +173,7 @@ impl<'src> Token<'src> {
|
||||
|
||||
pub fn kind(&self) -> TokenKind {
|
||||
match self {
|
||||
Token::Async => TokenKind::Async,
|
||||
Token::Bang => TokenKind::Bang,
|
||||
Token::Bool => TokenKind::Bool,
|
||||
Token::Boolean(_) => TokenKind::Boolean,
|
||||
@ -309,6 +313,7 @@ pub enum TokenOwned {
|
||||
WriteLine,
|
||||
|
||||
// Symbols
|
||||
Async,
|
||||
Bang,
|
||||
Colon,
|
||||
Comma,
|
||||
@ -342,6 +347,7 @@ pub enum TokenOwned {
|
||||
impl Display for TokenOwned {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TokenOwned::Async => Token::Async.fmt(f),
|
||||
TokenOwned::Bang => Token::Bang.fmt(f),
|
||||
TokenOwned::Bool => write!(f, "bool"),
|
||||
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
|
||||
@ -407,6 +413,7 @@ pub enum TokenKind {
|
||||
String,
|
||||
|
||||
// Keywords
|
||||
Async,
|
||||
Bool,
|
||||
Else,
|
||||
FloatKeyword,
|
||||
@ -455,6 +462,7 @@ pub enum TokenKind {
|
||||
impl Display for TokenKind {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TokenKind::Async => Token::Async.fmt(f),
|
||||
TokenKind::Bang => Token::Bang.fmt(f),
|
||||
TokenKind::Bool => Token::Bool.fmt(f),
|
||||
TokenKind::Boolean => write!(f, "boolean value"),
|
||||
@ -510,8 +518,9 @@ impl Display for TokenKind {
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
pub fn all_tokens<'src>() -> [Token<'src>; 46] {
|
||||
pub fn all_tokens<'src>() -> [Token<'src>; 47] {
|
||||
[
|
||||
Token::Async,
|
||||
Token::Bang,
|
||||
Token::Bool,
|
||||
Token::Boolean("true"),
|
||||
|
@ -9,6 +9,8 @@ use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
};
|
||||
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
use crate::{
|
||||
parse, value::ValueInner, AbstractSyntaxTree, Analyzer, AssignmentOperator, BinaryOperator,
|
||||
BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement,
|
||||
@ -173,6 +175,17 @@ impl Vm {
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
Statement::AsyncBlock(statements) => {
|
||||
let error_option = statements
|
||||
.into_par_iter()
|
||||
.find_map_any(|statement| self.run_statement(statement).err());
|
||||
|
||||
if let Some(error) = error_option {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
Statement::BinaryOperation {
|
||||
left,
|
||||
operator,
|
||||
@ -856,6 +869,13 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn async_block() {
|
||||
let input = "x = 1; async { x += 1; x -= 1; } x";
|
||||
|
||||
assert!(run(input).unwrap().unwrap().as_integer().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn define_and_instantiate_fields_struct() {
|
||||
let input = "struct Foo { bar: int, baz: float } Foo { bar = 42, baz = 4.0 }";
|
||||
|
Loading…
Reference in New Issue
Block a user