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>>, value: Box<Node<Statement>>,
}, },
// A sequence of statements // Statement blocks, delimited by curly braces
AsyncBlock(Vec<Node<Statement>>),
Block(Vec<Node<Statement>>), Block(Vec<Node<Statement>>),
// Logic, math and comparison expressions with two operands // Logic, math and comparison expressions with two operands
@ -139,8 +140,9 @@ pub enum Statement {
impl Statement { impl Statement {
pub fn expected_type(&self, context: &Context) -> Option<Type> { pub fn expected_type(&self, context: &Context) -> Option<Type> {
match self { match self {
Statement::AsyncBlock(_) => None,
Statement::Assignment { .. } => 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 { Statement::BinaryOperation {
left, left,
operator, operator,
@ -247,6 +249,19 @@ impl Display for Statement {
} => { } => {
write!(f, "{identifier} {operator} {value}") 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) => { Statement::Block(statements) => {
write!(f, "{{ ")?; write!(f, "{{ ")?;

View File

@ -93,6 +93,13 @@ impl<'a> Analyzer<'a> {
return Ok(()); return Ok(());
} }
Statement::AsyncBlock(statements) => {
for statement in statements {
self.analyze_statement(statement)?;
}
return Ok(());
}
Statement::BinaryOperation { Statement::BinaryOperation {
left, left,
operator, operator,

View File

@ -419,6 +419,7 @@ impl Lexer {
let token = match string { let token = match string {
"Infinity" => Token::Float("Infinity"), "Infinity" => Token::Float("Infinity"),
"NaN" => Token::Float("NaN"), "NaN" => Token::Float("NaN"),
"async" => Token::Async,
"bool" => Token::Bool, "bool" => Token::Bool,
"else" => Token::Else, "else" => Token::Else,
"false" => Token::Boolean("false"), "false" => Token::Boolean("false"),
@ -507,27 +508,28 @@ mod tests {
#[test] #[test]
fn all_keywords() { 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!( assert_eq!(
lex(input), lex(input),
Ok(vec![ Ok(vec![
(Token::Bool, (0, 4)), (Token::Async, (0, 5)),
(Token::Else, (5, 9)), (Token::Bool, (6, 10)),
(Token::Boolean("false"), (10, 15)), (Token::Else, (11, 15)),
(Token::FloatKeyword, (16, 21)), (Token::Boolean("false"), (16, 21)),
(Token::If, (22, 24)), (Token::FloatKeyword, (22, 27)),
(Token::Int, (25, 28)), (Token::If, (28, 30)),
(Token::IsEven, (29, 36)), (Token::Int, (31, 34)),
(Token::IsOdd, (37, 43)), (Token::IsEven, (35, 42)),
(Token::Length, (44, 50)), (Token::IsOdd, (43, 49)),
(Token::ReadLine, (51, 60)), (Token::Length, (50, 56)),
(Token::Struct, (61, 67)), (Token::ReadLine, (57, 66)),
(Token::ToString, (68, 77)), (Token::Struct, (67, 73)),
(Token::Boolean("true"), (78, 82)), (Token::ToString, (74, 83)),
(Token::While, (83, 88)), (Token::Boolean("true"), (84, 88)),
(Token::WriteLine, (89, 99)), (Token::While, (89, 94)),
(Token::Eof, (99, 99)), (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> { fn parse_primary(&mut self) -> Result<Node<Statement>, ParseError> {
match self.current { 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) => { (Token::Boolean(text), position) => {
self.next_token()?; self.next_token()?;
@ -1180,6 +1211,48 @@ mod tests {
use super::*; 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] #[test]
fn tuple_struct_access() { fn tuple_struct_access() {
let input = "Foo(42, 'bar').0"; let input = "Foo(42, 'bar').0";

View File

@ -17,6 +17,7 @@ pub enum Token<'src> {
String(&'src str), String(&'src str),
// Keywords // Keywords
Async,
Bool, Bool,
Else, Else,
FloatKeyword, FloatKeyword,
@ -65,6 +66,7 @@ pub enum Token<'src> {
impl<'src> Token<'src> { impl<'src> Token<'src> {
pub fn to_owned(&self) -> TokenOwned { pub fn to_owned(&self) -> TokenOwned {
match self { match self {
Token::Async => TokenOwned::Async,
Token::Bang => TokenOwned::Bang, Token::Bang => TokenOwned::Bang,
Token::Bool => TokenOwned::Bool, Token::Bool => TokenOwned::Bool,
Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()), Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()),
@ -123,6 +125,7 @@ impl<'src> Token<'src> {
Token::Integer(integer_text) => integer_text, Token::Integer(integer_text) => integer_text,
Token::String(text) => text, Token::String(text) => text,
Token::Async => "async",
Token::Bang => "!", Token::Bang => "!",
Token::Bool => "bool", Token::Bool => "bool",
Token::Colon => ":", Token::Colon => ":",
@ -170,6 +173,7 @@ impl<'src> Token<'src> {
pub fn kind(&self) -> TokenKind { pub fn kind(&self) -> TokenKind {
match self { match self {
Token::Async => TokenKind::Async,
Token::Bang => TokenKind::Bang, Token::Bang => TokenKind::Bang,
Token::Bool => TokenKind::Bool, Token::Bool => TokenKind::Bool,
Token::Boolean(_) => TokenKind::Boolean, Token::Boolean(_) => TokenKind::Boolean,
@ -309,6 +313,7 @@ pub enum TokenOwned {
WriteLine, WriteLine,
// Symbols // Symbols
Async,
Bang, Bang,
Colon, Colon,
Comma, Comma,
@ -342,6 +347,7 @@ pub enum TokenOwned {
impl Display for TokenOwned { impl Display for TokenOwned {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
TokenOwned::Async => Token::Async.fmt(f),
TokenOwned::Bang => Token::Bang.fmt(f), TokenOwned::Bang => Token::Bang.fmt(f),
TokenOwned::Bool => write!(f, "bool"), TokenOwned::Bool => write!(f, "bool"),
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"), TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
@ -407,6 +413,7 @@ pub enum TokenKind {
String, String,
// Keywords // Keywords
Async,
Bool, Bool,
Else, Else,
FloatKeyword, FloatKeyword,
@ -455,6 +462,7 @@ pub enum TokenKind {
impl Display for TokenKind { impl Display for TokenKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
TokenKind::Async => Token::Async.fmt(f),
TokenKind::Bang => Token::Bang.fmt(f), TokenKind::Bang => Token::Bang.fmt(f),
TokenKind::Bool => Token::Bool.fmt(f), TokenKind::Bool => Token::Bool.fmt(f),
TokenKind::Boolean => write!(f, "boolean value"), TokenKind::Boolean => write!(f, "boolean value"),
@ -510,8 +518,9 @@ impl Display for TokenKind {
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
pub fn all_tokens<'src>() -> [Token<'src>; 46] { pub fn all_tokens<'src>() -> [Token<'src>; 47] {
[ [
Token::Async,
Token::Bang, Token::Bang,
Token::Bool, Token::Bool,
Token::Boolean("true"), Token::Boolean("true"),

View File

@ -9,6 +9,8 @@ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
}; };
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use crate::{ use crate::{
parse, value::ValueInner, AbstractSyntaxTree, Analyzer, AssignmentOperator, BinaryOperator, parse, value::ValueInner, AbstractSyntaxTree, Analyzer, AssignmentOperator, BinaryOperator,
BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement, BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement,
@ -173,6 +175,17 @@ impl Vm {
Ok(None) 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 { Statement::BinaryOperation {
left, left,
operator, operator,
@ -856,6 +869,13 @@ mod tests {
use super::*; 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] #[test]
fn define_and_instantiate_fields_struct() { fn define_and_instantiate_fields_struct() {
let input = "struct Foo { bar: int, baz: float } Foo { bar = 42, baz = 4.0 }"; let input = "struct Foo { bar: int, baz: float } Foo { bar = 42, baz = 4.0 }";