From 0faf161ed956bd79f7e0f4b9b9e14043f5da4c60 Mon Sep 17 00:00:00 2001 From: Ryan Chandler Date: Tue, 9 Aug 2022 20:57:55 +0100 Subject: [PATCH] parser: support splatting args into method call and dynamic property/method calls --- trunk_parser/src/ast.rs | 5 +-- trunk_parser/src/parser/ident.rs | 3 +- trunk_parser/src/parser/mod.rs | 54 +++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/trunk_parser/src/ast.rs b/trunk_parser/src/ast.rs index 58df8bd..f447310 100644 --- a/trunk_parser/src/ast.rs +++ b/trunk_parser/src/ast.rs @@ -378,7 +378,7 @@ pub enum Expression { }, PropertyFetch { target: Box, - property: Identifier, + property: Box, }, StaticPropertyFetch { target: Box, @@ -390,7 +390,7 @@ pub enum Expression { }, MethodCall { target: Box, - method: Identifier, + method: Box, args: Vec }, StaticMethodCall { @@ -450,6 +450,7 @@ pub enum Expression { pub struct Arg { pub name: Option, pub value: Expression, + pub unpack: bool, } #[derive(Debug, PartialEq, Clone, Serialize)] diff --git a/trunk_parser/src/parser/ident.rs b/trunk_parser/src/parser/ident.rs index d760fc7..2c84241 100644 --- a/trunk_parser/src/parser/ident.rs +++ b/trunk_parser/src/parser/ident.rs @@ -38,7 +38,8 @@ impl Parser { TokenKind::Static | TokenKind::Abstract | TokenKind::Final | TokenKind::For | TokenKind::Private | TokenKind::Protected | TokenKind::Public | TokenKind::Require | TokenKind::RequireOnce | TokenKind::New | TokenKind::Clone | TokenKind::If | - TokenKind::Else | TokenKind::ElseIf | TokenKind::Default | TokenKind::Enum | TokenKind::Match => { + TokenKind::Else | TokenKind::ElseIf | TokenKind::Default | TokenKind::Enum | + TokenKind::Match | TokenKind::Catch | TokenKind::Finally => { let string = self.current.kind.to_string(); self.next(); Ok(string) diff --git a/trunk_parser/src/parser/mod.rs b/trunk_parser/src/parser/mod.rs index 841e4c3..4831f47 100644 --- a/trunk_parser/src/parser/mod.rs +++ b/trunk_parser/src/parser/mod.rs @@ -1394,15 +1394,20 @@ impl Parser { let mut args = Vec::new(); while ! self.is_eof() && self.current.kind != TokenKind::RightParen { let mut name = None; + let mut unpack = false; if matches!(self.current.kind, TokenKind::Identifier(_)) && self.peek.kind == TokenKind::Colon { name = Some(self.ident_maybe_reserved()?); self.next(); + } else if self.current.kind == TokenKind::Ellipsis { + self.next(); + unpack = true; } let value = self.expression(0)?; args.push(Arg { name, + unpack, value }); @@ -1448,15 +1453,20 @@ impl Parser { let mut args = vec![]; while ! self.is_eof() && self.current.kind != TokenKind::RightParen { let mut name = None; + let mut unpack = false; if matches!(self.current.kind, TokenKind::Identifier(_)) && self.peek.kind == TokenKind::Colon { name = Some(self.ident_maybe_reserved()?); self.next(); + } else if self.current.kind == TokenKind::Ellipsis { + self.next(); + unpack = true; } let value = self.expression(0)?; args.push(Arg { name, + unpack, value }); @@ -1473,8 +1483,17 @@ impl Parser { } }, TokenKind::Arrow => { - // TODO: Add support for dynamic property fetch or method call here. - let property = self.ident_maybe_reserved()?; + let property = match self.current.kind { + TokenKind::LeftBrace => { + self.lbrace()?; + let expr = self.expression(0)?; + self.rbrace()?; + expr + }, + _ => { + Expression::Identifier { name: self.ident_maybe_reserved()? } + } + }; if self.current.kind == TokenKind::LeftParen { self.next(); @@ -1482,16 +1501,21 @@ impl Parser { let mut args = Vec::new(); while ! self.is_eof() && self.current.kind != TokenKind::RightParen { let mut name = None; + let mut unpack = false; if matches!(self.current.kind, TokenKind::Identifier(_)) && self.peek.kind == TokenKind::Colon { name = Some(self.ident_maybe_reserved()?); self.next(); + } else if self.current.kind == TokenKind::Ellipsis { + self.next(); + unpack = true; } let value = self.expression(0)?; args.push(Arg { name, - value + value, + unpack }); self.optional_comma()?; @@ -1499,9 +1523,9 @@ impl Parser { self.rparen()?; - Expression::MethodCall { target: Box::new(lhs), method: property.into(), args } + Expression::MethodCall { target: Box::new(lhs), method: Box::new(property), args } } else { - Expression::PropertyFetch { target: Box::new(lhs), property: property.into() } + Expression::PropertyFetch { target: Box::new(lhs), property: Box::new(property) } } }, TokenKind::Increment => { @@ -1890,14 +1914,14 @@ mod tests { assert_ast("bar; $foo->bar->baz;", &[ expr!(Expression::PropertyFetch { target: Box::new(Expression::Variable { name: "foo".into() }), - property: Identifier::from("bar") + property: Box::new(Expression::Identifier { name: "bar".into() }) }), expr!(Expression::PropertyFetch { target: Box::new(Expression::PropertyFetch { target: Box::new(Expression::Variable { name: "foo".into() }), - property: Identifier::from("bar") + property: Box::new(Expression::Identifier { name: "bar".into() }) }), - property: Identifier::from("baz") + property: Box::new(Expression::Identifier { name: "baz".into() }) }) ]); } @@ -1907,7 +1931,7 @@ mod tests { assert_ast("bar();", &[ expr!(Expression::MethodCall { target: Box::new(Expression::Variable { name: "foo".into() }), - method: Identifier::from("bar"), + method: Box::new(Expression::Identifier { name: "bar".into() }), args: vec![] }) ]); @@ -1916,10 +1940,10 @@ mod tests { expr!(Expression::MethodCall { target: Box::new(Expression::MethodCall { target: Box::new(Expression::Variable { name: "foo".into() }), - method: Identifier::from("bar"), + method: Box::new(Expression::Identifier { name: "bar".into() }), args: vec![] }), - method: Identifier::from("baz"), + method: Box::new(Expression::Identifier { name: "baz".into() }), args: vec![] }) ]); @@ -1928,7 +1952,7 @@ mod tests { expr!(Expression::Call { target: Box::new(Expression::MethodCall { target: Box::new(Expression::Variable{ name: "foo".into() }), - method: Identifier::from("bar"), + method: Box::new(Expression::Identifier { name: "bar".into() }), args: vec![] }), args: vec![] @@ -2005,7 +2029,8 @@ mod tests { lhs: Box::new(Expression::Variable { name: "n".into() }), op: InfixOp::Sub, rhs: Box::new(Expression::Int { i: 1 }), - } + }, + unpack: false, } ] }), @@ -2019,7 +2044,8 @@ mod tests { lhs: Box::new(Expression::Variable { name: "n".into() }), op: InfixOp::Sub, rhs: Box::new(Expression::Int { i: 2 }), - } + }, + unpack: false, } ] }),