|
|
@ -4,19 +4,47 @@ declare(strict_types=1); |
|
|
|
|
|
|
|
namespace App\Engine\Cfnpp\Expression; |
|
|
|
|
|
|
|
/** |
|
|
|
* Expression that can be evaluated. |
|
|
|
* |
|
|
|
* @author Adam Pippin <hello@adampippin.ca> |
|
|
|
*/ |
|
|
|
class Expression |
|
|
|
{ |
|
|
|
/** |
|
|
|
* List of Tokens in the expression. |
|
|
|
* @var Token[] |
|
|
|
*/ |
|
|
|
protected $tokens; |
|
|
|
|
|
|
|
/** |
|
|
|
* Evaluation stack. |
|
|
|
* @var mixed[] |
|
|
|
*/ |
|
|
|
protected $stack; |
|
|
|
|
|
|
|
/** |
|
|
|
* Variables that can be referenced in the expression. |
|
|
|
* @var array<string,mixed> |
|
|
|
*/ |
|
|
|
protected $variables; |
|
|
|
|
|
|
|
/** |
|
|
|
* Create a new expression. |
|
|
|
* |
|
|
|
* @param Token[] $tokens |
|
|
|
*/ |
|
|
|
public function __construct(array $tokens) |
|
|
|
{ |
|
|
|
$this->tokens = $tokens; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Evaluate the tokens contained in this expression. |
|
|
|
* |
|
|
|
* @param array<string,mixed> $variables variables that can be referenced |
|
|
|
* @return mixed |
|
|
|
*/ |
|
|
|
public function evaluate(array $variables = []) |
|
|
|
{ |
|
|
|
$this->variables = $variables; |
|
|
@ -26,6 +54,7 @@ class Expression |
|
|
|
while (sizeof($tokens)) |
|
|
|
{ |
|
|
|
$token = array_shift($tokens); |
|
|
|
assert(isset($token)); |
|
|
|
|
|
|
|
if ($token instanceof TokenNumericLiteral) |
|
|
|
{ |
|
|
@ -49,25 +78,45 @@ class Expression |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (sizeof($this->stack) != 1) |
|
|
|
if (sizeof($this->stack) == 1) |
|
|
|
{ |
|
|
|
throw new \Exception('Expression did not evaluate down to a single value'); |
|
|
|
return end($this->stack); |
|
|
|
} |
|
|
|
|
|
|
|
return $this->stack[0]; |
|
|
|
throw new \Exception('Expression did not evaluate down to a single value'); |
|
|
|
} |
|
|
|
|
|
|
|
protected function evaluateTokenNumericLiteral(TokenNumericLiteral $token) |
|
|
|
/** |
|
|
|
* Evaluate and mutate the stack given a numeric literal. |
|
|
|
* |
|
|
|
* @param TokenNumericLiteral $token |
|
|
|
* @return void |
|
|
|
*/ |
|
|
|
protected function evaluateTokenNumericLiteral(TokenNumericLiteral $token): void |
|
|
|
{ |
|
|
|
$this->push($token->getValue()); |
|
|
|
} |
|
|
|
|
|
|
|
protected function evaluateTokenStringLiteral(TokenStringLiteral $token) |
|
|
|
/** |
|
|
|
* Evaluate and mutate the stack given a string literal. |
|
|
|
* |
|
|
|
* @param TokenStringLiteral $token |
|
|
|
* @return void |
|
|
|
*/ |
|
|
|
protected function evaluateTokenStringLiteral(TokenStringLiteral $token): void |
|
|
|
{ |
|
|
|
$this->push($token->getValue()); |
|
|
|
} |
|
|
|
|
|
|
|
protected function evaluateTokenOperator(TokenOperator $token) |
|
|
|
/** |
|
|
|
* Evaluate and mutate the stack given a comparison operator. |
|
|
|
* |
|
|
|
* $this->pop() == $this->pop() is valid. |
|
|
|
* @suppress PhanPluginDuplicateExpressionBinaryOp |
|
|
|
* @param TokenOperator $token |
|
|
|
* @return void |
|
|
|
*/ |
|
|
|
protected function evaluateTokenOperator(TokenOperator $token): void |
|
|
|
{ |
|
|
|
switch ($token->getOperator()) |
|
|
|
{ |
|
|
@ -102,6 +151,12 @@ class Expression |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Evaluate and mutate the stack given a variable reference. |
|
|
|
* |
|
|
|
* @param TokenVariable $token |
|
|
|
* @return void |
|
|
|
*/ |
|
|
|
protected function evaluateTokenVariable(TokenVariable $token) |
|
|
|
{ |
|
|
|
$name = $token->getName(); |
|
|
@ -112,12 +167,28 @@ class Expression |
|
|
|
$this->push($this->variables[$name]); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Pop an item off the stack. |
|
|
|
* |
|
|
|
* @param int $offset offset from the end of the stack to pop from |
|
|
|
* @return mixed |
|
|
|
*/ |
|
|
|
protected function pop(int $offset = 0) |
|
|
|
{ |
|
|
|
if (sizeof($this->stack) < $offset + 1) |
|
|
|
{ |
|
|
|
throw new \Exception('Expression stack underflow!'); |
|
|
|
} |
|
|
|
return array_splice($this->stack, -1 * ($offset + 1), 1)[0]; |
|
|
|
} |
|
|
|
|
|
|
|
protected function push($value) |
|
|
|
/** |
|
|
|
* Push an item onto the end of the stack. |
|
|
|
* |
|
|
|
* @param mixed $value |
|
|
|
* @return void |
|
|
|
*/ |
|
|
|
protected function push($value): void |
|
|
|
{ |
|
|
|
array_push($this->stack, $value); |
|
|
|
} |
|
|
|