Add async blocks
This commit is contained in:
parent
d32633272c
commit
17286896a8
@ -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, "{{ ")?;
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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)),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
@ -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"),
|
||||||
|
@ -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 }";
|
||||||
|
Loading…
Reference in New Issue
Block a user