/vendor/phug/phug/src/Phug/Lexer/Lexer/Scanner/InterpolationScanner.php
https://github.com/welaika/wordless · PHP · 167 lines · 121 code · 36 blank · 10 comment · 8 complexity · f1dce95d42b1a1c3118a1e252f9780c9 MD5 · raw file
- <?php
- /**
- * @example p Text #{'interpolation'} text
- */
- namespace Phug\Lexer\Scanner;
- use Phug\Lexer\ScannerInterface;
- use Phug\Lexer\State;
- use Phug\Lexer\Token\ExpressionToken;
- use Phug\Lexer\Token\InterpolationEndToken;
- use Phug\Lexer\Token\InterpolationStartToken;
- use Phug\Lexer\Token\NewLineToken;
- use Phug\Lexer\Token\TagInterpolationEndToken;
- use Phug\Lexer\Token\TagInterpolationStartToken;
- use Phug\Lexer\Token\TextToken;
- class InterpolationScanner implements ScannerInterface
- {
- protected $interpolationChars = [
- 'tagInterpolation' => ['[', ']'],
- 'interpolation' => ['{', '}'],
- ];
- protected $regExp;
- public function __construct()
- {
- $interpolations = [];
- $backIndex = 2;
- foreach ($this->interpolationChars as $name => list($start, $end)) {
- $start = preg_quote($start, '/');
- $end = preg_quote($end, '/');
- $interpolations[] = $start.'(?<'.$name.'>'.
- '(?>"(?:\\\\[\\S\\s]|[^"\\\\])*"|\'(?:\\\\[\\S\\s]|[^\'\\\\])*\'|[^'.
- $start.$end.
- '\'"]++|(?-'.$backIndex.'))*+'.
- ')'.$end;
- $backIndex++;
- }
- $this->regExp = '(?<text>.*?)'.
- '(?<!\\\\)'.
- '(?<escape>#|!(?='.preg_quote($this->interpolationChars['interpolation'][0], '/').'))'.
- '(?<wrap>'.implode('|', $interpolations).')';
- }
- protected function throwEndOfLineExceptionIf(State $state, $condition)
- {
- if ($condition) {
- $state->throwException('End of line was reached with no closing bracket for interpolation.');
- }
- }
- protected function scanTagInterpolation(State $state, $tagInterpolation)
- {
- /** @var TagInterpolationStartToken $start */
- $start = $state->createToken(TagInterpolationStartToken::class);
- /** @var TagInterpolationEndToken $end */
- $end = $state->createToken(TagInterpolationEndToken::class);
- $start->setEnd($end);
- $end->setStart($start);
- $lexer = $state->getLexer();
- yield $start;
- foreach ($lexer->lex($tagInterpolation) as $token) {
- $this->throwEndOfLineExceptionIf($state, $token instanceof NewLineToken);
- yield $token;
- }
- yield $end;
- }
- protected function scanExpressionInterpolation(State $state, $interpolation, $escape)
- {
- /** @var InterpolationStartToken $start */
- $start = $state->createToken(InterpolationStartToken::class);
- /** @var InterpolationEndToken $end */
- $end = $state->createToken(InterpolationEndToken::class);
- $start->setEnd($end);
- $end->setStart($start);
- /** @var ExpressionToken $token */
- $token = $state->createToken(ExpressionToken::class);
- $token->setValue($interpolation);
- if ($escape === '#') {
- $token->escape();
- }
- yield $start;
- yield $token;
- yield $end;
- }
- protected function scanInterpolation(State $state, $tagInterpolation, $interpolation, $escape)
- {
- $this->throwEndOfLineExceptionIf(
- $state,
- !$state->getOption('multiline_interpolation') && strpos($interpolation, "\n") !== false
- );
- if ($tagInterpolation) {
- return $this->scanTagInterpolation($state, $tagInterpolation);
- }
- return $this->scanExpressionInterpolation($state, $interpolation, $escape);
- }
- protected function needSeparationBlankLine(State $state)
- {
- $reader = $state->getReader();
- if (!$reader->peekNewLine()) {
- return false;
- }
- $indentWidth = $state->getIndentWidth();
- $indentation = $indentWidth > 0 ? $state->getIndentStyle().'{'.$state->getIndentWidth().'}' : '';
- return $reader->match('\n*'.$indentation.'\|');
- }
- public function scan(State $state)
- {
- $reader = $state->getReader();
- while ($reader->match($this->regExp)) {
- $text = $reader->getMatch('text');
- $text = preg_replace('/\\\\([#!]\\[|#{)/', '$1', $text);
- if (mb_strlen($text) > 0) {
- /** @var TextToken $token */
- $token = $state->createToken(TextToken::class);
- $token->setValue($text);
- yield $token;
- }
- foreach ($this->scanInterpolation(
- $state,
- $reader->getMatch('tagInterpolation'),
- $reader->getMatch('interpolation'),
- $reader->getMatch('escape')
- ) as $token) {
- yield $token;
- }
- $reader->consume();
- if ($this->needSeparationBlankLine($state)) {
- /** @var TextToken $token */
- $token = $state->createToken(TextToken::class);
- $token->setValue("\n");
- yield $token;
- }
- }
- }
- }