Merge pull request #80 from ryangjchandler/feature/halt-compiler

This commit is contained in:
Ryan Chandler 2022-09-14 23:57:11 +01:00 committed by GitHub
commit 0146d3e1d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 2 deletions

View File

@ -4,6 +4,7 @@ use crate::{ByteString, OpenTagKind, Token, TokenKind};
pub enum LexerState {
Initial,
Scripting,
Halted,
}
#[allow(dead_code)]
@ -78,6 +79,16 @@ impl Lexer {
tokens.push(self.scripting()?);
}
// The "Halted" state is entered when the `__halt_compiler` token is encountered.
// In this state, all the text that follows is no longer parsed as PHP as is collected
// into a single "InlineHtml" token (kind of cheating, oh well).
LexerState::Halted => {
tokens.push(Token {
kind: TokenKind::InlineHtml(self.chars[self.cursor..].into()),
span: (self.line, self.col),
});
break;
}
}
}
@ -300,8 +311,21 @@ impl Lexer {
if qualified {
TokenKind::QualifiedIdentifier(buffer.into())
} else {
identifier_to_keyword(&buffer)
.unwrap_or_else(|| TokenKind::Identifier(buffer.into()))
let kind = identifier_to_keyword(&buffer)
.unwrap_or_else(|| TokenKind::Identifier(buffer.into()));
if kind == TokenKind::HaltCompiler {
match self.peek_buf() {
[b'(', b')', b';', ..] => {
self.skip(3);
self.col += 3;
self.enter_state(LexerState::Halted);
}
_ => return Err(LexerError::InvalidHaltCompiler),
}
}
kind
}
}
[b'/', b'*', ..] => {
@ -755,6 +779,7 @@ impl Lexer {
#[allow(dead_code)]
fn identifier_to_keyword(ident: &[u8]) -> Option<TokenKind> {
Some(match ident {
b"__halt_compiler" | b"__HALT_COMPILER" => TokenKind::HaltCompiler,
b"readonly" => TokenKind::Readonly,
b"global" => TokenKind::Global,
b"match" => TokenKind::Match,
@ -817,6 +842,7 @@ fn identifier_to_keyword(ident: &[u8]) -> Option<TokenKind> {
pub enum LexerError {
UnexpectedEndOfFile,
UnexpectedCharacter(u8),
InvalidHaltCompiler,
}
#[cfg(test)]
@ -1080,6 +1106,32 @@ function hello_world() {
);
}
#[test]
fn halt_compiler() {
assert_tokens(
"<?php __halt_compiler();",
&[open!(), TokenKind::HaltCompiler],
);
assert_tokens(
"<?php __HALT_COMPILER();",
&[open!(), TokenKind::HaltCompiler],
);
assert_tokens(
"<?php __halt_compiler(); Some jargon that comes after the halt, oops!",
&[
open!(),
TokenKind::HaltCompiler,
TokenKind::InlineHtml(
" Some jargon that comes after the halt, oops!"
.as_bytes()
.into(),
),
],
);
}
fn assert_tokens<B: ?Sized + AsRef<[u8]>>(source: &B, expected: &[TokenKind]) {
let mut kinds = vec![];

View File

@ -11,6 +11,7 @@ pub enum OpenTagKind {
#[derive(Debug, PartialEq, Clone)]
pub enum TokenKind {
HaltCompiler,
Readonly,
Global,
Abstract,
@ -172,6 +173,7 @@ impl Default for Token {
impl Display for TokenKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::HaltCompiler => "__halt_compiler",
Self::Readonly => "readonly",
Self::AsteriskEqual => "*=",
Self::ObjectCast => "(object)",

View File

@ -170,6 +170,9 @@ impl From<&TokenKind> for IncludeKind {
#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
InlineHtml(ByteString),
HaltCompiler {
content: Option<ByteString>,
},
Static {
vars: Vec<StaticVar>,
},

View File

@ -171,6 +171,18 @@ impl Parser {
self.skip_comments();
let statement = match &self.current.kind {
TokenKind::HaltCompiler => {
self.next();
let content = if let TokenKind::InlineHtml(content) = self.current.kind.clone() {
self.next();
Some(content)
} else {
None
};
Statement::HaltCompiler { content }
}
TokenKind::Declare => {
self.next();
self.lparen()?;