Browse Source

Changed to using an error handler for the scope parser

main
Dustin Wilson 3 years ago
parent
commit
b7d700416d
  1. 17
      lib/Scope/Data.php
  2. 36
      lib/Scope/Exception.php
  3. 47
      lib/Scope/ParseError.php
  4. 35
      lib/Scope/Parser.php

17
lib/Scope/Data.php

@ -13,7 +13,7 @@ class Data {
protected int $endPosition;
public function __construct(string $data) {
preg_match_all('/[BLR]:|[A-Za-z0-9-+_\*\.]+|[\,\|\-\(\)&]/', $data, $matches);
preg_match_all('/[BLR]:|[A-Za-z0-9-+_\*\.]+|[\,\|\-\(\)&]/', $data, $matches, PREG_OFFSET_CAPTURE);
$this->data = $matches[0] ?? [];
$this->endPosition = count($this->data) - 1;
}
@ -23,24 +23,23 @@ class Data {
return false;
}
return $this->data[++$this->_position];
return $this->data[++$this->_position][0];
}
public function peek(): string|bool {
if ($this->_position === $this->endPosition) {
public function offset(): int|bool {
if ($this->_position > $this->endPosition) {
return false;
}
return $this->data[$this->_position + 1];
return $this->data[$this->_position][1];
}
public function unconsume(): bool {
if ($this->_position < 0) {
public function peek(): string|bool {
if ($this->_position === $this->endPosition) {
return false;
}
$this->_position--;
return true;
return $this->data[$this->_position + 1][0];
}
public function __get(string $name) {

36
lib/Scope/Exception.php

@ -1,36 +0,0 @@
<?php
/** @license MIT
* Copyright 2021 Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace dW\Highlighter\Scope;
class Exception extends \Exception {
const MESSAGE = '%s expected; found %s';
public function __construct(array|string $expected, string|bool $found) {
if (!is_string($expected)) {
$expectedLen = count($expected);
if ($expectedLen === 1) {
$expected = ($expected[0] !== false) ? $expected[0] : 'end of input';
} else {
$temp = [];
for ($i = 0; $i < $strlen; $i++) {
$temp[] = ($expected[$i] !== false) ? "{$expected[$i]}" : 'end of input';
}
$expected = $temp;
if ($expectedLen > 2) {
$last = array_pop($expected);
$expected = implode(', ', $expected) . ', or ' . $last;
} else {
$expected = implode(' or ', $expected);
}
}
}
$found = ($found !== false) ? "\"$found\"" : 'end of input';
parent::__construct(sprintf(self::MESSAGE, $expected, $found), 2112);
}
}

47
lib/Scope/ParseError.php

@ -0,0 +1,47 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace dW\Highlighter\Scope;
class ParseError {
const MESSAGE = 'Highlighter Scope Parse Error: %s expected; found %s at offset %s';
public static function clearHandler() {
restore_error_handler();
}
public static function errorHandler(int $code, string $message) {
echo "$message\n";
}
public static function setHandler() {
set_error_handler([__CLASS__, 'errorHandler'], \E_USER_ERROR);
}
public static function trigger(array|string $expected, string|bool $found, int $offset) {
if (!is_string($expected)) {
$expectedLen = count($expected);
if ($expectedLen === 1) {
$expected = ($expected[0] !== false) ? $expected[0] : 'end of input';
} else {
$expected = array_map(function($n) {
$n = ($n !== false) ? $n : 'end of input';
}, $expected);
if ($expectedLen > 2) {
$last = array_pop($expected);
$expected = implode(', ', $expected) . ', or ' . $last;
} else {
$expected = implode(' or ', $expected);
}
}
}
$found = ($found !== false) ? "\"$found\"" : 'end of input';
trigger_error(sprintf(self::MESSAGE, $expected, $found, $offset), \E_USER_ERROR);
exit(1);
}
}

35
lib/Scope/Parser.php

@ -13,12 +13,24 @@ class Parser {
protected int $debugCount = 1;
protected static Parser $instance;
protected const SCOPE_REGEX = '/^[A-Za-z0-9-+_\.\*]+$/S';
public static function parse(string $selector): Matcher|false {
self::$instance = new self($selector);
return self::parseSelector();
$errorReporting = false;
if ((error_reporting() & \E_USER_ERROR)) {
$errorReporting = true;
ParseError::setHandler();
}
$result = self::parseSelector();
if ($errorReporting) {
ParseError::clearHandler();
}
return $result;
}
@ -75,19 +87,20 @@ class Parser {
$peek = self::$instance->data->peek();
$prefix = null;
if (in_array($peek[0], [ 'B', 'L', 'R' ]) && $peek[1] === ':') {
if ($peek !== false && in_array($peek[0], [ 'B', 'L', 'R' ]) && $peek[1] === ':') {
$prefix = $peek[0];
self::$instance->data->consume();
$peek = self::$instance->data->peek();
}
$peek = self::$instance->data->peek();
if ($peek === '(') {
self::$instance->data->consume();
$result = self::parseGroup($prefix);
} elseif (preg_match(self::SCOPE_REGEX, $peek)) {
} elseif (strspn($peek, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+_.*') === strlen($peek)) {
$result = self::parsePath($prefix);
} else {
throw new Exception([ 'Instance of ' . __NAMESPACE__ . 'GroupMatcher', 'Instance of ' . __NAMESPACE__ . 'PathMatcher' ], $peek);
// TODO: Take the effort to make this more descriptive
self::error([ 'Group', 'Path' ], $peek);
}
if (self::$debug) {
@ -105,7 +118,7 @@ class Parser {
$result = self::parseSelector();
$token = self::$instance->data->consume();
if ($token !== ')') {
throw new Exception(')', $token);
self::error('")"', $token);
}
$result = ($prefix === null) ? $result : new GroupMatcher($prefix, $result);
@ -126,7 +139,7 @@ class Parser {
$result[] = self::parseScope();
$peek = self::$instance->data->peek();
while (!in_array($peek, [ '-', false ]) && preg_match(self::SCOPE_REGEX, $peek)) {
while (!in_array($peek, [ '-', false ]) && strspn($peek, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+_.*') === strlen($peek)) {
$result[] = self::parseScope();
$peek = self::$instance->data->peek();
}
@ -172,7 +185,7 @@ class Parser {
$token = self::$instance->data->consume();
if ($token === false || !preg_match('/^(?:[A-Za-z0-9-_]+|\*)(?:\.(?:[A-Za-z0-9-+_]+|\*))*$/S', $token)) {
// TODO: Take the effort to make this more descriptive
throw new Exception('valid scope syntax', $token);
self::error('valid scope syntax', $token);
}
$segments = explode('.', $token);
@ -220,4 +233,8 @@ class Parser {
debug_backtrace()[1]['function'],
str_replace([ '::__set_state(array', __NAMESPACE__ ], '', var_export($result, true)));
}
protected static function error(array|string $expected, string|bool $found) {
ParseError::trigger($expected, $found, self::$instance->data->offset());
}
}

Loading…
Cancel
Save