From 71013086f8cdd345c6440c388710a9cfd6c2caa4 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Tue, 5 Mar 2024 14:25:32 +0330 Subject: [PATCH] fix(parser): support for else and elseif expressions. --- crates/fuse-parser/src/parsers/expressions.rs | 27 +++- crates/fuse-parser/src/parsers/statements.rs | 4 +- .../cases/pass/if-expression-02/ast.snap | 50 +++++++ .../cases/pass/if-expression-02/case.fuse | 5 + .../cases/pass/if-expression-02/tokens.snap | 140 ++++++++++++++++++ 5 files changed, 217 insertions(+), 9 deletions(-) create mode 100644 crates/fuse-parser/tests/cases/pass/if-expression-02/ast.snap create mode 100644 crates/fuse-parser/tests/cases/pass/if-expression-02/case.fuse create mode 100644 crates/fuse-parser/tests/cases/pass/if-expression-02/tokens.snap diff --git a/crates/fuse-parser/src/parsers/expressions.rs b/crates/fuse-parser/src/parsers/expressions.rs index a7a82ea..a5d7a62 100644 --- a/crates/fuse-parser/src/parsers/expressions.rs +++ b/crates/fuse-parser/src/parsers/expressions.rs @@ -3,7 +3,7 @@ use crate::{ Parser, ParserResult, }; use fuse_ast::{ - BindingPattern, BindingPatternKind, BooleanLiteral, Expression, Function, FunctionBody, + BindingPattern, BindingPatternKind, BooleanLiteral, Else, Expression, Function, FunctionBody, FunctionParameter, FunctionParameters, Identifier, If, TypeAnnotation, }; use fuse_common::Span; @@ -57,30 +57,43 @@ impl<'a> Parser<'a> { } fn parse_if_expression(&mut self) -> ParserResult { + self.parse_if().map(|expr| self.ast.if_expression(expr)) + } + + fn parse_if(&mut self) -> ParserResult { let start = self.start_span(); // Consume the keyword self.consume(); let cond = self.parse_expression()?; self.consume_expect(TokenKind::Then)?; let body = self.parse_block_while(|kind| { - matches! { + println!("IF KIND {kind:?}"); + !matches! { kind, | TokenKind::End | TokenKind::Else | TokenKind::ElseIf } })?; - let r#else = match self.consume().kind() { - TokenKind::End => None, - _ => todo!(), + let r#else = match self.cur_kind() { + TokenKind::End => { + self.consume(); + None + } + TokenKind::ElseIf => Some(Else::If(Box::from(self.parse_if()?))), + TokenKind::Else => { + self.consume(); + Some(Else::Block(Box::from(self.parse_block()?))) + } + _ => return Err(Self::unexpected_error(&self.prev_token)), }; // how to detect end of block? // maybe via a predicate function? - Ok(self.ast.if_expression(If { + Ok(If { span: self.end_span(start), cond, body, r#else, - })) + }) } } diff --git a/crates/fuse-parser/src/parsers/statements.rs b/crates/fuse-parser/src/parsers/statements.rs index aa96e0b..31b58b2 100644 --- a/crates/fuse-parser/src/parsers/statements.rs +++ b/crates/fuse-parser/src/parsers/statements.rs @@ -6,7 +6,7 @@ impl<'a> Parser<'a> { /// Parse a block of statements until and including the `end` token. pub(crate) fn parse_block(&mut self) -> ParserResult { let result = self - .parse_statements(|kind| kind == TokenKind::End) + .parse_statements(|kind| kind != TokenKind::End) .map(|stmts| self.ast.block(stmts)); // Eat the end token. self.consume(); @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { ) -> ParserResult> { let mut statements = Vec::new(); - while !self.at(TokenKind::Eof) && !predicate(self.cur_kind()) { + while !self.at(TokenKind::Eof) && predicate(self.cur_kind()) { match self.parse_statement() { ParserResult::Ok(stmt) => { statements.push(stmt); diff --git a/crates/fuse-parser/tests/cases/pass/if-expression-02/ast.snap b/crates/fuse-parser/tests/cases/pass/if-expression-02/ast.snap new file mode 100644 index 0000000..d9b3ac1 --- /dev/null +++ b/crates/fuse-parser/tests/cases/pass/if-expression-02/ast.snap @@ -0,0 +1,50 @@ +--- +source: crates/fuse-parser/tests/cases/mod.rs +expression: parsed.chunk +input_file: crates/fuse-parser/tests/cases/pass/if-expression-02/case.fuse +--- +Some(Chunk( + span: Span( + start: 0, + end: 35, + ), + body: Block( + statements: [ + Expression(If(If( + span: Span( + start: 0, + end: 34, + ), + cond: Identifier(Identifier( + span: Span( + start: 3, + end: 7, + ), + name: Atom("cond"), + )), + body: Block( + statements: [ + Expression(BooleanLiteral(BooleanLiteral( + span: Span( + start: 14, + end: 18, + ), + value: true, + ))), + ], + ), + else: Some(Block(Block( + statements: [ + Expression(BooleanLiteral(BooleanLiteral( + span: Span( + start: 25, + end: 30, + ), + value: false, + ))), + ], + ))), + ))), + ], + ), +)) diff --git a/crates/fuse-parser/tests/cases/pass/if-expression-02/case.fuse b/crates/fuse-parser/tests/cases/pass/if-expression-02/case.fuse new file mode 100644 index 0000000..b49c819 --- /dev/null +++ b/crates/fuse-parser/tests/cases/pass/if-expression-02/case.fuse @@ -0,0 +1,5 @@ +if cond then + true +else + false +end diff --git a/crates/fuse-parser/tests/cases/pass/if-expression-02/tokens.snap b/crates/fuse-parser/tests/cases/pass/if-expression-02/tokens.snap new file mode 100644 index 0000000..35e5e4e --- /dev/null +++ b/crates/fuse-parser/tests/cases/pass/if-expression-02/tokens.snap @@ -0,0 +1,140 @@ +--- +source: crates/fuse-parser/tests/cases/mod.rs +expression: tokens +input_file: crates/fuse-parser/tests/cases/pass/if-expression-02/case.fuse +--- +[ + TokenReference( + token: Token( + span: Span( + start: 0, + end: 2, + ), + kind: If, + ), + leading_trivia: [], + trailing_trivia: [ + Token( + span: Span( + start: 2, + end: 3, + ), + kind: Whitespace, + ), + ], + ), + TokenReference( + token: Token( + span: Span( + start: 3, + end: 7, + ), + kind: Identifier, + ), + leading_trivia: [], + trailing_trivia: [ + Token( + span: Span( + start: 7, + end: 8, + ), + kind: Whitespace, + ), + ], + ), + TokenReference( + token: Token( + span: Span( + start: 8, + end: 12, + ), + kind: Then, + ), + leading_trivia: [], + trailing_trivia: [ + Token( + span: Span( + start: 12, + end: 14, + ), + kind: Whitespace, + ), + ], + ), + TokenReference( + token: Token( + span: Span( + start: 14, + end: 18, + ), + kind: True, + ), + leading_trivia: [], + trailing_trivia: [ + Token( + span: Span( + start: 18, + end: 19, + ), + kind: Whitespace, + ), + ], + ), + TokenReference( + token: Token( + span: Span( + start: 19, + end: 23, + ), + kind: Else, + ), + leading_trivia: [], + trailing_trivia: [ + Token( + span: Span( + start: 23, + end: 25, + ), + kind: Whitespace, + ), + ], + ), + TokenReference( + token: Token( + span: Span( + start: 25, + end: 30, + ), + kind: False, + ), + leading_trivia: [], + trailing_trivia: [ + Token( + span: Span( + start: 30, + end: 31, + ), + kind: Whitespace, + ), + ], + ), + TokenReference( + token: Token( + span: Span( + start: 31, + end: 34, + ), + kind: End, + ), + leading_trivia: [], + trailing_trivia: [ + Token( + span: Span( + start: 34, + end: 35, + ), + kind: Whitespace, + ), + ], + ), +]