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 { Class {
name: Identifier, name: Identifier,
extends: Option<Identifier>,
implements: Vec<Identifier>,
body: Block, body: Block,
}, },
Method { Method {

View File

@ -1,6 +1,6 @@
use std::{vec::IntoIter, fmt::Display}; use std::{vec::IntoIter, fmt::Display};
use trunk_lexer::{Token, TokenKind, Span}; 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 { macro_rules! expect {
($parser:expr, $expected:pat, $out:expr, $message:literal) => { ($parser:expr, $expected:pat, $out:expr, $message:literal) => {
@ -73,6 +73,26 @@ impl Parser {
self.next(); self.next();
let name = expect!(self, TokenKind::Identifier(i), i, "expected class name"); 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"); expect!(self, TokenKind::LeftBrace, "expected left-brace");
let mut body = Vec::new(); let mut body = Vec::new();
@ -90,7 +110,7 @@ impl Parser {
expect!(self, TokenKind::RightBrace, "expected right-brace"); expect!(self, TokenKind::RightBrace, "expected right-brace");
Statement::Class { name: name.into(), body } Statement::Class { name: name.into(), extends, implements, body }
}, },
TokenKind::Echo => { TokenKind::Echo => {
self.next(); self.next();
@ -348,12 +368,24 @@ mod tests {
Statement::Class { Statement::Class {
name: $name.to_string().into(), name: $name.to_string().into(),
body: vec![], body: vec![],
extends: None,
implements: vec![],
} }
}; };
($name:literal, $body:expr) => { ($name:literal, $body:expr) => {
Statement::Class { Statement::Class {
name: $name.to_string().into(), name: $name.to_string().into(),
body: $body.to_vec(), 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]) { fn assert_ast(source: &str, expected: &[Statement]) {
let mut lexer = Lexer::new(None); let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(source).unwrap(); let tokens = lexer.tokenize(source).unwrap();