From ec647a950350532efaad0d37a86d9df807bedcb3 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 09:55:42 -0500 Subject: [PATCH 01/10] test(parser): update tests to new stateless parse() API --- tests/unit/Parser/ParserTest.php | 203 +++++++++++++++++-------------- 1 file changed, 115 insertions(+), 88 deletions(-) diff --git a/tests/unit/Parser/ParserTest.php b/tests/unit/Parser/ParserTest.php index 243fa29..978da9a 100644 --- a/tests/unit/Parser/ParserTest.php +++ b/tests/unit/Parser/ParserTest.php @@ -21,15 +21,15 @@ final class ParserTest extends TestCase { public function testEmptyTemplateReturnsEmptyArray(): void { - $parser = new Parser(''); + $parser = new Parser(); - $this->assertSame([], $parser->parseView('test.volt')); + $this->assertSame([], $parser->parse('', 'test.volt')); } public function testRawTextFragment(): void { - $parser = new Parser('Hello World'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('Hello World', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -39,8 +39,8 @@ public function testRawTextFragment(): void public function testEchoVariable(): void { - $parser = new Parser('{{ name }}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{{ name }}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -50,8 +50,8 @@ public function testEchoVariable(): void public function testIfStatement(): void { - $parser = new Parser('{% if active %}yes{% endif %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% if active %}yes{% endif %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -60,8 +60,8 @@ public function testIfStatement(): void public function testForLoop(): void { - $parser = new Parser('{% for item in items %}{{ item }}{% endfor %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% for item in items %}{{ item }}{% endfor %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -72,8 +72,8 @@ public function testForLoop(): void public function testSetStatement(): void { - $parser = new Parser('{% set x = 1 %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% set x = 1 %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -82,8 +82,11 @@ public function testSetStatement(): void public function testExtendsStatement(): void { - $parser = new Parser("{% extends 'base.volt' %}{% block content %}hello{% endblock %}"); - $result = $parser->parseView('child.volt'); + $parser = new Parser(); + $result = $parser->parse( + "{% extends 'base.volt' %}{% block content %}hello{% endblock %}", + 'child.volt' + ); $this->assertIsArray($result); } @@ -92,15 +95,15 @@ public function testSyntaxErrorThrowsException(): void { $this->expectException(Exception::class); - $parser = new Parser('{% endif %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% endif %}', 'test.volt'); } public function testTemplatePathInErrorMessage(): void { try { - $parser = new Parser('{% endif %}'); - $parser->parseView('mytemplate.volt'); + $parser = new Parser(); + $parser->parse('{% endif %}', 'mytemplate.volt'); $this->fail('Expected exception not thrown'); } catch (Exception $e) { $this->assertStringContainsString('mytemplate.volt', $e->getMessage()); @@ -111,26 +114,17 @@ public function testNestedSwitchThrowsException(): void { $this->expectException(Exception::class); - $parser = new Parser('{% switch x %}{% switch y %}{% endswitch %}{% endswitch %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% switch x %}{% switch y %}{% endswitch %}{% endswitch %}', 'test.volt'); } public function testDebugMode(): void { - $debugFile = sys_get_temp_dir() . '/volt_debug_test.txt'; - $parser = new Parser('{{ name }}'); - - $ref = new \ReflectionClass($parser); - - $debugProp = $ref->getProperty('debug'); - $debugProp->setAccessible(true); - $debugProp->setValue($parser, true); - - $fileProp = $ref->getProperty('debugFile'); - $fileProp->setAccessible(true); - $fileProp->setValue($parser, $debugFile); + $debugFile = '/app/volt_debug_test.txt'; + $parser = new Parser(); + $parser->setDebug(true)->setDebugFile($debugFile); - $result = $parser->parseView('test.volt'); + $result = $parser->parse('{{ name }}', 'test.volt'); $this->assertIsArray($result); $this->assertFileExists($debugFile); @@ -140,8 +134,11 @@ public function testDebugMode(): void public function testElseForViaElse(): void { - $parser = new Parser('{% for x in items %}{{ x }}{% else %}empty{% endfor %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse( + '{% for x in items %}{{ x }}{% else %}empty{% endfor %}', + 'test.volt' + ); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -150,8 +147,11 @@ public function testElseForViaElse(): void public function testElsefor(): void { - $parser = new Parser('{% for x in items %}{{ x }}{% elsefor %}empty{% endfor %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse( + '{% for x in items %}{{ x }}{% elsefor %}empty{% endfor %}', + 'test.volt' + ); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -163,14 +163,14 @@ public function testElseifWithoutIfThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Unexpected ENDIF'); - $parser = new Parser('{% elseif x %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% elseif x %}', 'test.volt'); } public function testSwitchWithCase(): void { - $parser = new Parser('{% switch x %}{% case 1 %}one{% endswitch %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% switch x %}{% case 1 %}one{% endswitch %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -179,8 +179,11 @@ public function testSwitchWithCase(): void public function testSwitchWithDefault(): void { - $parser = new Parser('{% switch x %}{% default %}fallback{% endswitch %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse( + '{% switch x %}{% default %}fallback{% endswitch %}', + 'test.volt' + ); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -192,8 +195,8 @@ public function testCaseWithoutSwitchThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Unexpected CASE'); - $parser = new Parser('{% case 1 %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% case 1 %}', 'test.volt'); } public function testEndswitchWithoutSwitchThrowsException(): void @@ -201,8 +204,8 @@ public function testEndswitchWithoutSwitchThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Unexpected ENDSWITCH'); - $parser = new Parser('{% endswitch %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% endswitch %}', 'test.volt'); } public function testBlockInsideBlockThrowsException(): void @@ -210,8 +213,11 @@ public function testBlockInsideBlockThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Embedding blocks into other blocks is not supported'); - $parser = new Parser('{% block outer %}{% block inner %}{% endblock %}{% endblock %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse( + '{% block outer %}{% block inner %}{% endblock %}{% endblock %}', + 'test.volt' + ); } public function testMacroInsideMacroThrowsException(): void @@ -219,14 +225,20 @@ public function testMacroInsideMacroThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Embedding macros into other macros is not allowed'); - $parser = new Parser('{% macro outer() %}{% macro inner() %}{% endmacro %}{% endmacro %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse( + '{% macro outer() %}{% macro inner() %}{% endmacro %}{% endmacro %}', + 'test.volt' + ); } public function testMacroAndEndmacro(): void { - $parser = new Parser('{% macro greet(name) %}Hello {{ name }}{% endmacro %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse( + '{% macro greet(name) %}Hello {{ name }}{% endmacro %}', + 'test.volt' + ); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -235,8 +247,8 @@ public function testMacroAndEndmacro(): void public function testCallAndEndcall(): void { - $parser = new Parser('{% call greet() %}{% endcall %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% call greet() %}{% endcall %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -245,8 +257,8 @@ public function testCallAndEndcall(): void public function testRawAndEndraw(): void { - $parser = new Parser('{% raw %}{{ not_evaluated }}{% endraw %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% raw %}{{ not_evaluated }}{% endraw %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -255,8 +267,8 @@ public function testRawAndEndraw(): void public function testIncludeWith(): void { - $parser = new Parser('{% include "partial.volt" with vars %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% include "partial.volt" with vars %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -265,8 +277,11 @@ public function testIncludeWith(): void public function testReturnInMacro(): void { - $parser = new Parser('{% macro compute(x) %}{% return x %}{% endmacro %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse( + '{% macro compute(x) %}{% return x %}{% endmacro %}', + 'test.volt' + ); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -275,8 +290,8 @@ public function testReturnInMacro(): void public function testAddAssign(): void { - $parser = new Parser('{% set counter += 1 %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% set counter += 1 %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -285,8 +300,8 @@ public function testAddAssign(): void public function testSubAssign(): void { - $parser = new Parser('{% set counter -= 1 %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% set counter -= 1 %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -295,8 +310,8 @@ public function testSubAssign(): void public function testMulAssign(): void { - $parser = new Parser('{% set counter *= 2 %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% set counter *= 2 %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -305,8 +320,8 @@ public function testMulAssign(): void public function testDivAssign(): void { - $parser = new Parser('{% set counter /= 2 %}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{% set counter /= 2 %}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -318,8 +333,8 @@ public function testOpenEdelimiterInExtendsModeThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Child templates only may contain blocks'); - $parser = new Parser('{% extends "base.volt" %}{{ something }}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% extends "base.volt" %}{{ something }}', 'test.volt'); } public function testForInExtendsModeThrowsException(): void @@ -327,8 +342,8 @@ public function testForInExtendsModeThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Child templates only may contain blocks'); - $parser = new Parser('{% extends "base.volt" %}{% for x in y %}{% endfor %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% extends "base.volt" %}{% for x in y %}{% endfor %}', 'test.volt'); } public function testSwitchInExtendsModeThrowsException(): void @@ -336,8 +351,8 @@ public function testSwitchInExtendsModeThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Child templates only may contain blocks'); - $parser = new Parser('{% extends "base.volt" %}{% switch x %}{% endswitch %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% extends "base.volt" %}{% switch x %}{% endswitch %}', 'test.volt'); } public function testSetInExtendsModeThrowsException(): void @@ -345,8 +360,8 @@ public function testSetInExtendsModeThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Child templates only may contain blocks'); - $parser = new Parser('{% extends "base.volt" %}{% set x = 1 %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% extends "base.volt" %}{% set x = 1 %}', 'test.volt'); } public function testRawFragmentInExtendsModeThrowsException(): void @@ -354,8 +369,8 @@ public function testRawFragmentInExtendsModeThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Child templates only may contain blocks'); - $parser = new Parser('{% extends "base.volt" %}non-blank content'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% extends "base.volt" %}non-blank content', 'test.volt'); } public function testScannerErrorNearEof(): void @@ -363,10 +378,11 @@ public function testScannerErrorNearEof(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Scanning error'); - $parser = new Parser( - '{{ link_to("album/" ~ album.id ~ "/" ~ $album.uri, "test") }}' + $parser = new Parser(); + $parser->parse( + '{{ link_to("album/" ~ album.id ~ "/" ~ $album.uri, "test") }}', + 'test.volt' ); - $parser->parseView('test.volt'); } public function testIfInExtendsModeThrowsException(): void @@ -374,14 +390,14 @@ public function testIfInExtendsModeThrowsException(): void $this->expectException(Exception::class); $this->expectExceptionMessage('Child templates only may contain blocks'); - $parser = new Parser('{% extends "base.volt" %}{% if x %}yes{% endif %}'); - $parser->parseView('test.volt'); + $parser = new Parser(); + $parser->parse('{% extends "base.volt" %}{% if x %}yes{% endif %}', 'test.volt'); } public function testCurlyBracketEmptyExpression(): void { - $parser = new Parser('{{ {} }}'); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse('{{ {} }}', 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); @@ -390,11 +406,22 @@ public function testCurlyBracketEmptyExpression(): void public function testCurlyBracketDictExpression(): void { - $parser = new Parser("{{ {'key': 'value'} }}"); - $result = $parser->parseView('test.volt'); + $parser = new Parser(); + $result = $parser->parse("{{ {'key': 'value'} }}", 'test.volt'); $this->assertIsArray($result); $this->assertCount(1, $result); $this->assertSame(359, $result[0]['type']); // PHVOLT_T_ECHO } + + public function testParserIsReusable(): void + { + $parser = new Parser(); + + $result1 = $parser->parse('{{ name }}', 'first.volt'); + $result2 = $parser->parse('{% if active %}yes{% endif %}', 'second.volt'); + + $this->assertSame(359, $result1[0]['type']); // PHVOLT_T_ECHO + $this->assertSame(300, $result2[0]['type']); // PHVOLT_T_IF + } } From 5852611d129ee51e1735a8f0c9defb2d995607ae Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 09:57:47 -0500 Subject: [PATCH 02/10] test(parser): strengthen testParserIsReusable with assertCount --- tests/unit/Parser/ParserTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/Parser/ParserTest.php b/tests/unit/Parser/ParserTest.php index 978da9a..797c224 100644 --- a/tests/unit/Parser/ParserTest.php +++ b/tests/unit/Parser/ParserTest.php @@ -421,6 +421,8 @@ public function testParserIsReusable(): void $result1 = $parser->parse('{{ name }}', 'first.volt'); $result2 = $parser->parse('{% if active %}yes{% endif %}', 'second.volt'); + $this->assertCount(1, $result1); + $this->assertCount(1, $result2); $this->assertSame(359, $result1[0]['type']); // PHVOLT_T_ECHO $this->assertSame(300, $result2[0]['type']); // PHVOLT_T_IF } From 4f67ebd0defa8943cea7c3036087cc106602b735 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 10:04:46 -0500 Subject: [PATCH 03/10] refactor(parser): remove constructor arg, rename to parse(), add fluent debug setters, extract opcode handlers --- src/Parser/Parser.php | 555 ++++++++++++++++++++++++------------------ 1 file changed, 312 insertions(+), 243 deletions(-) diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index b2286d1..ef3a912 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -33,45 +33,56 @@ class Parser private string $debugFile = 'volt.txt'; - private ?Token $token = null; + public function setDebug(bool $debug): static + { + $this->debug = $debug; + + return $this; + } - public function __construct(private string $code) + public function setDebugFile(string $debugFile): static { + $this->debugFile = $debugFile; + + return $this; } /** + * @param string $code * @param string $templatePath * * @return array * @throws Exception */ - public function parseView(string $templatePath): array + public function parse(string $code, string $templatePath = ''): array { - if (strlen($this->code) === 0) { + if (strlen($code) === 0) { return []; } - $debug = null; + $debugHandle = null; if ($this->debug) { - $debug = fopen($this->debugFile, 'w+'); + $debugHandle = fopen($this->debugFile, 'w+'); } - $codeLength = strlen($this->code); - $parserState = new State($this->code); + $codeLength = strlen($code); + $parserState = new State($code); $parserState->setActiveFile($templatePath); $parserStatus = new Status($parserState); - $scanner = new Scanner($parserStatus->getState()); + $scanner = new Scanner($parserStatus->getState()); $parser = new phvolt_Parser($parserStatus); - $parser->phvolt_Trace($debug); + $parser->phvolt_Trace($debugHandle); + + $state = $parserStatus->getState(); + $scannerStatus = ScannerStatus::OK; - $state = $parserStatus->getState(); while (($scannerStatus = $scanner->scanForToken()) === ScannerStatus::OK) { - $this->token = $scanner->getToken(); - $parserStatus->setToken($this->token); + $token = $scanner->getToken(); + $parserStatus->setToken($token); $state->setStartLength($codeLength - $state->getCursor()); - $opcode = $this->token->opcode; + $opcode = $token->opcode; $state->setActiveToken($opcode); switch ($opcode) { @@ -207,16 +218,7 @@ public function parseView(string $templatePath): array break; case Compiler::PHVOLT_T_OPEN_EDELIMITER: - if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { - $this->createErrorMessage( - $parserStatus, - 'Child templates only may contain blocks' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $parser->phvolt_(Opcode::OPEN_EDELIMITER->value); + $this->handleOpenEdelimiter($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_CLOSE_EDELIMITER: @@ -236,51 +238,23 @@ public function parseView(string $templatePath): array break; case Compiler::PHVOLT_T_INTEGER: - $this->phvoltParseWithToken( - $parser, - Compiler::PHVOLT_T_INTEGER, - Opcode::INTEGER - ); + $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_INTEGER, Opcode::INTEGER); break; case Compiler::PHVOLT_T_DOUBLE: - $this->phvoltParseWithToken( - $parser, - Compiler::PHVOLT_T_DOUBLE, - Opcode::DOUBLE - ); + $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_DOUBLE, Opcode::DOUBLE); break; case Compiler::PHVOLT_T_STRING: - $this->phvoltParseWithToken( - $parser, - Compiler::PHVOLT_T_STRING, - Opcode::STRING - ); + $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_STRING, Opcode::STRING); break; case Compiler::PHVOLT_T_IDENTIFIER: - $this->phvoltParseWithToken( - $parser, - Compiler::PHVOLT_T_IDENTIFIER, - Opcode::IDENTIFIER - ); + $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_IDENTIFIER, Opcode::IDENTIFIER); break; case Compiler::PHVOLT_T_IF: - if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { - $this->createErrorMessage( - $parserStatus, - 'Child templates only may contain blocks' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $state->incrementIfLevel(); - $state->incrementBlockLevel(); - - $parser->phvolt_(Opcode::IF->value); + $this->handleIf($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_ELSE: @@ -296,36 +270,15 @@ public function parseView(string $templatePath): array break; case Compiler::PHVOLT_T_ELSEIF: - if ($state->getIfLevel() === 0) { - $this->createErrorMessage($parserStatus, 'Unexpected ENDIF'); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $parser->phvolt_(Opcode::ELSEIF->value); + $this->handleElseif($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_ENDIF: - $state->decrementBlockLevel(); - $state->decrementIfLevel(); - $parser->phvolt_(Opcode::ENDIF->value); + $this->handleEndif($parser, $state); break; case Compiler::PHVOLT_T_FOR: - if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { - $this->createErrorMessage( - $parserStatus, - 'Child templates only may contain blocks' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $state->setOldIfLevel($state->getIfLevel()); - $state->setIfLevel(0); - $state->incrementForLevel(); - $state->incrementBlockLevel(); - $parser->phvolt_(Opcode::FOR->value); + $this->handleFor($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_IN: @@ -333,108 +286,31 @@ public function parseView(string $templatePath): array break; case Compiler::PHVOLT_T_ENDFOR: - $state->decrementBlockLevel(); - $state->decrementForLevel(); - $state->setIfLevel($state->getOldIfLevel()); - $parser->phvolt_(Opcode::ENDFOR->value); + $this->handleEndfor($parser, $state); break; case Compiler::PHVOLT_T_SWITCH: - if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { - $this->createErrorMessage( - $parserStatus, - 'Child templates only may contain blocks' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } elseif ($state->getSwitchLevel() > 0) { - $this->createErrorMessage( - $parserStatus, - 'A nested switch detected. There is no nested switch-case statements support' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $state->setSwitchLevel(1); - $state->incrementBlockLevel(); - $parser->phvolt_(Opcode::SWITCH->value); + $this->handleSwitch($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_CASE: - if ($state->getSwitchLevel() === 0) { - $this->createErrorMessage($parserStatus, 'Unexpected CASE'); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $parser->phvolt_(Opcode::CASE->value); + $this->handleCase($parser, $parserStatus); break; - /* only for switch-case statements */ case Compiler::PHVOLT_T_DEFAULT: - if ($state->getSwitchLevel() !== 0) { - $parser->phvolt_(Opcode::DEFAULT->value); - unset($this->token); - } else { - $this->phvoltParseWithToken( - $parser, - Compiler::PHVOLT_T_IDENTIFIER, - Opcode::IDENTIFIER, - ); - } - + $this->handleDefault($parser, $parserStatus, $token, $state); break; case Compiler::PHVOLT_T_ENDSWITCH: - if ($state->getSwitchLevel() === 0) { - $this->createErrorMessage($parserStatus, 'Unexpected ENDSWITCH'); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $state->decrementBlockLevel(); - $state->setSwitchLevel(0); - $parser->phvolt_(Opcode::ENDSWITCH->value); + $this->handleEndswitch($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_RAW_FRAGMENT: - if ($this->token->length > 0) { - /** @var string $rawValue */ - $rawValue = $this->token->value ?? ''; - $value = trim($rawValue); - if ($value !== '' && $state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { - $this->createErrorMessage( - $parserStatus, - 'Child templates only may contain blocks' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - if (!$this->phvoltIsBlankString($this->token)) { - $state->incrementStatementPosition(); - } - - $this->phvoltParseWithToken( - $parser, - Compiler::PHVOLT_T_RAW_FRAGMENT, - Opcode::RAW_FRAGMENT - ); - } + $this->handleRawFragment($parser, $parserStatus, $token, $state, $codeLength); break; case Compiler::PHVOLT_T_SET: - if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { - $this->createErrorMessage( - $parserStatus, - 'Child templates only may contain blocks' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $parser->phvolt_(Opcode::SET->value); + $this->handleSet($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_ASSIGN: @@ -466,41 +342,19 @@ public function parseView(string $templatePath): array break; case Compiler::PHVOLT_T_BLOCK: - if ($state->getBlockLevel() > 0) { - $this->createErrorMessage( - $parserStatus, - 'Embedding blocks into other blocks is not supported' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $state->incrementBlockLevel(); - $parser->phvolt_(Opcode::BLOCK->value); + $this->handleBlock($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_ENDBLOCK: - $state->decrementBlockLevel(); - $parser->phvolt_(Opcode::ENDBLOCK->value); + $this->handleEndblock($parser, $state); break; case Compiler::PHVOLT_T_MACRO: - if ($state->getMacroLevel() > 0) { - $this->createErrorMessage( - $parserStatus, - 'Embedding macros into other macros is not allowed' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $state->incrementMacroLevel(); - $parser->phvolt_(Opcode::MACRO->value); + $this->handleMacro($parser, $parserStatus, $state); break; case Compiler::PHVOLT_T_ENDMACRO: - $state->decrementMacroLevel(); - $parser->phvolt_(Opcode::ENDMACRO->value); + $this->handleEndmacro($parser, $state); break; case Compiler::PHVOLT_T_CALL: @@ -520,13 +374,11 @@ public function parseView(string $templatePath): array break; case Compiler::PHVOLT_T_RAW: - $parser->phvolt_(Opcode::RAW->value); - $state->incrementForcedRawState(); + $this->handleRaw($parser, $state); break; case Compiler::PHVOLT_T_ENDRAW: - $parser->phvolt_(Opcode::ENDRAW->value); - $state->decrementForcedRawState(); + $this->handleEndraw($parser, $state); break; case Compiler::PHVOLT_T_INCLUDE: @@ -590,25 +442,11 @@ public function parseView(string $templatePath): array break; case Compiler::PHVOLT_T_EXTENDS: - if ($state->getStatementPosition() !== 1) { - $this->createErrorMessage( - $parserStatus, - 'Extends statement must be placed at the first line in the template' - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); - break; - } - - $state->setExtendsMode(1); - $parser->phvolt_(Opcode::EXTENDS->value); + $this->handleExtends($parser, $parserStatus, $state); break; default: - $this->createErrorMessage( - $parserStatus, - sprintf('Scanner: unknown opcode %d', $opcode) - ); - $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + $this->handleUnknownOpcode($parserStatus, $opcode); break; } @@ -638,16 +476,10 @@ public function parseView(string $templatePath): array return $parser->getOutput(); } - /** - * @param Status $parserStatus - * @param string $message - * - * @return void - */ private function createErrorMessage(Status $parserStatus, string $message): void { $length = 128 + strlen($parserStatus->getState()->getActiveFile()); - $str = sprintf( + $str = sprintf( "%s in %s on line %d", $message, $parserStatus->getState()->getActiveFile(), @@ -660,43 +492,282 @@ private function createErrorMessage(Status $parserStatus, string $message): void private function createScannerErrorMessage(Status $parserStatus): string { $state = $parserStatus->getState(); + if ($state->getStartLength() > 0) { if ($state->getStartLength() > 16) { $part = substr($state->getRawBuffer(), $state->getCursor(), 16); - $error = sprintf( + + return sprintf( "Scanning error before '%s...' in %s on line %d", $part, $state->getActiveFile(), $state->getActiveLine(), ); - } else { - $error = sprintf( - "Scanning error before '%s' in %s on line %d", - $state->getStart(), - $state->getActiveFile(), - $state->getActiveLine(), - ); } - } else { - $error = sprintf( - "Scanning error near to EOF in %s", + + return sprintf( + "Scanning error before '%s' in %s on line %d", + $state->getStart(), $state->getActiveFile(), + $state->getActiveLine(), ); } - return $error; + return sprintf( + "Scanning error near to EOF in %s", + $state->getActiveFile(), + ); } - /** - * @param Token $token - * - * @return bool - */ - private function phvoltIsBlankString(Token $token): bool + private function handleBlock(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getBlockLevel() > 0) { + $this->createErrorMessage($parserStatus, 'Embedding blocks into other blocks is not supported'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $state->incrementBlockLevel(); + $parser->phvolt_(Opcode::BLOCK->value); + } + + private function handleCase(phvolt_Parser $parser, Status $parserStatus): void + { + if ($parserStatus->getState()->getSwitchLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Unexpected CASE'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $parser->phvolt_(Opcode::CASE->value); + } + + private function handleDefault( + phvolt_Parser $parser, + Status $parserStatus, + Token $token, + State $state + ): void { + if ($state->getSwitchLevel() !== 0) { + $parser->phvolt_(Opcode::DEFAULT->value); + + return; + } + + $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_IDENTIFIER, Opcode::IDENTIFIER); + } + + private function handleElseif(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getIfLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Unexpected ENDIF'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $parser->phvolt_(Opcode::ELSEIF->value); + } + + private function handleEndblock(phvolt_Parser $parser, State $state): void + { + $state->decrementBlockLevel(); + $parser->phvolt_(Opcode::ENDBLOCK->value); + } + + private function handleEndfor(phvolt_Parser $parser, State $state): void + { + $state->decrementBlockLevel(); + $state->decrementForLevel(); + $state->setIfLevel($state->getOldIfLevel()); + $parser->phvolt_(Opcode::ENDFOR->value); + } + + private function handleEndif(phvolt_Parser $parser, State $state): void + { + $state->decrementBlockLevel(); + $state->decrementIfLevel(); + $parser->phvolt_(Opcode::ENDIF->value); + } + + private function handleEndmacro(phvolt_Parser $parser, State $state): void + { + $state->decrementMacroLevel(); + $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) { + $this->createErrorMessage($parserStatus, 'Unexpected ENDSWITCH'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $state->decrementBlockLevel(); + $state->setSwitchLevel(0); + $parser->phvolt_(Opcode::ENDSWITCH->value); + } + + private function handleExtends(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getStatementPosition() !== 1) { + $this->createErrorMessage( + $parserStatus, + 'Extends statement must be placed at the first line in the template' + ); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $state->setExtendsMode(1); + $parser->phvolt_(Opcode::EXTENDS->value); + } + + private function handleFor(phvolt_Parser $parser, Status $parserStatus, State $state): void { - /** @var string $marker */ - $marker = $token->value ?? ''; - $len = strlen($marker); + if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Child templates only may contain blocks'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $state->setOldIfLevel($state->getIfLevel()); + $state->setIfLevel(0); + $state->incrementForLevel(); + $state->incrementBlockLevel(); + $parser->phvolt_(Opcode::FOR->value); + } + + private function handleIf(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Child templates only may contain blocks'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $state->incrementIfLevel(); + $state->incrementBlockLevel(); + $parser->phvolt_(Opcode::IF->value); + } + + private function handleMacro(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getMacroLevel() > 0) { + $this->createErrorMessage($parserStatus, 'Embedding macros into other macros is not allowed'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $state->incrementMacroLevel(); + $parser->phvolt_(Opcode::MACRO->value); + } + + private function handleOpenEdelimiter(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Child templates only may contain blocks'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $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, + Token $token, + State $state, + int $codeLength + ): void { + if ($token->length === 0) { + return; + } + + $value = trim((string)$token->value); + + if ($value !== '' && $state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Child templates only may contain blocks'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + if (!$this->isBlankString($token)) { + $state->incrementStatementPosition(); + } + + $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_RAW_FRAGMENT, Opcode::RAW_FRAGMENT); + } + + private function handleSet(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Child templates only may contain blocks'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $parser->phvolt_(Opcode::SET->value); + } + + private function handleSwitch(phvolt_Parser $parser, Status $parserStatus, State $state): void + { + if ($state->getExtendsMode() === 1 && $state->getBlockLevel() === 0) { + $this->createErrorMessage($parserStatus, 'Child templates only may contain blocks'); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + if ($state->getSwitchLevel() > 0) { + $this->createErrorMessage( + $parserStatus, + 'A nested switch detected. There is no nested switch-case statements support' + ); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + + return; + } + + $state->setSwitchLevel(1); + $state->incrementBlockLevel(); + $parser->phvolt_(Opcode::SWITCH->value); + } + + private function handleUnknownOpcode(Status $parserStatus, int $opcode): void + { + $this->createErrorMessage($parserStatus, sprintf('Scanner: unknown opcode %d', $opcode)); + $parserStatus->setStatus(Status::PHVOLT_PARSING_FAILED); + } + + private function isBlankString(Token $token): bool + { + $marker = (string)$token->value; + $len = strlen($marker); for ($i = 0; $i < $len; $i++) { $ch = $marker[$i]; @@ -708,11 +779,9 @@ private function phvoltIsBlankString(Token $token): bool return true; } - private function phvoltParseWithToken(phvolt_Parser $parser, int $opcode, Opcode $parserCode): void + private function parseWithToken(phvolt_Parser $parser, Token $token, int $opcode, Opcode $parserCode): void { - $newToken = new Token($opcode, $this->token?->value); - - $this->token = $newToken; + $newToken = new Token($opcode, $token->value); $parser->phvolt_($parserCode->value, $newToken); } From 6b41e0f9e00b3540086ffc7247c65265239ce5a9 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 10:08:01 -0500 Subject: [PATCH 04/10] refactor(parser): replace switch with match for opcode dispatch --- src/Parser/Parser.php | 463 +++++++++--------------------------------- 1 file changed, 99 insertions(+), 364 deletions(-) diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index ef3a912..fc6fbb6 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -85,370 +85,105 @@ public function parse(string $code, string $templatePath = ''): array $opcode = $token->opcode; $state->setActiveToken($opcode); - switch ($opcode) { - case Compiler::PHVOLT_T_IGNORE: - break; - - case Compiler::PHVOLT_T_ADD: - $parser->phvolt_(Opcode::PLUS->value); - break; - - case Compiler::PHVOLT_T_SUB: - $parser->phvolt_(Opcode::MINUS->value); - break; - - case Compiler::PHVOLT_T_MUL: - $parser->phvolt_(Opcode::TIMES->value); - break; - - case Compiler::PHVOLT_T_DIV: - $parser->phvolt_(Opcode::DIVIDE->value); - break; - - case Compiler::PHVOLT_T_MOD: - $parser->phvolt_(Opcode::MOD->value); - break; - - case Compiler::PHVOLT_T_AND: - $parser->phvolt_(Opcode::AND->value); - break; - - case Compiler::PHVOLT_T_OR: - $parser->phvolt_(Opcode::OR->value); - break; - - case Compiler::PHVOLT_T_IS: - $parser->phvolt_(Opcode::IS->value); - break; - - case Compiler::PHVOLT_T_EQUALS: - $parser->phvolt_(Opcode::EQUALS->value); - break; - - case Compiler::PHVOLT_T_NOTEQUALS: - $parser->phvolt_(Opcode::NOTEQUALS->value); - break; - - case Compiler::PHVOLT_T_LESS: - $parser->phvolt_(Opcode::LESS->value); - break; - - case Compiler::PHVOLT_T_GREATER: - $parser->phvolt_(Opcode::GREATER->value); - break; - - case Compiler::PHVOLT_T_GREATEREQUAL: - $parser->phvolt_(Opcode::GREATEREQUAL->value); - break; - - case Compiler::PHVOLT_T_LESSEQUAL: - $parser->phvolt_(Opcode::LESSEQUAL->value); - break; - - case Compiler::PHVOLT_T_IDENTICAL: - $parser->phvolt_(Opcode::IDENTICAL->value); - break; - - case Compiler::PHVOLT_T_NOTIDENTICAL: - $parser->phvolt_(Opcode::NOTIDENTICAL->value); - break; - - case Compiler::PHVOLT_T_NOT: - $parser->phvolt_(Opcode::NOT->value); - break; - - case Compiler::PHVOLT_T_DOT: - $parser->phvolt_(Opcode::DOT->value); - break; - - case Compiler::PHVOLT_T_CONCAT: - $parser->phvolt_(Opcode::CONCAT->value); - break; - - case Compiler::PHVOLT_T_RANGE: - $parser->phvolt_(Opcode::RANGE->value); - break; - - case Compiler::PHVOLT_T_PIPE: - $parser->phvolt_(Opcode::PIPE->value); - break; - - case Compiler::PHVOLT_T_COMMA: - $parser->phvolt_(Opcode::COMMA->value); - break; - - case Compiler::PHVOLT_T_COLON: - $parser->phvolt_(Opcode::COLON->value); - break; - - case Compiler::PHVOLT_T_QUESTION: - $parser->phvolt_(Opcode::QUESTION->value); - break; - - case Compiler::PHVOLT_T_PARENTHESES_OPEN: - $parser->phvolt_(Opcode::PARENTHESES_OPEN->value); - break; - - case Compiler::PHVOLT_T_PARENTHESES_CLOSE: - $parser->phvolt_(Opcode::PARENTHESES_CLOSE->value); - break; - - case Compiler::PHVOLT_T_SBRACKET_OPEN: - $parser->phvolt_(Opcode::SBRACKET_OPEN->value); - break; - - case Compiler::PHVOLT_T_SBRACKET_CLOSE: - $parser->phvolt_(Opcode::SBRACKET_CLOSE->value); - break; - - case Compiler::PHVOLT_T_CBRACKET_OPEN: - $parser->phvolt_(Opcode::CBRACKET_OPEN->value); - break; - - case Compiler::PHVOLT_T_CBRACKET_CLOSE: - $parser->phvolt_(Opcode::CBRACKET_CLOSE->value); - break; - - case Compiler::PHVOLT_T_OPEN_DELIMITER: - $parser->phvolt_(Opcode::OPEN_DELIMITER->value); - break; - - case Compiler::PHVOLT_T_CLOSE_DELIMITER: - $parser->phvolt_(Opcode::CLOSE_DELIMITER->value); - break; - - case Compiler::PHVOLT_T_OPEN_EDELIMITER: - $this->handleOpenEdelimiter($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_CLOSE_EDELIMITER: - $parser->phvolt_(Opcode::CLOSE_EDELIMITER->value); - break; - - case Compiler::PHVOLT_T_NULL: - $parser->phvolt_(Opcode::NULL->value); - break; - - case Compiler::PHVOLT_T_TRUE: - $parser->phvolt_(Opcode::TRUE->value); - break; - - case Compiler::PHVOLT_T_FALSE: - $parser->phvolt_(Opcode::FALSE->value); - break; - - case Compiler::PHVOLT_T_INTEGER: - $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_INTEGER, Opcode::INTEGER); - break; - - case Compiler::PHVOLT_T_DOUBLE: - $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_DOUBLE, Opcode::DOUBLE); - break; - - case Compiler::PHVOLT_T_STRING: - $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_STRING, Opcode::STRING); - break; - - case Compiler::PHVOLT_T_IDENTIFIER: - $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_IDENTIFIER, Opcode::IDENTIFIER); - break; - - case Compiler::PHVOLT_T_IF: - $this->handleIf($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_ELSE: - if ($state->getIfLevel() === 0 && $state->getForLevel() > 0) { - $parser->phvolt_(Opcode::ELSEFOR->value); - } else { - $parser->phvolt_(Opcode::ELSE->value); - } - break; - - case Compiler::PHVOLT_T_ELSEFOR: - $parser->phvolt_(Opcode::ELSEFOR->value); - break; - - case Compiler::PHVOLT_T_ELSEIF: - $this->handleElseif($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_ENDIF: - $this->handleEndif($parser, $state); - break; - - case Compiler::PHVOLT_T_FOR: - $this->handleFor($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_IN: - $parser->phvolt_(Opcode::IN->value); - break; - - case Compiler::PHVOLT_T_ENDFOR: - $this->handleEndfor($parser, $state); - break; - - case Compiler::PHVOLT_T_SWITCH: - $this->handleSwitch($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_CASE: - $this->handleCase($parser, $parserStatus); - break; - - case Compiler::PHVOLT_T_DEFAULT: - $this->handleDefault($parser, $parserStatus, $token, $state); - break; - - case Compiler::PHVOLT_T_ENDSWITCH: - $this->handleEndswitch($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_RAW_FRAGMENT: - $this->handleRawFragment($parser, $parserStatus, $token, $state, $codeLength); - break; - - case Compiler::PHVOLT_T_SET: - $this->handleSet($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_ASSIGN: - $parser->phvolt_(Opcode::ASSIGN->value); - break; - - case Compiler::PHVOLT_T_ADD_ASSIGN: - $parser->phvolt_(Opcode::ADD_ASSIGN->value); - break; - - case Compiler::PHVOLT_T_SUB_ASSIGN: - $parser->phvolt_(Opcode::SUB_ASSIGN->value); - break; - - case Compiler::PHVOLT_T_MUL_ASSIGN: - $parser->phvolt_(Opcode::MUL_ASSIGN->value); - break; - - case Compiler::PHVOLT_T_DIV_ASSIGN: - $parser->phvolt_(Opcode::DIV_ASSIGN->value); - break; - - case Compiler::PHVOLT_T_INCR: - $parser->phvolt_(Opcode::INCR->value); - break; - - case Compiler::PHVOLT_T_DECR: - $parser->phvolt_(Opcode::DECR->value); - break; - - case Compiler::PHVOLT_T_BLOCK: - $this->handleBlock($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_ENDBLOCK: - $this->handleEndblock($parser, $state); - break; - - case Compiler::PHVOLT_T_MACRO: - $this->handleMacro($parser, $parserStatus, $state); - break; - - case Compiler::PHVOLT_T_ENDMACRO: - $this->handleEndmacro($parser, $state); - break; - - case Compiler::PHVOLT_T_CALL: - $parser->phvolt_(Opcode::CALL->value); - break; - - case Compiler::PHVOLT_T_ENDCALL: - $parser->phvolt_(Opcode::ENDCALL->value); - break; - - case Compiler::PHVOLT_T_CACHE: - $parser->phvolt_(Opcode::CACHE->value); - break; - - case Compiler::PHVOLT_T_ENDCACHE: - $parser->phvolt_(Opcode::ENDCACHE->value); - break; - - case Compiler::PHVOLT_T_RAW: - $this->handleRaw($parser, $state); - break; - - case Compiler::PHVOLT_T_ENDRAW: - $this->handleEndraw($parser, $state); - break; - - case Compiler::PHVOLT_T_INCLUDE: - $parser->phvolt_(Opcode::INCLUDE->value); - break; - - case Compiler::PHVOLT_T_WITH: - $parser->phvolt_(Opcode::WITH->value); - break; - - case Compiler::PHVOLT_T_DEFINED: - $parser->phvolt_(Opcode::DEFINED->value); - break; - - case Compiler::PHVOLT_T_EMPTY: - $parser->phvolt_(Opcode::EMPTY->value); - break; - - case Compiler::PHVOLT_T_EVEN: - $parser->phvolt_(Opcode::EVEN->value); - break; - - case Compiler::PHVOLT_T_ODD: - $parser->phvolt_(Opcode::ODD->value); - break; - - case Compiler::PHVOLT_T_NUMERIC: - $parser->phvolt_(Opcode::NUMERIC->value); - break; - - case Compiler::PHVOLT_T_SCALAR: - $parser->phvolt_(Opcode::SCALAR->value); - break; - - case Compiler::PHVOLT_T_ITERABLE: - $parser->phvolt_(Opcode::ITERABLE->value); - break; - - case Compiler::PHVOLT_T_DO: - $parser->phvolt_(Opcode::DO->value); - break; - - case Compiler::PHVOLT_T_RETURN: - $parser->phvolt_(Opcode::RETURN->value); - break; - - case Compiler::PHVOLT_T_AUTOESCAPE: - $parser->phvolt_(Opcode::AUTOESCAPE->value); - break; - - case Compiler::PHVOLT_T_ENDAUTOESCAPE: - $parser->phvolt_(Opcode::ENDAUTOESCAPE->value); - break; - - case Compiler::PHVOLT_T_BREAK: - $parser->phvolt_(Opcode::BREAK->value); - break; - - case Compiler::PHVOLT_T_CONTINUE: - $parser->phvolt_(Opcode::CONTINUE->value); - break; - - case Compiler::PHVOLT_T_EXTENDS: - $this->handleExtends($parser, $parserStatus, $state); - break; - - default: - $this->handleUnknownOpcode($parserStatus, $opcode); - break; - } + match ($opcode) { + Compiler::PHVOLT_T_IGNORE => null, + Compiler::PHVOLT_T_ADD => $parser->phvolt_(Opcode::PLUS->value), + Compiler::PHVOLT_T_SUB => $parser->phvolt_(Opcode::MINUS->value), + Compiler::PHVOLT_T_MUL => $parser->phvolt_(Opcode::TIMES->value), + Compiler::PHVOLT_T_DIV => $parser->phvolt_(Opcode::DIVIDE->value), + Compiler::PHVOLT_T_MOD => $parser->phvolt_(Opcode::MOD->value), + Compiler::PHVOLT_T_AND => $parser->phvolt_(Opcode::AND->value), + Compiler::PHVOLT_T_OR => $parser->phvolt_(Opcode::OR->value), + Compiler::PHVOLT_T_IS => $parser->phvolt_(Opcode::IS->value), + Compiler::PHVOLT_T_EQUALS => $parser->phvolt_(Opcode::EQUALS->value), + Compiler::PHVOLT_T_NOTEQUALS => $parser->phvolt_(Opcode::NOTEQUALS->value), + Compiler::PHVOLT_T_LESS => $parser->phvolt_(Opcode::LESS->value), + Compiler::PHVOLT_T_GREATER => $parser->phvolt_(Opcode::GREATER->value), + Compiler::PHVOLT_T_GREATEREQUAL => $parser->phvolt_(Opcode::GREATEREQUAL->value), + Compiler::PHVOLT_T_LESSEQUAL => $parser->phvolt_(Opcode::LESSEQUAL->value), + Compiler::PHVOLT_T_IDENTICAL => $parser->phvolt_(Opcode::IDENTICAL->value), + Compiler::PHVOLT_T_NOTIDENTICAL => $parser->phvolt_(Opcode::NOTIDENTICAL->value), + Compiler::PHVOLT_T_NOT => $parser->phvolt_(Opcode::NOT->value), + Compiler::PHVOLT_T_DOT => $parser->phvolt_(Opcode::DOT->value), + Compiler::PHVOLT_T_CONCAT => $parser->phvolt_(Opcode::CONCAT->value), + Compiler::PHVOLT_T_RANGE => $parser->phvolt_(Opcode::RANGE->value), + Compiler::PHVOLT_T_PIPE => $parser->phvolt_(Opcode::PIPE->value), + Compiler::PHVOLT_T_COMMA => $parser->phvolt_(Opcode::COMMA->value), + Compiler::PHVOLT_T_COLON => $parser->phvolt_(Opcode::COLON->value), + Compiler::PHVOLT_T_QUESTION => $parser->phvolt_(Opcode::QUESTION->value), + Compiler::PHVOLT_T_PARENTHESES_OPEN => $parser->phvolt_(Opcode::PARENTHESES_OPEN->value), + Compiler::PHVOLT_T_PARENTHESES_CLOSE => $parser->phvolt_(Opcode::PARENTHESES_CLOSE->value), + Compiler::PHVOLT_T_SBRACKET_OPEN => $parser->phvolt_(Opcode::SBRACKET_OPEN->value), + Compiler::PHVOLT_T_SBRACKET_CLOSE => $parser->phvolt_(Opcode::SBRACKET_CLOSE->value), + Compiler::PHVOLT_T_CBRACKET_OPEN => $parser->phvolt_(Opcode::CBRACKET_OPEN->value), + Compiler::PHVOLT_T_CBRACKET_CLOSE => $parser->phvolt_(Opcode::CBRACKET_CLOSE->value), + Compiler::PHVOLT_T_OPEN_DELIMITER => $parser->phvolt_(Opcode::OPEN_DELIMITER->value), + Compiler::PHVOLT_T_CLOSE_DELIMITER => $parser->phvolt_(Opcode::CLOSE_DELIMITER->value), + Compiler::PHVOLT_T_OPEN_EDELIMITER => $this->handleOpenEdelimiter($parser, $parserStatus, $state), + Compiler::PHVOLT_T_CLOSE_EDELIMITER => $parser->phvolt_(Opcode::CLOSE_EDELIMITER->value), + Compiler::PHVOLT_T_NULL => $parser->phvolt_(Opcode::NULL->value), + Compiler::PHVOLT_T_TRUE => $parser->phvolt_(Opcode::TRUE->value), + Compiler::PHVOLT_T_FALSE => $parser->phvolt_(Opcode::FALSE->value), + Compiler::PHVOLT_T_INTEGER + => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_INTEGER, Opcode::INTEGER), + Compiler::PHVOLT_T_DOUBLE + => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_DOUBLE, Opcode::DOUBLE), + Compiler::PHVOLT_T_STRING + => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_STRING, Opcode::STRING), + Compiler::PHVOLT_T_IDENTIFIER + => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_IDENTIFIER, Opcode::IDENTIFIER), + Compiler::PHVOLT_T_IF => $this->handleIf($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ELSE => $state->getIfLevel() === 0 && $state->getForLevel() > 0 + ? $parser->phvolt_(Opcode::ELSEFOR->value) + : $parser->phvolt_(Opcode::ELSE->value), + Compiler::PHVOLT_T_ELSEFOR => $parser->phvolt_(Opcode::ELSEFOR->value), + Compiler::PHVOLT_T_ELSEIF => $this->handleElseif($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ENDIF => $this->handleEndif($parser, $state), + Compiler::PHVOLT_T_FOR => $this->handleFor($parser, $parserStatus, $state), + Compiler::PHVOLT_T_IN => $parser->phvolt_(Opcode::IN->value), + Compiler::PHVOLT_T_ENDFOR => $this->handleEndfor($parser, $state), + Compiler::PHVOLT_T_SWITCH => $this->handleSwitch($parser, $parserStatus, $state), + Compiler::PHVOLT_T_CASE => $this->handleCase($parser, $parserStatus), + Compiler::PHVOLT_T_DEFAULT => $this->handleDefault($parser, $parserStatus, $token, $state), + Compiler::PHVOLT_T_ENDSWITCH => $this->handleEndswitch($parser, $parserStatus, $state), + Compiler::PHVOLT_T_RAW_FRAGMENT + => $this->handleRawFragment($parser, $parserStatus, $token, $state, $codeLength), + Compiler::PHVOLT_T_SET => $this->handleSet($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ASSIGN => $parser->phvolt_(Opcode::ASSIGN->value), + Compiler::PHVOLT_T_ADD_ASSIGN => $parser->phvolt_(Opcode::ADD_ASSIGN->value), + Compiler::PHVOLT_T_SUB_ASSIGN => $parser->phvolt_(Opcode::SUB_ASSIGN->value), + Compiler::PHVOLT_T_MUL_ASSIGN => $parser->phvolt_(Opcode::MUL_ASSIGN->value), + Compiler::PHVOLT_T_DIV_ASSIGN => $parser->phvolt_(Opcode::DIV_ASSIGN->value), + Compiler::PHVOLT_T_INCR => $parser->phvolt_(Opcode::INCR->value), + Compiler::PHVOLT_T_DECR => $parser->phvolt_(Opcode::DECR->value), + Compiler::PHVOLT_T_BLOCK => $this->handleBlock($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ENDBLOCK => $this->handleEndblock($parser, $state), + Compiler::PHVOLT_T_MACRO => $this->handleMacro($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ENDMACRO => $this->handleEndmacro($parser, $state), + Compiler::PHVOLT_T_CALL => $parser->phvolt_(Opcode::CALL->value), + Compiler::PHVOLT_T_ENDCALL => $parser->phvolt_(Opcode::ENDCALL->value), + Compiler::PHVOLT_T_CACHE => $parser->phvolt_(Opcode::CACHE->value), + Compiler::PHVOLT_T_ENDCACHE => $parser->phvolt_(Opcode::ENDCACHE->value), + Compiler::PHVOLT_T_RAW => $this->handleRaw($parser, $state), + Compiler::PHVOLT_T_ENDRAW => $this->handleEndraw($parser, $state), + Compiler::PHVOLT_T_INCLUDE => $parser->phvolt_(Opcode::INCLUDE->value), + Compiler::PHVOLT_T_WITH => $parser->phvolt_(Opcode::WITH->value), + Compiler::PHVOLT_T_DEFINED => $parser->phvolt_(Opcode::DEFINED->value), + Compiler::PHVOLT_T_EMPTY => $parser->phvolt_(Opcode::EMPTY->value), + Compiler::PHVOLT_T_EVEN => $parser->phvolt_(Opcode::EVEN->value), + Compiler::PHVOLT_T_ODD => $parser->phvolt_(Opcode::ODD->value), + Compiler::PHVOLT_T_NUMERIC => $parser->phvolt_(Opcode::NUMERIC->value), + Compiler::PHVOLT_T_SCALAR => $parser->phvolt_(Opcode::SCALAR->value), + Compiler::PHVOLT_T_ITERABLE => $parser->phvolt_(Opcode::ITERABLE->value), + Compiler::PHVOLT_T_DO => $parser->phvolt_(Opcode::DO->value), + Compiler::PHVOLT_T_RETURN => $parser->phvolt_(Opcode::RETURN->value), + Compiler::PHVOLT_T_AUTOESCAPE => $parser->phvolt_(Opcode::AUTOESCAPE->value), + Compiler::PHVOLT_T_ENDAUTOESCAPE => $parser->phvolt_(Opcode::ENDAUTOESCAPE->value), + Compiler::PHVOLT_T_BREAK => $parser->phvolt_(Opcode::BREAK->value), + Compiler::PHVOLT_T_CONTINUE => $parser->phvolt_(Opcode::CONTINUE->value), + Compiler::PHVOLT_T_EXTENDS => $this->handleExtends($parser, $parserStatus, $state), + default => $this->handleUnknownOpcode($parserStatus, $opcode), + }; if ($parserStatus->getStatus() !== Status::PHVOLT_PARSING_OK) { break; From 24d71536a5f0c74295506b5b2d8902a1a0a25860 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 14:55:25 -0500 Subject: [PATCH 05/10] refactor(parser): sort public methods alphabetically (parse before setters) --- src/Parser/Parser.php | 237 +++++++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 109 deletions(-) diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index fc6fbb6..4a4ee19 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -33,20 +33,6 @@ class Parser private string $debugFile = 'volt.txt'; - public function setDebug(bool $debug): static - { - $this->debug = $debug; - - return $this; - } - - public function setDebugFile(string $debugFile): static - { - $this->debugFile = $debugFile; - - return $this; - } - /** * @param string $code * @param string $templatePath @@ -86,103 +72,123 @@ public function parse(string $code, string $templatePath = ''): array $state->setActiveToken($opcode); match ($opcode) { - Compiler::PHVOLT_T_IGNORE => null, - Compiler::PHVOLT_T_ADD => $parser->phvolt_(Opcode::PLUS->value), - Compiler::PHVOLT_T_SUB => $parser->phvolt_(Opcode::MINUS->value), - Compiler::PHVOLT_T_MUL => $parser->phvolt_(Opcode::TIMES->value), - Compiler::PHVOLT_T_DIV => $parser->phvolt_(Opcode::DIVIDE->value), - Compiler::PHVOLT_T_MOD => $parser->phvolt_(Opcode::MOD->value), - Compiler::PHVOLT_T_AND => $parser->phvolt_(Opcode::AND->value), - Compiler::PHVOLT_T_OR => $parser->phvolt_(Opcode::OR->value), - Compiler::PHVOLT_T_IS => $parser->phvolt_(Opcode::IS->value), - Compiler::PHVOLT_T_EQUALS => $parser->phvolt_(Opcode::EQUALS->value), - Compiler::PHVOLT_T_NOTEQUALS => $parser->phvolt_(Opcode::NOTEQUALS->value), - Compiler::PHVOLT_T_LESS => $parser->phvolt_(Opcode::LESS->value), - Compiler::PHVOLT_T_GREATER => $parser->phvolt_(Opcode::GREATER->value), - Compiler::PHVOLT_T_GREATEREQUAL => $parser->phvolt_(Opcode::GREATEREQUAL->value), - Compiler::PHVOLT_T_LESSEQUAL => $parser->phvolt_(Opcode::LESSEQUAL->value), - Compiler::PHVOLT_T_IDENTICAL => $parser->phvolt_(Opcode::IDENTICAL->value), - Compiler::PHVOLT_T_NOTIDENTICAL => $parser->phvolt_(Opcode::NOTIDENTICAL->value), - Compiler::PHVOLT_T_NOT => $parser->phvolt_(Opcode::NOT->value), - Compiler::PHVOLT_T_DOT => $parser->phvolt_(Opcode::DOT->value), - Compiler::PHVOLT_T_CONCAT => $parser->phvolt_(Opcode::CONCAT->value), - Compiler::PHVOLT_T_RANGE => $parser->phvolt_(Opcode::RANGE->value), - Compiler::PHVOLT_T_PIPE => $parser->phvolt_(Opcode::PIPE->value), - Compiler::PHVOLT_T_COMMA => $parser->phvolt_(Opcode::COMMA->value), - Compiler::PHVOLT_T_COLON => $parser->phvolt_(Opcode::COLON->value), - Compiler::PHVOLT_T_QUESTION => $parser->phvolt_(Opcode::QUESTION->value), + Compiler::PHVOLT_T_IGNORE => null, + Compiler::PHVOLT_T_ADD => $parser->phvolt_(Opcode::PLUS->value), + Compiler::PHVOLT_T_SUB => $parser->phvolt_(Opcode::MINUS->value), + Compiler::PHVOLT_T_MUL => $parser->phvolt_(Opcode::TIMES->value), + Compiler::PHVOLT_T_DIV => $parser->phvolt_(Opcode::DIVIDE->value), + Compiler::PHVOLT_T_MOD => $parser->phvolt_(Opcode::MOD->value), + Compiler::PHVOLT_T_AND => $parser->phvolt_(Opcode::AND->value), + Compiler::PHVOLT_T_OR => $parser->phvolt_(Opcode::OR->value), + Compiler::PHVOLT_T_IS => $parser->phvolt_(Opcode::IS->value), + Compiler::PHVOLT_T_EQUALS => $parser->phvolt_(Opcode::EQUALS->value), + Compiler::PHVOLT_T_NOTEQUALS => $parser->phvolt_(Opcode::NOTEQUALS->value), + Compiler::PHVOLT_T_LESS => $parser->phvolt_(Opcode::LESS->value), + Compiler::PHVOLT_T_GREATER => $parser->phvolt_(Opcode::GREATER->value), + Compiler::PHVOLT_T_GREATEREQUAL => $parser->phvolt_(Opcode::GREATEREQUAL->value), + Compiler::PHVOLT_T_LESSEQUAL => $parser->phvolt_(Opcode::LESSEQUAL->value), + Compiler::PHVOLT_T_IDENTICAL => $parser->phvolt_(Opcode::IDENTICAL->value), + Compiler::PHVOLT_T_NOTIDENTICAL => $parser->phvolt_(Opcode::NOTIDENTICAL->value), + Compiler::PHVOLT_T_NOT => $parser->phvolt_(Opcode::NOT->value), + Compiler::PHVOLT_T_DOT => $parser->phvolt_(Opcode::DOT->value), + Compiler::PHVOLT_T_CONCAT => $parser->phvolt_(Opcode::CONCAT->value), + Compiler::PHVOLT_T_RANGE => $parser->phvolt_(Opcode::RANGE->value), + Compiler::PHVOLT_T_PIPE => $parser->phvolt_(Opcode::PIPE->value), + Compiler::PHVOLT_T_COMMA => $parser->phvolt_(Opcode::COMMA->value), + Compiler::PHVOLT_T_COLON => $parser->phvolt_(Opcode::COLON->value), + Compiler::PHVOLT_T_QUESTION => $parser->phvolt_(Opcode::QUESTION->value), Compiler::PHVOLT_T_PARENTHESES_OPEN => $parser->phvolt_(Opcode::PARENTHESES_OPEN->value), Compiler::PHVOLT_T_PARENTHESES_CLOSE => $parser->phvolt_(Opcode::PARENTHESES_CLOSE->value), - Compiler::PHVOLT_T_SBRACKET_OPEN => $parser->phvolt_(Opcode::SBRACKET_OPEN->value), - Compiler::PHVOLT_T_SBRACKET_CLOSE => $parser->phvolt_(Opcode::SBRACKET_CLOSE->value), - Compiler::PHVOLT_T_CBRACKET_OPEN => $parser->phvolt_(Opcode::CBRACKET_OPEN->value), - Compiler::PHVOLT_T_CBRACKET_CLOSE => $parser->phvolt_(Opcode::CBRACKET_CLOSE->value), - Compiler::PHVOLT_T_OPEN_DELIMITER => $parser->phvolt_(Opcode::OPEN_DELIMITER->value), - Compiler::PHVOLT_T_CLOSE_DELIMITER => $parser->phvolt_(Opcode::CLOSE_DELIMITER->value), - Compiler::PHVOLT_T_OPEN_EDELIMITER => $this->handleOpenEdelimiter($parser, $parserStatus, $state), - Compiler::PHVOLT_T_CLOSE_EDELIMITER => $parser->phvolt_(Opcode::CLOSE_EDELIMITER->value), - Compiler::PHVOLT_T_NULL => $parser->phvolt_(Opcode::NULL->value), - Compiler::PHVOLT_T_TRUE => $parser->phvolt_(Opcode::TRUE->value), - Compiler::PHVOLT_T_FALSE => $parser->phvolt_(Opcode::FALSE->value), - Compiler::PHVOLT_T_INTEGER - => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_INTEGER, Opcode::INTEGER), - Compiler::PHVOLT_T_DOUBLE - => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_DOUBLE, Opcode::DOUBLE), - Compiler::PHVOLT_T_STRING - => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_STRING, Opcode::STRING), - Compiler::PHVOLT_T_IDENTIFIER - => $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_IDENTIFIER, Opcode::IDENTIFIER), - Compiler::PHVOLT_T_IF => $this->handleIf($parser, $parserStatus, $state), - Compiler::PHVOLT_T_ELSE => $state->getIfLevel() === 0 && $state->getForLevel() > 0 + Compiler::PHVOLT_T_SBRACKET_OPEN => $parser->phvolt_(Opcode::SBRACKET_OPEN->value), + Compiler::PHVOLT_T_SBRACKET_CLOSE => $parser->phvolt_(Opcode::SBRACKET_CLOSE->value), + Compiler::PHVOLT_T_CBRACKET_OPEN => $parser->phvolt_(Opcode::CBRACKET_OPEN->value), + Compiler::PHVOLT_T_CBRACKET_CLOSE => $parser->phvolt_(Opcode::CBRACKET_CLOSE->value), + Compiler::PHVOLT_T_OPEN_DELIMITER => $parser->phvolt_(Opcode::OPEN_DELIMITER->value), + Compiler::PHVOLT_T_CLOSE_DELIMITER => $parser->phvolt_(Opcode::CLOSE_DELIMITER->value), + Compiler::PHVOLT_T_OPEN_EDELIMITER => $this->handleOpenEdelimiter($parser, $parserStatus, $state), + Compiler::PHVOLT_T_CLOSE_EDELIMITER => $parser->phvolt_(Opcode::CLOSE_EDELIMITER->value), + Compiler::PHVOLT_T_NULL => $parser->phvolt_(Opcode::NULL->value), + Compiler::PHVOLT_T_TRUE => $parser->phvolt_(Opcode::TRUE->value), + Compiler::PHVOLT_T_FALSE => $parser->phvolt_(Opcode::FALSE->value), + Compiler::PHVOLT_T_INTEGER => $this->parseWithToken( + $parser, + $token, + Compiler::PHVOLT_T_INTEGER, + Opcode::INTEGER + ), + Compiler::PHVOLT_T_DOUBLE => $this->parseWithToken( + $parser, + $token, + Compiler::PHVOLT_T_DOUBLE, + Opcode::DOUBLE + ), + Compiler::PHVOLT_T_STRING => $this->parseWithToken( + $parser, + $token, + Compiler::PHVOLT_T_STRING, + Opcode::STRING + ), + Compiler::PHVOLT_T_IDENTIFIER => $this->parseWithToken( + $parser, + $token, + Compiler::PHVOLT_T_IDENTIFIER, + Opcode::IDENTIFIER + ), + Compiler::PHVOLT_T_IF => $this->handleIf($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ELSE => $state->getIfLevel() === 0 && $state->getForLevel() > 0 ? $parser->phvolt_(Opcode::ELSEFOR->value) : $parser->phvolt_(Opcode::ELSE->value), - Compiler::PHVOLT_T_ELSEFOR => $parser->phvolt_(Opcode::ELSEFOR->value), - Compiler::PHVOLT_T_ELSEIF => $this->handleElseif($parser, $parserStatus, $state), - Compiler::PHVOLT_T_ENDIF => $this->handleEndif($parser, $state), - Compiler::PHVOLT_T_FOR => $this->handleFor($parser, $parserStatus, $state), - Compiler::PHVOLT_T_IN => $parser->phvolt_(Opcode::IN->value), - Compiler::PHVOLT_T_ENDFOR => $this->handleEndfor($parser, $state), - Compiler::PHVOLT_T_SWITCH => $this->handleSwitch($parser, $parserStatus, $state), - Compiler::PHVOLT_T_CASE => $this->handleCase($parser, $parserStatus), - Compiler::PHVOLT_T_DEFAULT => $this->handleDefault($parser, $parserStatus, $token, $state), - Compiler::PHVOLT_T_ENDSWITCH => $this->handleEndswitch($parser, $parserStatus, $state), - Compiler::PHVOLT_T_RAW_FRAGMENT - => $this->handleRawFragment($parser, $parserStatus, $token, $state, $codeLength), - Compiler::PHVOLT_T_SET => $this->handleSet($parser, $parserStatus, $state), - Compiler::PHVOLT_T_ASSIGN => $parser->phvolt_(Opcode::ASSIGN->value), - Compiler::PHVOLT_T_ADD_ASSIGN => $parser->phvolt_(Opcode::ADD_ASSIGN->value), - Compiler::PHVOLT_T_SUB_ASSIGN => $parser->phvolt_(Opcode::SUB_ASSIGN->value), - Compiler::PHVOLT_T_MUL_ASSIGN => $parser->phvolt_(Opcode::MUL_ASSIGN->value), - Compiler::PHVOLT_T_DIV_ASSIGN => $parser->phvolt_(Opcode::DIV_ASSIGN->value), - Compiler::PHVOLT_T_INCR => $parser->phvolt_(Opcode::INCR->value), - Compiler::PHVOLT_T_DECR => $parser->phvolt_(Opcode::DECR->value), - Compiler::PHVOLT_T_BLOCK => $this->handleBlock($parser, $parserStatus, $state), - Compiler::PHVOLT_T_ENDBLOCK => $this->handleEndblock($parser, $state), - Compiler::PHVOLT_T_MACRO => $this->handleMacro($parser, $parserStatus, $state), - Compiler::PHVOLT_T_ENDMACRO => $this->handleEndmacro($parser, $state), - Compiler::PHVOLT_T_CALL => $parser->phvolt_(Opcode::CALL->value), - Compiler::PHVOLT_T_ENDCALL => $parser->phvolt_(Opcode::ENDCALL->value), - Compiler::PHVOLT_T_CACHE => $parser->phvolt_(Opcode::CACHE->value), - Compiler::PHVOLT_T_ENDCACHE => $parser->phvolt_(Opcode::ENDCACHE->value), - Compiler::PHVOLT_T_RAW => $this->handleRaw($parser, $state), - Compiler::PHVOLT_T_ENDRAW => $this->handleEndraw($parser, $state), - Compiler::PHVOLT_T_INCLUDE => $parser->phvolt_(Opcode::INCLUDE->value), - Compiler::PHVOLT_T_WITH => $parser->phvolt_(Opcode::WITH->value), - Compiler::PHVOLT_T_DEFINED => $parser->phvolt_(Opcode::DEFINED->value), - Compiler::PHVOLT_T_EMPTY => $parser->phvolt_(Opcode::EMPTY->value), - Compiler::PHVOLT_T_EVEN => $parser->phvolt_(Opcode::EVEN->value), - Compiler::PHVOLT_T_ODD => $parser->phvolt_(Opcode::ODD->value), - Compiler::PHVOLT_T_NUMERIC => $parser->phvolt_(Opcode::NUMERIC->value), - Compiler::PHVOLT_T_SCALAR => $parser->phvolt_(Opcode::SCALAR->value), - Compiler::PHVOLT_T_ITERABLE => $parser->phvolt_(Opcode::ITERABLE->value), - Compiler::PHVOLT_T_DO => $parser->phvolt_(Opcode::DO->value), - Compiler::PHVOLT_T_RETURN => $parser->phvolt_(Opcode::RETURN->value), - Compiler::PHVOLT_T_AUTOESCAPE => $parser->phvolt_(Opcode::AUTOESCAPE->value), - Compiler::PHVOLT_T_ENDAUTOESCAPE => $parser->phvolt_(Opcode::ENDAUTOESCAPE->value), - Compiler::PHVOLT_T_BREAK => $parser->phvolt_(Opcode::BREAK->value), - Compiler::PHVOLT_T_CONTINUE => $parser->phvolt_(Opcode::CONTINUE->value), - Compiler::PHVOLT_T_EXTENDS => $this->handleExtends($parser, $parserStatus, $state), - default => $this->handleUnknownOpcode($parserStatus, $opcode), + Compiler::PHVOLT_T_ELSEFOR => $parser->phvolt_(Opcode::ELSEFOR->value), + Compiler::PHVOLT_T_ELSEIF => $this->handleElseif($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ENDIF => $this->handleEndif($parser, $state), + Compiler::PHVOLT_T_FOR => $this->handleFor($parser, $parserStatus, $state), + Compiler::PHVOLT_T_IN => $parser->phvolt_(Opcode::IN->value), + Compiler::PHVOLT_T_ENDFOR => $this->handleEndfor($parser, $state), + Compiler::PHVOLT_T_SWITCH => $this->handleSwitch($parser, $parserStatus, $state), + Compiler::PHVOLT_T_CASE => $this->handleCase($parser, $parserStatus), + Compiler::PHVOLT_T_DEFAULT => $this->handleDefault($parser, $parserStatus, $token, $state), + Compiler::PHVOLT_T_ENDSWITCH => $this->handleEndswitch($parser, $parserStatus, $state), + Compiler::PHVOLT_T_RAW_FRAGMENT => $this->handleRawFragment( + $parser, + $parserStatus, + $token, + $state + ), + Compiler::PHVOLT_T_SET => $this->handleSet($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ASSIGN => $parser->phvolt_(Opcode::ASSIGN->value), + Compiler::PHVOLT_T_ADD_ASSIGN => $parser->phvolt_(Opcode::ADD_ASSIGN->value), + Compiler::PHVOLT_T_SUB_ASSIGN => $parser->phvolt_(Opcode::SUB_ASSIGN->value), + Compiler::PHVOLT_T_MUL_ASSIGN => $parser->phvolt_(Opcode::MUL_ASSIGN->value), + Compiler::PHVOLT_T_DIV_ASSIGN => $parser->phvolt_(Opcode::DIV_ASSIGN->value), + Compiler::PHVOLT_T_INCR => $parser->phvolt_(Opcode::INCR->value), + Compiler::PHVOLT_T_DECR => $parser->phvolt_(Opcode::DECR->value), + Compiler::PHVOLT_T_BLOCK => $this->handleBlock($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ENDBLOCK => $this->handleEndblock($parser, $state), + Compiler::PHVOLT_T_MACRO => $this->handleMacro($parser, $parserStatus, $state), + Compiler::PHVOLT_T_ENDMACRO => $this->handleEndmacro($parser, $state), + Compiler::PHVOLT_T_CALL => $parser->phvolt_(Opcode::CALL->value), + Compiler::PHVOLT_T_ENDCALL => $parser->phvolt_(Opcode::ENDCALL->value), + Compiler::PHVOLT_T_CACHE => $parser->phvolt_(Opcode::CACHE->value), + Compiler::PHVOLT_T_ENDCACHE => $parser->phvolt_(Opcode::ENDCACHE->value), + Compiler::PHVOLT_T_RAW => $this->handleRaw($parser, $state), + Compiler::PHVOLT_T_ENDRAW => $this->handleEndraw($parser, $state), + Compiler::PHVOLT_T_INCLUDE => $parser->phvolt_(Opcode::INCLUDE->value), + Compiler::PHVOLT_T_WITH => $parser->phvolt_(Opcode::WITH->value), + Compiler::PHVOLT_T_DEFINED => $parser->phvolt_(Opcode::DEFINED->value), + Compiler::PHVOLT_T_EMPTY => $parser->phvolt_(Opcode::EMPTY->value), + Compiler::PHVOLT_T_EVEN => $parser->phvolt_(Opcode::EVEN->value), + Compiler::PHVOLT_T_ODD => $parser->phvolt_(Opcode::ODD->value), + Compiler::PHVOLT_T_NUMERIC => $parser->phvolt_(Opcode::NUMERIC->value), + Compiler::PHVOLT_T_SCALAR => $parser->phvolt_(Opcode::SCALAR->value), + Compiler::PHVOLT_T_ITERABLE => $parser->phvolt_(Opcode::ITERABLE->value), + Compiler::PHVOLT_T_DO => $parser->phvolt_(Opcode::DO->value), + Compiler::PHVOLT_T_RETURN => $parser->phvolt_(Opcode::RETURN->value), + Compiler::PHVOLT_T_AUTOESCAPE => $parser->phvolt_(Opcode::AUTOESCAPE->value), + Compiler::PHVOLT_T_ENDAUTOESCAPE => $parser->phvolt_(Opcode::ENDAUTOESCAPE->value), + Compiler::PHVOLT_T_BREAK => $parser->phvolt_(Opcode::BREAK->value), + Compiler::PHVOLT_T_CONTINUE => $parser->phvolt_(Opcode::CONTINUE->value), + Compiler::PHVOLT_T_EXTENDS => $this->handleExtends($parser, $parserStatus, $state), + default => $this->handleUnknownOpcode($parserStatus, $opcode), }; if ($parserStatus->getStatus() !== Status::PHVOLT_PARSING_OK) { @@ -211,6 +217,20 @@ public function parse(string $code, string $templatePath = ''): array return $parser->getOutput(); } + public function setDebug(bool $debug): static + { + $this->debug = $debug; + + return $this; + } + + public function setDebugFile(string $debugFile): static + { + $this->debugFile = $debugFile; + + return $this; + } + private function createErrorMessage(Status $parserStatus, string $message): void { $length = 128 + strlen($parserStatus->getState()->getActiveFile()); @@ -434,8 +454,7 @@ private function handleRawFragment( phvolt_Parser $parser, Status $parserStatus, Token $token, - State $state, - int $codeLength + State $state ): void { if ($token->length === 0) { return; From 3e605224956f8226c91dac97351b4ba7c2a45bd3 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 15:05:26 -0500 Subject: [PATCH 06/10] refactor(parser): simplify parseWithToken to use token's own opcode, inline handleDefault relabelling --- src/Parser/Parser.php | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index 4a4ee19..a6a398b 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -110,30 +110,10 @@ public function parse(string $code, string $templatePath = ''): array Compiler::PHVOLT_T_NULL => $parser->phvolt_(Opcode::NULL->value), Compiler::PHVOLT_T_TRUE => $parser->phvolt_(Opcode::TRUE->value), Compiler::PHVOLT_T_FALSE => $parser->phvolt_(Opcode::FALSE->value), - Compiler::PHVOLT_T_INTEGER => $this->parseWithToken( - $parser, - $token, - Compiler::PHVOLT_T_INTEGER, - Opcode::INTEGER - ), - Compiler::PHVOLT_T_DOUBLE => $this->parseWithToken( - $parser, - $token, - Compiler::PHVOLT_T_DOUBLE, - Opcode::DOUBLE - ), - Compiler::PHVOLT_T_STRING => $this->parseWithToken( - $parser, - $token, - Compiler::PHVOLT_T_STRING, - Opcode::STRING - ), - Compiler::PHVOLT_T_IDENTIFIER => $this->parseWithToken( - $parser, - $token, - Compiler::PHVOLT_T_IDENTIFIER, - Opcode::IDENTIFIER - ), + Compiler::PHVOLT_T_INTEGER => $this->parseWithToken($parser, $token, Opcode::INTEGER), + Compiler::PHVOLT_T_DOUBLE => $this->parseWithToken($parser, $token, Opcode::DOUBLE), + Compiler::PHVOLT_T_STRING => $this->parseWithToken($parser, $token, Opcode::STRING), + Compiler::PHVOLT_T_IDENTIFIER => $this->parseWithToken($parser, $token, Opcode::IDENTIFIER), Compiler::PHVOLT_T_IF => $this->handleIf($parser, $parserStatus, $state), Compiler::PHVOLT_T_ELSE => $state->getIfLevel() === 0 && $state->getForLevel() > 0 ? $parser->phvolt_(Opcode::ELSEFOR->value) @@ -311,7 +291,8 @@ private function handleDefault( return; } - $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_IDENTIFIER, Opcode::IDENTIFIER); + $newToken = new Token(Compiler::PHVOLT_T_IDENTIFIER, $token->value); + $parser->phvolt_(Opcode::IDENTIFIER->value, $newToken); } private function handleElseif(phvolt_Parser $parser, Status $parserStatus, State $state): void @@ -473,7 +454,7 @@ private function handleRawFragment( $state->incrementStatementPosition(); } - $this->parseWithToken($parser, $token, Compiler::PHVOLT_T_RAW_FRAGMENT, Opcode::RAW_FRAGMENT); + $this->parseWithToken($parser, $token, Opcode::RAW_FRAGMENT); } private function handleSet(phvolt_Parser $parser, Status $parserStatus, State $state): void @@ -533,9 +514,9 @@ private function isBlankString(Token $token): bool return true; } - private function parseWithToken(phvolt_Parser $parser, Token $token, int $opcode, Opcode $parserCode): void + private function parseWithToken(phvolt_Parser $parser, Token $token, Opcode $parserCode): void { - $newToken = new Token($opcode, $token->value); + $newToken = new Token($token->opcode, $token->value); $parser->phvolt_($parserCode->value, $newToken); } From cd2073e49c61db38d58d88b44e5518170821a988 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 15:23:01 -0500 Subject: [PATCH 07/10] refactoring compiler; --- src/Compiler.php | 6 +- src/Scanner/Opcode.php | 246 ++++++++++++++++++++--------------------- 2 files changed, 126 insertions(+), 126 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index a5d7730..2622d7c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1385,8 +1385,8 @@ public function compileSource(string $viewCode, bool $extendsMode = false): stri $this->autoescape = (bool)$this->options['autoescape']; } - $parser = new Parser($viewCode); - $intermediate = $parser->parseView($this->currentPath); + $parser = new Parser(); + $intermediate = $parser->parse($viewCode, $this->currentPath ?? ''); $compilation = $this->statementList($intermediate, $extendsMode); /** @@ -2215,7 +2215,7 @@ public function getUniquePrefix(): string */ public function parse(string $viewCode): array { - return (new Parser($viewCode))->parseView("eval code"); + return (new Parser())->parse($viewCode, 'eval code'); } /** diff --git a/src/Scanner/Opcode.php b/src/Scanner/Opcode.php index b08b78a..fc68ebe 100644 --- a/src/Scanner/Opcode.php +++ b/src/Scanner/Opcode.php @@ -15,135 +15,135 @@ enum Opcode: int { - case ADD_ASSIGN = 46; - case AND = 6; - case ASSIGN = 45; - case AUTOESCAPE = 75; - case BLOCK = 64; - case BREAK = 77; - case CACHE = 66; - case CALL = 60; - case CASE = 42; - case CBRACKET_CLOSE = 88; - case CBRACKET_OPEN = 87; - case CLOSE_DELIMITER = 32; - case CLOSE_EDELIMITER = 63; - case COLON = 4; - case COMMA = 2; - case CONCAT = 23; - case CONTINUE = 78; - case DECR = 28; - case DEFAULT = 43; - case DEFINED = 80; - case DIVIDE = 18; - case DIV_ASSIGN = 49; - case DO = 73; - case DOT = 30; - case DOUBLE = 56; - case ELSE = 34; - case ELSEFOR = 36; - case ELSEIF = 35; - case EMPTY = 81; - case ENDAUTOESCAPE = 76; - case ENDBLOCK = 65; - case ENDCACHE = 67; - case ENDCALL = 61; - case ENDFOR = 39; - case ENDIF = 33; - case ENDMACRO = 53; - case ENDRAW = 69; - case ENDSWITCH = 41; - case EQUALS = 10; - case EVEN = 82; - case EXTENDS = 70; - case FALSE = 58; - case FOR = 37; - case GREATER = 13; - case GREATEREQUAL = 14; - case IDENTICAL = 16; - case IDENTIFIER = 38; - case IF = 31; - case IN = 8; - case INCLUDE = 71; - case INCR = 27; - case INTEGER = 54; - case IS = 9; - case ITERABLE = 86; - case LESS = 12; - case LESSEQUAL = 15; - case MACRO = 51; - case MINUS = 22; - case MOD = 20; - case MUL_ASSIGN = 48; - case NOT = 26; - case NOTEQUALS = 11; - case NOTIDENTICAL = 17; - case NULL = 57; - case NUMERIC = 84; - case ODD = 83; - case OPEN_DELIMITER = 1; - case OPEN_EDELIMITER = 62; - case OR = 7; + case ADD_ASSIGN = 46; + case AND = 6; + case ASSIGN = 45; + case AUTOESCAPE = 75; + case BLOCK = 64; + case BREAK = 77; + case CACHE = 66; + case CALL = 60; + case CASE = 42; + case CBRACKET_CLOSE = 88; + case CBRACKET_OPEN = 87; + case CLOSE_DELIMITER = 32; + case CLOSE_EDELIMITER = 63; + case COLON = 4; + case COMMA = 2; + case CONCAT = 23; + case CONTINUE = 78; + case DECR = 28; + case DEFAULT = 43; + case DEFINED = 80; + case DIVIDE = 18; + case DIV_ASSIGN = 49; + case DO = 73; + case DOT = 30; + case DOUBLE = 56; + case ELSE = 34; + case ELSEFOR = 36; + case ELSEIF = 35; + case EMPTY = 81; + case ENDAUTOESCAPE = 76; + case ENDBLOCK = 65; + case ENDCACHE = 67; + case ENDCALL = 61; + case ENDFOR = 39; + case ENDIF = 33; + case ENDMACRO = 53; + case ENDRAW = 69; + case ENDSWITCH = 41; + case EQUALS = 10; + case EVEN = 82; + case EXTENDS = 70; + case FALSE = 58; + case FOR = 37; + case GREATER = 13; + case GREATEREQUAL = 14; + case IDENTICAL = 16; + case IDENTIFIER = 38; + case IF = 31; + case IN = 8; + case INCLUDE = 71; + case INCR = 27; + case INTEGER = 54; + case IS = 9; + case ITERABLE = 86; + case LESS = 12; + case LESSEQUAL = 15; + case MACRO = 51; + case MINUS = 22; + case MOD = 20; + case MUL_ASSIGN = 48; + case NOT = 26; + case NOTEQUALS = 11; + case NOTIDENTICAL = 17; + case NULL = 57; + case NUMERIC = 84; + case ODD = 83; + case OPEN_DELIMITER = 1; + case OPEN_EDELIMITER = 62; + case OR = 7; case PARENTHESES_CLOSE = 52; - case PARENTHESES_OPEN = 29; - case PIPE = 25; - case PLUS = 21; - case QUESTION = 3; - case RANGE = 5; - case RAW = 68; - case RAW_FRAGMENT = 79; - case RETURN = 74; - case SBRACKET_CLOSE = 50; - case SBRACKET_OPEN = 24; - case SCALAR = 85; - case SET = 44; - case STRING = 55; - case SUB_ASSIGN = 47; - case SWITCH = 40; - case TIMES = 19; - case TRUE = 59; - case WITH = 72; + case PARENTHESES_OPEN = 29; + case PIPE = 25; + case PLUS = 21; + case QUESTION = 3; + case RANGE = 5; + case RAW = 68; + case RAW_FRAGMENT = 79; + case RETURN = 74; + case SBRACKET_CLOSE = 50; + case SBRACKET_OPEN = 24; + case SCALAR = 85; + case SET = 44; + case STRING = 55; + case SUB_ASSIGN = 47; + case SWITCH = 40; + case TIMES = 19; + case TRUE = 59; + case WITH = 72; public function label(): string { return match ($this) { - self::ADD_ASSIGN => '+=', - self::CBRACKET_CLOSE => '}', - self::CBRACKET_OPEN => '{', - self::CLOSE_DELIMITER => '%}', - self::CLOSE_EDELIMITER => '}}', - self::COLON => ':', - self::COMMA => ',', - self::CONCAT => '~', - self::DECR => '--', - self::DIVIDE => '/', - self::DIV_ASSIGN => '/=', - self::DOT => '.', - self::EQUALS => '=', - self::GREATER => '>', - self::GREATEREQUAL => '>=', - self::IDENTICAL => '===', - self::INCR => '++', - self::LESS => '<', - self::LESSEQUAL => '<=', - self::MINUS => '-', - self::MOD => '%', - self::MUL_ASSIGN => '*=', - self::NOT => '!', - self::NOTEQUALS => '!=', - self::NOTIDENTICAL => '!==', - self::OPEN_DELIMITER => '{%', - self::OPEN_EDELIMITER => '{{', + self::ADD_ASSIGN => '+=', + self::CBRACKET_CLOSE => '}', + self::CBRACKET_OPEN => '{', + self::CLOSE_DELIMITER => '%}', + self::CLOSE_EDELIMITER => '}}', + self::COLON => ':', + self::COMMA => ',', + self::CONCAT => '~', + self::DECR => '--', + self::DIVIDE => '/', + self::DIV_ASSIGN => '/=', + self::DOT => '.', + self::EQUALS => '=', + self::GREATER => '>', + self::GREATEREQUAL => '>=', + self::IDENTICAL => '===', + self::INCR => '++', + self::LESS => '<', + self::LESSEQUAL => '<=', + self::MINUS => '-', + self::MOD => '%', + self::MUL_ASSIGN => '*=', + self::NOT => '!', + self::NOTEQUALS => '!=', + self::NOTIDENTICAL => '!==', + self::OPEN_DELIMITER => '{%', + self::OPEN_EDELIMITER => '{{', self::PARENTHESES_CLOSE => ')', - self::PARENTHESES_OPEN => '(', - self::PIPE => '|', - self::PLUS => '+', - self::QUESTION => '?', - self::SBRACKET_CLOSE => ']', - self::SBRACKET_OPEN => '[', - self::SUB_ASSIGN => '-=', - self::TIMES => '*', - default => $this->name, + self::PARENTHESES_OPEN => '(', + self::PIPE => '|', + self::PLUS => '+', + self::QUESTION => '?', + self::SBRACKET_CLOSE => ']', + self::SBRACKET_OPEN => '[', + self::SUB_ASSIGN => '-=', + self::TIMES => '*', + default => $this->name, }; } } From 87011b9bce0124f152cb120eb49316d066111012 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 15:39:35 -0500 Subject: [PATCH 08/10] stan correction --- phpstan-baseline.neon | 6 ------ 1 file changed, 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3bd0684..dee77c4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -624,12 +624,6 @@ parameters: count: 1 path: src/Compiler.php - - - message: '#^Parameter \#1 \$templatePath of method Phalcon\\Volt\\Parser\\Parser\:\:parseView\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: src/Compiler.php - - message: '#^Parameter \#1 \$test of method Phalcon\\Volt\\Compiler\:\:resolveTest\(\) expects array, mixed given\.$#' identifier: argument.type From e91b248afd970365133fdbee7faf7056d6cee17f Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 15:39:48 -0500 Subject: [PATCH 09/10] aligning with cphalcon for error messages --- src/Parser/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index a6a398b..4df2ccb 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -40,7 +40,7 @@ class Parser * @return array * @throws Exception */ - public function parse(string $code, string $templatePath = ''): array + public function parse(string $code, string $templatePath = 'eval code'): array { if (strlen($code) === 0) { return []; From 86297a26e83480ca02873beb1e40eb398c19fc4d Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Mon, 13 Apr 2026 15:47:13 -0500 Subject: [PATCH 10/10] correcting test --- tests/unit/Parser/ParserTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/Parser/ParserTest.php b/tests/unit/Parser/ParserTest.php index 797c224..3cf5c25 100644 --- a/tests/unit/Parser/ParserTest.php +++ b/tests/unit/Parser/ParserTest.php @@ -120,7 +120,8 @@ public function testNestedSwitchThrowsException(): void public function testDebugMode(): void { - $debugFile = '/app/volt_debug_test.txt'; + $testsDir = dirname(__FILE__, 3); + $debugFile = $testsDir . '/_output/volt_debug_test.txt'; $parser = new Parser(); $parser->setDebug(true)->setDebugFile($debugFile);