Browse Source

Getting further with parsing

main
Dustin Wilson 3 years ago
parent
commit
6badf94277
  1. 24
      lib/Scope/Data.php
  2. 117
      lib/Scope/Parser.php

24
lib/Scope/Data.php

@ -9,37 +9,43 @@ namespace dW\Highlighter\Scope;
class Data { class Data {
protected array $data; protected array $data;
protected int $position = 0; protected int $_position = -1;
protected int $endPosition; protected int $endPosition;
public function __construct(string $data) { public function __construct(string $data) {
preg_match('/[LRB]:|[A-Za-z0-9-+_\*\.]+|[\,\|\-\(\)&]/', $data, $matches); preg_match_all('/[BLR]:|[A-Za-z0-9-+_\*\.]+|[\,\|\-\(\)&]/', $data, $matches);
$this->data = $matches[1] ?? []; $this->data = $matches[0] ?? [];
$this->endPosition = count($this->data); $this->endPosition = count($this->data);
} }
public function consume(): string|bool { public function consume(): string|bool {
if ($this->position === $this->endPosition) { if ($this->_position === $this->endPosition) {
return false; return false;
} }
return $this->data[$this->position++]; return $this->data[++$this->_position];
} }
public function peek(): string|bool { public function peek(): string|bool {
if ($this->position === $this->endPosition) { if ($this->_position === $this->endPosition) {
return false; return false;
} }
return $this->data[$this->position + 1]; return $this->data[$this->_position + 1];
} }
public function unconsume(): bool { public function unconsume(): bool {
if ($this->position < 0) { if ($this->_position < 0) {
return false; return false;
} }
$this->position--; $this->_position--;
return true; return true;
} }
public function __get(string $name) {
if ($name === 'position') {
return $this->_position;
}
}
} }

117
lib/Scope/Parser.php

