chore: remove Parser structure

Signed-off-by: azjezz <azjezz@protonmail.com>
This commit is contained in:
azjezz 2022-12-07 23:11:37 +01:00
parent 8de571f97f
commit ad0623ed20
No known key found for this signature in database
GPG Key ID: B00E0A46B3F1C157
33 changed files with 3261 additions and 2977 deletions

View File

@ -26,12 +26,11 @@ cargo add php-parser-rs
### Example
```rust
use php_parser_rs::parser::Parser;
use php_parser_rs::parse;
use php_parser_rs::lexer::Lexer;
fn main() -> ParseResult<()> {
let lexer = Lexer::new();
let parser = Parser::new();
let code = "
<?php
@ -44,7 +43,7 @@ hello();
";
let tokens = lexer.tokenize(code.as_bytes())?;
let ast = parser.parse(tokens)?;
let ast = parse(tokens)?;
dbg!(ast);

View File

@ -3,9 +3,8 @@ use std::fs::read_dir;
use std::path::PathBuf;
use php_parser_rs::lexer::Lexer;
use php_parser_rs::parser::Parser;
use php_parser_rs::parse;
static PARSER: Parser = Parser::new();
static LEXER: Lexer = Lexer::new();
fn main() {
@ -57,7 +56,7 @@ fn main() {
entry.to_string_lossy()
);
let ast = PARSER.parse(tokens);
let ast = parse(tokens);
match ast {
Ok(ast) => {
std::fs::write(ast_filename, format!("{:#?}\n", ast)).unwrap();

View File

@ -1,2 +1,4 @@
pub mod lexer;
pub mod parser;
pub use parser::parse;

View File

@ -1,6 +1,5 @@
use php_parser_rs::lexer::Lexer;
use php_parser_rs::parser::error::ParseResult;
use php_parser_rs::parser::Parser;
fn main() -> ParseResult<()> {
let file = match std::env::args().nth(1) {
@ -22,12 +21,11 @@ fn main() -> ParseResult<()> {
};
let lexer = Lexer::new();
let parser = Parser::new();
let tokens = lexer.tokenize(&contents)?;
// dbg!(&tokens);
let ast = parser.parse(tokens)?;
let ast = php_parser_rs::parse(tokens)?;
dbg!(ast);

View File

@ -11,6 +11,7 @@ pub mod variables;
use std::fmt::Display;
use crate::lexer::byte_string::ByteString;
use crate::lexer::token::Span;
use crate::lexer::token::TokenKind;
use crate::parser::ast::attributes::AttributeGroup;
use crate::parser::ast::classish::ClassishConstant;
@ -316,7 +317,7 @@ pub enum Statement {
declares: Vec<DeclareItem>,
body: Block,
},
Noop,
Noop(Span),
}
#[derive(Debug, Clone, PartialEq)]

View File

@ -1,4 +1,5 @@
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::ArrayItem;
use crate::parser::ast::Expression;
use crate::parser::ast::ListItem;
@ -7,27 +8,48 @@ use crate::parser::error::ParseResult;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::State;
use crate::parser::Parser;
impl Parser {
pub(in crate::parser) fn list_expression(&self, state: &mut State) -> ParseResult<Expression> {
utils::skip(state, TokenKind::List)?;
utils::skip_left_parenthesis(state)?;
pub fn list_expression(state: &mut State) -> ParseResult<Expression> {
utils::skip(state, TokenKind::List)?;
utils::skip_left_parenthesis(state)?;
let mut items = Vec::new();
let mut has_atleast_one_key = false;
let mut items = Vec::new();
let mut has_atleast_one_key = false;
while state.current.kind != TokenKind::RightParen {
if state.current.kind == TokenKind::Comma {
items.push(ListItem {
key: None,
value: Expression::Empty,
});
state.next();
continue;
while state.current.kind != TokenKind::RightParen {
if state.current.kind == TokenKind::Comma {
items.push(ListItem {
key: None,
value: Expression::Empty,
});
state.next();
continue;
}
let mut key = None;
if state.current.kind == TokenKind::Ellipsis {
return Err(ParseError::IllegalSpreadOperator(state.current.span));
}
if state.current.kind == TokenKind::Ampersand {
return Err(ParseError::CannotAssignReferenceToNonReferencableValue(
state.current.span,
));
}
let mut value = parser::expression(state, Precedence::Lowest)?;
if state.current.kind == TokenKind::DoubleArrow {
if !has_atleast_one_key && !items.is_empty() {
return Err(ParseError::CannotMixKeyedAndUnkeyedEntries(
state.current.span,
));
}
let mut key = None;
state.next();
key = Some(value);
if state.current.kind == TokenKind::Ellipsis {
return Err(ParseError::IllegalSpreadOperator(state.current.span));
@ -39,94 +61,77 @@ impl Parser {
));
}
let mut value = self.expression(state, Precedence::Lowest)?;
if state.current.kind == TokenKind::DoubleArrow {
if !has_atleast_one_key && !items.is_empty() {
return Err(ParseError::CannotMixKeyedAndUnkeyedEntries(
state.current.span,
));
}
state.next();
key = Some(value);
if state.current.kind == TokenKind::Ellipsis {
return Err(ParseError::IllegalSpreadOperator(state.current.span));
}
if state.current.kind == TokenKind::Ampersand {
return Err(ParseError::CannotAssignReferenceToNonReferencableValue(
state.current.span,
));
}
has_atleast_one_key = true;
value = self.expression(state, Precedence::Lowest)?;
} else if has_atleast_one_key {
return Err(ParseError::CannotMixKeyedAndUnkeyedEntries(
state.current.span,
));
}
items.push(ListItem { key, value });
state.skip_comments();
if state.current.kind == TokenKind::Comma {
state.next();
state.skip_comments();
} else {
break;
}
has_atleast_one_key = true;
value = parser::expression(state, Precedence::Lowest)?;
} else if has_atleast_one_key {
return Err(ParseError::CannotMixKeyedAndUnkeyedEntries(
state.current.span,
));
}
utils::skip_right_parenthesis(state)?;
items.push(ListItem { key, value });
Ok(Expression::List { items })
}
pub(in crate::parser) fn array_expression(&self, state: &mut State) -> ParseResult<Expression> {
utils::skip(state, TokenKind::LeftBracket)?;
let mut items = Vec::new();
state.skip_comments();
while state.current.kind != TokenKind::RightBracket {
// TODO: return an error here instead of
// an empty array element
// see: https://3v4l.org/uLTVA
if state.current.kind == TokenKind::Comma {
items.push(ArrayItem {
key: None,
value: Expression::Empty,
unpack: false,
by_ref: false,
});
state.next();
continue;
}
items.push(self.array_pair(state)?);
state.skip_comments();
if state.current.kind != TokenKind::Comma {
break;
}
if state.current.kind == TokenKind::Comma {
state.next();
state.skip_comments();
} else {
break;
}
}
utils::skip_right_parenthesis(state)?;
Ok(Expression::List { items })
}
pub fn array_expression(state: &mut State) -> ParseResult<Expression> {
utils::skip(state, TokenKind::LeftBracket)?;
let mut items = Vec::new();
state.skip_comments();
while state.current.kind != TokenKind::RightBracket {
// TODO: return an error here instead of
// an empty array element
// see: https://3v4l.org/uLTVA
if state.current.kind == TokenKind::Comma {
items.push(ArrayItem {
key: None,
value: Expression::Empty,
unpack: false,
by_ref: false,
});
state.next();
continue;
}
items.push(array_pair(state)?);
state.skip_comments();
utils::skip_right_bracket(state)?;
if state.current.kind != TokenKind::Comma {
break;
}
Ok(Expression::Array { items })
state.next();
state.skip_comments();
}
pub(in crate::parser) fn array_pair(&self, state: &mut State) -> ParseResult<ArrayItem> {
state.skip_comments();
utils::skip_right_bracket(state)?;
Ok(Expression::Array { items })
}
pub fn legacy_array_expression(state: &mut State) -> ParseResult<Expression> {
utils::skip(state, TokenKind::Array)?;
utils::skip_left_parenthesis(state)?;
let mut items = vec![];
while state.current.kind != TokenKind::RightParen {
let mut key = None;
let unpack = if state.current.kind == TokenKind::Ellipsis {
state.next();
@ -143,7 +148,9 @@ impl Parser {
(false, (0, 0))
};
let mut value = self.expression(state, Precedence::Lowest)?;
let mut value = parser::expression(state, Precedence::Lowest)?;
// TODO: return error for `[...$a => $b]`.
if state.current.kind == TokenKind::DoubleArrow {
state.next();
@ -155,20 +162,80 @@ impl Parser {
}
key = Some(value);
by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
value = self.expression(state, Precedence::Lowest)?;
value = parser::expression(state, Precedence::Lowest)?;
}
Ok(ArrayItem {
items.push(ArrayItem {
key,
value,
unpack,
by_ref,
})
});
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
state.skip_comments();
}
utils::skip_right_parenthesis(state)?;
Ok(Expression::Array { items })
}
fn array_pair(state: &mut State) -> ParseResult<ArrayItem> {
let mut key = None;
let unpack = if state.current.kind == TokenKind::Ellipsis {
state.next();
true
} else {
false
};
let (mut by_ref, amper_span) = if state.current.kind == TokenKind::Ampersand {
let span = state.current.span;
state.next();
(true, span)
} else {
(false, (0, 0))
};
let mut value = parser::expression(state, Precedence::Lowest)?;
if state.current.kind == TokenKind::DoubleArrow {
state.next();
if by_ref {
return Err(ParseError::UnexpectedToken(
TokenKind::Ampersand.to_string(),
amper_span,
));
}
key = Some(value);
by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
value = parser::expression(state, Precedence::Lowest)?;
}
Ok(ArrayItem {
key,
value,
unpack,
by_ref,
})
}

View File

@ -1,52 +1,50 @@
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::attributes::Attribute;
use crate::parser::ast::attributes::AttributeGroup;
use crate::parser::error::ParseResult;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::State;
use crate::parser::Parser;
impl Parser {
pub(in crate::parser) fn gather_attributes(&self, state: &mut State) -> ParseResult<bool> {
state.gather_comments();
pub fn gather_attributes(state: &mut State) -> ParseResult<bool> {
state.gather_comments();
if state.current.kind != TokenKind::Attribute {
return Ok(false);
}
if state.current.kind != TokenKind::Attribute {
return Ok(false);
}
let start = state.current.span;
let mut members = vec![];
state.next();
while state.current.kind != TokenKind::RightBracket {
let start = state.current.span;
let mut members = vec![];
let expression = parser::expression(state, Precedence::Lowest)?;
let end = state.current.span;
state.next();
while state.current.kind != TokenKind::RightBracket {
let start = state.current.span;
let expression = self.expression(state, Precedence::Lowest)?;
let end = state.current.span;
members.push(Attribute {
start,
expression,
end,
});
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
let end = utils::skip_right_bracket(state)?;
state.attribute(AttributeGroup {
members.push(Attribute {
start,
members,
expression,
end,
});
// recursive, looking for multiple attribute brackets after each other.
self.gather_attributes(state).map(|_| true)
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
let end = utils::skip_right_bracket(state)?;
state.attribute(AttributeGroup {
start,
members,
end,
});
// recursive, looking for multiple attribute brackets after each other.
gather_attributes(state).map(|_| true)
}

View File

@ -1,41 +1,35 @@
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::Block;
use crate::parser::ast::Statement;
use crate::parser::error::ParseResult;
use crate::parser::internal::utils;
use crate::parser::state::State;
use crate::parser::Parser;
impl Parser {
pub(in crate::parser) fn block_statement(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip_left_brace(state)?;
pub fn block_statement(state: &mut State) -> ParseResult<Statement> {
utils::skip_left_brace(state)?;
let body = self.body(state, &TokenKind::RightBrace)?;
let body = body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
utils::skip_right_brace(state)?;
Ok(Statement::Block { body })
}
Ok(Statement::Block { body })
}
pub(in crate::parser) fn body(
&self,
state: &mut State,
until: &TokenKind,
) -> ParseResult<Block> {
state.skip_comments();
pub fn body(state: &mut State, until: &TokenKind) -> ParseResult<Block> {
state.skip_comments();
let mut block = Block::new();
let mut block = Block::new();
while !state.is_eof() && &state.current.kind != until {
if let TokenKind::OpenTag(_) = state.current.kind {
state.next();
continue;
}
block.push(self.statement(state)?);
state.skip_comments();
while !state.is_eof() && &state.current.kind != until {
if let TokenKind::OpenTag(_) = state.current.kind {
state.next();
continue;
}
Ok(block)
block.push(parser::statement(state)?);
state.skip_comments();
}
Ok(block)
}

View File

@ -7,239 +7,171 @@ use crate::parser::ast::identifiers::Identifier;
use crate::parser::ast::Expression;
use crate::parser::ast::Statement;
use crate::parser::error::ParseResult;
use crate::parser::internal::attributes;
use crate::parser::internal::classish_statements;
use crate::parser::internal::identifiers;
use crate::parser::internal::modifiers;
use crate::parser::internal::parameters;
use crate::parser::internal::utils;
use crate::parser::state::Scope;
use crate::parser::state::State;
use crate::parser::Parser;
use crate::scoped;
impl Parser {
pub(in crate::parser) fn class_definition(&self, state: &mut State) -> ParseResult<Statement> {
let modifiers = modifiers::class_group(modifiers::collect(state)?)?;
pub fn class_definition(state: &mut State) -> ParseResult<Statement> {
let modifiers = modifiers::class_group(modifiers::collect(state)?)?;
utils::skip(state, TokenKind::Class)?;
utils::skip(state, TokenKind::Class)?;
let name = identifiers::ident(state)?;
let name = identifiers::ident(state)?;
let mut has_parent = false;
let mut extends: Option<Identifier> = None;
let mut has_parent = false;
let mut extends: Option<Identifier> = None;
if state.current.kind == TokenKind::Extends {
state.next();
extends = Some(identifiers::full_name(state)?);
has_parent = true;
if state.current.kind == TokenKind::Extends {
state.next();
extends = Some(identifiers::full_name(state)?);
has_parent = true;
}
let implements = if state.current.kind == TokenKind::Implements {
state.next();
at_least_one_comma_separated::<Identifier>(state, &identifiers::full_name)?
} else {
Vec::new()
};
let attributes = state.get_attributes();
utils::skip_left_brace(state)?;
let body = scoped!(
state,
Scope::Class(name.clone(), modifiers.clone(), has_parent),
{
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace {
state.gather_comments();
if state.current.kind == TokenKind::RightBrace {
state.clear_comments();
break;
}
body.push(classish_statements::class_like_statement(state)?);
}
body
}
);
let implements = if state.current.kind == TokenKind::Implements {
utils::skip_right_brace(state)?;
Ok(Statement::Class {
name,
attributes,
extends,
implements,
body,
modifiers,
})
}
pub fn interface_definition(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Interface)?;
let name = identifiers::ident(state)?;
scoped!(state, Scope::Interface(name.clone()), {
let extends = if state.current.kind == TokenKind::Extends {
state.next();
self.at_least_one_comma_separated::<Identifier>(state, &|_, state| {
at_least_one_comma_separated::<Identifier>(state, &|state| {
identifiers::full_name(state)
})?
} else {
Vec::new()
};
let attributes = state.get_attributes();
utils::skip_left_brace(state)?;
let body = scoped!(
state,
Scope::Class(name.clone(), modifiers.clone(), has_parent),
{
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace {
state.gather_comments();
let attributes = state.get_attributes();
if state.current.kind == TokenKind::RightBrace {
state.clear_comments();
break;
}
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
state.gather_comments();
body.push(self.class_like_statement(state)?);
}
body
if state.current.kind == TokenKind::RightBrace {
state.clear_comments();
break;
}
);
body.push(classish_statements::interface_statement(state)?);
}
utils::skip_right_brace(state)?;
Ok(Statement::Class {
Ok(Statement::Interface {
name,
attributes,
extends,
implements,
body,
modifiers,
})
}
})
}
pub(in crate::parser) fn interface_definition(
&self,
state: &mut State,
) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Interface)?;
pub fn trait_definition(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Trait)?;
let name = identifiers::ident(state)?;
let name = identifiers::ident(state)?;
scoped!(state, Scope::Interface(name.clone()), {
let extends = if state.current.kind == TokenKind::Extends {
state.next();
scoped!(state, Scope::Trait(name.clone()), {
utils::skip_left_brace(state)?;
self.at_least_one_comma_separated::<Identifier>(state, &|_, state| {
identifiers::full_name(state)
})?
} else {
Vec::new()
};
let attributes = state.get_attributes();
utils::skip_left_brace(state)?;
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
state.gather_comments();
let attributes = state.get_attributes();
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
state.gather_comments();
if state.current.kind == TokenKind::RightBrace {
state.clear_comments();
break;
}
body.push(self.interface_statement(state)?);
if state.current.kind == TokenKind::RightBrace {
state.clear_comments();
break;
}
utils::skip_right_brace(state)?;
Ok(Statement::Interface {
name,
attributes,
extends,
body,
})
})
}
pub(in crate::parser) fn trait_definition(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Trait)?;
let name = identifiers::ident(state)?;
scoped!(state, Scope::Trait(name.clone()), {
utils::skip_left_brace(state)?;
let attributes = state.get_attributes();
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
state.gather_comments();
if state.current.kind == TokenKind::RightBrace {
state.clear_comments();
break;
}
body.push(self.class_like_statement(state)?);
}
utils::skip_right_brace(state)?;
Ok(Statement::Trait {
name,
attributes,
body,
})
})
}
pub(in crate::parser) fn anonymous_class_definition(
&self,
state: &mut State,
) -> ParseResult<Expression> {
utils::skip(state, TokenKind::New)?;
self.gather_attributes(state)?;
utils::skip(state, TokenKind::Class)?;
let mut args = vec![];
if state.current.kind == TokenKind::LeftParen {
args = self.args_list(state)?;
body.push(classish_statements::class_like_statement(state)?);
}
utils::skip_right_brace(state)?;
let mut has_parent = false;
let mut extends: Option<Identifier> = None;
if state.current.kind == TokenKind::Extends {
state.next();
extends = Some(identifiers::full_name(state)?);
has_parent = true;
}
scoped!(state, Scope::AnonymousClass(has_parent), {
let mut implements = Vec::new();
if state.current.kind == TokenKind::Implements {
state.next();
while state.current.kind != TokenKind::LeftBrace {
implements.push(identifiers::full_name(state)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
}
utils::skip_left_brace(state)?;
let attributes = state.get_attributes();
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
body.push(self.class_like_statement(state)?);
}
utils::skip_right_brace(state)?;
Ok(Expression::New {
target: Box::new(Expression::AnonymousClass {
attributes,
extends,
implements,
body,
}),
args,
})
Ok(Statement::Trait {
name,
attributes,
body,
})
})
}
pub fn anonymous_class_definition(state: &mut State) -> ParseResult<Expression> {
utils::skip(state, TokenKind::New)?;
attributes::gather_attributes(state)?;
utils::skip(state, TokenKind::Class)?;
let mut args = vec![];
if state.current.kind == TokenKind::LeftParen {
args = parameters::args_list(state)?;
}
pub(in crate::parser) fn enum_definition(&self, state: &mut State) -> ParseResult<Statement> {
let start = state.current.span;
let mut has_parent = false;
let mut extends: Option<Identifier> = None;
utils::skip(state, TokenKind::Enum)?;
let name = identifiers::ident(state)?;
let backed_type: Option<BackedEnumType> = if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
expect_token!([
TokenKind::Identifier(s) if s == b"string" || s == b"int" => {
Some(match &s[..] {
b"string" => BackedEnumType::String,
b"int" => BackedEnumType::Int,
_ => unreachable!(),
})
},
], state, ["`string`", "`int`",])
} else {
None
};
if state.current.kind == TokenKind::Extends {
state.next();
extends = Some(identifiers::full_name(state)?);
has_parent = true;
}
scoped!(state, Scope::AnonymousClass(has_parent), {
let mut implements = Vec::new();
if state.current.kind == TokenKind::Implements {
state.next();
@ -255,72 +187,131 @@ impl Parser {
}
}
utils::skip_left_brace(state)?;
let attributes = state.get_attributes();
if let Some(backed_type) = backed_type {
let (members, end) = scoped!(state, Scope::Enum(name.clone(), true), {
utils::skip_left_brace(state)?;
// TODO(azjezz): we know members might have corrupted start span, we could updated it here?
// as we know the correct start span is `state.current.span`.
let mut members = Vec::new();
while state.current.kind != TokenKind::RightBrace {
state.skip_comments();
members.push(self.backed_enum_member(state)?);
}
let end = utils::skip_right_brace(state)?;
(members, end)
});
Ok(Statement::BackedEnum(BackedEnum {
start,
end,
name,
backed_type,
attributes,
implements,
members,
}))
} else {
let (members, end) = scoped!(state, Scope::Enum(name.clone(), false), {
utils::skip_left_brace(state)?;
let mut members = Vec::new();
while state.current.kind != TokenKind::RightBrace {
state.skip_comments();
members.push(self.unit_enum_member(state)?);
}
(members, utils::skip_right_brace(state)?)
});
Ok(Statement::UnitEnum(UnitEnum {
start,
end,
name,
attributes,
implements,
members,
}))
let mut body = Vec::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
body.push(classish_statements::class_like_statement(state)?);
}
}
fn at_least_one_comma_separated<T>(
&self,
state: &mut State,
func: &(dyn Fn(&Parser, &mut State) -> ParseResult<T>),
) -> ParseResult<Vec<T>> {
let mut result: Vec<T> = vec![];
loop {
result.push(func(self, state)?);
if state.current.kind != TokenKind::Comma {
utils::skip_right_brace(state)?;
Ok(Expression::New {
target: Box::new(Expression::AnonymousClass {
attributes,
extends,
implements,
body,
}),
args,
})
})
}
pub fn enum_definition(state: &mut State) -> ParseResult<Statement> {
let start = state.current.span;
utils::skip(state, TokenKind::Enum)?;
let name = identifiers::ident(state)?;
let backed_type: Option<BackedEnumType> = if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
expect_token!([
TokenKind::Identifier(s) if s == b"string" || s == b"int" => {
Some(match &s[..] {
b"string" => BackedEnumType::String,
b"int" => BackedEnumType::Int,
_ => unreachable!(),
})
},
], state, ["`string`", "`int`",])
} else {
None
};
let mut implements = Vec::new();
if state.current.kind == TokenKind::Implements {
state.next();
while state.current.kind != TokenKind::LeftBrace {
implements.push(identifiers::full_name(state)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
state.next();
}
}
Ok(result)
let attributes = state.get_attributes();
if let Some(backed_type) = backed_type {
let (members, end) = scoped!(state, Scope::Enum(name.clone(), true), {
utils::skip_left_brace(state)?;
// TODO(azjezz): we know members might have corrupted start span, we could updated it here?
// as we know the correct start span is `state.current.span`.
let mut members = Vec::new();
while state.current.kind != TokenKind::RightBrace {
state.skip_comments();
members.push(classish_statements::backed_enum_member(state)?);
}
let end = utils::skip_right_brace(state)?;
(members, end)
});
Ok(Statement::BackedEnum(BackedEnum {
start,
end,
name,
backed_type,
attributes,
implements,
members,
}))
} else {
let (members, end) = scoped!(state, Scope::Enum(name.clone(), false), {
utils::skip_left_brace(state)?;
let mut members = Vec::new();
while state.current.kind != TokenKind::RightBrace {
state.skip_comments();
members.push(classish_statements::unit_enum_member(state)?);
}
(members, utils::skip_right_brace(state)?)
});
Ok(Statement::UnitEnum(UnitEnum {
start,
end,
name,
attributes,
implements,
members,
}))
}
}
fn at_least_one_comma_separated<T>(
state: &mut State,
func: &(dyn Fn(&mut State) -> ParseResult<T>),
) -> ParseResult<Vec<T>> {
let mut result: Vec<T> = vec![];
loop {
result.push(func(state)?);
if state.current.kind != TokenKind::Comma {
break;
}
state.next();
}
Ok(result)
}

View File

@ -2,6 +2,7 @@ use crate::expect_token;
use crate::expected_scope;
use crate::lexer::token::Span;
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::classish::ClassishConstant;
use crate::parser::ast::enums::BackedEnumCase;
use crate::parser::ast::enums::BackedEnumMember;
@ -14,291 +15,277 @@ use crate::parser::ast::Statement;
use crate::parser::ast::TraitAdaptation;
use crate::parser::error::ParseError;
use crate::parser::error::ParseResult;
use crate::parser::internal::attributes;
use crate::parser::internal::data_type;
use crate::parser::internal::functions;
use crate::parser::internal::identifiers;
use crate::parser::internal::modifiers;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::Scope;
use crate::parser::state::State;
use crate::parser::Parser;
use crate::peek_token;
impl Parser {
pub(in crate::parser) fn interface_statement(
&self,
state: &mut State,
) -> ParseResult<Statement> {
let has_attributes = self.gather_attributes(state)?;
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
pub fn interface_statement(state: &mut State) -> ParseResult<Statement> {
let has_attributes = attributes::gather_attributes(state)?;
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
// if we have attributes, don't check const, we need a method.
if has_attributes || state.current.kind == TokenKind::Function {
Ok(Statement::Method(self.method(
state,
modifiers::interface_method_group(modifiers)?,
start,
)?))
} else {
Ok(Statement::ClassishConstant(self.constant(
state,
modifiers::interface_constant_group(modifiers)?,
start,
)?))
}
// if we have attributes, don't check const, we need a method.
if has_attributes || state.current.kind == TokenKind::Function {
Ok(Statement::Method(functions::method(
state,
modifiers::interface_method_group(modifiers)?,
start,
)?))
} else {
Ok(Statement::ClassishConstant(constant(
state,
modifiers::interface_constant_group(modifiers)?,
start,
)?))
}
}
pub(in crate::parser) fn unit_enum_member(
&self,
state: &mut State,
) -> ParseResult<UnitEnumMember> {
let enum_name = expected_scope!([
pub fn unit_enum_member(state: &mut State) -> ParseResult<UnitEnumMember> {
let enum_name = expected_scope!([
Scope::Enum(enum_name, _) => enum_name,
], state);
let has_attributes = self.gather_attributes(state)?;
if !has_attributes && state.current.kind == TokenKind::Case {
let start = state.current.span;
state.next();
let name = identifiers::ident(state)?;
if state.current.kind == TokenKind::Equals {
return Err(ParseError::CaseValueForUnitEnum(
name.to_string(),
state.named(&enum_name),
state.current.span,
));
}
let end = utils::skip_semicolon(state)?;
return Ok(UnitEnumMember::Case(UnitEnumCase { start, end, name }));
}
let has_attributes = attributes::gather_attributes(state)?;
if !has_attributes && state.current.kind == TokenKind::Case {
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
state.next();
// if we have attributes, don't check const, we need a method.
if has_attributes || state.current.kind == TokenKind::Function {
Ok(UnitEnumMember::Method(self.method(
state,
modifiers::enum_method_group(modifiers)?,
start,
)?))
} else {
Ok(UnitEnumMember::Constant(self.constant(
state,
modifiers::constant_group(modifiers)?,
start,
)?))
let name = identifiers::ident(state)?;
if state.current.kind == TokenKind::Equals {
return Err(ParseError::CaseValueForUnitEnum(
name.to_string(),
state.named(&enum_name),
state.current.span,
));
}
let end = utils::skip_semicolon(state)?;
return Ok(UnitEnumMember::Case(UnitEnumCase { start, end, name }));
}
pub(in crate::parser) fn backed_enum_member(
&self,
state: &mut State,
) -> ParseResult<BackedEnumMember> {
let enum_name = expected_scope!([
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
// if we have attributes, don't check const, we need a method.
if has_attributes || state.current.kind == TokenKind::Function {
Ok(UnitEnumMember::Method(functions::method(
state,
modifiers::enum_method_group(modifiers)?,
start,
)?))
} else {
Ok(UnitEnumMember::Constant(constant(
state,
modifiers::constant_group(modifiers)?,
start,
)?))
}
}
pub fn backed_enum_member(state: &mut State) -> ParseResult<BackedEnumMember> {
let enum_name = expected_scope!([
Scope::Enum(enum_name, _) => enum_name,
], state);
let has_attributes = self.gather_attributes(state)?;
if !has_attributes && state.current.kind == TokenKind::Case {
let start = state.current.span;
state.next();
let name = identifiers::ident(state)?;
if state.current.kind == TokenKind::SemiColon {
return Err(ParseError::MissingCaseValueForBackedEnum(
name.to_string(),
state.named(&enum_name),
state.current.span,
));
}
utils::skip(state, TokenKind::Equals)?;
let value = self.expression(state, Precedence::Lowest)?;
let end = utils::skip_semicolon(state)?;
return Ok(BackedEnumMember::Case(BackedEnumCase {
start,
end,
name,
value,
}));
}
let has_attributes = attributes::gather_attributes(state)?;
if !has_attributes && state.current.kind == TokenKind::Case {
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
state.next();
// if we have attributes, don't check const, we need a method.
if has_attributes || state.current.kind == TokenKind::Function {
Ok(BackedEnumMember::Method(self.method(
state,
modifiers::enum_method_group(modifiers)?,
start,
)?))
} else {
Ok(BackedEnumMember::Constant(self.constant(
state,
modifiers::constant_group(modifiers)?,
start,
)?))
let name = identifiers::ident(state)?;
if state.current.kind == TokenKind::SemiColon {
return Err(ParseError::MissingCaseValueForBackedEnum(
name.to_string(),
state.named(&enum_name),
state.current.span,
));
}
utils::skip(state, TokenKind::Equals)?;
let value = parser::expression(state, Precedence::Lowest)?;
let end = utils::skip_semicolon(state)?;
return Ok(BackedEnumMember::Case(BackedEnumCase {
start,
end,
name,
value,
}));
}
pub(in crate::parser) fn class_like_statement(
&self,
state: &mut State,
) -> ParseResult<Statement> {
let has_attributes = self.gather_attributes(state)?;
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
// if we have attributes, don't check const, we need a method.
if has_attributes || state.current.kind == TokenKind::Function {
Ok(BackedEnumMember::Method(functions::method(
state,
modifiers::enum_method_group(modifiers)?,
start,
)?))
} else {
Ok(BackedEnumMember::Constant(constant(
state,
modifiers::constant_group(modifiers)?,
start,
)?))
}
}
if !has_attributes {
if state.current.kind == TokenKind::Use {
return self.parse_classish_uses(state);
}
pub fn class_like_statement(state: &mut State) -> ParseResult<Statement> {
let has_attributes = attributes::gather_attributes(state)?;
if state.current.kind == TokenKind::Const {
return Ok(Statement::ClassishConstant(self.constant(
state,
modifiers::constant_group(modifiers)?,
start,
)?));
}
let start = state.current.span;
let modifiers = modifiers::collect(state)?;
if !has_attributes {
if state.current.kind == TokenKind::Use {
return parse_classish_uses(state);
}
if state.current.kind == TokenKind::Function {
return Ok(Statement::Method(self.method(
if state.current.kind == TokenKind::Const {
return Ok(Statement::ClassishConstant(constant(
state,
modifiers::method_group(modifiers)?,
modifiers::constant_group(modifiers)?,
start,
)?));
}
}
// e.g: public static
let modifiers = modifiers::property_group(modifiers)?;
// e.g: string
let ty = data_type::optional_data_type(state)?;
// e.g: $name
let var = identifiers::var(state)?;
if state.current.kind == TokenKind::Function {
return Ok(Statement::Method(functions::method(
state,
modifiers::method_group(modifiers)?,
start,
)?));
}
let mut value = None;
// e.g: = "foo";
if state.current.kind == TokenKind::Equals {
state.next();
value = Some(self.expression(state, Precedence::Lowest)?);
}
// e.g: public static
let modifiers = modifiers::property_group(modifiers)?;
// e.g: string
let ty = data_type::optional_data_type(state)?;
// e.g: $name
let var = identifiers::var(state)?;
let class_name: String = expected_scope!([
let mut value = None;
// e.g: = "foo";
if state.current.kind == TokenKind::Equals {
state.next();
value = Some(parser::expression(state, Precedence::Lowest)?);
}
let class_name: String = expected_scope!([
Scope::Trait(name) | Scope::Class(name, _, _) => state.named(&name),
Scope::AnonymousClass(_) => state.named("class@anonymous"),
], state);
if modifiers.has_readonly() {
if modifiers.has_static() {
return Err(ParseError::StaticPropertyUsingReadonlyModifier(
class_name,
var.to_string(),
state.current.span,
));
}
if value.is_some() {
return Err(ParseError::ReadonlyPropertyHasDefaultValue(
class_name,
var.to_string(),
state.current.span,
));
}
if modifiers.has_readonly() {
if modifiers.has_static() {
return Err(ParseError::StaticPropertyUsingReadonlyModifier(
class_name,
var.to_string(),
state.current.span,
));
}
match &ty {
Some(ty) => {
if ty.includes_callable() || ty.is_bottom() {
return Err(ParseError::ForbiddenTypeUsedInProperty(
class_name,
var.to_string(),
ty.clone(),
state.current.span,
));
}
}
None => {
if modifiers.has_readonly() {
return Err(ParseError::MissingTypeForReadonlyProperty(
class_name,
var.to_string(),
state.current.span,
));
}
}
if value.is_some() {
return Err(ParseError::ReadonlyPropertyHasDefaultValue(
class_name,
var.to_string(),
state.current.span,
));
}
utils::skip_semicolon(state)?;
Ok(Statement::Property {
var,
value,
r#type: ty,
modifiers,
attributes: state.get_attributes(),
})
}
fn parse_classish_uses(&self, state: &mut State) -> ParseResult<Statement> {
state.next();
let mut traits = Vec::new();
while state.current.kind != TokenKind::SemiColon
&& state.current.kind != TokenKind::LeftBrace
{
let t = identifiers::full_name(state)?;
traits.push(t);
if state.current.kind == TokenKind::Comma {
if state.peek.kind == TokenKind::SemiColon {
// will fail with unexpected token `,`
// as `use` doesn't allow for trailing commas.
utils::skip_semicolon(state)?;
} else if state.peek.kind == TokenKind::LeftBrace {
// will fail with unexpected token `{`
// as `use` doesn't allow for trailing commas.
utils::skip_left_brace(state)?;
} else {
state.next();
}
} else {
break;
match &ty {
Some(ty) => {
if ty.includes_callable() || ty.is_bottom() {
return Err(ParseError::ForbiddenTypeUsedInProperty(
class_name,
var.to_string(),
ty.clone(),
state.current.span,
));
}
}
None => {
if modifiers.has_readonly() {
return Err(ParseError::MissingTypeForReadonlyProperty(
class_name,
var.to_string(),
state.current.span,
));
}
}
}
let mut adaptations = Vec::new();
if state.current.kind == TokenKind::LeftBrace {
utils::skip_left_brace(state)?;
utils::skip_semicolon(state)?;
while state.current.kind != TokenKind::RightBrace {
let (r#trait, method): (Option<Identifier>, Identifier) = match state.peek.kind {
TokenKind::DoubleColon => {
let r#trait = identifiers::full_name(state)?;
state.next();
let method = identifiers::ident(state)?;
(Some(r#trait), method)
}
_ => (None, identifiers::ident(state)?),
};
Ok(Statement::Property {
var,
value,
r#type: ty,
modifiers,
attributes: state.get_attributes(),
})
}
expect_token!([
fn parse_classish_uses(state: &mut State) -> ParseResult<Statement> {
state.next();
let mut traits = Vec::new();
while state.current.kind != TokenKind::SemiColon && state.current.kind != TokenKind::LeftBrace {
let t = identifiers::full_name(state)?;
traits.push(t);
if state.current.kind == TokenKind::Comma {
if state.peek.kind == TokenKind::SemiColon {
// will fail with unexpected token `,`
// as `use` doesn't allow for trailing commas.
utils::skip_semicolon(state)?;
} else if state.peek.kind == TokenKind::LeftBrace {
// will fail with unexpected token `{`
// as `use` doesn't allow for trailing commas.
utils::skip_left_brace(state)?;
} else {
state.next();
}
} else {
break;
}
}
let mut adaptations = Vec::new();
if state.current.kind == TokenKind::LeftBrace {
utils::skip_left_brace(state)?;
while state.current.kind != TokenKind::RightBrace {
let (r#trait, method): (Option<Identifier>, Identifier) = match state.peek.kind {
TokenKind::DoubleColon => {
let r#trait = identifiers::full_name(state)?;
state.next();
let method = identifiers::ident(state)?;
(Some(r#trait), method)
}
_ => (None, identifiers::ident(state)?),
};
expect_token!([
TokenKind::As => {
match state.current.kind {
TokenKind::Public | TokenKind::Protected | TokenKind::Private => {
@ -383,42 +370,40 @@ impl Parser {
}
], state, ["`as`", "`insteadof`"]);
utils::skip_semicolon(state)?;
}
utils::skip_right_brace(state)?;
} else {
utils::skip_semicolon(state)?;
}
Ok(Statement::TraitUse {
traits,
adaptations,
})
utils::skip_right_brace(state)?;
} else {
utils::skip_semicolon(state)?;
}
fn constant(
&self,
state: &mut State,
modifiers: ConstantModifierGroup,
start: Span,
) -> ParseResult<ClassishConstant> {
state.next();
let name = identifiers::ident(state)?;
utils::skip(state, TokenKind::Equals)?;
let value = self.expression(state, Precedence::Lowest)?;
let end = utils::skip_semicolon(state)?;
Ok(ClassishConstant {
start,
end,
name,
value,
modifiers,
})
}
Ok(Statement::TraitUse {
traits,
adaptations,
})
}
fn constant(
state: &mut State,
modifiers: ConstantModifierGroup,
start: Span,
) -> ParseResult<ClassishConstant> {
state.next();
let name = identifiers::ident(state)?;
utils::skip(state, TokenKind::Equals)?;
let value = parser::expression(state, Precedence::Lowest)?;
let end = utils::skip_semicolon(state)?;
Ok(ClassishConstant {
start,
end,
name,
value,
modifiers,
})
}

View File

@ -1,108 +1,212 @@
use crate::expected_token_err;
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::Block;
use crate::parser::ast::Case;
use crate::parser::ast::DefaultMatchArm;
use crate::parser::ast::ElseIf;
use crate::parser::ast::Expression;
use crate::parser::ast::MatchArm;
use crate::parser::ast::Statement;
use crate::parser::error::ParseError;
use crate::parser::error::ParseResult;
use crate::parser::internal::blocks;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::State;
use crate::parser::Parser;
impl Parser {
pub(in crate::parser) fn switch_statement(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Switch)?;
pub fn match_expression(state: &mut State) -> ParseResult<Expression> {
utils::skip(state, TokenKind::Match)?;
utils::skip_left_parenthesis(state)?;
utils::skip_left_parenthesis(state)?;
let condition = self.expression(state, Precedence::Lowest)?;
let condition = Box::new(parser::expression(state, Precedence::Lowest)?);
utils::skip_right_parenthesis(state)?;
utils::skip_right_parenthesis(state)?;
utils::skip_left_brace(state)?;
let end_token = if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
TokenKind::EndSwitch
let mut default = None;
let mut arms = Vec::new();
while state.current.kind != TokenKind::RightBrace {
state.skip_comments();
if state.current.kind == TokenKind::Default {
if default.is_some() {
return Err(ParseError::MatchExpressionWithMultipleDefaultArms(
state.current.span,
));
}
state.next();
// match conditions can have an extra comma at the end, including `default`.
if state.current.kind == TokenKind::Comma {
state.next();
}
utils::skip_double_arrow(state)?;
let body = parser::expression(state, Precedence::Lowest)?;
default = Some(Box::new(DefaultMatchArm { body }));
} else {
utils::skip_left_brace(state)?;
TokenKind::RightBrace
};
let mut conditions = Vec::new();
while state.current.kind != TokenKind::DoubleArrow {
conditions.push(parser::expression(state, Precedence::Lowest)?);
let mut cases = Vec::new();
while state.current.kind != end_token {
match state.current.kind {
TokenKind::Case => {
if state.current.kind == TokenKind::Comma {
state.next();
let condition = self.expression(state, Precedence::Lowest)?;
utils::skip_any_of(state, &[TokenKind::Colon, TokenKind::SemiColon])?;
let mut body = Block::new();
while state.current.kind != TokenKind::Case
&& state.current.kind != TokenKind::Default
&& state.current.kind != TokenKind::RightBrace
{
body.push(self.statement(state)?);
state.skip_comments();
}
cases.push(Case {
condition: Some(condition),
body,
});
}
TokenKind::Default => {
state.next();
utils::skip_any_of(state, &[TokenKind::Colon, TokenKind::SemiColon])?;
let mut body = Block::new();
while state.current.kind != TokenKind::Case
&& state.current.kind != TokenKind::Default
&& state.current.kind != TokenKind::RightBrace
{
body.push(self.statement(state)?);
}
cases.push(Case {
condition: None,
body,
});
}
_ => {
return expected_token_err!(["`case`", "`default`"], state);
} else {
break;
}
}
if !conditions.is_empty() {
utils::skip_double_arrow(state)?;
} else {
break;
}
let body = parser::expression(state, Precedence::Lowest)?;
arms.push(MatchArm { conditions, body });
}
if end_token == TokenKind::EndSwitch {
utils::skip(state, TokenKind::EndSwitch)?;
utils::skip_semicolon(state)?;
if state.current.kind == TokenKind::Comma {
state.next();
} else {
utils::skip_right_brace(state)?;
break;
}
Ok(Statement::Switch { condition, cases })
}
pub(in crate::parser) fn if_statement(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::If)?;
utils::skip_right_brace(state)?;
utils::skip_left_parenthesis(state)?;
Ok(Expression::Match {
condition,
default,
arms,
})
}
let condition = self.expression(state, Precedence::Lowest)?;
pub fn switch_statement(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Switch)?;
utils::skip_right_parenthesis(state)?;
utils::skip_left_parenthesis(state)?;
// FIXME: Tidy up duplication and make the intent a bit clearer.
let condition = parser::expression(state, Precedence::Lowest)?;
utils::skip_right_parenthesis(state)?;
let end_token = if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
TokenKind::EndSwitch
} else {
utils::skip_left_brace(state)?;
TokenKind::RightBrace
};
let mut cases = Vec::new();
while state.current.kind != end_token {
match state.current.kind {
TokenKind::Colon => {
utils::colon(state)?;
TokenKind::Case => {
state.next();
let mut then = vec![];
let condition = parser::expression(state, Precedence::Lowest)?;
utils::skip_any_of(state, &[TokenKind::Colon, TokenKind::SemiColon])?;
let mut body = Block::new();
while state.current.kind != TokenKind::Case
&& state.current.kind != TokenKind::Default
&& state.current.kind != TokenKind::RightBrace
{
body.push(parser::statement(state)?);
state.skip_comments();
}
cases.push(Case {
condition: Some(condition),
body,
});
}
TokenKind::Default => {
state.next();
utils::skip_any_of(state, &[TokenKind::Colon, TokenKind::SemiColon])?;
let mut body = Block::new();
while state.current.kind != TokenKind::Case
&& state.current.kind != TokenKind::Default
&& state.current.kind != TokenKind::RightBrace
{
body.push(parser::statement(state)?);
}
cases.push(Case {
condition: None,
body,
});
}
_ => {
return expected_token_err!(["`case`", "`default`"], state);
}
}
}
if end_token == TokenKind::EndSwitch {
utils::skip(state, TokenKind::EndSwitch)?;
utils::skip_semicolon(state)?;
} else {
utils::skip_right_brace(state)?;
}
Ok(Statement::Switch { condition, cases })
}
pub fn if_statement(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::If)?;
utils::skip_left_parenthesis(state)?;
let condition = parser::expression(state, Precedence::Lowest)?;
utils::skip_right_parenthesis(state)?;
// FIXME: Tidy up duplication and make the intent a bit clearer.
match state.current.kind {
TokenKind::Colon => {
utils::skip_colon(state)?;
let mut then = vec![];
while !matches!(
state.current.kind,
TokenKind::ElseIf | TokenKind::Else | TokenKind::EndIf
) {
if let TokenKind::OpenTag(_) = state.current.kind {
state.next();
continue;
}
then.push(parser::statement(state)?);
}
let mut else_ifs = vec![];
loop {
if state.current.kind != TokenKind::ElseIf {
break;
}
state.next();
utils::skip_left_parenthesis(state)?;
let condition = parser::expression(state, Precedence::Lowest)?;
utils::skip_right_parenthesis(state)?;
utils::skip_colon(state)?;
let mut body = vec![];
while !matches!(
state.current.kind,
TokenKind::ElseIf | TokenKind::Else | TokenKind::EndIf
@ -112,122 +216,94 @@ impl Parser {
continue;
}
then.push(self.statement(state)?);
body.push(parser::statement(state)?);
}
let mut else_ifs = vec![];
loop {
if state.current.kind != TokenKind::ElseIf {
break;
}
else_ifs.push(ElseIf { condition, body });
}
let mut r#else = None;
if state.current.kind == TokenKind::Else {
state.next();
utils::skip_colon(state)?;
let body = blocks::body(state, &TokenKind::EndIf)?;
r#else = Some(body);
}
utils::skip(state, TokenKind::EndIf)?;
utils::skip_semicolon(state)?;
Ok(Statement::If {
condition,
then,
else_ifs,
r#else,
})
}
_ => {
let then = if state.current.kind == TokenKind::LeftBrace {
utils::skip_left_brace(state)?;
let then = blocks::body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
then
} else {
vec![parser::statement(state)?]
};
let mut else_ifs: Vec<ElseIf> = Vec::new();
loop {
if state.current.kind == TokenKind::ElseIf {
state.next();
utils::skip_left_parenthesis(state)?;
let condition = self.expression(state, Precedence::Lowest)?;
let condition = parser::expression(state, Precedence::Lowest)?;
utils::skip_right_parenthesis(state)?;
utils::colon(state)?;
utils::skip_left_brace(state)?;
let mut body = vec![];
while !matches!(
state.current.kind,
TokenKind::ElseIf | TokenKind::Else | TokenKind::EndIf
) {
if let TokenKind::OpenTag(_) = state.current.kind {
state.next();
continue;
}
let body = blocks::body(state, &TokenKind::RightBrace)?;
body.push(self.statement(state)?);
}
utils::skip_right_brace(state)?;
else_ifs.push(ElseIf { condition, body });
} else {
break;
}
}
let mut r#else = None;
if state.current.kind == TokenKind::Else {
state.next();
utils::colon(state)?;
let body = self.body(state, &TokenKind::EndIf)?;
r#else = Some(body);
}
utils::skip(state, TokenKind::EndIf)?;
utils::skip_semicolon(state)?;
Ok(Statement::If {
if state.current.kind != TokenKind::Else {
return Ok(Statement::If {
condition,
then,
else_ifs,
r#else,
})
r#else: None,
});
}
_ => {
let then = if state.current.kind == TokenKind::LeftBrace {
utils::skip_left_brace(state)?;
let then = self.body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
then
} else {
vec![self.statement(state)?]
};
let mut else_ifs: Vec<ElseIf> = Vec::new();
loop {
if state.current.kind == TokenKind::ElseIf {
state.next();
utils::skip(state, TokenKind::Else)?;
utils::skip_left_parenthesis(state)?;
let r#else;
if state.current.kind == TokenKind::LeftBrace {
utils::skip_left_brace(state)?;
let condition = self.expression(state, Precedence::Lowest)?;
r#else = blocks::body(state, &TokenKind::RightBrace)?;
utils::skip_right_parenthesis(state)?;
utils::skip_left_brace(state)?;
let body = self.body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
else_ifs.push(ElseIf { condition, body });
} else {
break;
}
}
if state.current.kind != TokenKind::Else {
return Ok(Statement::If {
condition,
then,
else_ifs,
r#else: None,
});
}
utils::skip(state, TokenKind::Else)?;
let r#else;
if state.current.kind == TokenKind::LeftBrace {
utils::skip_left_brace(state)?;
r#else = self.body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
} else {
r#else = vec![self.statement(state)?];
}
Ok(Statement::If {
condition,
then,
else_ifs,
r#else: Some(r#else),
})
utils::skip_right_brace(state)?;
} else {
r#else = vec![parser::statement(state)?];
}
Ok(Statement::If {
condition,
then,
else_ifs,
r#else: Some(r#else),
})
}
}
}

View File

@ -1,6 +1,7 @@
use crate::expected_scope;
use crate::lexer::token::Span;
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::functions::ArrowFunction;
use crate::parser::ast::functions::Closure;
use crate::parser::ast::functions::ClosureUse;
@ -12,239 +13,235 @@ use crate::parser::ast::Expression;
use crate::parser::ast::Statement;
use crate::parser::error::ParseError;
use crate::parser::error::ParseResult;
use crate::parser::internal::blocks;
use crate::parser::internal::data_type;
use crate::parser::internal::identifiers;
use crate::parser::internal::parameters;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::Scope;
use crate::parser::state::State;
use crate::parser::Parser;
use crate::scoped;
impl Parser {
pub(in crate::parser) fn anonymous_function(
&self,
state: &mut State,
) -> ParseResult<Expression> {
let start = state.current.span;
pub fn anonymous_function(state: &mut State) -> ParseResult<Expression> {
let start = state.current.span;
let is_static = if state.current.kind == TokenKind::Static {
state.next();
let is_static = if state.current.kind == TokenKind::Static {
state.next();
true
} else {
false
};
true
} else {
false
};
utils::skip(state, TokenKind::Function)?;
utils::skip(state, TokenKind::Function)?;
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
let attributes = state.get_attributes();
let parameters = self.function_parameter_list(state)?;
let attributes = state.get_attributes();
let parameters = parameters::function_parameter_list(state)?;
let mut uses = vec![];
if state.current.kind == TokenKind::Use {
state.next();
let mut uses = vec![];
if state.current.kind == TokenKind::Use {
state.next();
utils::skip_left_parenthesis(state)?;
utils::skip_left_parenthesis(state)?;
while state.current.kind != TokenKind::RightParen {
let mut by_ref = false;
if state.current.kind == TokenKind::Ampersand {
state.next();
while state.current.kind != TokenKind::RightParen {
let mut by_ref = false;
if state.current.kind == TokenKind::Ampersand {
state.next();
by_ref = true;
}
// TODO(azjezz): this shouldn't call expr, we should have a function
// just for variables, so we don't have to go through the whole `match` in `expression(...)`
let var = match self.expression(state, Precedence::Lowest)? {
s @ Expression::Variable { .. } => ClosureUse { var: s, by_ref },
_ => {
return Err(ParseError::UnexpectedToken(
"expected variable".into(),
state.current.span,
))
}
};
uses.push(var);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
by_ref = true;
}
utils::skip_right_parenthesis(state)?;
}
// TODO(azjezz): this shouldn't call expr, we should have a function
// just for variables, so we don't have to go through the whole `match` in `expression(...)`
let var = match parser::expression(state, Precedence::Lowest)? {
s @ Expression::Variable { .. } => ClosureUse { var: s, by_ref },
_ => {
return Err(ParseError::UnexpectedToken(
"expected variable".into(),
state.current.span,
))
}
};
let mut return_ty = None;
if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
uses.push(var);
return_ty = Some(data_type::data_type(state)?);
}
let (body, end) = scoped!(state, Scope::AnonymousFunction(is_static), {
utils::skip_left_brace(state)?;
let body = self.body(state, &TokenKind::RightBrace)?;
let end = utils::skip_right_brace(state)?;
(body, end)
});
Ok(Expression::Closure(Closure {
start,
end,
attributes,
parameters,
uses,
return_ty,
body,
r#static: is_static,
by_ref,
}))
}
pub(in crate::parser) fn arrow_function(&self, state: &mut State) -> ParseResult<Expression> {
let start = state.current.span;
let is_static = if state.current.kind == TokenKind::Static {
state.next();
true
} else {
false
};
utils::skip(state, TokenKind::Fn)?;
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
let attributes = state.get_attributes();
let parameters = self.function_parameter_list(state)?;
let mut return_type = None;
if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
return_type = Some(data_type::data_type(state)?);
}
utils::skip(state, TokenKind::DoubleArrow)?;
let body = scoped!(state, Scope::ArrowFunction(is_static), {
Box::new(self.expression(state, Precedence::Lowest)?)
});
let end = state.current.span;
Ok(Expression::ArrowFunction(ArrowFunction {
start,
end,
attributes,
parameters,
return_type,
body,
by_ref,
r#static: is_static,
}))
}
pub(in crate::parser) fn function(&self, state: &mut State) -> ParseResult<Statement> {
let start = state.current.span;
utils::skip(state, TokenKind::Function)?;
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
let name = if state.current.kind == TokenKind::Null {
let start = state.current.span;
let end = (start.0, start.1 + 4);
state.next();
Identifier {
start,
name: "null".into(),
end,
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
} else {
identifiers::ident(state)?
};
// get attributes before processing parameters, otherwise
// parameters will steal attributes of this function.
let attributes = state.get_attributes();
let parameters = self.function_parameter_list(state)?;
let mut return_type = None;
if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
return_type = Some(data_type::data_type(state)?);
}
let (body, end) = scoped!(state, Scope::Function(name.clone()), {
utils::skip_left_brace(state)?;
let body = self.body(state, &TokenKind::RightBrace)?;
let end = utils::skip_right_brace(state)?;
(body, end)
});
Ok(Statement::Function(Function {
start,
end,
name,
attributes,
parameters,
return_type,
body,
by_ref,
}))
utils::skip_right_parenthesis(state)?;
}
pub(in crate::parser) fn method(
&self,
state: &mut State,
modifiers: MethodModifierGroup,
start: Span,
) -> ParseResult<Method> {
utils::skip(state, TokenKind::Function)?;
let mut return_ty = None;
if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
return_ty = Some(data_type::data_type(state)?);
}
let name = identifiers::ident_maybe_reserved(state)?;
let (body, end) = scoped!(state, Scope::AnonymousFunction(is_static), {
utils::skip_left_brace(state)?;
let has_body = expected_scope!([
let body = blocks::body(state, &TokenKind::RightBrace)?;
let end = utils::skip_right_brace(state)?;
(body, end)
});
Ok(Expression::Closure(Closure {
start,
end,
attributes,
parameters,
uses,
return_ty,
body,
r#static: is_static,
by_ref,
}))
}
pub fn arrow_function(state: &mut State) -> ParseResult<Expression> {
let start = state.current.span;
let is_static = if state.current.kind == TokenKind::Static {
state.next();
true
} else {
false
};
utils::skip(state, TokenKind::Fn)?;
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
let attributes = state.get_attributes();
let parameters = parameters::function_parameter_list(state)?;
let mut return_type = None;
if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
return_type = Some(data_type::data_type(state)?);
}
utils::skip(state, TokenKind::DoubleArrow)?;
let body = scoped!(state, Scope::ArrowFunction(is_static), {
Box::new(parser::expression(state, Precedence::Lowest)?)
});
let end = state.current.span;
Ok(Expression::ArrowFunction(ArrowFunction {
start,
end,
attributes,
parameters,
return_type,
body,
by_ref,
r#static: is_static,
}))
}
pub fn function(state: &mut State) -> ParseResult<Statement> {
let start = state.current.span;
utils::skip(state, TokenKind::Function)?;
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
let name = if state.current.kind == TokenKind::Null {
let start = state.current.span;
let end = (start.0, start.1 + 4);
state.next();
Identifier {
start,
name: "null".into(),
end,
}
} else {
identifiers::ident(state)?
};
// get attributes before processing parameters, otherwise
// parameters will steal attributes of this function.
let attributes = state.get_attributes();
let parameters = parameters::function_parameter_list(state)?;
let mut return_type = None;
if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
return_type = Some(data_type::data_type(state)?);
}
let (body, end) = scoped!(state, Scope::Function(name.clone()), {
utils::skip_left_brace(state)?;
let body = blocks::body(state, &TokenKind::RightBrace)?;
let end = utils::skip_right_brace(state)?;
(body, end)
});
Ok(Statement::Function(Function {
start,
end,
name,
attributes,
parameters,
return_type,
body,
by_ref,
}))
}
pub fn method(
state: &mut State,
modifiers: MethodModifierGroup,
start: Span,
) -> ParseResult<Method> {
utils::skip(state, TokenKind::Function)?;
let by_ref = if state.current.kind == TokenKind::Ampersand {
state.next();
true
} else {
false
};
let name = identifiers::ident_maybe_reserved(state)?;
let has_body = expected_scope!([
Scope::Class(_, class_modifiers, _) => {
if !class_modifiers.has_abstract() && modifiers.has_abstract() {
return Err(ParseError::AbstractModifierOnNonAbstractClassMethod(
@ -269,47 +266,46 @@ impl Parser {
Scope::AnonymousClass(_) => true,
], state);
// get attributes before processing parameters, otherwise
// parameters will steal attributes of this method.
let attributes = state.get_attributes();
// get attributes before processing parameters, otherwise
// parameters will steal attributes of this method.
let attributes = state.get_attributes();
let (parameters, body, return_type, end) =
scoped!(state, Scope::Method(name.clone(), modifiers.clone()), {
let parameters = self.method_parameter_list(state)?;
let (parameters, body, return_type, end) =
scoped!(state, Scope::Method(name.clone(), modifiers.clone()), {
let parameters = parameters::method_parameter_list(state)?;
let mut return_type = None;
let mut return_type = None;
if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
return_type = Some(data_type::data_type(state)?);
}
return_type = Some(data_type::data_type(state)?);
}
if !has_body {
let end = utils::skip_semicolon(state)?;
if !has_body {
let end = utils::skip_semicolon(state)?;
(parameters, None, return_type, end)
} else {
utils::skip_left_brace(state)?;
(parameters, None, return_type, end)
} else {
utils::skip_left_brace(state)?;
let body = self.body(state, &TokenKind::RightBrace)?;
let body = blocks::body(state, &TokenKind::RightBrace)?;
let end = utils::skip_right_brace(state)?;
let end = utils::skip_right_brace(state)?;
(parameters, Some(body), return_type, end)
}
});
(parameters, Some(body), return_type, end)
}
});
Ok(Method {
start,
end,
attributes,
name,
parameters,
body,
return_type,
by_ref,
modifiers,
})
}
Ok(Method {
start,
end,
attributes,
name,
parameters,
body,
return_type,
by_ref,
modifiers,
})
}

View File

@ -0,0 +1,24 @@
use crate::lexer::token::TokenKind;
use crate::parser::ast::Statement;
use crate::parser::error::ParseResult;
use crate::parser::internal::identifiers;
use crate::parser::internal::utils;
use crate::parser::state::State;
pub fn label_statement(state: &mut State) -> ParseResult<Statement> {
let label = identifiers::ident(state)?;
utils::skip_colon(state)?;
Ok(Statement::Label { label })
}
pub fn goto_statement(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Goto)?;
let label = identifiers::ident(state)?;
utils::skip_semicolon(state)?;
Ok(Statement::Goto { label })
}

View File

@ -1,229 +1,225 @@
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::Statement;
use crate::parser::error::ParseResult;
use crate::parser::internal::blocks;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::State;
use crate::parser::Parser;
impl Parser {
pub(in crate::parser) fn foreach_loop(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Foreach)?;
pub fn foreach_loop(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Foreach)?;
utils::skip_left_parenthesis(state)?;
utils::skip_left_parenthesis(state)?;
let expr = self.expression(state, Precedence::Lowest)?;
let expr = parser::expression(state, Precedence::Lowest)?;
utils::skip(state, TokenKind::As)?;
utils::skip(state, TokenKind::As)?;
let mut by_ref = state.current.kind == TokenKind::Ampersand;
let mut by_ref = state.current.kind == TokenKind::Ampersand;
if by_ref {
state.next();
}
let mut key_var = None;
let mut value_var = parser::expression(state, Precedence::Lowest)?;
if state.current.kind == TokenKind::DoubleArrow {
state.next();
key_var = Some(value_var.clone());
by_ref = state.current.kind == TokenKind::Ampersand;
if by_ref {
state.next();
}
let mut key_var = None;
let mut value_var = self.expression(state, Precedence::Lowest)?;
if state.current.kind == TokenKind::DoubleArrow {
state.next();
key_var = Some(value_var.clone());
by_ref = state.current.kind == TokenKind::Ampersand;
if by_ref {
state.next();
}
value_var = self.expression(state, Precedence::Lowest)?;
}
utils::skip_right_parenthesis(state)?;
let end_token = if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
TokenKind::EndForeach
} else {
utils::skip_left_brace(state)?;
TokenKind::RightBrace
};
let body = self.body(state, &end_token)?;
if end_token == TokenKind::EndForeach {
utils::skip(state, TokenKind::EndForeach)?;
utils::skip_semicolon(state)?;
} else {
utils::skip_right_brace(state)?;
}
Ok(Statement::Foreach {
expr,
by_ref,
key_var,
value_var,
body,
})
value_var = parser::expression(state, Precedence::Lowest)?;
}
pub(in crate::parser) fn for_loop(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::For)?;
utils::skip_left_parenthesis(state)?;
let mut init = Vec::new();
loop {
if state.current.kind == TokenKind::SemiColon {
break;
}
init.push(self.expression(state, Precedence::Lowest)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
utils::skip_semicolon(state)?;
let mut condition = Vec::new();
loop {
if state.current.kind == TokenKind::SemiColon {
break;
}
condition.push(self.expression(state, Precedence::Lowest)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
utils::skip_semicolon(state)?;
let mut r#loop = Vec::new();
loop {
if state.current.kind == TokenKind::RightParen {
break;
}
r#loop.push(self.expression(state, Precedence::Lowest)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
utils::skip_right_parenthesis(state)?;
let end_token = if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
TokenKind::EndFor
} else {
utils::skip_left_brace(state)?;
TokenKind::RightBrace
};
let then = self.body(state, &end_token)?;
if end_token == TokenKind::EndFor {
utils::skip(state, TokenKind::EndFor)?;
utils::skip_semicolon(state)?;
} else {
utils::skip_right_brace(state)?;
};
Ok(Statement::For {
init,
condition,
r#loop,
then,
})
}
pub(in crate::parser) fn do_loop(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Do)?;
utils::skip_right_parenthesis(state)?;
let end_token = if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
TokenKind::EndForeach
} else {
utils::skip_left_brace(state)?;
let body = self.body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
TokenKind::RightBrace
};
utils::skip(state, TokenKind::While)?;
let body = blocks::body(state, &end_token)?;
utils::skip_left_parenthesis(state)?;
let condition = self.expression(state, Precedence::Lowest)?;
utils::skip_right_parenthesis(state)?;
if end_token == TokenKind::EndForeach {
utils::skip(state, TokenKind::EndForeach)?;
utils::skip_semicolon(state)?;
Ok(Statement::DoWhile { condition, body })
} else {
utils::skip_right_brace(state)?;
}
pub(in crate::parser) fn while_loop(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::While)?;
Ok(Statement::Foreach {
expr,
by_ref,
key_var,
value_var,
body,
})
}
utils::skip_left_parenthesis(state)?;
pub fn for_loop(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::For)?;
let condition = self.expression(state, Precedence::Lowest)?;
utils::skip_left_parenthesis(state)?;
utils::skip_right_parenthesis(state)?;
let mut init = Vec::new();
loop {
if state.current.kind == TokenKind::SemiColon {
break;
}
let body = if state.current.kind == TokenKind::SemiColon {
utils::skip_semicolon(state)?;
vec![]
init.push(parser::expression(state, Precedence::Lowest)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
let end_token = if state.current.kind == TokenKind::Colon {
utils::colon(state)?;
TokenKind::EndWhile
} else {
utils::skip_left_brace(state)?;
TokenKind::RightBrace
};
break;
}
}
let body = self.body(state, &end_token)?;
utils::skip_semicolon(state)?;
if end_token == TokenKind::RightBrace {
utils::skip_right_brace(state)?;
} else {
utils::skip(state, TokenKind::EndWhile)?;
utils::skip_semicolon(state)?;
}
let mut condition = Vec::new();
loop {
if state.current.kind == TokenKind::SemiColon {
break;
}
body
condition.push(parser::expression(state, Precedence::Lowest)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
utils::skip_semicolon(state)?;
let mut r#loop = Vec::new();
loop {
if state.current.kind == TokenKind::RightParen {
break;
}
r#loop.push(parser::expression(state, Precedence::Lowest)?);
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
utils::skip_right_parenthesis(state)?;
let end_token = if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
TokenKind::EndFor
} else {
utils::skip_left_brace(state)?;
TokenKind::RightBrace
};
let then = blocks::body(state, &end_token)?;
if end_token == TokenKind::EndFor {
utils::skip(state, TokenKind::EndFor)?;
utils::skip_semicolon(state)?;
} else {
utils::skip_right_brace(state)?;
};
Ok(Statement::For {
init,
condition,
r#loop,
then,
})
}
pub fn do_loop(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Do)?;
utils::skip_left_brace(state)?;
let body = blocks::body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
utils::skip(state, TokenKind::While)?;
utils::skip_left_parenthesis(state)?;
let condition = parser::expression(state, Precedence::Lowest)?;
utils::skip_right_parenthesis(state)?;
utils::skip_semicolon(state)?;
Ok(Statement::DoWhile { condition, body })
}
pub fn while_loop(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::While)?;
utils::skip_left_parenthesis(state)?;
let condition = parser::expression(state, Precedence::Lowest)?;
utils::skip_right_parenthesis(state)?;
let body = if state.current.kind == TokenKind::SemiColon {
utils::skip_semicolon(state)?;
vec![]
} else {
let end_token = if state.current.kind == TokenKind::Colon {
utils::skip_colon(state)?;
TokenKind::EndWhile
} else {
utils::skip_left_brace(state)?;
TokenKind::RightBrace
};
Ok(Statement::While { condition, body })
}
let body = blocks::body(state, &end_token)?;
pub(in crate::parser) fn continue_statement(
&self,
state: &mut State,
) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Continue)?;
let mut num = None;
if state.current.kind != TokenKind::SemiColon {
num = Some(self.expression(state, Precedence::Lowest)?);
if end_token == TokenKind::RightBrace {
utils::skip_right_brace(state)?;
} else {
utils::skip(state, TokenKind::EndWhile)?;
utils::skip_semicolon(state)?;
}
utils::skip_semicolon(state)?;
body
};
Ok(Statement::Continue { num })
}
pub(in crate::parser) fn break_statement(&self, state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Break)?;
let mut num = None;
if state.current.kind != TokenKind::SemiColon {
num = Some(self.expression(state, Precedence::Lowest)?);
}
utils::skip_semicolon(state)?;
Ok(Statement::Break { num })
}
Ok(Statement::While { condition, body })
}
pub fn continue_statement(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Continue)?;
let mut num = None;
if state.current.kind != TokenKind::SemiColon {
num = Some(parser::expression(state, Precedence::Lowest)?);
}
utils::skip_semicolon(state)?;
Ok(Statement::Continue { num })
}
pub fn break_statement(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Break)?;
let mut num = None;
if state.current.kind != TokenKind::SemiColon {
num = Some(parser::expression(state, Precedence::Lowest)?);
}
utils::skip_semicolon(state)?;
Ok(Statement::Break { num })
}

View File

@ -6,6 +6,7 @@ pub(in crate::parser) mod classish_statements;
pub(in crate::parser) mod control_flow;
pub(in crate::parser) mod data_type;
pub(in crate::parser) mod functions;
pub(in crate::parser) mod goto;
pub(in crate::parser) mod identifiers;
pub(in crate::parser) mod loops;
pub(in crate::parser) mod modifiers;
@ -13,5 +14,6 @@ pub(in crate::parser) mod namespaces;
pub(in crate::parser) mod parameters;
pub(in crate::parser) mod precedences;
pub(in crate::parser) mod try_block;
pub(in crate::parser) mod uses;
pub(in crate::parser) mod utils;
pub(in crate::parser) mod variables;

View File

@ -1,4 +1,5 @@
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::identifiers::Identifier;
use crate::parser::ast::Block;
use crate::parser::ast::Statement;
@ -9,72 +10,65 @@ use crate::parser::internal::utils;
use crate::parser::state::NamespaceType;
use crate::parser::state::Scope;
use crate::parser::state::State;
use crate::parser::Parser;
use crate::scoped;
impl Parser {
pub(in crate::parser) fn namespace(&self, state: &mut State) -> ParseResult<Statement> {
state.next();
pub fn namespace(state: &mut State) -> ParseResult<Statement> {
state.next();
let name = identifiers::optional_name(state);
let name = identifiers::optional_name(state);
if let Some(name) = &name {
if state.current.kind != TokenKind::LeftBrace {
if let Some(NamespaceType::Braced) = state.namespace_type() {
return Err(ParseError::MixingBracedAndUnBracedNamespaceDeclarations(
state.current.span,
));
}
return self.unbraced_namespace(state, name.clone());
if let Some(name) = &name {
if state.current.kind != TokenKind::LeftBrace {
if let Some(NamespaceType::Braced) = state.namespace_type() {
return Err(ParseError::MixingBracedAndUnBracedNamespaceDeclarations(
state.current.span,
));
}
}
match state.namespace_type() {
Some(NamespaceType::Unbraced) => Err(
ParseError::MixingBracedAndUnBracedNamespaceDeclarations(state.current.span),
),
Some(NamespaceType::Braced) if state.namespace().is_some() => {
Err(ParseError::NestedNamespaceDeclarations(state.current.span))
}
_ => self.braced_namespace(state, name),
return unbraced_namespace(state, name.clone());
}
}
fn unbraced_namespace(&self, state: &mut State, name: Identifier) -> ParseResult<Statement> {
let body = scoped!(state, Scope::Namespace(name.clone()), {
let mut body = Block::new();
// since this is an unbraced namespace, as soon as we encouter another
// `namespace` token as a top level statement, this namespace scope ends.
// otherwise we will end up with nested namespace statements.
while state.current.kind != TokenKind::Namespace && !state.is_eof() {
body.push(self.top_level_statement(state)?);
}
body
});
Ok(Statement::Namespace { name, body })
}
fn braced_namespace(
&self,
state: &mut State,
name: Option<Identifier>,
) -> ParseResult<Statement> {
utils::skip_left_brace(state)?;
let body = scoped!(state, Scope::BracedNamespace(name.clone()), {
let mut body = Block::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
body.push(self.top_level_statement(state)?);
}
body
});
utils::skip_right_brace(state)?;
Ok(Statement::BracedNamespace { name, body })
match state.namespace_type() {
Some(NamespaceType::Unbraced) => Err(
ParseError::MixingBracedAndUnBracedNamespaceDeclarations(state.current.span),
),
Some(NamespaceType::Braced) if state.namespace().is_some() => {
Err(ParseError::NestedNamespaceDeclarations(state.current.span))
}
_ => braced_namespace(state, name),
}
}
fn unbraced_namespace(state: &mut State, name: Identifier) -> ParseResult<Statement> {
let body = scoped!(state, Scope::Namespace(name.clone()), {
let mut body = Block::new();
// since this is an unbraced namespace, as soon as we encouter another
// `namespace` token as a top level statement, this namespace scope ends.
// otherwise we will end up with nested namespace statements.
while state.current.kind != TokenKind::Namespace && !state.is_eof() {
body.push(parser::top_level_statement(state)?);
}
body
});
Ok(Statement::Namespace { name, body })
}
fn braced_namespace(state: &mut State, name: Option<Identifier>) -> ParseResult<Statement> {
utils::skip_left_brace(state)?;
let body = scoped!(state, Scope::BracedNamespace(name.clone()), {
let mut body = Block::new();
while state.current.kind != TokenKind::RightBrace && !state.is_eof() {
body.push(parser::top_level_statement(state)?);
}
body
});
utils::skip_right_brace(state)?;
Ok(Statement::BracedNamespace { name, body })
}

View File

@ -1,5 +1,6 @@
use super::identifiers;
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::functions::FunctionParameter;
use crate::parser::ast::functions::FunctionParameterList;
use crate::parser::ast::functions::MethodParameter;
@ -8,298 +9,290 @@ use crate::parser::ast::Arg;
use crate::parser::ast::Expression;
use crate::parser::error::ParseError;
use crate::parser::error::ParseResult;
use crate::parser::internal::attributes;
use crate::parser::internal::data_type;
use crate::parser::internal::modifiers;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::Scope;
use crate::parser::state::State;
use crate::parser::Parser;
impl Parser {
pub(in crate::parser) fn function_parameter_list(
&self,
state: &mut State,
) -> Result<FunctionParameterList, ParseError> {
let mut members = Vec::new();
pub fn function_parameter_list(state: &mut State) -> Result<FunctionParameterList, ParseError> {
let mut members = Vec::new();
let list_start = state.current.span;
utils::skip_left_parenthesis(state)?;
let list_start = state.current.span;
utils::skip_left_parenthesis(state)?;
state.skip_comments();
while !state.is_eof() && state.current.kind != TokenKind::RightParen {
let start = state.current.span;
attributes::gather_attributes(state)?;
let ty = data_type::optional_data_type(state)?;
let mut variadic = false;
let mut by_ref = false;
if state.current.kind == TokenKind::Ampersand {
state.next();
by_ref = true;
}
if state.current.kind == TokenKind::Ellipsis {
state.next();
variadic = true;
}
// 2. Then expect a variable.
let var = identifiers::var(state)?;
let mut default = None;
if state.current.kind == TokenKind::Equals {
state.next();
default = Some(parser::expression(state, Precedence::Lowest)?);
}
let end = state.current.span;
members.push(FunctionParameter {
start,
end,
name: var,
attributes: state.get_attributes(),
r#type: ty,
variadic,
default,
by_ref,
});
state.skip_comments();
while !state.is_eof() && state.current.kind != TokenKind::RightParen {
let start = state.current.span;
self.gather_attributes(state)?;
let ty = data_type::optional_data_type(state)?;
let mut variadic = false;
let mut by_ref = false;
if state.current.kind == TokenKind::Ampersand {
state.next();
by_ref = true;
}
if state.current.kind == TokenKind::Ellipsis {
state.next();
variadic = true;
}
// 2. Then expect a variable.
let var = identifiers::var(state)?;
let mut default = None;
if state.current.kind == TokenKind::Equals {
state.next();
default = Some(self.expression(state, Precedence::Lowest)?);
}
let end = state.current.span;
members.push(FunctionParameter {
start,
end,
name: var,
attributes: state.get_attributes(),
r#type: ty,
variadic,
default,
by_ref,
});
state.skip_comments();
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
utils::skip_right_parenthesis(state)?;
let list_end = state.current.span;
Ok(FunctionParameterList {
start: list_start,
end: list_end,
members,
})
}
/// TODO(azjezz): split this into `method_parameter_list` and `abstract_method_parameter_list`?
/// abstract method parameter list won't have a promoted property, so some of the logic
/// here can be avoided for performance.
pub(in crate::parser) fn method_parameter_list(
&self,
state: &mut State,
) -> Result<MethodParameterList, ParseError> {
let mut class_name = String::new();
let construct: i8 = match state.scope()? {
Scope::Method(name, modifiers) => {
if name.to_string() != "__construct" {
0
} else {
match state.parent()? {
// can only have abstract ctor
Scope::Interface(_) => 1,
// can only have concret ctor
Scope::AnonymousClass(_) => {
class_name = state.named("class@anonymous");
utils::skip_right_parenthesis(state)?;
let list_end = state.current.span;
Ok(FunctionParameterList {
start: list_start,
end: list_end,
members,
})
}
/// TODO(azjezz): split this into `method_parameter_list` and `abstract_method_parameter_list`?
/// abstract method parameter list won't have a promoted property, so some of the logic
/// here can be avoided for performance.
pub fn method_parameter_list(state: &mut State) -> Result<MethodParameterList, ParseError> {
let mut class_name = String::new();
let construct: i8 = match state.scope()? {
Scope::Method(name, modifiers) => {
if name.to_string() != "__construct" {
0
} else {
match state.parent()? {
// can only have abstract ctor
Scope::Interface(_) => 1,
// can only have concret ctor
Scope::AnonymousClass(_) => {
class_name = state.named("class@anonymous");
2
}
// can have either abstract or concret ctor,
// depens on method modifiers.
Scope::Class(name, _, _) | Scope::Trait(name) => {
if modifiers.has_abstract() {
1
} else {
class_name = state.named(name);
2
}
// can have either abstract or concret ctor,
// depens on method modifiers.
Scope::Class(name, _, _) | Scope::Trait(name) => {
if modifiers.has_abstract() {
1
} else {
class_name = state.named(name);
2
}
}
_ => unreachable!(),
}
_ => unreachable!(),
}
}
scope => unreachable!("shouldn't reach scope `{:?}`", scope),
};
}
scope => unreachable!("shouldn't reach scope `{:?}`", scope),
};
let mut members = Vec::new();
let mut members = Vec::new();
let list_start = state.current.span;
utils::skip_left_parenthesis(state)?;
let list_start = state.current.span;
utils::skip_left_parenthesis(state)?;
state.skip_comments();
state.skip_comments();
while !state.is_eof() && state.current.kind != TokenKind::RightParen {
let start = state.current.span;
while !state.is_eof() && state.current.kind != TokenKind::RightParen {
let start = state.current.span;
self.gather_attributes(state)?;
attributes::gather_attributes(state)?;
let modifiers = modifiers::promoted_property_group(modifiers::collect(state)?)?;
let modifiers = modifiers::promoted_property_group(modifiers::collect(state)?)?;
let ty = data_type::optional_data_type(state)?;
let ty = data_type::optional_data_type(state)?;
let mut variadic = false;
let mut by_ref = false;
let mut variadic = false;
let mut by_ref = false;
if matches!(state.current.kind, TokenKind::Ampersand) {
state.next();
by_ref = true;
}
if matches!(state.current.kind, TokenKind::Ellipsis) {
state.next();
if !modifiers.is_empty() {
return Err(ParseError::VariadicPromotedProperty(state.current.span));
}
variadic = true;
}
// 2. Then expect a variable.
let var = identifiers::var(state)?;
if matches!(state.current.kind, TokenKind::Ampersand) {
state.next();
by_ref = true;
}
if matches!(state.current.kind, TokenKind::Ellipsis) {
state.next();
if !modifiers.is_empty() {
match construct {
0 => {
return Err(ParseError::PromotedPropertyOutsideConstructor(
return Err(ParseError::VariadicPromotedProperty(state.current.span));
}
variadic = true;
}
// 2. Then expect a variable.
let var = identifiers::var(state)?;
if !modifiers.is_empty() {
match construct {
0 => {
return Err(ParseError::PromotedPropertyOutsideConstructor(
state.current.span,
));
}
1 => {
return Err(ParseError::PromotedPropertyOnAbstractConstructor(
state.current.span,
));
}
_ => {}
}
match &ty {
Some(ty) => {
if ty.includes_callable() || ty.is_bottom() {
return Err(ParseError::ForbiddenTypeUsedInProperty(
class_name,
var.to_string(),
ty.clone(),
state.current.span,
));
}
1 => {
return Err(ParseError::PromotedPropertyOnAbstractConstructor(
}
None => {
if modifiers.has_readonly() {
return Err(ParseError::MissingTypeForReadonlyProperty(
class_name,
var.to_string(),
state.current.span,
));
}
_ => {}
}
match &ty {
Some(ty) => {
if ty.includes_callable() || ty.is_bottom() {
return Err(ParseError::ForbiddenTypeUsedInProperty(
class_name,
var.to_string(),
ty.clone(),
state.current.span,
));
}
}
None => {
if modifiers.has_readonly() {
return Err(ParseError::MissingTypeForReadonlyProperty(
class_name,
var.to_string(),
state.current.span,
));
}
}
}
}
let mut default = None;
if state.current.kind == TokenKind::Equals {
state.next();
default = Some(self.expression(state, Precedence::Lowest)?);
}
let end = state.current.span;
members.push(MethodParameter {
start,
end,
name: var,
attributes: state.get_attributes(),
r#type: ty,
variadic,
default,
modifiers,
by_ref,
});
state.skip_comments();
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
utils::skip_right_parenthesis(state)?;
let mut default = None;
if state.current.kind == TokenKind::Equals {
state.next();
default = Some(parser::expression(state, Precedence::Lowest)?);
}
let list_end = state.current.span;
let end = state.current.span;
Ok(MethodParameterList {
start: list_start,
end: list_end,
members,
})
}
members.push(MethodParameter {
start,
end,
name: var,
attributes: state.get_attributes(),
r#type: ty,
variadic,
default,
modifiers,
by_ref,
});
pub(in crate::parser) fn args_list(&self, state: &mut State) -> ParseResult<Vec<Arg>> {
utils::skip_left_parenthesis(state)?;
state.skip_comments();
let mut args = Vec::new();
let mut has_used_named_arguments = false;
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
while !state.is_eof() && state.current.kind != TokenKind::RightParen {
let mut name = None;
let mut unpack = false;
if (matches!(state.current.kind, TokenKind::Identifier(_))
|| identifiers::is_reserved_ident(&state.current.kind))
&& state.peek.kind == TokenKind::Colon
{
name = Some(identifiers::ident_maybe_reserved(state)?);
has_used_named_arguments = true;
state.next();
} else if state.current.kind == TokenKind::Ellipsis {
state.next();
unpack = true;
}
utils::skip_right_parenthesis(state)?;
if name.is_none() && has_used_named_arguments {
return Err(ParseError::CannotUsePositionalArgumentAfterNamedArgument(
state.current.span,
));
}
let list_end = state.current.span;
if unpack && state.current.kind == TokenKind::RightParen {
args.push(Arg {
name: None,
unpack: false,
value: Expression::VariadicPlaceholder,
});
Ok(MethodParameterList {
start: list_start,
end: list_end,
members,
})
}
break;
}
pub fn args_list(state: &mut State) -> ParseResult<Vec<Arg>> {
utils::skip_left_parenthesis(state)?;
state.skip_comments();
let value = self.expression(state, Precedence::Lowest)?;
let mut args = Vec::new();
let mut has_used_named_arguments = false;
args.push(Arg {
name,
unpack,
value,
});
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
while !state.is_eof() && state.current.kind != TokenKind::RightParen {
let mut name = None;
let mut unpack = false;
if (matches!(state.current.kind, TokenKind::Identifier(_))
|| identifiers::is_reserved_ident(&state.current.kind))
&& state.peek.kind == TokenKind::Colon
{
name = Some(identifiers::ident_maybe_reserved(state)?);
has_used_named_arguments = true;
state.next();
} else if state.current.kind == TokenKind::Ellipsis {
state.next();
unpack = true;
}
utils::skip_right_parenthesis(state)?;
if name.is_none() && has_used_named_arguments {
return Err(ParseError::CannotUsePositionalArgumentAfterNamedArgument(
state.current.span,
));
}
Ok(args)
if unpack && state.current.kind == TokenKind::RightParen {
args.push(Arg {
name: None,
unpack: false,
value: Expression::VariadicPlaceholder,
});
break;
}
let value = parser::expression(state, Precedence::Lowest)?;
args.push(Arg {
name,
unpack,
value,
});
if state.current.kind == TokenKind::Comma {
state.next();
} else {
break;
}
}
utils::skip_right_parenthesis(state)?;
Ok(args)
}

View File

@ -1,4 +1,5 @@
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::try_block::CatchBlock;
use crate::parser::ast::try_block::CatchType;
use crate::parser::ast::try_block::FinallyBlock;
@ -6,92 +7,90 @@ use crate::parser::ast::try_block::TryBlock;
use crate::parser::ast::Statement;
use crate::parser::error::ParseError;
use crate::parser::error::ParseResult;
use crate::parser::internal::blocks;
use crate::parser::internal::identifiers;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::State;
use crate::parser::Parser;
impl Parser {
pub(in crate::parser) fn try_block(&self, state: &mut State) -> ParseResult<Statement> {
let start = state.current.span;
pub fn try_block(state: &mut State) -> ParseResult<Statement> {
let start = state.current.span;
state.next();
utils::skip_left_brace(state)?;
let body = blocks::body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
let mut catches = Vec::new();
loop {
if state.current.kind != TokenKind::Catch {
break;
}
let catch_start = state.current.span;
state.next();
utils::skip_left_parenthesis(state)?;
let types = catch_type(state)?;
let var = if state.current.kind == TokenKind::RightParen {
None
} else {
// TODO(azjezz): this is a variable, no an expression?
Some(parser::expression(state, Precedence::Lowest)?)
};
utils::skip_right_parenthesis(state)?;
utils::skip_left_brace(state)?;
let body = self.body(state, &TokenKind::RightBrace)?;
let catch_body = blocks::body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
let mut catches = Vec::new();
loop {
if state.current.kind != TokenKind::Catch {
break;
}
let catch_end = state.current.span;
let catch_start = state.current.span;
state.next();
utils::skip_left_parenthesis(state)?;
let types = catch_type(state)?;
let var = if state.current.kind == TokenKind::RightParen {
None
} else {
// TODO(azjezz): this is a variable, no an expression?
Some(self.expression(state, Precedence::Lowest)?)
};
utils::skip_right_parenthesis(state)?;
utils::skip_left_brace(state)?;
let catch_body = self.body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
let catch_end = state.current.span;
catches.push(CatchBlock {
start: catch_start,
end: catch_end,
types,
var,
body: catch_body,
})
}
let mut finally = None;
if state.current.kind == TokenKind::Finally {
let finally_start = state.current.span;
state.next();
utils::skip_left_brace(state)?;
let finally_body = self.body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
let finally_end = state.current.span;
finally = Some(FinallyBlock {
start: finally_start,
end: finally_end,
body: finally_body,
});
}
if catches.is_empty() && finally.is_none() {
return Err(ParseError::TryWithoutCatchOrFinally(start));
}
let end = state.current.span;
Ok(Statement::Try(TryBlock {
start,
end,
body,
catches,
finally,
}))
catches.push(CatchBlock {
start: catch_start,
end: catch_end,
types,
var,
body: catch_body,
})
}
let mut finally = None;
if state.current.kind == TokenKind::Finally {
let finally_start = state.current.span;
state.next();
utils::skip_left_brace(state)?;
let finally_body = blocks::body(state, &TokenKind::RightBrace)?;
utils::skip_right_brace(state)?;
let finally_end = state.current.span;
finally = Some(FinallyBlock {
start: finally_start,
end: finally_end,
body: finally_body,
});
}
if catches.is_empty() && finally.is_none() {
return Err(ParseError::TryWithoutCatchOrFinally(start));
}
let end = state.current.span;
Ok(Statement::Try(TryBlock {
start,
end,
body,
catches,
finally,
}))
}
#[inline(always)]

View File

@ -0,0 +1,75 @@
use crate::lexer::token::TokenKind;
use crate::parser::ast::Statement;
use crate::parser::ast::Use;
use crate::parser::ast::UseKind;
use crate::parser::error::ParseResult;
use crate::parser::internal::identifiers;
use crate::parser::internal::utils;
use crate::parser::state::State;
pub fn use_statement(state: &mut State) -> ParseResult<Statement> {
state.next();
let kind = match state.current.kind {
TokenKind::Function => {
state.next();
UseKind::Function
}
TokenKind::Const => {
state.next();
UseKind::Const
}
_ => UseKind::Normal,
};
if state.peek.kind == TokenKind::LeftBrace {
let prefix = identifiers::full_name(state)?;
state.next();
let mut uses = Vec::new();
while state.current.kind != TokenKind::RightBrace {
let name = identifiers::full_name(state)?;
let mut alias = None;
if state.current.kind == TokenKind::As {
state.next();
alias = Some(identifiers::ident(state)?);
}
uses.push(Use { name, alias });
if state.current.kind == TokenKind::Comma {
state.next();
continue;
}
}
utils::skip_right_brace(state)?;
utils::skip_semicolon(state)?;
Ok(Statement::GroupUse { prefix, kind, uses })
} else {
let mut uses = Vec::new();
while !state.is_eof() {
let name = identifiers::full_name(state)?;
let mut alias = None;
if state.current.kind == TokenKind::As {
state.next();
alias = Some(identifiers::ident(state)?);
}
uses.push(Use { name, alias });
if state.current.kind == TokenKind::Comma {
state.next();
continue;
}
utils::skip_semicolon(state)?;
break;
}
Ok(Statement::Use { uses, kind })
}
}

View File

@ -70,7 +70,7 @@ pub fn skip_double_colon(state: &mut State) -> ParseResult<Span> {
skip(state, TokenKind::DoubleColon)
}
pub fn colon(state: &mut State) -> ParseResult<Span> {
pub fn skip_colon(state: &mut State) -> ParseResult<Span> {
let span = skip(state, TokenKind::Colon)?;
// A closing PHP tag is valid after a colon, since
// that typically indicates the start of a block (control structures).
@ -81,8 +81,6 @@ pub fn colon(state: &mut State) -> ParseResult<Span> {
}
pub fn skip(state: &mut State, kind: TokenKind) -> ParseResult<Span> {
state.skip_comments();
if state.current.kind == kind {
let end = state.current.span;

View File

@ -1,36 +1,35 @@
use crate::lexer::token::TokenKind;
use crate::parser;
use crate::parser::ast::Expression;
use crate::parser::error::ParseResult;
use crate::parser::internal::identifiers;
use crate::parser::internal::precedences::Precedence;
use crate::parser::internal::utils;
use crate::parser::state::State;
use crate::parser::Parser;
use crate::peek_token;
impl Parser {
pub(in crate::parser) fn dynamic_variable(&self, state: &mut State) -> ParseResult<Expression> {
state.next();
pub fn dynamic_variable(state: &mut State) -> ParseResult<Expression> {
state.next();
let expr = peek_token!([
TokenKind::LeftBrace => {
state.next();
let expr = peek_token!([
TokenKind::LeftBrace => {
state.next();
let name = self.expression(state, Precedence::Lowest)?;
// TODO(azjezz): this is not an expression! it's a constant expression
let name = parser::expression(state, Precedence::Lowest)?;
utils::skip_right_brace(state)?;
utils::skip_right_brace(state)?;
Expression::DynamicVariable {
name: Box::new(name),
}
},
TokenKind::Variable(_) => {
Expression::DynamicVariable {
name: Box::new(Expression::Variable(identifiers::var(state)?)),
}
Expression::DynamicVariable {
name: Box::new(name),
}
], state, ["`{`", "a variable"]);
},
TokenKind::Variable(_) => {
Expression::DynamicVariable {
name: Box::new(Expression::Variable(identifiers::var(state)?)),
}
}
], state, ["`{`", "a variable"]);
Ok(expr)
}
Ok(expr)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,8 @@
[
Noop,
Noop(
(
1,
7,
),
),
]

View File

@ -12,7 +12,12 @@
),
},
body: [
Noop,
Noop(
(
3,
18,
),
),
Class {
name: Identifier {
start: (

View File

@ -12,7 +12,12 @@
),
},
body: [
Noop,
Noop(
(
3,
18,
),
),
Class {
name: Identifier {
start: (

View File

@ -12,7 +12,12 @@
),
},
body: [
Noop,
Noop(
(
3,
18,
),
),
Function(
Function {
start: (
@ -66,7 +71,12 @@
),
},
body: [
Noop,
Noop(
(
7,
18,
),
),
Function(
Function {
start: (

View File

@ -33,7 +33,12 @@
),
},
body: [
Noop,
Noop(
(
13,
23,
),
),
Use {
uses: [
Use {

View File

@ -19,7 +19,12 @@
modifiers: [],
},
},
Noop,
Noop(
(
3,
13,
),
),
Expression {
expr: Infix {
lhs: Variable(

9
tests/fixtures/0274/code.php vendored Normal file
View File

@ -0,0 +1,9 @@
<?php
class a {
public function foo() {
$q = function() {
return parent::bar();
};
}
}

1
tests/fixtures/0274/parser-error.txt vendored Normal file
View File

@ -0,0 +1 @@
CannotFindTypeInCurrentScope("parent", (6, 20)) -> Parse Error: Cannot find type `parent` in this scope on line 6 on column 20

201
tests/fixtures/0274/tokens.txt vendored Normal file
View File

@ -0,0 +1,201 @@
[
Token {
kind: OpenTag(
Full,
),
span: (
1,
1,
),
},
Token {
kind: Class,
span: (
3,
1,
),
},
Token {
kind: Identifier(
"a",
),
span: (
3,
7,
),
},
Token {
kind: LeftBrace,
span: (
3,
9,
),
},
Token {
kind: Public,
span: (
4,
5,
),
},
Token {
kind: Function,
span: (
4,
12,
),
},
Token {
kind: Identifier(
"foo",
),
span: (
4,
21,
),
},
Token {
kind: LeftParen,
span: (
4,
24,
),
},
Token {
kind: RightParen,
span: (
4,
25,
),
},
Token {
kind: LeftBrace,
span: (
4,
27,
),
},
Token {
kind: Variable(
"q",
),
span: (
5,
9,
),
},
Token {
kind: Equals,
span: (
5,
12,
),
},
Token {
kind: Function,
span: (
5,
14,
),
},
Token {
kind: LeftParen,
span: (
5,
22,
),
},
Token {
kind: RightParen,
span: (
5,
23,
),
},
Token {
kind: LeftBrace,
span: (
5,
25,
),
},
Token {
kind: Return,
span: (
6,
13,
),
},
Token {
kind: Parent,
span: (
6,
20,
),
},
Token {
kind: DoubleColon,
span: (
6,
26,
),
},
Token {
kind: Identifier(
"bar",
),
span: (
6,
28,
),
},
Token {
kind: LeftParen,
span: (
6,
31,
),
},
Token {
kind: RightParen,
span: (
6,
32,
),
},
Token {
kind: SemiColon,
span: (
6,
33,
),
},
Token {
kind: RightBrace,
span: (
7,
9,
),
},
Token {
kind: SemiColon,
span: (
7,
10,
),
},
Token {
kind: RightBrace,
span: (
8,
5,
),
},
Token {
kind: RightBrace,
span: (
9,
1,
),
},
]

View File

@ -5,10 +5,8 @@ use std::path::PathBuf;
use pretty_assertions::assert_str_eq;
use php_parser_rs::lexer::Lexer;
use php_parser_rs::parser::Parser;
static LEXER: Lexer = Lexer::new();
static PARSER: Parser = Parser::new();
#[test]
fn test_fixtures() {
@ -71,7 +69,7 @@ fn test_fixtures() {
if ast_file.exists() {
let expected_ast = std::fs::read_to_string(&ast_file).unwrap();
let ast = PARSER.parse(tokens).unwrap();
let ast = php_parser_rs::parse(tokens).unwrap();
assert_str_eq!(
expected_ast.trim(),
format!("{:#?}", ast),
@ -89,7 +87,7 @@ fn test_fixtures() {
);
let expected_error = std::fs::read_to_string(&parse_err_file).unwrap();
let error = PARSER.parse(tokens).err().unwrap();
let error = php_parser_rs::parse(tokens).err().unwrap();
assert_str_eq!(
expected_error.trim(),

View File

@ -4,7 +4,6 @@ use std::path::PathBuf;
use std::process::Command;
use php_parser_rs::lexer::Lexer;
use php_parser_rs::parser::Parser;
#[test]
fn third_party_1_php_standard_library() {
@ -148,8 +147,7 @@ fn test_file(name: &str, filename: PathBuf) {
Lexer::new()
.tokenize(&code)
.map(|tokens| {
Parser::new()
.parse(tokens)
php_parser_rs::parse(tokens)
.map(|_| {
println!("✅ successfully parsed file: `\"{}\"`.", name);
})