Merge pull request #103 from ryangjchandler/feature/yields

This commit is contained in:
Ryan Chandler 2022-09-16 23:20:04 +01:00 committed by GitHub
commit c2486d39db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 5 deletions

View File

@ -815,6 +815,7 @@ impl Lexer {
fn identifier_to_keyword(ident: &[u8]) -> Option<TokenKind> {
Some(match ident {
b"from" => TokenKind::From,
b"and" => TokenKind::LogicalAnd,
b"or" => TokenKind::LogicalOr,
b"xor" => TokenKind::LogicalXor,

View File

@ -11,6 +11,7 @@ pub enum OpenTagKind {
#[derive(Debug, PartialEq, Clone)]
pub enum TokenKind {
From,
Print,
Dollar,
HaltCompiler,
@ -194,6 +195,7 @@ impl Default for Token {
impl Display for TokenKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::From => "from",
Self::Print => "print",
Self::BitwiseNot => "~",
Self::Dollar => "$",

View File

@ -557,7 +557,11 @@ pub enum Expression {
value: Box<Expression>,
},
Yield {
value: Box<Expression>,
key: Option<Box<Self>>,
value: Option<Box<Self>>,
},
YieldFrom {
value: Box<Self>,
},
Negate {
value: Box<Self>,

View File

@ -1473,12 +1473,40 @@ impl Parser {
TokenKind::Yield => {
self.next();
let value = self.expression(Precedence::Lowest)?;
if self.current.kind == TokenKind::SemiColon {
Expression::Yield {
key: None,
value: None,
}
} else {
let mut from = false;
// FIXME: Check for presence of => here to allow yielding key and value.
if self.current.kind == TokenKind::From {
self.next();
from = true;
}
Expression::Yield {
value: Box::new(value),
let mut key = None;
let mut value = Box::new(self.expression(if from {
Precedence::YieldFrom
} else {
Precedence::Yield
})?);
if self.current.kind == TokenKind::DoubleArrow && !from {
self.next();
key = Some(value.clone());
value = Box::new(self.expression(Precedence::Yield)?);
}
if from {
Expression::YieldFrom { value }
} else {
Expression::Yield {
key,
value: Some(value),
}
}
}
}
TokenKind::Clone => {
@ -4674,6 +4702,49 @@ mod tests {
);
}
#[test]
fn empty_yield() {
assert_ast(
"<?php yield;",
&[expr!(Expression::Yield {
key: None,
value: None
})],
);
}
#[test]
fn simple_yield() {
assert_ast(
"<?php yield 1;",
&[expr!(Expression::Yield {
key: None,
value: Some(Box::new(Expression::Int { i: 1 }))
})],
);
}
#[test]
fn yield_with_key() {
assert_ast(
"<?php yield 0 => 1;",
&[expr!(Expression::Yield {
key: Some(Box::new(Expression::Int { i: 0 })),
value: Some(Box::new(Expression::Int { i: 1 }))
})],
);
}
#[test]
fn yield_from() {
assert_ast(
"<?php yield from 1;",
&[expr!(Expression::YieldFrom {
value: Box::new(Expression::Int { i: 1 })
})],
);
}
fn assert_ast(source: &str, expected: &[Statement]) {
let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(source).unwrap();