Skip to content

Commit

Permalink
TemplateLexer: exceptions are thrown in parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Sep 8, 2023
1 parent 915ebd7 commit 5b53e86
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 46 deletions.
4 changes: 0 additions & 4 deletions ncs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@
<exclude-pattern>*.php</exclude-pattern>
</rule>

<rule ref="Squiz.Operators.ValidLogicalOperators.NotAllowed">
<exclude-pattern>./src/Latte/Compiler/TemplateLexer.php</exclude-pattern>
</rule>

<rule ref="SlevomatCodingStandard.Namespaces.MultipleUsesPerLine.MultipleUsesPerLine">
<exclude-pattern>./src/Latte/Essential/Filters.php</exclude-pattern>
</rule>
Expand Down
49 changes: 26 additions & 23 deletions src/Latte/Compiler/TemplateLexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ private function statePlain(): \Generator

private function stateLatteTag(): \Generator
{
$pos = $this->states[0]['pos'];

yield from $this->match('~
(?<Slash>/)?
(?<Latte_Name> = | _(?!_) | [a-z]\w*+(?:[.:-]\w+)*+(?!::|\(|\\\\))? # name, /name, but not function( or class:: or namespace\
Expand All @@ -103,19 +101,19 @@ private function stateLatteTag(): \Generator
(?<Slash>/)?
(?<Latte_TagClose>' . $this->closeDelimiter . ')
(?<Newline>[ \t]*\R)?
~xsiAu')
or throw new CompileException('Unterminated Latte tag', $pos);
~xsiAu');
}


private function stateLatteComment(): \Generator
{
yield from $this->match('~
(?<Text>.+?)??
(?<Latte_CommentClose>\*' . $this->closeDelimiter . ')
(?<Newline>[ \t]*\R{1,2})?
~xsiAu')
or throw new CompileException('Unterminated Latte comment', $this->states[0]['pos']);
(
(?<Latte_CommentClose>\*' . $this->closeDelimiter . ')(?<Newline>[ \t]*\R{1,2})?|
$
)
~xsiAu');
}


Expand Down Expand Up @@ -155,22 +153,26 @@ private function stateHtmlTag(): \Generator
private function stateHtmlQuotedValue(string $quote): \Generator
{
yield from $this->match('~
(?<Text>.+?)??(
(?<Text>.+?)??
(
(?<Quote>' . $quote . ')|
(?<Latte_TagOpen>' . $this->openDelimiter . '(?!\*))| # {tag
(?<Latte_CommentOpen>' . $this->openDelimiter . '\*) # {* comment
(?<Latte_CommentOpen>' . $this->openDelimiter . '\*)| # {* comment
$
)
~xsiAu')
or throw new CompileException('Unterminated HTML attribute value', $this->states[0]['pos']);
~xsiAu');
}


private function stateHtmlQuotedNAttrValue(string $quote): \Generator
{
yield from $this->match('~
(?<Text>.+?)??(?<Quote>' . $quote . ')
~xsiAu')
or throw new CompileException('Unterminated n:attribute value', $this->states[0]['pos']);
(?<Text>.+?)??
(
(?<Quote>' . $quote . ')|
$
)
~xsiAu');
}


Expand All @@ -196,23 +198,24 @@ private function stateHtmlComment(): \Generator
(
(?<Html_CommentClose>-->)| # -->
(?<Indentation>(?<=\n|^)[ \t]+)?(?<Latte_TagOpen>' . $this->openDelimiter . '(?!\*))| # {tag
(?<Indentation>(?<=\n|^)[ \t]+)?(?<Latte_CommentOpen>' . $this->openDelimiter . '\*) # {* comment
(?<Indentation>(?<=\n|^)[ \t]+)?(?<Latte_CommentOpen>' . $this->openDelimiter . '\*)| # {* comment
$
)
~xsiAu')
or throw new CompileException('Unterminated HTML comment', $this->states[0]['pos']);
~xsiAu');
}


private function stateHtmlBogus(): \Generator
{
yield from $this->match('~
(?<Text>.+?)??(
(?<Text>.+?)??
(
(?<Html_TagClose>>)| # >
(?<Latte_TagOpen>' . $this->openDelimiter . '(?!\*))| # {tag
(?<Latte_CommentOpen>' . $this->openDelimiter . '\*) # {* comment
(?<Latte_CommentOpen>' . $this->openDelimiter . '\*)| # {* comment
$
)
~xsiAu')
or throw new CompileException('Unterminated HTML tag', $this->states[0]['pos']);
~xsiAu');
}


Expand Down Expand Up @@ -246,7 +249,7 @@ public function setContentType(string $type): static

public function setState(string $state, ...$args): void
{
$this->states[0] = ['name' => $state, 'args' => $args, 'pos' => $this->position];
$this->states[0] = ['name' => $state, 'args' => $args];
}


Expand Down
6 changes: 3 additions & 3 deletions src/Latte/Compiler/TemplateParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,10 @@ public function parseLatteComment(): Nodes\NopNode
if (str_ends_with($this->stream->peek(-1)?->text ?? "\n", "\n")) {
$this->lastIndentation ??= new Nodes\TextNode('');
}
$this->stream->consume(Token::Latte_CommentOpen);
$openToken = $this->stream->consume(Token::Latte_CommentOpen);
$this->lexer->pushState(TemplateLexer::StateLatteComment);
$this->stream->consume(Token::Text);
$this->stream->consume(Token::Latte_CommentClose);
$this->stream->tryConsume(Token::Latte_CommentClose) || $this->stream->throwUnexpectedException([Token::Latte_CommentClose], addendum: " started $openToken->position");
$this->lexer->popState();
return new Nodes\NopNode;
}
Expand Down Expand Up @@ -272,7 +272,7 @@ private function parseLatteTag(): Tag
location: $this->location,
htmlElement: $this->html->getElement(),
);
$stream->consume(Token::Latte_TagClose);
$stream->tryConsume(Token::Latte_TagClose) || $stream->throwUnexpectedException([Token::Latte_TagClose], addendum: " started $openToken->position");
$this->lexer->popState();
return $tag;
}
Expand Down
9 changes: 5 additions & 4 deletions src/Latte/Compiler/TemplateParserHtml.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ private function parseAttributeValue(): ?array
if ($quoteToken = $stream->tryConsume(Token::Quote)) {
$this->parser->getLexer()->setState(TemplateLexer::StateHtmlQuotedValue, $quoteToken->text);
$value = $this->parser->parseFragment([$this->parser, 'inTextResolve']);
$stream->consume(Token::Quote);
$stream->tryConsume(Token::Quote) || $stream->throwUnexpectedException([$quoteToken->text], addendum: ", end of HTML attribute started $quoteToken->position");
$this->parser->getLexer()->setState(TemplateLexer::StateHtmlTag);
return [$value, $quoteToken->text];
}
Expand Down Expand Up @@ -349,7 +349,7 @@ private function parseNAttribute(): Nodes\TextNode
$this->parser->getLexer()->setState(TemplateLexer::StateHtmlQuotedNAttrValue, $quoteToken->text);
$valueToken = $stream->tryConsume(Token::Text);
$pos = $stream->peek()->position;
$stream->consume(Token::Quote);
$stream->tryConsume(Token::Quote) || $stream->throwUnexpectedException([$quoteToken->text], addendum: ", end of n:attribute started $quoteToken->position");
$this->parser->getLexer()->setState(TemplateLexer::StateHtmlTag);
} else {
$valueToken = $stream->consume(Token::Html_Name);
Expand Down Expand Up @@ -381,11 +381,12 @@ private function parseComment(): Html\CommentNode
$this->parser->location = $this->parser::LocationTag;
$this->parser->getLexer()->setState(TemplateLexer::StateHtmlComment);
$stream = $this->parser->getStream();
$openToken = $stream->consume(Token::Html_CommentOpen);
$node = new Html\CommentNode(
position: $stream->consume(Token::Html_CommentOpen)->position,
position: $openToken->position,
content: $this->parser->parseFragment([$this->parser, 'inTextResolve']),
);
$stream->consume(Token::Html_CommentClose);
$stream->tryConsume(Token::Html_CommentClose) || $stream->throwUnexpectedException([Token::Html_CommentClose], addendum: " started $openToken->position");
$this->parser->getLexer()->setState(TemplateLexer::StateHtmlText);
$this->parser->location = $this->parser::LocationText;
return $node;
Expand Down
8 changes: 4 additions & 4 deletions src/Latte/Compiler/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,11 @@ final class Token
self::Equals => "'='",
self::Quote => 'quote',

self::Latte_TagOpen => "'{'",
self::Latte_TagClose => "'}'",
self::Latte_TagOpen => 'Latte tag',
self::Latte_TagClose => 'end of Latte tag',
self::Latte_Name => 'tag name',
self::Latte_CommentOpen => "'{*'",
self::Latte_CommentClose => "'*}'",
self::Latte_CommentOpen => 'Latte comment',
self::Latte_CommentClose => 'end of Latte comment',

self::Html_TagOpen => 'HTML tag',
self::Html_TagClose => 'end of HTML tag',
Expand Down
34 changes: 26 additions & 8 deletions tests/common/Compiler.errors.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,55 @@ $latte->setLoader(new Latte\Loaders\StringLoader);
Assert::exception(
fn() => $latte->compile('{'),
Latte\CompileException::class,
'Unterminated Latte tag (on line 1 at column 1)',
'Unexpected end, expecting end of Latte tag started on line 1 at column 1 (on line 1 at column 2)',
);

Assert::exception(
fn() => $latte->compile('{foo'),
Latte\CompileException::class,
'Unexpected end, expecting end of Latte tag started on line 1 at column 1 (on line 1 at column 5)',
);

Assert::exception(
fn() => $latte->compile("{* \n'abc}"),
Latte\CompileException::class,
'Unterminated Latte comment (on line 1 at column 1)',
'Unexpected end, expecting end of Latte comment started on line 1 at column 1 (on line 2 at column 6)',
);

Assert::exception(
fn() => $latte->compile('{syntax double} {{a'),
Latte\CompileException::class,
'Unexpected end, expecting end of Latte tag started on line 1 at column 17 (on line 1 at column 20)',
);

Assert::exception(
fn() => $latte->compile('{syntax double} {{a } b'),
Latte\CompileException::class,
"Unexpected '} b' (on line 1 at column 21)",
);

Assert::exception(
fn() => $latte->compile('<!'),
fn() => $latte->compile('<! foo'),
Latte\CompileException::class,
'Unterminated HTML tag (on line 1 at column 1)',
'Unexpected end, expecting end of HTML tag (on line 1 at column 7)',
);

Assert::exception(
fn() => $latte->compile("<a href='xx{* xx *}>"),
Latte\CompileException::class,
'Unterminated HTML attribute value (on line 1 at column 9)',
"Unexpected end, expecting ', end of HTML attribute started on line 1 at column 9 (on line 1 at column 21)",
);

Assert::exception(
fn() => $latte->compile("<a n:href='xx>"),
Latte\CompileException::class,
'Unterminated n:attribute value (on line 1 at column 11)',
"Unexpected end, expecting ', end of n:attribute started on line 1 at column 11 (on line 1 at column 15)",
);

Assert::exception(
fn() => $latte->compile('<!--'),
fn() => $latte->compile('<!-- xxx'),
Latte\CompileException::class,
'Unterminated HTML comment (on line 1 at column 1)',
'Unexpected end, expecting end of HTML comment started on line 1 at column 1 (on line 1 at column 9)',
);

Assert::exception(
Expand Down

0 comments on commit 5b53e86

Please sign in to comment.