diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 62350e88..ca591e26 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -437,6 +437,16 @@ pub enum ClauseGuard { Constant(Constant), } +pub type TypedIfBranch = IfBranch; +pub type UntypedIfBranch = IfBranch; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IfBranch { + pub condition: Expr, + pub body: Expr, + pub location: Span, +} + pub struct TypedRecordUpdateArg { pub label: String, pub location: Span, diff --git a/crates/lang/src/expr.rs b/crates/lang/src/expr.rs index fbfa66b7..9690c0e5 100644 --- a/crates/lang/src/expr.rs +++ b/crates/lang/src/expr.rs @@ -4,8 +4,8 @@ use vec1::Vec1; use crate::{ ast::{ - Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, Pattern, RecordUpdateSpread, Span, - TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg, + Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, IfBranch, Pattern, + RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg, }, tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor}, }; @@ -104,6 +104,13 @@ pub enum TypedExpr { clauses: Vec, String>>, }, + If { + location: Span, + branches: Vec1>, + final_else: Box, + tipo: Arc, + }, + RecordAccess { location: Span, tipo: Arc, @@ -233,6 +240,12 @@ pub enum UntypedExpr { clauses: Vec>, }, + If { + location: Span, + branches: Vec1>, + final_else: Box, + }, + FieldAccess { location: Span, label: String, @@ -338,7 +351,8 @@ impl UntypedExpr { | Self::TupleIndex { location, .. } | Self::FieldAccess { location, .. } | Self::RecordUpdate { location, .. } - | Self::Negate { location, .. } => *location, + | Self::Negate { location, .. } + | Self::If { location, .. } => *location, Self::Sequence { location, expressions, diff --git a/crates/lang/src/lexer.rs b/crates/lang/src/lexer.rs index 431b532f..b826acbd 100644 --- a/crates/lang/src/lexer.rs +++ b/crates/lang/src/lexer.rs @@ -25,8 +25,8 @@ pub fn lexer() -> impl Parser, Error = ParseError> { just("|>").to(Token::Pipe), just(',').to(Token::Comma), just(':').to(Token::Colon), - just('|').to(Token::Vbar), just("||").to(Token::VbarVbar), + just('|').to(Token::Vbar), just("&&").to(Token::AmperAmper), )); @@ -63,6 +63,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { "const" => Token::Const, "fn" => Token::Fn, "if" => Token::If, + "else" => Token::Else, "is" => Token::Is, "let" => Token::Let, "opaque" => Token::Opaque, diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 95d20514..52c6488e 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -443,6 +443,40 @@ pub fn expr_parser( }, ); + let if_parser = just(Token::If) + .ignore_then(r.clone().then(block_parser.clone()).map_with_span( + |(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }, + )) + .then( + just(Token::Else) + .ignore_then(just(Token::If)) + .ignore_then(r.clone().then(block_parser.clone()).map_with_span( + |(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }, + )) + .repeated(), + ) + .then_ignore(just(Token::Else)) + .then(block_parser.clone()) + .map_with_span(|((first, alternative_branches), final_else), span| { + let mut branches = vec1::vec1![first]; + + branches.extend(alternative_branches); + + expr::UntypedExpr::If { + location: span, + branches, + final_else: Box::new(final_else), + } + }); + let expr_unit_parser = choice(( string_parser, int_parser, @@ -454,6 +488,7 @@ pub fn expr_parser( when_parser, let_parser, assert_parser, + if_parser, )); // Parsing a function call into the appropriate structure diff --git a/crates/lang/src/tests/parser.rs b/crates/lang/src/tests/parser.rs index bb2c6922..2ea5e300 100644 --- a/crates/lang/src/tests/parser.rs +++ b/crates/lang/src/tests/parser.rs @@ -93,6 +93,18 @@ fn module() { fn update_name(user: User, name: String) -> User { User { ..user, name: "Aiken", } } + + fn ifs() { + if True { + 1 + 1 + } else if a < 4 { + 5 + } else if a || b { + 6 + } else { + 3 + } + } "#; let len = code.chars().count(); @@ -992,6 +1004,81 @@ fn module() { },), return_type: (), }, + ast::UntypedDefinition::Fn { + arguments: vec![], + body: expr::UntypedExpr::If { + location: Span::new(SrcId::empty(), 1823..2036), + branches: vec1::vec1![ + ast::IfBranch { + condition: expr::UntypedExpr::Var { + location: Span::new(SrcId::empty(), 1826..1830), + name: "True".to_string(), + }, + body: expr::UntypedExpr::BinOp { + location: Span::new(SrcId::empty(), 1853..1858), + name: ast::BinOp::AddInt, + left: Box::new(expr::UntypedExpr::Int { + location: Span::new(SrcId::empty(), 1853..1854), + value: "1".to_string(), + }), + right: Box::new(expr::UntypedExpr::Int { + location: Span::new(SrcId::empty(), 1857..1858), + value: "1".to_string(), + }), + }, + location: Span::new(SrcId::empty(), 1826..1876), + }, + ast::IfBranch { + condition: expr::UntypedExpr::BinOp { + location: Span::new(SrcId::empty(), 1885..1890), + name: ast::BinOp::LtInt, + left: Box::new(expr::UntypedExpr::Var { + location: Span::new(SrcId::empty(), 1885..1886), + name: "a".to_string(), + }), + right: Box::new(expr::UntypedExpr::Int { + location: Span::new(SrcId::empty(), 1889..1890), + value: "4".to_string(), + }), + }, + body: expr::UntypedExpr::Int { + location: Span::new(SrcId::empty(), 1913..1914), + value: "5".to_string(), + }, + location: Span::new(SrcId::empty(), 1885..1932), + }, + ast::IfBranch { + condition: expr::UntypedExpr::BinOp { + location: Span::new(SrcId::empty(), 1941..1947), + name: ast::BinOp::Or, + left: Box::new(expr::UntypedExpr::Var { + location: Span::new(SrcId::empty(), 1941..1942), + name: "a".to_string(), + }), + right: Box::new(expr::UntypedExpr::Var { + location: Span::new(SrcId::empty(), 1946..1947), + name: "b".to_string(), + }), + }, + body: expr::UntypedExpr::Int { + location: Span::new(SrcId::empty(), 1970..1971), + value: "6".to_string(), + }, + location: Span::new(SrcId::empty(), 1941..1989), + }, + ], + final_else: Box::new(expr::UntypedExpr::Int { + location: Span::new(SrcId::empty(), 2017..2018), + value: "3".to_string(), + }), + }, + doc: None, + location: Span::new(SrcId::empty(), 1796..2050), + name: "ifs".to_string(), + public: false, + return_annotation: None, + return_type: (), + }, ] }, ); diff --git a/crates/lang/src/token.rs b/crates/lang/src/token.rs index cf6856e0..24134528 100644 --- a/crates/lang/src/token.rs +++ b/crates/lang/src/token.rs @@ -61,6 +61,7 @@ pub enum Token { Const, Fn, If, + Else, Is, Let, Opaque, @@ -134,6 +135,7 @@ impl fmt::Display for Token { Token::Const => "const", Token::Fn => "fn", Token::If => "if", + Token::Else => "else", Token::Use => "import", Token::Let => "let", Token::Opaque => "opaque",