diff --git a/src/parser/classish_statement.rs b/src/parser/classish_statement.rs index 96d086e7..0f382281 100644 --- a/src/parser/classish_statement.rs +++ b/src/parser/classish_statement.rs @@ -11,6 +11,7 @@ use crate::parser::Parser; use crate::expect_token; use crate::expected_token_err; +use crate::peek_token; #[derive(Debug)] pub enum ClassishDefinitionType { @@ -38,16 +39,15 @@ impl Parser { return self.method(ClassishDefinitionType::Interface, vec![]); } - let member_flags = self.class_members_flags()?; + let member_flags = self.interface_members_flags()?; - match &self.current.kind { + peek_token!([ TokenKind::Const => self.parse_classish_const(member_flags), TokenKind::Function => self.method( ClassishDefinitionType::Interface, member_flags.iter().map(|t| t.clone().into()).collect(), - ), - _ => expected_token_err!(["`const`", "`function`"], self), - } + ) + ], self, ["`const`", "`function`"]) } pub(in crate::parser) fn trait_statement(&mut self) -> ParseResult { @@ -91,14 +91,13 @@ impl Parser { let member_flags = self.enum_members_flags()?; - match &self.current.kind { + peek_token!([ TokenKind::Const => self.parse_classish_const(member_flags), TokenKind::Function => self.method( ClassishDefinitionType::Enum, member_flags.iter().map(|t| t.clone().into()).collect(), - ), - _ => expected_token_err!(["`const`", "`function`"], self), - } + ) + ], self, ["`const`", "`function`"]) } fn complete_class_statement( diff --git a/src/parser/flags.rs b/src/parser/flags.rs index 278b8094..847827ee 100644 --- a/src/parser/flags.rs +++ b/src/parser/flags.rs @@ -3,10 +3,12 @@ use crate::parser::error::ParseError; use crate::parser::error::ParseResult; use crate::parser::Parser; +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] enum FlagTarget { Class, EnumMember, ClassMember, + InterfaceMember, PromotedProperty, } @@ -18,6 +20,13 @@ impl Parser { ) } + pub(in crate::parser) fn interface_members_flags(&mut self) -> ParseResult> { + self.collect( + vec![TokenKind::Public, TokenKind::Static], + FlagTarget::InterfaceMember, + ) + } + pub(in crate::parser) fn class_members_flags(&mut self) -> ParseResult> { self.collect( vec![ @@ -92,34 +101,36 @@ impl Parser { { return Err(ParseError::MultipleAccessModifiers(self.current.span)); } - TokenKind::Final if collected.contains(&TokenKind::Abstract) => match target { - FlagTarget::Class => { - return Err(ParseError::FinalModifierOnAbstractClass( - self.current.span, - )); - } - FlagTarget::ClassMember => { - return Err(ParseError::FinalModifierOnAbstractClassMember( - self.current.span, - )); - } - _ => {} - }, - TokenKind::Abstract if collected.contains(&TokenKind::Final) => match target { - FlagTarget::Class => { - return Err(ParseError::FinalModifierOnAbstractClass( - self.current.span, - )); + _ => {} + }; + + if matches!(target, FlagTarget::ClassMember | FlagTarget::Class) { + match self.current.kind { + TokenKind::Final if collected.contains(&TokenKind::Abstract) => { + if target == FlagTarget::Class { + return Err(ParseError::FinalModifierOnAbstractClass( + self.current.span, + )); + } else { + return Err(ParseError::FinalModifierOnAbstractClassMember( + self.current.span, + )); + } } - FlagTarget::ClassMember => { - return Err(ParseError::FinalModifierOnAbstractClassMember( - self.current.span, - )); + TokenKind::Abstract if collected.contains(&TokenKind::Final) => { + if target == FlagTarget::Class { + return Err(ParseError::FinalModifierOnAbstractClass( + self.current.span, + )); + } else { + return Err(ParseError::FinalModifierOnAbstractClassMember( + self.current.span, + )); + } } _ => {} - }, - _ => {} - }; + }; + } collected.push(self.current.kind.clone()); self.next(); diff --git a/src/parser/macros.rs b/src/parser/macros.rs index f869a91b..1bc4f279 100644 --- a/src/parser/macros.rs +++ b/src/parser/macros.rs @@ -1,13 +1,10 @@ #[macro_export] -macro_rules! expect_token { +macro_rules! peek_token { ([ $($expected:pat => $out:expr),+ $(,)? ], $parser:expr, [ $($message:literal),+ $(,)? ]) => {{ $parser.skip_comments(); match $parser.current.kind.clone() { $( - $expected => { - $parser.next(); - $out - } + $expected => $out, )+ _ => { return $crate::expected_token_err!([ $($message,)+ ], $parser); @@ -16,22 +13,31 @@ macro_rules! expect_token { }}; ([ $($expected:pat),+ $(,)? ], $parser:expr, [ $($message:literal),+ $(,)? ]) => {{ $parser.skip_comments(); - match $parser.current.kind.clone() { - $( - $expected => { - $parser.next(); - } - )+ - _ => { - return $crate::expected_token_err!([ $($message,)+ ], $parser); - } + if !matches!($parser.current.kind, $(| $expected )+) { + return $crate::expected_token_err!([ $($message,)+ ], $parser); } }}; ([ $($expected:pat => $out:expr),+ $(,)? ], $parser:expr, $message:literal) => { - $crate::expect_token!([ $($expected => $out,)+ ], $parser, [$message]) + $crate::peek_token!([ $($expected => $out,)+ ], $parser, [$message]) + }; + ([ $($expected:pat),+ $(,)? ], $parser:expr, $message:literal) => { + $crate::peek_token!([ $($expected,)+ ], $parser, [$message]) + }; +} + +#[macro_export] +macro_rules! expect_token { + ([ $($expected:pat => $out:expr),+ $(,)? ], $parser:expr, [ $($message:literal),+ $(,)? ]) => { + $crate::peek_token!([ $($expected => { $parser.next(); $out },)+ ], $parser, [$($message,)+]) + }; + ([ $($expected:pat),+ $(,)? ], $parser:expr, [ $($message:literal),+ $(,)? ]) => { + $crate::peek_token!([ $($expected => { $parser.next(); },)+ ], $parser, [$($message,)+]) + }; + ([ $($expected:pat => $out:expr),+ $(,)? ], $parser:expr, $message:literal) => { + $crate::peek_token!([ $($expected => { $parser.next(); $out },)+ ], $parser, [$message]) }; ([ $($expected:pat),+ $(,)? ], $parser:expr, $message:literal) => { - $crate::expect_token!([ $($expected,)+ ], $parser, [$message]) + $crate::peek_token!([ $($expected => { $parser.next(); },)+ ], $parser, [$message]) }; } diff --git a/tests/0132/code.php b/tests/0132/code.php new file mode 100644 index 00000000..a47a8b00 --- /dev/null +++ b/tests/0132/code.php @@ -0,0 +1,5 @@ + Parse error: unexpected token `abstract`, expecting `const`, or `function` on line 4 column 12 diff --git a/tests/0132/tokens.txt b/tests/0132/tokens.txt new file mode 100644 index 00000000..9004779d --- /dev/null +++ b/tests/0132/tokens.txt @@ -0,0 +1,108 @@ +[ + Token { + kind: OpenTag( + Full, + ), + span: ( + 1, + 1, + ), + }, + Token { + kind: Interface, + span: ( + 3, + 1, + ), + }, + Token { + kind: Identifier( + "foo", + ), + span: ( + 3, + 11, + ), + }, + Token { + kind: LeftBrace, + span: ( + 3, + 15, + ), + }, + Token { + kind: Public, + span: ( + 4, + 5, + ), + }, + Token { + kind: Abstract, + span: ( + 4, + 12, + ), + }, + Token { + kind: Function, + span: ( + 4, + 21, + ), + }, + Token { + kind: Identifier( + "foo", + ), + span: ( + 4, + 30, + ), + }, + Token { + kind: LeftParen, + span: ( + 4, + 33, + ), + }, + Token { + kind: RightParen, + span: ( + 4, + 34, + ), + }, + Token { + kind: Colon, + span: ( + 4, + 35, + ), + }, + Token { + kind: Identifier( + "void", + ), + span: ( + 4, + 37, + ), + }, + Token { + kind: SemiColon, + span: ( + 4, + 41, + ), + }, + Token { + kind: RightBrace, + span: ( + 5, + 1, + ), + }, +] diff --git a/tests/0141/code.php b/tests/0141/code.php new file mode 100644 index 00000000..5395fb09 --- /dev/null +++ b/tests/0141/code.php @@ -0,0 +1,5 @@ + Parse error: unexpected token `private`, expecting `const`, or `function` on line 4 column 5 diff --git a/tests/0141/tokens.txt b/tests/0141/tokens.txt new file mode 100644 index 00000000..6cf60816 --- /dev/null +++ b/tests/0141/tokens.txt @@ -0,0 +1,85 @@ +[ + Token { + kind: OpenTag( + Full, + ), + span: ( + 1, + 1, + ), + }, + Token { + kind: Interface, + span: ( + 3, + 1, + ), + }, + Token { + kind: Identifier( + "foo", + ), + span: ( + 3, + 11, + ), + }, + Token { + kind: LeftBrace, + span: ( + 3, + 15, + ), + }, + Token { + kind: Private, + span: ( + 4, + 5, + ), + }, + Token { + kind: Function, + span: ( + 4, + 13, + ), + }, + Token { + kind: Identifier( + "bar", + ), + span: ( + 4, + 22, + ), + }, + Token { + kind: LeftParen, + span: ( + 4, + 25, + ), + }, + Token { + kind: RightParen, + span: ( + 4, + 26, + ), + }, + Token { + kind: SemiColon, + span: ( + 4, + 27, + ), + }, + Token { + kind: RightBrace, + span: ( + 5, + 1, + ), + }, +] diff --git a/tests/0142/code.php b/tests/0142/code.php new file mode 100644 index 00000000..2b8eda87 --- /dev/null +++ b/tests/0142/code.php @@ -0,0 +1,5 @@ + Parse error: unexpected token `protected`, expecting `const`, or `function` on line 4 column 5 diff --git a/tests/0142/tokens.txt b/tests/0142/tokens.txt new file mode 100644 index 00000000..b313fe6e --- /dev/null +++ b/tests/0142/tokens.txt @@ -0,0 +1,85 @@ +[ + Token { + kind: OpenTag( + Full, + ), + span: ( + 1, + 1, + ), + }, + Token { + kind: Interface, + span: ( + 3, + 1, + ), + }, + Token { + kind: Identifier( + "foo", + ), + span: ( + 3, + 11, + ), + }, + Token { + kind: LeftBrace, + span: ( + 3, + 15, + ), + }, + Token { + kind: Protected, + span: ( + 4, + 5, + ), + }, + Token { + kind: Function, + span: ( + 4, + 15, + ), + }, + Token { + kind: Identifier( + "bar", + ), + span: ( + 4, + 24, + ), + }, + Token { + kind: LeftParen, + span: ( + 4, + 27, + ), + }, + Token { + kind: RightParen, + span: ( + 4, + 28, + ), + }, + Token { + kind: SemiColon, + span: ( + 4, + 29, + ), + }, + Token { + kind: RightBrace, + span: ( + 5, + 1, + ), + }, +] diff --git a/tests/0143/ast.txt b/tests/0143/ast.txt new file mode 100644 index 00000000..fde9fd17 --- /dev/null +++ b/tests/0143/ast.txt @@ -0,0 +1,21 @@ +[ + Interface { + name: Identifier { + name: "foo", + }, + extends: [], + body: [ + AbstractMethod { + name: Identifier { + name: "bar", + }, + params: [], + flags: [ + Public, + ], + return_type: None, + by_ref: false, + }, + ], + }, +] diff --git a/tests/0143/code.php b/tests/0143/code.php new file mode 100644 index 00000000..ed3e83ae --- /dev/null +++ b/tests/0143/code.php @@ -0,0 +1,5 @@ + Parse error: unexpected token `final`, expecting `const`, or `function` on line 4 column 5 diff --git a/tests/0144/tokens.txt b/tests/0144/tokens.txt new file mode 100644 index 00000000..31f09dc1 --- /dev/null +++ b/tests/0144/tokens.txt @@ -0,0 +1,92 @@ +[ + Token { + kind: OpenTag( + Full, + ), + span: ( + 1, + 1, + ), + }, + Token { + kind: Interface, + span: ( + 3, + 1, + ), + }, + Token { + kind: Identifier( + "foo", + ), + span: ( + 3, + 11, + ), + }, + Token { + kind: LeftBrace, + span: ( + 3, + 15, + ), + }, + Token { + kind: Final, + span: ( + 4, + 5, + ), + }, + Token { + kind: Public, + span: ( + 4, + 11, + ), + }, + Token { + kind: Function, + span: ( + 4, + 18, + ), + }, + Token { + kind: Identifier( + "bar", + ), + span: ( + 4, + 27, + ), + }, + Token { + kind: LeftParen, + span: ( + 4, + 30, + ), + }, + Token { + kind: RightParen, + span: ( + 4, + 31, + ), + }, + Token { + kind: SemiColon, + span: ( + 4, + 32, + ), + }, + Token { + kind: RightBrace, + span: ( + 5, + 1, + ), + }, +] diff --git a/tests/0145/ast.txt b/tests/0145/ast.txt new file mode 100644 index 00000000..89d413b0 --- /dev/null +++ b/tests/0145/ast.txt @@ -0,0 +1,22 @@ +[ + Interface { + name: Identifier { + name: "foo", + }, + extends: [], + body: [ + AbstractMethod { + name: Identifier { + name: "bar", + }, + params: [], + flags: [ + Public, + Static, + ], + return_type: None, + by_ref: false, + }, + ], + }, +] diff --git a/tests/0145/code.php b/tests/0145/code.php new file mode 100644 index 00000000..71fb9f7e --- /dev/null +++ b/tests/0145/code.php @@ -0,0 +1,5 @@ +