diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 7fa0307b..f035e257 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -706,6 +706,7 @@ pub enum Expression { }, Match { condition: Box, + default: Option>, arms: Vec, }, Throw { @@ -755,9 +756,14 @@ pub struct ClosureUse { pub by_ref: bool, } +#[derive(Debug, PartialEq, Clone)] +pub struct DefaultMatchArm { + pub body: Expression, +} + #[derive(Debug, PartialEq, Clone)] pub struct MatchArm { - pub conditions: Option>, + pub conditions: Vec, pub body: Expression, } diff --git a/src/parser/error.rs b/src/parser/error.rs index a201f392..90e41d6c 100644 --- a/src/parser/error.rs +++ b/src/parser/error.rs @@ -33,6 +33,7 @@ pub enum ParseError { MixingBracedAndUnBracedNamespaceDeclarations(Span), NestedNamespaceDeclarations(Span), ForbiddenTypeUsedInProperty(String, String, Type, Span), + MatchExpressionWithMultipleDefaultArms(Span), } impl Display for ParseError { @@ -78,6 +79,7 @@ impl Display for ParseError { Self::NestedNamespaceDeclarations(span) => write!(f, "Parse Error: Namespace declarations cannot be mixed on line {} column {}", span.0, span.1), Self::UnpredictableState(span) => write!(f, "Parse Error: Reached an unpredictable state on line {} column {}", span.0, span.1), Self::ForbiddenTypeUsedInProperty(class, prop, ty, span) => write!(f, "Parse Error: Property {}::${} cannot have type `{}` on line {} column {}", class, prop, ty, span.0, span.1), + Self::MatchExpressionWithMultipleDefaultArms(span) => write!(f, "Parse Error: Match expressions may only contain one default arm on line {} column {}", span.0, span.1), } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1aad12a8..b5aeb950 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12,6 +12,7 @@ use crate::parser::error::ParseResult; use crate::parser::internal::ident::is_reserved_ident; use crate::parser::internal::precedence::{Associativity, Precedence}; use crate::parser::state::State; +use crate::prelude::DefaultMatchArm; pub mod ast; pub mod error; @@ -998,11 +999,18 @@ impl Parser { self.rparen(state)?; self.lbrace(state)?; + let mut default = None; let mut arms = Vec::new(); while state.current.kind != TokenKind::RightBrace { state.skip_comments(); - let conditions = if state.current.kind == TokenKind::Default { + if state.current.kind == TokenKind::Default { + if default.is_some() { + return Err(ParseError::MatchExpressionWithMultipleDefaultArms( + state.current.span, + )); + } + state.next(); // match conditions can have an extra comma at the end, including `default`. @@ -1010,7 +1018,11 @@ impl Parser { state.next(); } - None + expect_token!([TokenKind::DoubleArrow], state, "`=>`"); + + let body = self.expression(state, Precedence::Lowest)?; + + default = Some(Box::new(DefaultMatchArm { body })); } else { let mut conditions = Vec::new(); while state.current.kind != TokenKind::DoubleArrow { @@ -1023,14 +1035,16 @@ impl Parser { } } - Some(conditions) - }; - - expect_token!([TokenKind::DoubleArrow], state, "`=>`"); + if !conditions.is_empty() { + expect_token!([TokenKind::DoubleArrow], state, "`=>`"); + } else { + break; + } - let body = self.expression(state, Precedence::Lowest)?; + let body = self.expression(state, Precedence::Lowest)?; - arms.push(MatchArm { conditions, body }); + arms.push(MatchArm { conditions, body }); + } if state.current.kind == TokenKind::Comma { state.next(); @@ -1041,7 +1055,11 @@ impl Parser { self.rbrace(state)?; - Expression::Match { condition, arms } + Expression::Match { + condition, + default, + arms, + } } TokenKind::Array => { let mut items = vec![]; diff --git a/tests/0193/ast.txt b/tests/0193/ast.txt index 72172f78..4928eb98 100644 --- a/tests/0193/ast.txt +++ b/tests/0193/ast.txt @@ -20,6 +20,7 @@ condition: Variable { name: "a", }, + default: None, arms: [], }, }, diff --git a/tests/0194/ast.txt b/tests/0194/ast.txt index f07faedc..63ccfc37 100644 --- a/tests/0194/ast.txt +++ b/tests/0194/ast.txt @@ -20,24 +20,23 @@ condition: Variable { name: "a", }, + default: None, arms: [ MatchArm { - conditions: Some( - [ - LiteralInteger { - i: 1, - }, - LiteralInteger { - i: 2, - }, - LiteralInteger { - i: 3, - }, - LiteralInteger { - i: 4, - }, - ], - ), + conditions: [ + LiteralInteger { + i: 1, + }, + LiteralInteger { + i: 2, + }, + LiteralInteger { + i: 3, + }, + LiteralInteger { + i: 4, + }, + ], body: Null, }, ], diff --git a/tests/0195/ast.txt b/tests/0195/ast.txt index f07faedc..63ccfc37 100644 --- a/tests/0195/ast.txt +++ b/tests/0195/ast.txt @@ -20,24 +20,23 @@ condition: Variable { name: "a", }, + default: None, arms: [ MatchArm { - conditions: Some( - [ - LiteralInteger { - i: 1, - }, - LiteralInteger { - i: 2, - }, - LiteralInteger { - i: 3, - }, - LiteralInteger { - i: 4, - }, - ], - ), + conditions: [ + LiteralInteger { + i: 1, + }, + LiteralInteger { + i: 2, + }, + LiteralInteger { + i: 3, + }, + LiteralInteger { + i: 4, + }, + ], body: Null, }, ], diff --git a/tests/0196/ast.txt b/tests/0196/ast.txt index f07faedc..63ccfc37 100644 --- a/tests/0196/ast.txt +++ b/tests/0196/ast.txt @@ -20,24 +20,23 @@ condition: Variable { name: "a", }, + default: None, arms: [ MatchArm { - conditions: Some( - [ - LiteralInteger { - i: 1, - }, - LiteralInteger { - i: 2, - }, - LiteralInteger { - i: 3, - }, - LiteralInteger { - i: 4, - }, - ], - ), + conditions: [ + LiteralInteger { + i: 1, + }, + LiteralInteger { + i: 2, + }, + LiteralInteger { + i: 3, + }, + LiteralInteger { + i: 4, + }, + ], body: Null, }, ], diff --git a/tests/0197/ast.txt b/tests/0197/ast.txt index 12ec3baa..ea309d0f 100644 --- a/tests/0197/ast.txt +++ b/tests/0197/ast.txt @@ -20,28 +20,27 @@ condition: Variable { name: "a", }, - arms: [ - MatchArm { - conditions: Some( - [ - LiteralInteger { - i: 1, - }, - LiteralInteger { - i: 2, - }, - LiteralInteger { - i: 3, - }, - LiteralInteger { - i: 4, - }, - ], - ), + default: Some( + DefaultMatchArm { body: Null, }, + ), + arms: [ MatchArm { - conditions: None, + conditions: [ + LiteralInteger { + i: 1, + }, + LiteralInteger { + i: 2, + }, + LiteralInteger { + i: 3, + }, + LiteralInteger { + i: 4, + }, + ], body: Null, }, ], diff --git a/tests/0210/code.php b/tests/0210/code.php new file mode 100644 index 00000000..82c213d4 --- /dev/null +++ b/tests/0210/code.php @@ -0,0 +1,7 @@ + 43, + default => 34, +}; diff --git a/tests/0210/parser-error.txt b/tests/0210/parser-error.txt new file mode 100644 index 00000000..68f81e7b --- /dev/null +++ b/tests/0210/parser-error.txt @@ -0,0 +1 @@ +MatchExpressionWithMultipleDefaultArms((6, 5)) -> Parse Error: Match expressions may only contain one default arm on line 6 column 5 diff --git a/tests/0210/tokens.txt b/tests/0210/tokens.txt new file mode 100644 index 00000000..bacc3c3e --- /dev/null +++ b/tests/0210/tokens.txt @@ -0,0 +1,122 @@ +[ + Token { + kind: OpenTag( + Full, + ), + span: ( + 1, + 1, + ), + }, + Token { + kind: Match, + span: ( + 4, + 1, + ), + }, + Token { + kind: LeftParen, + span: ( + 4, + 7, + ), + }, + Token { + kind: Variable( + "s", + ), + span: ( + 4, + 8, + ), + }, + Token { + kind: RightParen, + span: ( + 4, + 10, + ), + }, + Token { + kind: LeftBrace, + span: ( + 4, + 12, + ), + }, + Token { + kind: Default, + span: ( + 5, + 5, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 5, + 13, + ), + }, + Token { + kind: LiteralInteger( + 43, + ), + span: ( + 5, + 16, + ), + }, + Token { + kind: Comma, + span: ( + 5, + 18, + ), + }, + Token { + kind: Default, + span: ( + 6, + 5, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 6, + 13, + ), + }, + Token { + kind: LiteralInteger( + 34, + ), + span: ( + 6, + 16, + ), + }, + Token { + kind: Comma, + span: ( + 6, + 18, + ), + }, + Token { + kind: RightBrace, + span: ( + 7, + 1, + ), + }, + Token { + kind: SemiColon, + span: ( + 7, + 2, + ), + }, +] diff --git a/tests/0211/ast.txt b/tests/0211/ast.txt new file mode 100644 index 00000000..9738efd9 --- /dev/null +++ b/tests/0211/ast.txt @@ -0,0 +1,74 @@ +[ + Expression { + expr: Match { + condition: Variable { + name: "s", + }, + default: Some( + DefaultMatchArm { + body: LiteralInteger { + i: 124, + }, + }, + ), + arms: [ + MatchArm { + conditions: [ + LiteralInteger { + i: 1, + }, + ], + body: LiteralInteger { + i: 2, + }, + }, + MatchArm { + conditions: [ + LiteralInteger { + i: 3, + }, + ], + body: LiteralInteger { + i: 4, + }, + }, + MatchArm { + conditions: [ + LiteralInteger { + i: 5, + }, + LiteralInteger { + i: 6, + }, + ], + body: LiteralInteger { + i: 4, + }, + }, + MatchArm { + conditions: [ + LiteralInteger { + i: 9, + }, + LiteralInteger { + i: 123, + }, + ], + body: LiteralInteger { + i: 4, + }, + }, + MatchArm { + conditions: [ + Identifier { + name: "_", + }, + ], + body: LiteralInteger { + i: 43, + }, + }, + ], + }, + }, +] diff --git a/tests/0211/code.php b/tests/0211/code.php new file mode 100644 index 00000000..3eaa2024 --- /dev/null +++ b/tests/0211/code.php @@ -0,0 +1,11 @@ + 2, + 3, => 4, + 5,6 => 4, + 9, 123, => 4, + _ => 43, // _ here is a constant + default => 124, +}; diff --git a/tests/0211/tokens.txt b/tests/0211/tokens.txt new file mode 100644 index 00000000..de817fae --- /dev/null +++ b/tests/0211/tokens.txt @@ -0,0 +1,307 @@ +[ + Token { + kind: OpenTag( + Full, + ), + span: ( + 1, + 1, + ), + }, + Token { + kind: Match, + span: ( + 4, + 1, + ), + }, + Token { + kind: LeftParen, + span: ( + 4, + 7, + ), + }, + Token { + kind: Variable( + "s", + ), + span: ( + 4, + 8, + ), + }, + Token { + kind: RightParen, + span: ( + 4, + 10, + ), + }, + Token { + kind: LeftBrace, + span: ( + 4, + 12, + ), + }, + Token { + kind: LiteralInteger( + 1, + ), + span: ( + 5, + 5, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 5, + 7, + ), + }, + Token { + kind: LiteralInteger( + 2, + ), + span: ( + 5, + 10, + ), + }, + Token { + kind: Comma, + span: ( + 5, + 11, + ), + }, + Token { + kind: LiteralInteger( + 3, + ), + span: ( + 6, + 5, + ), + }, + Token { + kind: Comma, + span: ( + 6, + 6, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 6, + 8, + ), + }, + Token { + kind: LiteralInteger( + 4, + ), + span: ( + 6, + 11, + ), + }, + Token { + kind: Comma, + span: ( + 6, + 12, + ), + }, + Token { + kind: LiteralInteger( + 5, + ), + span: ( + 7, + 5, + ), + }, + Token { + kind: Comma, + span: ( + 7, + 6, + ), + }, + Token { + kind: LiteralInteger( + 6, + ), + span: ( + 7, + 7, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 7, + 9, + ), + }, + Token { + kind: LiteralInteger( + 4, + ), + span: ( + 7, + 12, + ), + }, + Token { + kind: Comma, + span: ( + 7, + 13, + ), + }, + Token { + kind: LiteralInteger( + 9, + ), + span: ( + 8, + 5, + ), + }, + Token { + kind: Comma, + span: ( + 8, + 6, + ), + }, + Token { + kind: LiteralInteger( + 123, + ), + span: ( + 8, + 8, + ), + }, + Token { + kind: Comma, + span: ( + 8, + 11, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 8, + 13, + ), + }, + Token { + kind: LiteralInteger( + 4, + ), + span: ( + 8, + 16, + ), + }, + Token { + kind: Comma, + span: ( + 8, + 17, + ), + }, + Token { + kind: Identifier( + "_", + ), + span: ( + 9, + 5, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 9, + 7, + ), + }, + Token { + kind: LiteralInteger( + 43, + ), + span: ( + 9, + 10, + ), + }, + Token { + kind: Comma, + span: ( + 9, + 12, + ), + }, + Token { + kind: Comment( + "// _ here is a constant", + ), + span: ( + 9, + 14, + ), + }, + Token { + kind: Default, + span: ( + 10, + 5, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 10, + 13, + ), + }, + Token { + kind: LiteralInteger( + 124, + ), + span: ( + 10, + 16, + ), + }, + Token { + kind: Comma, + span: ( + 10, + 19, + ), + }, + Token { + kind: RightBrace, + span: ( + 11, + 1, + ), + }, + Token { + kind: SemiColon, + span: ( + 11, + 2, + ), + }, +] diff --git a/tests/0212/code.php b/tests/0212/code.php new file mode 100644 index 00000000..e2caf7da --- /dev/null +++ b/tests/0212/code.php @@ -0,0 +1,7 @@ + 2, + => 43, +}; diff --git a/tests/0212/parser-error.txt b/tests/0212/parser-error.txt new file mode 100644 index 00000000..bffaff62 --- /dev/null +++ b/tests/0212/parser-error.txt @@ -0,0 +1 @@ +ExpectedToken(["`}`"], Some("=>"), (6, 5)) -> Parse Error: unexpected token `=>`, expecting `}` on line 6 column 5 diff --git a/tests/0212/tokens.txt b/tests/0212/tokens.txt new file mode 100644 index 00000000..60817e88 --- /dev/null +++ b/tests/0212/tokens.txt @@ -0,0 +1,117 @@ +[ + Token { + kind: OpenTag( + Full, + ), + span: ( + 1, + 1, + ), + }, + Token { + kind: Match, + span: ( + 4, + 1, + ), + }, + Token { + kind: LeftParen, + span: ( + 4, + 7, + ), + }, + Token { + kind: Variable( + "s", + ), + span: ( + 4, + 8, + ), + }, + Token { + kind: RightParen, + span: ( + 4, + 10, + ), + }, + Token { + kind: LeftBrace, + span: ( + 4, + 12, + ), + }, + Token { + kind: LiteralInteger( + 1, + ), + span: ( + 5, + 5, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 5, + 7, + ), + }, + Token { + kind: LiteralInteger( + 2, + ), + span: ( + 5, + 10, + ), + }, + Token { + kind: Comma, + span: ( + 5, + 11, + ), + }, + Token { + kind: DoubleArrow, + span: ( + 6, + 5, + ), + }, + Token { + kind: LiteralInteger( + 43, + ), + span: ( + 6, + 8, + ), + }, + Token { + kind: Comma, + span: ( + 6, + 10, + ), + }, + Token { + kind: RightBrace, + span: ( + 7, + 1, + ), + }, + Token { + kind: SemiColon, + span: ( + 7, + 2, + ), + }, +]