You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
2.5 KiB
124 lines
2.5 KiB
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Engine\Cfnpp\Expression;
|
|
|
|
class Expression
|
|
{
|
|
protected $tokens;
|
|
|
|
protected $stack;
|
|
|
|
protected $variables;
|
|
|
|
public function __construct(array $tokens)
|
|
{
|
|
$this->tokens = $tokens;
|
|
}
|
|
|
|
public function evaluate(array $variables = [])
|
|
{
|
|
$this->variables = $variables;
|
|
$this->stack = [];
|
|
$tokens = $this->tokens;
|
|
|
|
while (sizeof($tokens))
|
|
{
|
|
$token = array_shift($tokens);
|
|
|
|
if ($token instanceof TokenNumericLiteral)
|
|
{
|
|
$this->evaluateTokenNumericLiteral($token);
|
|
}
|
|
elseif ($token instanceof TokenStringLiteral)
|
|
{
|
|
$this->evaluateTokenStringLiteral($token);
|
|
}
|
|
elseif ($token instanceof TokenOperator)
|
|
{
|
|
$this->evaluateTokenOperator($token);
|
|
}
|
|
elseif ($token instanceof TokenVariable)
|
|
{
|
|
$this->evaluateTokenVariable($token);
|
|
}
|
|
else
|
|
{
|
|
throw new \Exception('Unhandled expression token type: '.basename(get_class($token)));
|
|
}
|
|
}
|
|
|
|
if (sizeof($this->stack) != 1)
|
|
{
|
|
throw new \Exception('Expression did not evaluate down to a single value');
|
|
}
|
|
|
|
return $this->stack[0];
|
|
}
|
|
|
|
protected function evaluateTokenNumericLiteral(TokenNumericLiteral $token)
|
|
{
|
|
$this->push($token->getValue());
|
|
}
|
|
|
|
protected function evaluateTokenStringLiteral(TokenStringLiteral $token)
|
|
{
|
|
$this->push($token->getValue());
|
|
}
|
|
|
|
protected function evaluateTokenOperator(TokenOperator $token)
|
|
{
|
|
switch ($token->getOperator())
|
|
{
|
|
case 'eq':
|
|
$this->push($this->pop() == $this->pop());
|
|
break;
|
|
case 'neq':
|
|
$this->push($this->pop() != $this->pop());
|
|
break;
|
|
case 'gt':
|
|
$this->push($this->pop(1) > $this->pop());
|
|
break;
|
|
case 'gte':
|
|
$this->push($this->pop(1) >= $this->pop());
|
|
break;
|
|
case 'lt':
|
|
$this->push($this->pop(1) < $this->pop());
|
|
break;
|
|
case 'lte':
|
|
$this->push($this->pop(1) <= $this->pop());
|
|
break;
|
|
case 'and':
|
|
$var1 = $this->pop();
|
|
$var2 = $this->pop();
|
|
$this->push($var1 && $var2);
|
|
break;
|
|
case 'or':
|
|
$var1 = $this->pop();
|
|
$var2 = $this->pop();
|
|
$this->push($var1 || $var2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected function evaluateTokenVariable(TokenVariable $token)
|
|
{
|
|
$name = $token->getName();
|
|
if (!isset($this->variables[$name]))
|
|
{
|
|
throw new \Exception('Undefined variable: '.$name);
|
|
}
|
|
$this->push($this->variables[$name]);
|
|
}
|
|
|
|
protected function pop(int $offset = 0)
|
|
{
|
|
return array_splice($this->stack, -1 * ($offset + 1), 1)[0];
|
|
}
|
|
|
|
protected function push($value)
|
|
{
|
|
array_push($this->stack, $value);
|
|
}
|
|
}
|
|
|