diff --git a/src/ast.rs b/src/ast.rs index da43382..248f59d 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -185,6 +185,18 @@ pub enum Tag { lhs: Node, rhs: Node, }, + ExprStatement { + expr: Node, + }, + IfExpr { + condition: Node, + body: Node, + }, + IfElseExpr { + condition: Node, + body: Node, + else_expr: Node, + }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src/parser.rs b/src/parser.rs index 333d080..1a8d82e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -930,10 +930,93 @@ impl Tree { } } + /// EXPR_OR_STATEMENT_OR_BLOCK <- + /// BLOCK + /// EXPR + /// EXPR ; + pub fn parse_expr_or_stmt_or_block(&mut self, tokens: &mut TokenIterator) -> Result { + let peek = tokens.peek_token_or_err()?; + let body = match peek.token() { + // block + Token::OpenBrace => { + let block = self.nodes.reserve_node(); + self.parse_block(tokens, Some(block))? + } + // expr + _ => { + let expr = self.parse_expr(tokens)?; + let block = if tokens.eat_token(Token::Semi).is_some() { + Tag::Block { + statements: vec![expr], + trailing_expr: None, + } + } else { + Tag::Block { + statements: vec![], + trailing_expr: Some(expr), + } + }; + self.nodes.push_tag(block) + } + }; + + Ok(body) + } + + /// ELSE_EXPR <- + /// 'else' (IF_EXPR | EXPR_OR_STATEMENT_OR_BLOCK) + pub fn try_parse_else_expr(&mut self, tokens: &mut TokenIterator) -> Result> { + if tokens.eat_token(Token::Else).is_none() { + return Ok(None); + } + + let expr = if let Some(if_expr) = self.try_parse_if_expr(tokens)? { + if_expr + } else { + self.parse_expr_or_stmt_or_block(tokens)? + }; + Ok(Some(expr)) + } + + /// IF_EXPR <- + /// 'if' ( EXPR ) EXPR_OR_STATEMENT_OR_BLOCK ELSE_EXPR? + pub fn try_parse_if_expr(&mut self, tokens: &mut TokenIterator) -> Result> { + if tokens.eat_token(Token::If).is_none() { + return Ok(None); + } + + _ = tokens.expect_token(Token::OpenParens)?; + let condition = self.parse_expr(tokens)?; + _ = tokens.expect_token(Token::CloseParens)?; + + let body = self.parse_expr_or_stmt_or_block(tokens)?; + + if let Some(else_expr) = self.try_parse_else_expr(tokens)? { + Ok(Some(self.nodes.push_tag(Tag::IfElseExpr { + condition, + body, + else_expr, + }))) + } else { + Ok(Some(self.nodes.push_tag(Tag::IfExpr { condition, body }))) + } + } + + /// IF_EXPR <- + /// 'if' ( EXPR ) EXPR_OR_STATEMENT_OR_BLOCK ELSE_EXPR? + pub fn parse_if_expr(&mut self, tokens: &mut TokenIterator) -> Result { + self.try_parse_if_expr(tokens)? + .ok_or(Error::ExpectedTokenNotFound(Token::If)) + } + /// EXPRESSION <- - /// ASSIGNMENT_EXPR + /// IF_EXPR + /// | ASSIGNMENT_EXPR pub fn parse_expr(&mut self, tokens: &mut TokenIterator) -> Result { - self.parse_assignment_expr(tokens) + match tokens.peek_token_or_err()?.token() { + Token::If => self.parse_if_expr(tokens), + _ => self.parse_assignment_expr(tokens), + } } /// PROGRAM <- @@ -1549,6 +1632,36 @@ impl Tree { self.strings.display_idx(bytes) ) } + Tag::ExprStatement { expr } => { + self.render_node(writer, expr, indent)?; + writeln_indented!(indent, writer, "%{node} = expr %{expr}",) + } + Tag::IfExpr { condition, body } => { + self.render_node(writer, condition, indent)?; + writeln_indented!( + indent, + writer, + "%{node} = if (condition: %{condition}, body: %{body}) {{", + )?; + self.render_node(writer, body, indent + 1)?; + writeln_indented!(indent, writer, "}})",) + } + Tag::IfElseExpr { + condition, + body, + else_expr, + } => { + self.render_node(writer, condition, indent)?; + writeln_indented!( + indent, + writer, + "%{node} = if (condition: %{condition}, body: %{body}) {{", + )?; + self.render_node(writer, body, indent + 1)?; + writeln_indented!(indent, writer, "}}, else: %{else_expr} {{",)?; + self.render_node(writer, else_expr, indent + 1)?; + writeln_indented!(indent, writer, "}})",) + } _ => unreachable!(), } } diff --git a/tests/legal/if.sea b/tests/legal/if.sea new file mode 100644 index 0000000..0e0c656 --- /dev/null +++ b/tests/legal/if.sea @@ -0,0 +1,7 @@ + +fn square_of_greater(a: i32, b: i32) -> i32 { + if (a < b) + a * a + else + b * b +} \ No newline at end of file