diff --git a/app/Engine/Cfnpp/Expression/Expression.php b/app/Engine/Cfnpp/Expression/Expression.php index 9cca07c..f70d4b3 100644 --- a/app/Engine/Cfnpp/Expression/Expression.php +++ b/app/Engine/Cfnpp/Expression/Expression.php @@ -39,6 +39,25 @@ class Expression $this->tokens = $tokens; } + /** + * Find all referenced variables so we can do proper ordering of variable + * block evaluate. + * + * @return string[] + */ + public function getReferencedVariables(): array + { + $variables = []; + foreach ($this->tokens as $token) + { + if ($token instanceof TokenVariable) + { + $variables[] = $token->getName(); + } + } + return array_values(array_unique($variables)); + } + /** * Evaluate the tokens contained in this expression. * @@ -56,21 +75,12 @@ class Expression $token = array_shift($tokens); assert(isset($token)); - if ($token instanceof TokenNumericLiteral) - { - $this->evaluateTokenNumericLiteral($token); - } - elseif ($token instanceof TokenStringLiteral) - { - $this->evaluateTokenStringLiteral($token); - } - elseif ($token instanceof TokenOperator) + $token_class = get_class($token); + $token_name = substr($token_class, strrpos($token_class, '\\') + 1); + $func = 'evaluate'.$token_name; + if (method_exists($this, $func)) { - $this->evaluateTokenOperator($token); - } - elseif ($token instanceof TokenVariable) - { - $this->evaluateTokenVariable($token); + $this->{$func}($token); } else { @@ -148,6 +158,32 @@ class Expression $var2 = $this->pop(); $this->push($var1 || $var2); break; + default: + throw new \Exception('Unhandled comparison operator: '.$token->getOperator()); + } + } + + /** + * Evaluate and mutate the stack given a function token. + * + * @param TokenFunction $token + * @return void + */ + protected function evaluateTokenFunction(TokenFunction $token): void + { + switch ($token->getFunction()) + { + case 'concat': + $this->push($this->pop(1).$this->pop()); + break; + case 'concat*': + while (sizeof($this->stack) > 1) + { + $this->push($this->pop(1).$this->pop()); + } + break; + default: + throw new \Exception('Unhandled function: '.$token->getFunction()); } } diff --git a/app/Engine/Cfnpp/Expression/Parser.php b/app/Engine/Cfnpp/Expression/Parser.php index 19cec7c..f7122c4 100644 --- a/app/Engine/Cfnpp/Expression/Parser.php +++ b/app/Engine/Cfnpp/Expression/Parser.php @@ -17,6 +17,7 @@ class Parser * @var string[] */ public const TOKEN_TYPES = [ + TokenFunction::class, TokenNumericLiteral::class, TokenOperator::class, TokenStringLiteral::class, diff --git a/app/Engine/Cfnpp/Expression/TokenFunction.php b/app/Engine/Cfnpp/Expression/TokenFunction.php new file mode 100644 index 0000000..0b99cef --- /dev/null +++ b/app/Engine/Cfnpp/Expression/TokenFunction.php @@ -0,0 +1,90 @@ + + */ +class TokenFunction extends Token +{ + /** + * List of valid functions this can parse. + * @var string[] + */ + public const FUNCTIONS = [ + 'concat*', + 'concat' + ]; + + /** + * Function this token represents. + * @var string + */ + protected $function; + + /** + * Create a new function token. + * + * @param string $function + */ + public function __construct($function) + { + $this->function = $function; + } + + /** + * Get the function this token represents. + * + * @return string + */ + public function getFunction(): string + { + return $this->function; + } + + /** + * Determine whether a function token can be parsed from a stream. + * + * @param string $stream + * @return bool + */ + public static function isToken(string $stream): bool + { + foreach (static::FUNCTIONS as $function) + { + if (strlen($stream) > strlen($function) && + substr($stream, 0, strlen($function)) == $function) + { + return true; + } + } + return false; + } + + /** + * Parse a function token from a stream. + * + * Returns token, and modifies stream to remove all consumed characters + * + * @param string $stream + * @return Token + */ + public static function getToken(string &$stream): Token + { + foreach (static::FUNCTIONS as $function) + { + if (strlen($stream) > strlen($function) && + substr($stream, 0, strlen($function)) == $function) + { + $function = substr($stream, 0, strlen($function)); + $stream = substr($stream, strlen($function)); + return new TokenFunction($function); + } + } + throw new \Exception('Could not parse function token!'); + } +}