Compare commits

...

5 Commits

Author SHA1 Message Date
Adam Pippin 6f90b5d222 Bugfix: Fix processing order for functions -- deepest nodes first 3 years ago
Adam Pippin 8dd7b8d1f0 Re-add !expr function 3 years ago
Adam Pippin 4294c5b007 Add concat/select operators 3 years ago
Adam Pippin 6ddf1531af Add boolean literals to expression parser 3 years ago
Adam Pippin 706242074d Allow !replace to accept a scalar 3 years ago
  1. 25
      app/Cfnpp/Compiler.php
  2. 1
      app/Cfnpp/Expression/Expression.php
  3. 75
      app/Cfnpp/Expression/Token/BooleanLiteral.php
  4. 36
      app/Cfnpp/Expression/Token/OperatorBinary.php
  5. 40
      app/Cfnpp/Functions.php

25
app/Cfnpp/Compiler.php

@ -123,15 +123,6 @@ class Compiler implements \App\Engine\ICompile
$this->pass_2($document, $options);
return $this->document;
// Process each passed document
/*
foreach ($documents as $next_document)
{
$this->runMergeFunctions($document, $next_document);
$this->merge($document, $next_document);
$this->runFunctions($document);
}
*/
}
/**
@ -302,14 +293,12 @@ class Compiler implements \App\Engine\ICompile
{
$variables[] = $node->getValue();
}
/*
// TODO: Reimplement
elseif ($node instanceof NodeFunctionValue &&
$node->getName() == 'expr')
{
$expression = new \App\Cfnpp\Expression\Expression($node->getValue());
$variables = array_merge($variables, $expression->getReferencedVariables());
}*/
}
}
return $variables;
@ -454,6 +443,12 @@ class Compiler implements \App\Engine\ICompile
*/
protected function runFunctions(Node $node): void
{
$children = $node->getChildren();
foreach ($children as $child)
{
$this->runFunctions($child);
}
if ($node->isFunctionParent() && isset($this->functions[$node[0]->getName()]))
{
$function_node = $node[0];
@ -471,12 +466,6 @@ class Compiler implements \App\Engine\ICompile
return;
}
}
$children = $node->getChildren();
foreach ($children as $child)
{
$this->runFunctions($child);
}
}
/**

1
app/Cfnpp/Expression/Expression.php

@ -20,6 +20,7 @@ class Expression
* @var string[]
*/
public const TOKEN_TYPES = [
Token\BooleanLiteral::class,
Token\NumericLiteral::class,
Token\OperatorUnary::class,
Token\OperatorBinary::class,

75
app/Cfnpp/Expression/Token/BooleanLiteral.php

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace App\Cfnpp\Expression\Token;
use App\Cfnpp\Expression\Token;
use App\Cfnpp\Expression\TokenLiteral;
/**
* A boolean literal (true/false).
*
* @author Adam Pippin <hello@adampippin.ca>
*/
class BooleanLiteral extends TokenLiteral
{
/**
* Value of this literal.
* @var bool
*/
protected $value;
/**
* New boolean literal.
*
* @param bool $value
*/
public function __construct(bool $value)
{
$this->value = $value;
}
/**
* Get the value of this literal.
*
* @return bool
*/
public function getValue(): bool
{
return $this->value;
}
public static function isToken(string $stream): bool
{
return
(strlen($stream) >= 4 && strtolower(substr($stream, 0, 4)) == 'true') ||
(strlen($stream) >= 5 && strtolower(substr($stream, 0, 5)) == 'false');
}
public static function getToken(string &$stream): Token
{
if (strlen($stream) >= 4 && strtolower(substr($stream, 0, 4)) == 'true')
{
$stream = substr($stream, 4);
return new BooleanLiteral(true);
}
elseif (strlen($stream) >= 5 && strtolower(substr($stream, 0, 5)) == 'false')
{
$stream = substr($stream, 5);
return new BooleanLiteral(false);
}
throw new \Exception('Unparseable boolean');
}
/**
* Get the value of this token.
*
* @param ?\App\Util\GraphNode[] $arguments
* @return \App\Util\GraphNode|Token|scalar|null
*/
public function execute(?array $arguments = null)
{
return $this->getValue();
}
}

36
app/Cfnpp/Expression/Token/OperatorBinary.php

@ -22,7 +22,9 @@ class OperatorBinary extends TokenBinary implements ICloudformationNative
public const OPERATORS = [
'and',
'or',
'eq'
'eq',
'concat',
'select'
];
/**
@ -147,8 +149,32 @@ class OperatorBinary extends TokenBinary implements ICloudformationNative
return null;
case 'concat':
if (is_scalar($value1) && is_scalar($value2))
{
return $value2.$value1;
}
elseif ($value1 instanceof Parameter && is_scalar($value2) && empty($value2))
{
return $value1;
}
elseif ($value2 instanceof Parameter && is_scalar($value1) && empty($value1))
{
return $value2;
}
return null;
case 'select':
if (is_scalar($value1) && is_array($value2))
{
return $value2[$value1];
}
return null;
default:
throw new \Exception('Missing implementation for unary operator: '.$this->getOperator());
throw new \Exception('Missing implementation for binary operator: '.$this->getOperator());
}
}
@ -174,8 +200,12 @@ class OperatorBinary extends TokenBinary implements ICloudformationNative
return ['Fn::Or' => [$value1, $value2]];
case 'eq':
return ['Fn::Equals' => [$value1, $value2]];
case 'concat':
return ['Fn::Join' => ['', [$value2, $value1]]];
case 'select':
return ['Fn::Select' => [$value1, $value2]];
default:
throw new \Exception('Missing implementation for unary operator: '.$this->getOperator());
throw new \Exception('Operator cannot be applied to parameters: '.$this->getOperator());
}
}
}

40
app/Cfnpp/Functions.php

@ -83,7 +83,10 @@ class Functions
*/
public function mf_replace(Node $original, Node $target, NodeFunction $function): ?Node
{
// TODO: Deal with nodefunctionvalue
if ($function instanceof NodeFunctionValue)
{
return new NodeValue(null, $target->hasName() ? $target->getName() : null, $function->getValue());
}
$replacement = new Node(null, $target->hasName() ? $target->getName() : null);
$replacement->setChildren($function->getChildren());
@ -212,23 +215,38 @@ class Functions
* @param NodeFunction $function
* @return ?Node
*/
/*
* TODO: Reimplement
public function f_expr(Node $node, NodeFunction $function): ?Node
{
if (!($function instanceof NodeFunctionValue))
{
throw new \Exception('!if requires scalar argument');
throw new \Exception('!expr requires scalar argument');
}
$parser = new \App\Cfnpp\Expression\Parser();
$expression = $parser->parse($function->getValue());
$result = $expression->evaluate($this->options->getVariables());
$expression = new \App\Cfnpp\Expression\Expression($function->getValue(), $this->options);
$result = Node::fromPhp($result);
$result->setName($node->hasName() ? $node->getName() : null);
if ($expression->isComplete())
{
// If we computed a final value/set of values, we can just insert those
// directly.
if ($expression->count() == 1)
{
$solution_node = Node::fromPhp($expression->getValue());
}
else
{
$solution_node = Node::fromPhp($expression->toArray());
}
}
else
{
// Otherwise let's convert it to cfn intrinsics
$solution_node = Node::fromPhp($expression->toCloudformation());
}
return $result;
if ($node->hasName())
{
$solution_node->setName($node->getName());
}
return $solution_node;
}
*/
}

Loading…
Cancel
Save