Commit 87c3ba64 authored by Pietro Saccardi's avatar Pietro Saccardi

Breaking down into multiple files

parent 5f3800c1
<?php
namespace AST;
use \Exception;
class UnknownTokenException extends Exception {
private $_text = null;
private $_position = null;
public function __construct($text, $position, $code = 0, Exception $previous = null) {
$this->_text = $text;
$this->_position = $position;
$message = 'Unknown token "' . substr($text, $position, 4) . '" at position ' . $position;
parent::__construct($message, $code, $previous);
}
public function getText() { return $this->_text; }
public function getPosition() { return $this->_position; }
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
class MalformedExpressionException extends Exception {
private $_elementInstance = null;
public function __construct($elementInstance, $message, $code = 0, Exception $previous = null) {
$this->_elementInstance = $elementInstance;
parent::__construct($message, $code, $previous);
}
public function getElementInstance() { return $this->_elementInstance; }
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
class InvalidExpressionException extends Exception {
private $_elementInstance = null;
public function __construct($elementInstance, $message, $code = 0, Exception $previous = null) {
$this->_elementInstance = $elementInstance;
parent::__construct($message, $code, $previous);
}
public function getElementInstance() { return $this->_elementInstance; }
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
class NotEnoughArgumentsException extends Exception {
private $_elementDefinition = null;
private $_firstTokenInstance = null;
public function __construct($elementDefinition, $firstTokenInstance, $code = 0, Exception $previous = null) {
$this->_elementDefinition = $elementDefinition;
$this->_firstTokenInstance = $firstTokenInstance;
$message = 'Not enough arguments for operator ' . $elementDefinition->name()
. ' encountered at position ' . $firstTokenInstance->position() . ', around "'
. substr($firstTokenInstance->text(), max(0, $firstTokenInstance->position() - 3), $firstTokenInstance->length() + 3)
. '".';
if ($elementDefinition->arity() > 0) {
$message .= ' Expected ' . $elementDefinition->arity() . ' arguments.';
}
parent::__construct($message, $code, $previous);
}
public function getFirstTokenInstance() { return $this->_firstTokenInstance; }
public function getElementDefinition() { return $this->_elementDefinition; }
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
class StrayTokenException extends Exception {
private $_tokenInstance = null;
public function __construct($tokenInstance, $code = 0, Exception $previous = null) {
$this->_tokenInstance = $tokenInstance;
$message = 'Stray token encountered at position ' . $tokenInstance->position() . ', around "'
. substr($tokenInstance->text(), max(0, $tokenInstance->position() - 3), $tokenInstance->length() + 3)
. '".';
parent::__construct($message, $code, $previous);
}
public function getTokenInstance() { return $this->_tokenInstance; }
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
class UnmatchedWrapperException extends Exception {
private $_elementDefinition = null;
private $_firstTokenInstance = null;
public function __construct($elementDefinition, $firstTokenInstance, $code = 0, Exception $previous = null) {
$this->_elementDefinition = $elementDefinition;
$this->_firstTokenInstance = $firstTokenInstance;
$message = 'Unmatched opening token ' . $elementDefinition->tokenDefs()[0] . ' for wrapping operator '
. $elementDefinition->name() . ' encountered at position ' . $firstTokenInstance->position() . ', around "'
. substr($firstTokenInstance->text(), max(0, $firstTokenInstance->position() - 3), $firstTokenInstance->length() + 3)
. '". The missing closing token is ' . $elementDefinition->tokenDefs()[1] . '.';
parent::__construct($message, $code, $previous);
}
public function getFirstTokenInstance() { return $this->_firstTokenInstance; }
public function getElementDefinition() { return $this->_elementDefinition; }
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
?>
\ No newline at end of file
<?php
require_once "parser.php";
require_once "tokenizer.php";
require_once "exceptions.php";
use \AST\ElementDefinition;
use \AST\TokenDefinition;
use \AST\InvalidExpressionException;
use \AST\MalformedExpressionException;
use \AST\Fixing;
class Literal extends ElementDefinition {
public function __construct() {
$T_LITERAL = new TokenDefinition(null, 'LIT', '/\w+/');
parent::__construct('Literal', Fixing::None, $T_LITERAL, 0);
}
public function _evaluateWellFormed($elmInstance) {
$key = 'REMOTE_USER';
if (array_key_exists($key, $_SERVER)) {
return $_SERVER[$key] == $elmInstance->getStringValue();
}
return false;
}
}
class SubExpr extends ElementDefinition {
public function __construct() {
$T_OPEN_PAREN = new TokenDefinition('(', 'OPENP');
$T_CLOSE_PAREN = new TokenDefinition(')', 'CLOSEP');
parent::__construct('SubExpr', Fixing::Wrap, array($T_OPEN_PAREN, $T_CLOSE_PAREN), 1, null, true);
}
public function ensureWellFormed($elmInstance) {
parent::ensureWellFormed($elmInstance);
if (count($elmInstance->args()) != 1) {
throw new MalformedExpressionException($elmInstance, 'A subexpression must have exactly one root');
}
}
public function _evaluateWellFormed($elmInstance) {
return $elmInstance->evaluateArgs();
}
}
class OpInGroup extends ElementDefinition {
public function __construct() {
$T_AT = new TokenDefinition('@', 'AT');
parent::__construct('InGroup', Fixing::Prefix, $T_AT, 2);
}
public function ensureWellFormed($elmInstance) {
parent::ensureWellFormed($elmInstance);
if (!($elmInstance->args()[0]->definition() instanceof Literal)) {
throw new MalformedExpressionException($elmInstance, 'A in-group operator <@> must take exactly one literal argument.');
}
}
public function _evaluateWellFormed($elmInstance) {
$groupName = $elmInstance->args()[0]->getStringValue();
global $INFO;
$key1 = 'userinfo';
$key2 = 'grps';
if (is_array($INFO) && array_key_exists($key1, $INFO)) {
if (is_array($INFO[$key1]) && array_key_exists($key2, $INFO[$key1])) {
return in_array($groupName, $INFO[$key1][$key2]);
}
}
return false;
}
}
class OpNot extends ElementDefinition {
public function __construct() {
$T_EXCL = new TokenDefinition('!', 'EXCL');
parent::__construct('Not', Fixing::Prefix, $T_EXCL, 3);
}
public function _evaluateWellFormed($elmInstance) {
$argValues = $elmInstance->evaluateArgs();
if (!is_bool($argValues[0])) {
throw new InvalidExpressionException($elmInstance, 'Not called on a non-boolean argument.');
}
return !$argValues[0];
}
}
class OpAnd extends ElementDefinition {
public function __construct() {
$T_AND = new TokenDefinition('&&', 'AND');
parent::__construct('And', Fixing::Infix, $T_AND, 4);
}
public function _evaluateWellFormed($elmInstance) {
$argValues = $elmInstance->evaluateArgs();
foreach ($argValues as $arg) {
if (!is_bool($arg)) {
throw new InvalidExpressionException($elmInstance, 'And called on non-boolean arguments.');
}
if (!$arg) {
return false;
}
}
return true;
}
}
class OpOr extends ElementDefinition {
public function __construct() {
$T_OR = new TokenDefinition('||', 'OR');
parent::__construct('Or', Fixing::Infix, $T_OR, 5);
}
public function _evaluateWellFormed($elmInstance) {
$argValues = $elmInstance->evaluateArgs();
foreach ($argValues as $arg) {
if (!is_bool($arg)) {
throw new InvalidExpressionException($elmInstance, 'Or called on non-boolean arguments.');
}
if (!$arg) {
return false;
}
}
return true;
}
}
$ALL_ELEMENTS = array(new Literal(), new SubExpr(), new OpInGroup(), new OpNot(), new OpAnd(), new OpOr());
$IGNORE_TOKENS = array(new TokenDefinition(' ', 'SPC', '/\s+/'));
$ALL_TOKENS = array_merge($IGNORE_TOKENS, ElementDefinition::extractUsedTokens($ALL_ELEMENTS));
function parse($expr) {
global $ALL_ELEMENTS;
global $IGNORE_TOKENS;
global $ALL_TOKENS;
return \AST\parse(\AST\tokenize($expr, $ALL_TOKENS, $IGNORE_TOKENS), $ALL_ELEMENTS);
}
?>
\ No newline at end of file
This diff is collapsed.
<?php
require_once "grammar.php";
$text = 'usr1 ||(!usr2&&@group || !usr3)';
echo 'Parsing "' . $text . '".' . "\n";
$expr = parse($text);
$expr->printTree();
var_dump($expr->evaluate());
?>
\ No newline at end of file
<?php
namespace AST;
use \InvalidArgumentException;
use \RuntimeException;
require_once "exceptions.php";
class TokenDefinition {
private $_representation = null;
private $_name = null;
private $_matchRegex = null;
public function __construct($representation, $name=null, $matchRegex=null) {
$this->_representation = $representation;
if ($name === null) {
$name = $representation;
}
$this->_name = $name;
if ($matchRegex === null) {
$matchRegex = '/' . preg_quote($representation) . '/';
}
$this->_matchRegex = $matchRegex;
}
public function representation() { return $this->_representation; }
public function name() { return $this->_name; }
public function tryMatch($text, $position) {
$matches = null;
$result = preg_match($this->_matchRegex, $text, $matches, PREG_OFFSET_CAPTURE, $position);
if ($result === 0) {
return null;
} elseif ($result === false) {
throw new InvalidArgumentException('An error occurred in preg_match.');
} elseif (count($matches) == 0) {
throw new RuntimeException('No matches?');
}
list($matchTxt, $matchOfs) = $matches[0];
if ($matchOfs > $position) {
return null;
}
return $matchTxt;
}
public function __toString() {
return '<' . $this->name() . ">\n";
}
}
class TokenInstance {
private $_definition = null;
private $_text = null;
private $_position = null;
private $_length = null;
public function __construct($definition, $text, $position, $length) {
$this->_definition = $definition;
$this->_text = $text;
$this->_position = $position;
$this->_length = $length;
}
public function definition() { return $this->_definition; }
public function text() { return $this->_text; }
public function position() { return $this->_position; }
public function length() { return $this->_length; }
public function match() { return substr($this->_text, $this->position(), $this->length()); }
public function __toString() {
return '<' . $this->definition()->name() . ':' . $this->match() . ">\n";
}
}
function tokenize(string $text, array $tokDefs, array $stripTokDefs) {
$tokInsts = array();
$foundTokInst = null;
for ($position = 0; $position < strlen($text); $position += $foundTokInst->length()) {
$foundTokInst = null;
foreach ($tokDefs as $tokDef) {
$match = $tokDef->tryMatch($text, $position);
if ($match !== null) {
$foundTokInst = new TokenInstance($tokDef, $text, $position, strlen($match));
break;
}
}
if ($foundTokInst === null) {
throw new UnknownTokenException($text, $position);
} elseif (!in_array($foundTokInst->definition(), $stripTokDefs)) {
$tokInsts[] = $foundTokInst;
}
}
return $tokInsts;
}
?>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment