Add async blocks

This commit is contained in:
Jeff 2024-08-13 23:45:17 -04:00
parent d32633272c
commit 17286896a8
6 changed files with 146 additions and 20 deletions

View File

@ -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, "{{ ")?;

View File

@ -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,

View File

@ -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)),
])
);
}

View File

@ -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";

View File

@ -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"),

View File

@ -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 }";