Browse Source

More parsing experimentation

main
Dustin Wilson 3 years ago
parent
commit
ce318968d6
  1. 6
      lib/Scope/Data.php
  2. 26
      lib/Scope/Matchers/AndMatcher.php
  3. 29
      lib/Scope/Matchers/OrMatcher.php
  4. 111
      lib/Scope/Parser.php

6
lib/Scope/Data.php

@ -7,14 +7,14 @@ declare(strict_types=1);
namespace dW\Highlighter\Scope; namespace dW\Highlighter\Scope;
class Data { class Data {
protected string $data; protected array $data;
protected int $position = 0; protected int $position = 0;
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('/[LRB]:|[A-Za-z0-9-+_\*\.]+|[\,\|\-\(\)&]/', $data, $matches);
$this->data = $matches[0] ?? []; $this->data = $matches[1] ?? [];
$this->endPosition = count($this->data); $this->endPosition = count($this->data);
} }

26
lib/Scope/Matchers/AndMatcher.php

@ -7,21 +7,31 @@ declare(strict_types=1);
namespace dW\Highlighter\Scope; namespace dW\Highlighter\Scope;
class AndMatcher extends Matcher { class AndMatcher extends Matcher {
protected Matcher $left; protected array $matchers = [];
protected Matcher $right;
public function __construct(Matcher $left, Matcher $right) { public function __construct(Matcher ...$matchers) {
$this->left = $left; $this->matchers = $matchers;
$this->right = $right; }
public function add(Matcher $matcher) {
$this->matchers[] = $matcher;
} }
public function matches(string ...$scopes): bool { public function matches(string ...$scopes): bool {
return ($this->left->matches(...$scopes) && $this->right->matches(...$scopes)); foreach ($this->matchers as $m) {
if (!$m->matches(...$scopes)) {
return false;
}
}
return true;
} }
public function getPrefix(string ...$scopes): string|null|false { public function getPrefix(string ...$scopes): string|null|false {
if ($this->left->matches(...$scopes) && $this->right->matches(...$scopes)) { if ($this->matches(...$scopes)) {
return $this->left->getPrefix(...$scopes); return $this->matches[0]->getPrefix(...$scopes);
} }
return null;
} }
} }

29
lib/Scope/Matchers/OrMatcher.php

@ -7,19 +7,34 @@ declare(strict_types=1);
namespace dW\Highlighter\Scope; namespace dW\Highlighter\Scope;
class OrMatcher extends Matcher { class OrMatcher extends Matcher {
protected Matcher $left; protected array $matchers = [];
protected Matcher $right;
public function __construct(Matcher $left, Matcher $right) { public function __construct(Matcher ...$matchers) {
$this->left = $left; $this->matchers = $matchers;
$this->right = $right; }
public function add(Matcher $matcher) {
$this->matchers[] = $matcher;
} }
public function matches(string ...$scopes): bool { public function matches(string ...$scopes): bool {
return ($this->left->matches(...$scopes) || $this->right->matches(...$scopes)); foreach ($this->matchers as $m) {
if ($m->matches(...$scopes)) {
return true;
}
}
return false;
} }
public function getPrefix(string ...$scopes): string|null|false { public function getPrefix(string ...$scopes): string|null|false {
return $this->left->getPrefix(...$scopes) || $this->right->getPrefix(...$scopes); foreach ($this->matchers as $m) {
$prefix = $m->getPrefix(...$scopes);
if ($prefix !== null) {
return $prefix;
}
}
return null;
} }
} }

111
lib/Scope/Parser.php

@ -8,12 +8,11 @@ namespace dW\Highlighter\Scope;
class Parser { class Parser {
protected Data $data; protected Data $data;
protected string $token;
protected static Parser $instance; protected static Parser $instance;
protected static bool $debug = false;
protected const PREFIX_REGEX = '/^[LRB]:$/S'; protected const SCOPE_REGEX = '/^[A-Za-z0-9-+_\.\*]+$/S';
protected const SCOPE_REGEX = '/^[A-Za-z0-9-+_\.]+$/S';
public static function parse(string $selector): Matcher|false { public static function parse(string $selector): Matcher|false {
@ -26,31 +25,99 @@ class Parser {
$this->data = new Data($selector); $this->data = new Data($selector);
} }
protected static function parseSelector(): Matcher { protected static function parseComposite(): Matcher {
while (self::$instance->token = self::$instance->data->consume()) { $result = self::parseExpression();
if (preg_match(self::PREFIX_REGEX, self::$instance->token)) {
$peek = self::$instance->data->peek(); $peek = self::$instance->data->peek();
if ($peek === '(') { while (in_array($peek, [ '|', '&', '-' ])) {
$result = self::parseGroup(); $token = self::$instance->data->consume();
} elseif (preg_match(self::SCOPE_REGEX, self::$instance->token)) { $new = self::parseExpression();
$result = self::parsePath();
} else { switch ($token) {
die('Group or path expected.'); case '|':
} if ($result instanceof OrMatcher) {
} elseif (preg_match(self::SCOPE_REGEX, self::$instance->token)) { $result = $result->add($new);
$result = self::parseScope(); }
} elseif (self::$instance->token === '(') {
continue; $result = new OrMatcher($result, $new);
} else { break;
die('Group, path, or scope expected.');
case '-':
$new = new NegateMatcher($new);
case '&':
if ($result instanceof AndMatcher) {
$result = $result->add($new);
}
$result = new AndMatcher($result, $new);
break;
} }
return $result; $peek = self::$instance->data->peek();
}
return $result;
}
protected static function parseExpression(): Matcher {
$token = self::$instance->data->consume();
$prefix = null;
if (in_array($token[0], [ 'B', 'L', 'R' ]) && $token[1] === ':') {
$prefix = $token[0];
}
$token = self::$instance->data->consume();
if ($token === '(') {
$result = self::parseGroup($prefix);
} elseif (preg_match(self::SCOPE_REGEX, $token)) {
$result = self::parsePath($prefix);
} else {
die('Group or path expected.');
}
return $result;
}
protected static function parseGroup(string|null $prefix = null): Matcher {
$result = self::parseSelector();
if (self::$instance->data->consume() !== ')') {
die('Close parenthesis expected');
}
return ($prefix === null) ? $result : new GroupMatcher($prefix, $result);
}
protected static function parsePath(string|null $prefix = null): Matcher {
$result = [];
$result[] = self::parseScope();
$peek = self::$instance->data->peek();
while (preg_match(self::SCOPE_REGEX, $peek)) {
self::$instance->data->consume();
$result[] = self::parseScope();
$peek = self::$instance->data->peek();
} }
return ($prefix !== null || count($result) > 1) ? new PathMatcher($prefix, ...$result) : $result;
}
protected static function parseSelector(): Matcher {
$result = [];
$result[] = self::parseComposite();
$peek = self::$instance->data->peek();
while ($peek === ',') {
self::$instance->data->consume();
$result[] = self::parseComposite();
$peek = self::$instance->data->peek();
}
return (count($result) > 1) ? new OrMatcher(...$result) : $result[0];
} }
protected static function parseScope(): Matcher { protected static function parseScope(): Matcher {
if (!preg_match('/^(?:[A-Za-z0-9-_]+|\*)(?:\.(?:[A-Za-z0-9-+_]+|\*))*$/S', self::$instance->token)) { $token = self::$instance->data->consume();
if (!preg_match('/^(?:[A-Za-z0-9-_]+|\*)(?:\.(?:[A-Za-z0-9-+_]+|\*))*$/S', $token)) {
die('Invalid scope'); die('Invalid scope');
} }

Loading…
Cancel
Save