Parsing Directive Parameters

Learn how to look to the next non-whitespace character and jump to its location.

We'll cover the following...

Parsing Blade directive parameters will be very similar to how we parse the directives themselves. The main difference is that we need a simple way to look to the next non-whitespace character and jump to its location. We can also implement this using a cursor; it will simply break when it encounters a non-whitespace character. Once we have located the first instance of a non-whitespace character, we can check if it is the ( character. If it is, we can make use of our existing PairedBracketCursor cursor to scan to the end of the directive’s argument group:

Press + to interact
<?php
// ...
class NonWhitespaceCursor extends AbstractStringCursor
{
public function accept(string $current,
?string $prev = null,
?string $next = null): ?bool
{
if (! ctype_space($current)) {
return $this->break();
}
return $this->continue();
}
}
class BladeDirectiveValidator
{
// ...
protected $nonWhitespaceCursor;
protected $pairedBracketCursor;
protected function parseArgs($offset)
{
$args = null;
$this->hasAdvancedIterator = false;
foreach ($this->string as $char) {
if (! $this->hasAdvancedIterator) {
$this->advance($offset);
continue;
}
// Get the argument string.
$result = $this->pairedBracketCursor->traverse();
$args = new ParsedArguments();
$this->fillStructureDetails($result, $args);
$args->content = $result->content;
$args->value = $result->value;
break;
}
return $args;
}
protected function parseDirective($offset)
{
$directive = null;
$this->hasAdvancedIterator = false;
foreach ($this->string as $char) {
if (!$this->hasAdvancedIterator) {
$this->advance($offset);
continue;
}
$result = $this->directiveCursor->traverse();
$directive = new ParsedDirective();
$this->fillStructureDetails($result, $directive);
if (str($result->value)->startsWith('@')) {
$directive->directiveName = str($result->value)
->substr(1)
->toString();
} else {
$directive->directiveName = $result->value;
}
if ($directive->endPosition + 1 < count($this->string)) {
$this->string->setIteratorPosition(
$directive->endPosition + 1,
$this->string->index()[$directive->endPosition + 1]
);
$nextNonWhitespace = $this->nonWhitespaceCursor
->traverse();
if (str($nextNonWhitespace->value)->endsWith('(')) {
$directive->args = $this->parseArgs(
$nextNonWhitespace->endPosition
);
}
}
break;
}
return $directive;
}
public function parse($value)
{
// ...
foreach ($htmlRegions as $region) {
$this->lineOffset = $region[2] - 1;
$this->string = new Utf8StringIterator($region[1]);
$this->locator = new CharacterPositionLocator($region[1]);
$this->directiveCursor = new BladeDirectiveCursor(
$this->string
);
$this->pairedBracketCursor = new PairedBracketCursor(
$this->string,
'(', ')'
);
$this->nonWhitespaceCursor = new NonWhitespaceCursor(
$this->string
);
}
return $this->structures;
}
}

The additions in the code above will feel similar to ...