parser: support splatting args into method call and dynamic property/method calls

This commit is contained in:
Ryan Chandler 2022-08-09 20:57:55 +01:00
parent e4ad01c545
commit 0faf161ed9
No known key found for this signature in database
GPG Key ID: F113BCADDB3B0CCA
3 changed files with 45 additions and 17 deletions

View File

@ -378,7 +378,7 @@ pub enum Expression {
},
PropertyFetch {
target: Box<Self>,
property: Identifier,
property: Box<Self>,
},
StaticPropertyFetch {
target: Box<Self>,
@ -390,7 +390,7 @@ pub enum Expression {
},
MethodCall {
target: Box<Self>,
method: Identifier,
method: Box<Self>,
args: Vec<Arg>
},
StaticMethodCall {
@ -450,6 +450,7 @@ pub enum Expression {
pub struct Arg {
pub name: Option<String>,
pub value: Expression,
pub unpack: bool,
}
#[derive(Debug, PartialEq, Clone, Serialize)]

View File

@ -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)

View File

@ -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("<?php $foo->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("<?php $foo->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,
}
]
}),