parser: support variadic function params

Closes #9
This commit is contained in:
Ryan Chandler 2022-07-28 01:04:20 +01:00
parent a502c21a7f
commit 479b323cd9
No known key found for this signature in database
GPG Key ID: F113BCADDB3B0CCA
5 changed files with 96 additions and 6 deletions

View File

@ -0,0 +1,7 @@
<?php
function foo(...$bar) {}
function bar(string ...$baz) {}
function baz($foo, $bar, ...$baz) {}

View File

@ -345,6 +345,20 @@ impl Lexer {
}
TokenKind::Float(buffer.parse().unwrap())
} else if let Some('.') = it.peek() {
it.next();
self.col += 1;
if let Some('.') = it.peek() {
it.next();
self.col += 1;
TokenKind::Ellipsis
} else {
todo!("don't know how to handle this case yet, it should just be 2 Dot tokens...")
}
} else {
TokenKind::Dot
}

View File

@ -41,11 +41,12 @@ pub type ParamList = Vec<Param>;
pub struct Param {
pub(crate) name: Expression,
pub(crate) r#type: Option<Type>,
pub(crate) variadic: bool,
}
impl From<String> for Param {
fn from(name: String) -> Self {
Self { name: Expression::Variable(name), r#type: None }
Self { name: Expression::Variable(name), r#type: None, variadic: false }
}
}

View File

@ -1332,7 +1332,66 @@ mod tests {
params: vec![
Param {
name: Expression::Variable("b".into()),
r#type: Some(Type::Plain("string".into()))
r#type: Some(Type::Plain("string".into())),
variadic: false,
}
],
body: vec![],
return_type: None,
}
]);
}
#[test]
fn variadic_params() {
assert_ast("<?php function foo(...$bar) {}", &[
Statement::Function {
name: "foo".to_string().into(),
params: vec![
Param {
name: Expression::Variable("bar".into()),
r#type: None,
variadic: true,
}
],
body: vec![],
return_type: None,
}
]);
assert_ast("<?php function foo(string ...$bar) {}", &[
Statement::Function {
name: "foo".to_string().into(),
params: vec![
Param {
name: Expression::Variable("bar".into()),
r#type: Some(Type::Plain("string".into())),
variadic: true,
}
],
body: vec![],
return_type: None,
}
]);
assert_ast("<?php function foo($bar, $baz, ...$car) {}", &[
Statement::Function {
name: "foo".to_string().into(),
params: vec![
Param {
name: Expression::Variable("bar".into()),
r#type: None,
variadic: false,
},
Param {
name: Expression::Variable("baz".into()),
r#type: None,
variadic: false,
},
Param {
name: Expression::Variable("car".into()),
r#type: None,
variadic: true,
}
],
body: vec![],
@ -1349,7 +1408,8 @@ mod tests {
params: vec![
Param {
name: Expression::Variable("b".into()),
r#type: Some(Type::Nullable("string".into()))
r#type: Some(Type::Nullable("string".into())),
variadic: false,
}
],
body: vec![],
@ -1369,7 +1429,8 @@ mod tests {
r#type: Some(Type::Union(vec![
"int".into(),
"float".into()
]))
])),
variadic: false,
}
],
body: vec![],
@ -1389,7 +1450,8 @@ mod tests {
r#type: Some(Type::Intersection(vec![
"Foo".into(),
"Bar".into()
]))
])),
variadic: false
}
],
body: vec![],

View File

@ -11,11 +11,16 @@ impl Parser {
let mut param_type = None;
// 1. If we don't see a variable, we should expect a type-string.
if ! matches!(self.current.kind, TokenKind::Variable(_)) {
if ! matches!(self.current.kind, TokenKind::Variable(_) | TokenKind::Ellipsis) {
// 1a. Try to parse the type.
param_type = Some(self.type_string()?);
}
let variadic = if self.current.kind == TokenKind::Ellipsis {
self.next();
true
} else { false };
// 2. Then expect a variable.
let var = expect!(self, TokenKind::Variable(v), v, "expected variable");
@ -23,6 +28,7 @@ impl Parser {
params.push(Param {
name: Expression::Variable(var),
r#type: param_type,
variadic
});
if self.current.kind == TokenKind::Comma {