parser: support class extends and implements

This commit is contained in:
Ryan Chandler 2022-07-19 15:13:53 +01:00
parent a8a29a0e7c
commit 33fc8c6b0e
No known key found for this signature in database
GPG Key ID: F113BCADDB3B0CCA
2 changed files with 58 additions and 2 deletions

View File

@ -61,6 +61,8 @@ pub enum Statement {
},
Class {
name: Identifier,
extends: Option<Identifier>,
implements: Vec<Identifier>,
body: Block,
},
Method {

View File

@ -1,6 +1,6 @@
use std::{vec::IntoIter, fmt::Display};
use trunk_lexer::{Token, TokenKind, Span};
use crate::{Program, Statement, Block, Expression, ast::MethodFlag};
use crate::{Program, Statement, Block, Expression, ast::MethodFlag, Identifier};
macro_rules! expect {
($parser:expr, $expected:pat, $out:expr, $message:literal) => {
@ -73,6 +73,26 @@ impl Parser {
self.next();
let name = expect!(self, TokenKind::Identifier(i), i, "expected class name");
let mut extends: Option<Identifier> = None;
if self.current.kind == TokenKind::Extends {
self.next();
extends = expect!(self, TokenKind::Identifier(i), Some(i.into()), "expected identifier");
}
let mut implements = Vec::new();
if self.current.kind == TokenKind::Implements {
self.next();
while self.current.kind != TokenKind::LeftBrace {
if self.current.kind == TokenKind::Comma {
self.next();
}
implements.push(expect!(self, TokenKind::Identifier(i), i.into(), "expected identifier"));
}
}
expect!(self, TokenKind::LeftBrace, "expected left-brace");
let mut body = Vec::new();
@ -90,7 +110,7 @@ impl Parser {
expect!(self, TokenKind::RightBrace, "expected right-brace");
Statement::Class { name: name.into(), body }
Statement::Class { name: name.into(), extends, implements, body }
},
TokenKind::Echo => {
self.next();
@ -348,12 +368,24 @@ mod tests {
Statement::Class {
name: $name.to_string().into(),
body: vec![],
extends: None,
implements: vec![],
}
};
($name:literal, $body:expr) => {
Statement::Class {
name: $name.to_string().into(),
body: $body.to_vec(),
extends: None,
implements: vec![],
}
};
($name:literal, $extends:expr, $implements:expr, $body:expr) => {
Statement::Class {
name: $name.to_string().into(),
body: $body.to_vec(),
extends: $extends,
implements: $implements.to_vec(),
}
};
}
@ -507,6 +539,28 @@ mod tests {
]);
}
#[test]
fn class_with_extends() {
assert_ast("\
<?php
class Foo extends Bar {}
", &[
class!("Foo", Some("Bar".to_string().into()), &[], &[]),
]);
}
#[test]
fn class_with_implements() {
assert_ast("\
<?php
class Foo implements Bar, Baz {}
", &[
class!("Foo", None, &["Bar".to_string().into(), "Baz".to_string().into()], &[]),
]);
}
fn assert_ast(source: &str, expected: &[Statement]) {
let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(source).unwrap();