@ -7,11 +7,12 @@ declare(strict_types=1);
namespace dW\Highlighter\Scope; namespace dW\Highlighter\Scope;
class Parser { class Parser {
public static bool $debug = false;
protected Data $data; protected Data $data;
protected int $debugCount = 1;
protected static Parser $instance; protected static Parser $instance;
protected static bool $debug = false;
protected const SCOPE_REGEX = '/^[A-Za-z0-9-+_\.\*]+$/S'; protected const SCOPE_REGEX = '/^[A-Za-z0-9-+_\.\*]+$/S';
@ -26,6 +27,10 @@ class Parser {
} }
protected static function parseComposite(): Matcher { protected static function parseComposite(): Matcher {
if (self::$debug) {
self::debug();
}
$result = self::parseExpression(); $result = self::parseExpression();
$peek = self::$instance->data->peek(); $peek = self::$instance->data->peek();
@ -56,52 +61,90 @@ class Parser {
$peek = self::$instance->data->peek(); $peek = self::$instance->data->peek();
} }
if (self::$debug) {
self::debugResult($result);
}
return $result; return $result;
} }
protected static function parseExpression(): Matcher { protected static function parseExpression(): Matcher {
$token = self::$instance->data->consume(); if (self::$debug) {
self::debug();
}
$peek = self::$instance->data->peek();
$prefix = null; $prefix = null;
if (in_array($token[0], [ 'B', 'L', 'R' ]) && $token[1] === ':') { if (in_array($peek[0], [ 'B', 'L', 'R' ]) && $peek[1] === ':') {
$prefix = $token[0]; $prefix = $peek[0];
self::$instance->data->consume();
} }
$token = self::$instance->data->consume(); $peek = self::$instance->data->peek();
if ($token === '(') { if ($peek === '(') {
self::$instance->data->consume();
$result = self::parseGroup($prefix); $result = self::parseGroup($prefix);
} elseif (preg_match(self::SCOPE_REGEX, $token)) { } elseif (preg_match(self::SCOPE_REGEX, $peek)) {
$result = self::parsePath($prefix); $result = self::parsePath($prefix);
} else { } else {
die('Group or path expected.'); die('Group or path expected.');
} }
if (self::$debug) {
self::debugResult($result);
}
return $result; return $result;
} }
protected static function parseGroup(string|null $prefix = null): Matcher { protected static function parseGroup(string|null $prefix = null): Matcher {
if (self::$debug) {
self::debug();
}
$result = self::parseSelector(); $result = self::parseSelector();
if (self::$instance->data->consume() !== ')') { if (self::$instance->data->consume() !== ')') {
die('Close parenthesis expected'); die('Close parenthesis expected');
} }
return ($prefix === null) ? $result : new GroupMatcher($prefix, $result); $result = ($prefix === null) ? $result : new GroupMatcher($prefix, $result);
if (self::$debug) {
self::debugResult($result);
}
return $result;
} }
protected static function parsePath(string|null $prefix = null): Matcher { protected static function parsePath(string|null $prefix = null): Matcher {
if (self::$debug) {
self::debug();
}
$result = []; $result = [];
$result[] = self::parseScope(); $result[] = self::parseScope();
$peek = self::$instance->data->peek(); $peek = self::$instance->data->peek();
while (preg_match(self::SCOPE_REGEX, $peek)) { while ($peek != '-' && preg_match(self::SCOPE_REGEX, $peek)) {
self::$instance->data->consume(); self::$instance->data->consume();
$result[] = self::parseScope(); $result[] = self::parseScope();
$peek = self::$instance->data->peek(); $peek = self::$instance->data->peek();
} }
return ($prefix !== null || count($result) > 1) ? new PathMatcher($prefix, ...$result) : $result; $result = ($prefix !== null || count($result) > 1) ? new PathMatcher($prefix, ...$result) : $result[0];
if (self::$debug) {
self::debugResult($result);
}
return $result;
} }
protected static function parseSelector(): Matcher { protected static function parseSelector(): Matcher {
if (self::$debug) {
self::debug();
}
$result = []; $result = [];
$result[] = self::parseComposite(); $result[] = self::parseComposite();
@ -112,10 +155,20 @@ class Parser {
$peek = self::$instance->data->peek(); $peek = self::$instance->data->peek();
} }
return (count($result) > 1) ? new OrMatcher(...$result) : $result[0]; $result = (count($result) > 1) ? new OrMatcher(...$result) : $result[0];
if (self::$debug) {
self::debugResult($result);
}
return $result;
} }
protected static function parseScope(): Matcher { protected static function parseScope(): Matcher {
if (self::$debug) {
self::debug();
}
$token = self::$instance->data->consume(); $token = self::$instance->data->consume();
if (!preg_match('/^(?:[A-Za-z0-9-_]+|\*)(?:\.(?:[A-Za-z0-9-+_]+|\*))*$/S', $token)) { if (!preg_match('/^(?:[A-Za-z0-9-_]+|\*)(?:\.(?:[A-Za-z0-9-+_]+|\*))*$/S', $token)) {
die('Invalid scope'); die('Invalid scope');
@ -126,6 +179,44 @@ class Parser {
$segments[$index] = ($segment !== '*') ? new SegmentMatcher($segment) : new TrueMatcher(); $segments[$index] = ($segment !== '*') ? new SegmentMatcher($segment) : new TrueMatcher();
} }
return new ScopeMatcher(...$segments); $result = new ScopeMatcher(...$segments);
if (self::$debug) {
self::debugResult($result);
}
return $result;
}
protected static function debug() {
$message = <<<DEBUG
------------------------------
%s
Method: %s
Position: %s
Token: %s
DEBUG;
$methodTree = '';
$backtrace = debug_backtrace();
array_shift($backtrace);
array_pop($backtrace);
foreach ($backtrace as $b) {
$methodTree = "->{$b['function']}$methodTree";
}
printf($message,
self::$instance->debugCount++,
ltrim($methodTree, '->'),
self::$instance->data->position + 1,
var_export(self::$instance->data->peek(), true)
);
}
protected static function debugResult($result) {
printf("%s Result: %s\n",
debug_backtrace()[1]['function'],
var_export($result, true));
} }
} }

Loading…
Cancel
Save