Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,780 changes: 844 additions & 936 deletions resources/files/parser.php

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions src/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ class Compiler
public const PHVOLT_T_ENDFOR = 305;
public const PHVOLT_T_ENDIF = 303;
public const PHVOLT_T_ENDMACRO = 323;
public const PHVOLT_T_ENDRAW = 401;
public const PHVOLT_T_ENDSWITCH = 414;
public const PHVOLT_T_ENDVERBATIM = 403;
public const PHVOLT_T_EQUALS = 272;
public const PHVOLT_T_EVEN = 381;
public const PHVOLT_T_EXPR = 354;
Expand Down Expand Up @@ -189,7 +189,6 @@ class Compiler
public const PHVOLT_T_QUALIFIED = 355;
public const PHVOLT_T_QUESTION = 63; //'?';
public const PHVOLT_T_RANGE = 276;
public const PHVOLT_T_RAW = 400;
public const PHVOLT_T_RAW_FRAGMENT = 357;
public const PHVOLT_T_RESOLVED_EXPR = 364;
public const PHVOLT_T_RETURN = 327;
Expand All @@ -207,6 +206,7 @@ class Compiler
public const PHVOLT_T_SWITCH = 411;
public const PHVOLT_T_TERNARY = 366;
public const PHVOLT_T_TRUE = 263;
public const PHVOLT_T_VERBATIM = 402;
public const PHVOLT_T_WITH = 324;

/**
Expand Down Expand Up @@ -794,7 +794,7 @@ public function compileDo(array $statement): string
}

/**
* Compiles a {% raw %}`{{` `}}`{% endraw %} statement returning PHP code
* Compiles a `{{` `}}` statement returning PHP code
*/
public function compileEcho(array $statement): string
{
Expand Down Expand Up @@ -1457,7 +1457,7 @@ public function compileSource(string $viewCode, bool $extendsMode = false): stri
* Compiles a template into a string
*
*```php
* echo $compiler->compileString({% raw %}'{{ "hello world" }}'{% endraw %});
* echo $compiler->compileString('{{ "hello world" }}');
*```
*
* @throws Exception
Expand Down Expand Up @@ -2240,7 +2240,7 @@ public function getUniquePrefix(): string
*
*```php
* print_r(
* $compiler->parse("{% raw %}{{ 3 + 2 }}{% endraw %}")
* $compiler->parse("{{ 3 + 2 }}")
* );
*```
* @throws Exception
Expand Down
4 changes: 2 additions & 2 deletions src/Compiler/Opcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ enum Opcode: int
case ENDFOR = 305;
case ENDIF = 303;
case ENDMACRO = 323;
case ENDRAW = 401;
case ENDSWITCH = 414;
case ENDVERBATIM = 403;
case EQUALS = 272;
case EVEN = 381;
case EXPR = 354;
Expand Down Expand Up @@ -117,7 +117,6 @@ enum Opcode: int
case QUALIFIED = 355;
case QUESTION = 63;
case RANGE = 276;
case RAW = 400;
case RAW_FRAGMENT = 357;
case RESOLVED_EXPR = 364;
case RETURN = 327;
Expand All @@ -132,5 +131,6 @@ enum Opcode: int
case SWITCH = 411;
case TERNARY = 366;
case TRUE = 263;
case VERBATIM = 402;
case WITH = 324;
}
16 changes: 2 additions & 14 deletions src/Parser/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ public function parse(string $code, string $templatePath = 'eval code'): array
CompilerOpcode::ENDCALL->value => $parser->phvolt_(Opcode::ENDCALL->value),
CompilerOpcode::CACHE->value => $parser->phvolt_(Opcode::CACHE->value),
CompilerOpcode::ENDCACHE->value => $parser->phvolt_(Opcode::ENDCACHE->value),
CompilerOpcode::RAW->value => $this->handleRaw($parser, $state),
CompilerOpcode::ENDRAW->value => $this->handleEndraw($parser, $state),
CompilerOpcode::VERBATIM->value => $parser->phvolt_(Opcode::VERBATIM->value),
CompilerOpcode::ENDVERBATIM->value => $parser->phvolt_(Opcode::ENDVERBATIM->value),
CompilerOpcode::INCLUDE->value => $parser->phvolt_(Opcode::INCLUDE->value),
CompilerOpcode::WITH->value => $parser->phvolt_(Opcode::WITH->value),
CompilerOpcode::DEFINED->value => $parser->phvolt_(Opcode::DEFINED->value),
Expand Down Expand Up @@ -356,12 +356,6 @@ private function handleEndmacro(phvolt_Parser $parser, State $state): void
$parser->phvolt_(Opcode::ENDMACRO->value);
}

private function handleEndraw(phvolt_Parser $parser, State $state): void
{
$parser->phvolt_(Opcode::ENDRAW->value);
$state->decrementForcedRawState();
}

private function handleEndswitch(phvolt_Parser $parser, Status $parserStatus, State $state): void
{
if ($state->getSwitchLevel() === 0) {
Expand Down Expand Up @@ -447,12 +441,6 @@ private function handleOpenEdelimiter(phvolt_Parser $parser, Status $parserStatu
$parser->phvolt_(Opcode::OPEN_EDELIMITER->value);
}

private function handleRaw(phvolt_Parser $parser, State $state): void
{
$parser->phvolt_(Opcode::RAW->value);
$state->incrementForcedRawState();
}

private function handleRawFragment(
phvolt_Parser $parser,
Status $parserStatus,
Expand Down
7 changes: 4 additions & 3 deletions src/Scanner/Mode.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@

enum Mode: int
{
case CODE = 1;
case COMMENT = 2;
case RAW = 0;
case CODE = 1;
case COMMENT = 2;
case RAW = 0;
case VERBATIM = 3;
}
4 changes: 2 additions & 2 deletions src/Scanner/Opcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ enum Opcode: int
case ENDFOR = 39;
case ENDIF = 33;
case ENDMACRO = 53;
case ENDRAW = 69;
case ENDSWITCH = 41;
case ENDVERBATIM = 69;
case EQUALS = 10;
case EVEN = 82;
case EXTENDS = 70;
Expand Down Expand Up @@ -90,7 +90,6 @@ enum Opcode: int
case PLUS = 21;
case QUESTION = 3;
case RANGE = 5;
case RAW = 68;
case RAW_FRAGMENT = 79;
case RETURN = 74;
case SBRACKET_CLOSE = 50;
Expand All @@ -102,6 +101,7 @@ enum Opcode: int
case SWITCH = 40;
case TIMES = 19;
case TRUE = 59;
case VERBATIM = 68;
case WITH = 72;

public function label(): string
Expand Down
151 changes: 139 additions & 12 deletions src/Scanner/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,64 @@ public function getToken(): Token
}

public function scanForToken(): ScannerStatus
{
$status = $this->scan();

/**
* "verbatim" / "endverbatim" are reserved words. The generated DFA
* matches them as plain identifiers, so they are reclassified here at
* the single scanner exit point - this emits the exact token stream
* cphalcon produces without rebuilding the generated state machine.
*/
if (
$status === ScannerStatus::OK
&& $this->state->getMode() === Mode::CODE->value
&& $this->token->opcode === Opcode::IDENTIFIER->value
) {
$keyword = strtolower((string) $this->token->value);

if ($keyword === 'verbatim') {
$this->state->incrementStatementPosition()->setVerbatim(1);
$this->token = new Token(Opcode::VERBATIM->value);
} elseif ($keyword === 'endverbatim') {
$this->state->incrementStatementPosition();
$this->token = new Token(Opcode::ENDVERBATIM->value);
}
}

return $status;
}

/**
* Looks ahead from a "{%" sequence inside a verbatim block to decide
* whether it opens the closing "{% endverbatim %}" tag. Mirrors the
* peek logic in cphalcon's scanner: optional "-" marker, surrounding
* whitespace, case-insensitive "endverbatim" as a whole word.
*/
private function isEndVerbatimAhead(): bool
{
$buffer = $this->state->getRawBuffer();
// The cursor is on "{" and the next character is "%".
$peek = $this->state->getCursor() + 2;

if (($buffer[$peek] ?? '') === '-') {
$peek++;
}

while (in_array($buffer[$peek] ?? '', [' ', "\t", "\r", "\n"], true)) {
$peek++;
}

if (strtolower(substr($buffer, $peek, 11)) !== 'endverbatim') {
return false;
}

$after = $buffer[$peek + 11] ?? '';

return $after === '' || (!ctype_alnum($after) && $after !== '_');
}

private function scan(): ScannerStatus
{
$start = $this->state->getCursor();
$status = ScannerStatus::IMPOSSIBLE;
Expand Down Expand Up @@ -88,6 +146,63 @@ public function scanForToken(): ScannerStatus

$this->state->appendToRawFragment($cursor);
$this->state->incrementStart();
} elseif ($mode === Mode::VERBATIM->value) {
if ($cursor === "\n") {
$this->state->incrementActiveLine();
}

/**
* End of input while still inside a verbatim block. Flush
* whatever has been captured so far; the parser then reports
* the missing {% endverbatim %}.
*/
if ($cursor === null) {
$this->state->setMode(Mode::CODE->value);

if ($this->state->getRawFragment() !== '') {
$this->token = new Token(
Opcode::RAW_FRAGMENT->value,
$this->state->getRawFragment()
);
$this->state->setRawFragment('');

return ScannerStatus::OK;
}

return ScannerStatus::EOF;
}

/**
* Detect the closing "{% endverbatim %}" tag, allowing the
* optional "-" whitespace-control marker and surrounding
* spaces. Everything else - including other "{{", "{%" and
* "{#" sequences - is captured as literal content.
*/
if ($cursor === '{' && $this->state->getNext() === '%' && $this->isEndVerbatimAhead()) {
$this->state->setMode(Mode::CODE->value);

if ($this->state->getRawFragment() !== '') {
$this->token = new Token(
Opcode::RAW_FRAGMENT->value,
$this->state->getRawFragment()
);
$this->state->setRawFragment('');
} else {
$this->token = new Token(Opcode::IGNORE->value);
}

/**
* Leave the cursor on "{%" so the {% endverbatim %} tag
* is tokenized on the next scan.
*/
return ScannerStatus::OK;
}

/**
* Literal content: buffer the current character and advance.
*/
$this->state->appendToRawFragment($cursor);
$this->state->incrementStart();
} else {
$vvch = $cursor;
switch ($vvch) {
Expand Down Expand Up @@ -915,7 +1030,12 @@ public function scanForToken(): ScannerStatus
}
vv85:
$this->state->incrementStart();
$this->state->setMode(Mode::RAW->value);
if ($this->state->getVerbatim() === 1) {
$this->state->setMode(Mode::VERBATIM->value);
$this->state->setVerbatim(0);
} else {
$this->state->setMode(Mode::RAW->value);
}
$this->token = new Token(Opcode::CLOSE_DELIMITER->value);
return ScannerStatus::OK;
vv87:
Expand Down Expand Up @@ -1789,6 +1909,7 @@ public function scanForToken(): ScannerStatus
vv156:
$this->state->incrementStart();
$this->state->setMode(Mode::RAW->value);
$this->state->setVerbatim(0);
$this->token = new Token(Opcode::CLOSE_EDELIMITER->value);
return ScannerStatus::OK;
vv158:
Expand All @@ -1798,7 +1919,12 @@ public function scanForToken(): ScannerStatus

vv160:
$this->state->incrementStart();
$this->state->setMode(Mode::RAW->value);
if ($this->state->getVerbatim() === 1) {
$this->state->setMode(Mode::VERBATIM->value);
$this->state->setVerbatim(0);
} else {
$this->state->setMode(Mode::RAW->value);
}
$this->state->setWhitespaceControl(true);
$this->token = new Token(Opcode::CLOSE_DELIMITER->value);
return ScannerStatus::OK;
Expand All @@ -1807,6 +1933,7 @@ public function scanForToken(): ScannerStatus
$this->state->incrementStart();
$this->state->setMode(Mode::RAW->value);
$this->state->setWhitespaceControl(true);
$this->state->setVerbatim(0);
$this->token = new Token(Opcode::CLOSE_EDELIMITER->value);
return ScannerStatus::OK;
vv164:
Expand Down Expand Up @@ -2423,11 +2550,11 @@ public function scanForToken(): ScannerStatus
goto vv198;
}
vv198:
{
$this->state->incrementStatementPosition();
$this->token = new Token(Opcode::RAW->value);
return ScannerStatus::OK;
}
$this->token = new Token(
Opcode::IDENTIFIER->value,
substr($this->state->getRawBuffer(), $start, $this->state->getCursor() - $start)
);
return ScannerStatus::OK;
vv199:
$vvch = $this->state->incrementStart()->getStart();
switch ($vvch) {
Expand Down Expand Up @@ -4413,11 +4540,11 @@ public function scanForToken(): ScannerStatus
goto vv304;
}
vv304:
{
$this->state->incrementStatementPosition();
$this->token = new Token(Opcode::ENDRAW->value);
return ScannerStatus::OK;
}
$this->token = new Token(
Opcode::IDENTIFIER->value,
substr($this->state->getRawBuffer(), $start, $this->state->getCursor() - $start)
);
return ScannerStatus::OK;
vv305:
$vvch = $this->state->incrementStart()->getStart();
switch ($vvch) {
Expand Down
Loading