|
|
@ -18,17 +18,22 @@ class Cfnpp implements ICompile |
|
|
|
/** @var array<string, callable> */ |
|
|
|
protected $functions; |
|
|
|
|
|
|
|
/** @var array<string, callable> */ |
|
|
|
protected $merge_functions; |
|
|
|
|
|
|
|
public function __construct() |
|
|
|
{ |
|
|
|
$this->document = new Document(); |
|
|
|
$this->functions = []; |
|
|
|
|
|
|
|
$this->registerFunction('Replace', static function(Node $orig_p, Node $tgt_p, Node $t) { |
|
|
|
if ($t instanceof NodeFunctionValue) |
|
|
|
{ |
|
|
|
return $t->getValue(); |
|
|
|
} |
|
|
|
$this->registerMergeFunction('Replace', static function(Node $orig_p, Node $tgt_p, Node $t) { |
|
|
|
// todo: nodefunctionvalue |
|
|
|
|
|
|
|
return $t->getChildren(); |
|
|
|
$repl = new Node(null, $tgt_p->hasName() ? $tgt_p->getName() : null); |
|
|
|
$repl->setChildren($t->getChildren()); |
|
|
|
return $repl; |
|
|
|
}); |
|
|
|
$this->registerFunction('Unset', static function(Node $node, Node $func) { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
@ -37,132 +42,152 @@ class Cfnpp implements ICompile |
|
|
|
$this->functions[$name] = $callback; |
|
|
|
} |
|
|
|
|
|
|
|
public function registerMergeFunction(string $name, callable $callback): void |
|
|
|
{ |
|
|
|
$this->merge_functions[$name] = $callback; |
|
|
|
} |
|
|
|
|
|
|
|
public function setDocument(Document $document): void |
|
|
|
{ |
|
|
|
$this->document = $document; |
|
|
|
} |
|
|
|
|
|
|
|
public function getDocument(): Document |
|
|
|
{ |
|
|
|
return $this->document; |
|
|
|
} |
|
|
|
|
|
|
|
public function compile(Document $document, IOptions $options): void |
|
|
|
{ |
|
|
|
$this->compileNodes($this->document, $document); |
|
|
|
$this->runMergeFunctions($this->document, $document); |
|
|
|
$this->merge($this->document, $document); |
|
|
|
$this->runFunctions($this->document); |
|
|
|
} |
|
|
|
|
|
|
|
protected function compileNodes(Node $original, Node $target): void |
|
|
|
protected function merge(Node $original, Node $target): void |
|
|
|
{ |
|
|
|
if ($target instanceof NodeValue) |
|
|
|
if ($original->isArray() && $target->isArray()) |
|
|
|
{ |
|
|
|
if ($original instanceof NodeValue) |
|
|
|
{ |
|
|
|
$original->setValue($target->getValue()); |
|
|
|
} |
|
|
|
else |
|
|
|
foreach ($target as $child) |
|
|
|
{ |
|
|
|
$parent = $original->getParent(); |
|
|
|
$parent->removeChild($original); |
|
|
|
$parent->addChild($target); |
|
|
|
$original->addChild($child); |
|
|
|
$child->setParent($original); |
|
|
|
} |
|
|
|
} |
|
|
|
elseif ($target instanceof NodeFunction) |
|
|
|
{ |
|
|
|
$this->resolveFunction($original->getParent(), $target->getParent(), $target); |
|
|
|
} |
|
|
|
elseif ($target instanceof Node) |
|
|
|
elseif ($original->isMap() && $target->isMap()) |
|
|
|
{ |
|
|
|
// if they're both maps, add/overwrite by name |
|
|
|
if ($target->isMap() && $original->isMap()) |
|
|
|
foreach ($target as $child) |
|
|
|
{ |
|
|
|
foreach ($target as $target_child) |
|
|
|
$orig_child = $original->getChildByName($child->getName()); |
|
|
|
// If the key doesn't exist on the source, just copy over and we're done |
|
|
|
if (!isset($orig_child)) |
|
|
|
{ |
|
|
|
$orig_child = $original->getChildByName($target_child->getName()); |
|
|
|
if ($target_child instanceof NodeFunction) |
|
|
|
{ |
|
|
|
if (!isset($orig_child)) |
|
|
|
{ |
|
|
|
$original->addChild($orig_child = new Node($original, $target->getName())); |
|
|
|
} |
|
|
|
$this->compileNodes($orig_child, $target_child); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if (!isset($orig_child)) |
|
|
|
{ |
|
|
|
$original->addChild($target_child); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
$this->compileNodes($orig_child, $target_child); |
|
|
|
} |
|
|
|
} |
|
|
|
$original->addChild($child); |
|
|
|
$child->setParent($original); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
// if they're both arrays, append |
|
|
|
elseif ($target->isArray() && $original->isArray()) |
|
|
|
{ |
|
|
|
foreach ($target as $child) |
|
|
|
|
|
|
|
// If this is a map or array, we need to descend into it |
|
|
|
if ($child->isMap() || $child->isArray()) |
|
|
|
{ |
|
|
|
$this->merge($orig_child, $child); |
|
|
|
} |
|
|
|
// Otherwise just replace it (nodefunction, nodevalue, whatever) |
|
|
|
else |
|
|
|
{ |
|
|
|
$original->removeChild($orig_child); |
|
|
|
$original->addChild($child); |
|
|
|
$child->setParent($original); |
|
|
|
} |
|
|
|
} |
|
|
|
// if there's a mismatch, replace |
|
|
|
else |
|
|
|
{ |
|
|
|
$parent = $original->getParent(); |
|
|
|
$parent->removeChild($original); |
|
|
|
$parent->addChild($target); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// If it's anything else, overwrite |
|
|
|
$original->remove(); |
|
|
|
$original->getParent()->addChild($target); |
|
|
|
$target->setParent($original); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected function resolveFunction(Node $original_parent, Node $target_parent, Node $target): void |
|
|
|
protected function runMergeFunctions(Node $original, Node $target): void |
|
|
|
{ |
|
|
|
// Execute nodefunction |
|
|
|
$function = $target->getName(); |
|
|
|
|
|
|
|
if (!isset($this->functions[$function])) |
|
|
|
if ($target->isFunctionParent() && isset($this->merge_functions[$target[0]->getName()])) |
|
|
|
{ |
|
|
|
throw new \Exception('Unrecognized function: '.$function); |
|
|
|
$function_node = $target[0]; |
|
|
|
$result = $this->runMergeFunction($original, $target, $function_node); |
|
|
|
|
|
|
|
$original->remove(); |
|
|
|
$target->remove(); |
|
|
|
|
|
|
|
if (isset($result)) |
|
|
|
{ |
|
|
|
$original_node = $result; |
|
|
|
$original_node->setParent($original->getParent()); |
|
|
|
$original->getParent()->addChild($original_node); |
|
|
|
$original = $original_node; |
|
|
|
|
|
|
|
$target_node = clone $result; |
|
|
|
$target_node->setParent($target->getParent()); |
|
|
|
$target->getParent()->addChild($target_node); |
|
|
|
$target = $target_node; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
$result = $this->functions[$function]($original_parent, $target_parent, $target); |
|
|
|
foreach ($target as $node) |
|
|
|
{ |
|
|
|
if ($node->hasName()) |
|
|
|
{ |
|
|
|
$orig_child = $original->getChildByName($node->getName()); |
|
|
|
if (!isset($orig_child)) |
|
|
|
{ |
|
|
|
$orig_child = new Node($original, $node->getName()); |
|
|
|
$original->addChild($orig_child); |
|
|
|
} |
|
|
|
|
|
|
|
$this->runMergeFunctions($orig_child, $node); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (is_scalar($result)) |
|
|
|
if (isset($result)) |
|
|
|
{ |
|
|
|
$original_grandparent = $original_parent->getParent(); |
|
|
|
$original_grandparent->removeChild($original_parent); |
|
|
|
$node = new NodeValue($original_grandparent, $target_parent->getName(), $result); |
|
|
|
$original_grandparent->addChild($node); |
|
|
|
// Remove processed function |
|
|
|
$target_parent->getParent()->removeChild($target_parent); |
|
|
|
$target->remove(); |
|
|
|
} |
|
|
|
elseif (is_array($result)) |
|
|
|
} |
|
|
|
|
|
|
|
protected function runFunctions(Node $node): void |
|
|
|
{ |
|
|
|
if ($node->isFunctionParent() && isset($this->functions[$node[0]->getName()])) |
|
|
|
{ |
|
|
|
// Update original _and_ target so we can reprocess sensibly |
|
|
|
$original_grandparent = $original_parent->getParent(); |
|
|
|
$original_grandparent->removeChild($original_parent); |
|
|
|
$original_parent = new Node($original_grandparent, $original_parent->getName()); |
|
|
|
$original_grandparent->addChild($original_parent); |
|
|
|
foreach ($result as $node) |
|
|
|
$function_node = $node[0]; |
|
|
|
$result = $this->runFunction($node, $function_node); |
|
|
|
|
|
|
|
$node->remove(); |
|
|
|
if (isset($result)) |
|
|
|
{ |
|
|
|
$original_parent->addChild($node); |
|
|
|
$node->setParent($original_parent->getParent()); |
|
|
|
$node->getParent()->addChild($result); |
|
|
|
$result->setParent($node->getParent()); |
|
|
|
$node = $result; |
|
|
|
} |
|
|
|
|
|
|
|
$target_grandparent = $target_parent->getParent(); |
|
|
|
$target_grandparent->removeChild($target_parent); |
|
|
|
$target_parent = new Node($target_grandparent, $target_parent->getName()); |
|
|
|
$target_grandparent->addChild($target_parent); |
|
|
|
foreach ($result as $node) |
|
|
|
else |
|
|
|
{ |
|
|
|
$node = clone $node; |
|
|
|
$target_parent->addChild($node); |
|
|
|
$node->setParent($target_parent->getParent()); |
|
|
|
return; |
|
|
|
} |
|
|
|
$this->compileNodes($original_parent, $target_parent); |
|
|
|
} |
|
|
|
|
|
|
|
foreach ($node as $child) |
|
|
|
{ |
|
|
|
$this->runFunctions($child); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public function getDocument(): Document |
|
|
|
protected function runFunction(Node $node, NodeFunction $function): ?Node |
|
|
|
{ |
|
|
|
return $this->document; |
|
|
|
return $this->functions[$function->getName()]($node, $function); |
|
|
|
} |
|
|
|
|
|
|
|
protected function runMergeFunction(Node $original, Node $target, NodeFunction $function): ?Node |
|
|
|
{ |
|
|
|
return $this->merge_functions[$function->getName()]($original, $target, $function); |
|
|
|
} |
|
|
|
} |
|
|
|