|
|
@ -7,7 +7,9 @@ namespace App\Engine\Cfnpp; |
|
|
|
use App\Dom\Document; |
|
|
|
use App\Dom\Node; |
|
|
|
use App\Dom\NodeFunction; |
|
|
|
use App\Dom\NodeFunctionValue; |
|
|
|
use App\Engine\IOptions; |
|
|
|
use App\Util\DependencyGraph; |
|
|
|
|
|
|
|
/** |
|
|
|
* Compiler that implements the cfnpp "language" and allows you to |
|
|
@ -87,7 +89,10 @@ class Compiler implements \App\Engine\ICompile |
|
|
|
$cfnpp_functions = new Functions($this, $options); |
|
|
|
$cfnpp_functions->register($this); |
|
|
|
|
|
|
|
return $this->pass_0($documents, $options); |
|
|
|
$document = $this->pass_0($documents, $options); |
|
|
|
$this->pass_1($document, $options); |
|
|
|
|
|
|
|
return $document; |
|
|
|
|
|
|
|
// Process each passed document |
|
|
|
/* |
|
|
@ -112,7 +117,7 @@ class Compiler implements \App\Engine\ICompile |
|
|
|
{ |
|
|
|
|
|
|
|
// Build dependency graph |
|
|
|
$graph = new \App\Util\DependencyGraph(); |
|
|
|
$graph = new DependencyGraph(); |
|
|
|
foreach ($documents as $doc) |
|
|
|
{ |
|
|
|
$stack = []; |
|
|
@ -146,7 +151,7 @@ class Compiler implements \App\Engine\ICompile |
|
|
|
|
|
|
|
// Run our merge + merge functions |
|
|
|
$document = new Document(); |
|
|
|
foreach ($documents as $next_document) |
|
|
|
foreach ($ordered_documents as $next_document) |
|
|
|
{ |
|
|
|
$this->runMergeFunctions($document, $next_document); |
|
|
|
$this->merge($document, $next_document); |
|
|
@ -164,6 +169,76 @@ class Compiler implements \App\Engine\ICompile |
|
|
|
return $document; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Compiler Pass 1 - Grab all variables, build dependency graph of |
|
|
|
* dependencies between variable values, resolve variables values. Then |
|
|
|
* set them on $options to include them in program state. |
|
|
|
* |
|
|
|
* @param Document $document |
|
|
|
* @param IOptions $options |
|
|
|
* @return void |
|
|
|
*/ |
|
|
|
protected function pass_1(Document $document, IOptions $options): void |
|
|
|
{ |
|
|
|
$variables_node = $document->getChildByPath('cfnpp.variables'); |
|
|
|
// If there are no variables, we're done here. |
|
|
|
if (!isset($variables_node)) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
$variables_raw = []; |
|
|
|
$graph = new DependencyGraph(); |
|
|
|
|
|
|
|
foreach ($variables_node as $variable_node) |
|
|
|
{ |
|
|
|
$variables_raw[$variable_node->getName()] = $variable_node; |
|
|
|
$graph->add($variable_node->getName(), $this->pass_1_getVariableDependencies($variable_node)); |
|
|
|
} |
|
|
|
|
|
|
|
$variables_ordered = $graph->solve(); |
|
|
|
|
|
|
|
foreach ($variables_ordered as $variable) |
|
|
|
{ |
|
|
|
$this->runFunctions($variables_raw[$variable]); |
|
|
|
$options->setVariable($variable, Node::toPhp($variables_node->getChildByName($variable))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Given a node tree, get a list of variables that are referenced in that |
|
|
|
* tree. |
|
|
|
* |
|
|
|
* @todo whenever we add expressions this will have to be extended/rewritten |
|
|
|
* @param Node $node |
|
|
|
* @return string[] |
|
|
|
*/ |
|
|
|
protected function pass_1_getVariableDependencies(Node $node): array |
|
|
|
{ |
|
|
|
$stack = [$node]; |
|
|
|
|
|
|
|
$variables = []; |
|
|
|
|
|
|
|
while (sizeof($stack)) |
|
|
|
{ |
|
|
|
$node = array_shift($stack); |
|
|
|
|
|
|
|
if ($node->hasChildren()) |
|
|
|
{ |
|
|
|
$stack = array_merge($stack, $node->getChildren()); |
|
|
|
} |
|
|
|
|
|
|
|
// We're really only handling one case here right now |
|
|
|
if ($node instanceof NodeFunctionValue && |
|
|
|
$node->getName() == 'var') |
|
|
|
{ |
|
|
|
$variables[] = $node->getValue(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return $variables; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Performs a basic recursive merge of two dom trees with target overwriting |
|
|
|
* original. |
|
|
|