Browse Source

Compiler pass 0 using new dependency graph/etc

This gets us back mostly to where we were, sans running non-merge !functions and variable/parameter support
master
Adam Pippin 3 years ago
parent
commit
0c70de8e8a
  1. 106
      app/Engine/Cfnpp/Compiler.php

106
app/Engine/Cfnpp/Compiler.php

@ -7,6 +7,7 @@ namespace App\Engine\Cfnpp;
use App\Dom\Document;
use App\Dom\Node;
use App\Dom\NodeFunction;
use App\Engine\IOptions;
/**
* Compiler that implements the cfnpp "language" and allows you to
@ -67,18 +68,18 @@ class Compiler implements \App\Engine\ICompile
* Compile a set of documents and return the result.
*
* @param Document[] $documents
* @param \App\Engine\IOptions $options
* @param IOptions $options
* @return void
*/
public function compile(array $documents, \App\Engine\IOptions $options): Document
public function compile(array $documents, IOptions $options): Document
{
if (!($options instanceof Options))
{
throw new \Exception('Cfnpp\Compiler requires Cfnpp\Options');
throw new \Exception('Cfnpp\\Compiler requires Cfnpp\\Options');
}
// Initialize state
$document = new Document();
$document = null;
$this->functions = [];
$this->merge_functions = [];
@ -86,13 +87,80 @@ class Compiler implements \App\Engine\ICompile
$cfnpp_functions = new Functions($this, $options);
$cfnpp_functions->register($this);
return $this->pass_0($documents, $options);
// Process each passed document
/*
foreach ($documents as $next_document)
{
$this->runMergeFunctions($document, $next_document);
$this->merge($document, $next_document);
$this->runFunctions($document);
}
*/
}
/**
* Compiler Pass 0 - Build a dependency graph, run mergeFunctions + merge
* for each as appropriate. Return the resulting document.
*
* @param Document[] $documents
* @param IOptions $options
* @return Document
*/
protected function pass_0(array $documents, IOptions $options): Document
{
// Build dependency graph
$graph = new \App\Util\DependencyGraph();
foreach ($documents as $doc)
{
$stack = [];
try
{
$stack = $doc->getMeta('stack');
}
catch (\Exception $ex)
{
}
$graph->add($doc->getDocumentName(), $stack);
}
$graph->link();
$docs = $graph->flatten();
// Reorder all our documents in whatever order the dependency solver
// says we should process them in.
// O(N^2), fix this if it ever actually becomes a problem.
$ordered_documents = [];
foreach ($docs as $doc_name)
{
foreach ($documents as $document)
{
if ($document->getDocumentName() == $doc_name)
{
$ordered_documents[] = $document;
continue 2;
}
}
}
// Run our merge + merge functions
$document = new Document();
foreach ($documents as $next_document)
{
$this->runMergeFunctions($document, $next_document);
$this->merge($document, $next_document);
}
// Throw some of our state into the document for posterity's sake
$prefix = $this->getShortestCommonPrefix($docs);
foreach ($docs as &$doc)
{
$doc = substr($doc, strlen($prefix));
}
unset($doc);
$document->setChildByPath('Metadata.Stack', Node::fromPhp($docs));
return $document;
}
@ -274,4 +342,34 @@ class Compiler implements \App\Engine\ICompile
{
return $this->merge_functions[$function->getName()]($original, $target, $function);
}
/**
* Given a list of strings, find and return the shortest common prefix of all
* strings. Can return an empty string if there's no prefix in common.
*
* @param string[] $strings
* @return string
*/
protected function getShortestCommonPrefix(array $strings): string
{
// Find longest common prefix
$shortest_string_length = array_reduce($strings, static function(int $carry, string $item) {
return min($carry, strlen($item));
}, PHP_INT_MAX);
for ($i = 0; $i < $shortest_string_length; $i++)
{
$c = $strings[0][$i];
for ($x = 1; $x < sizeof($strings); $x++)
{
if ($strings[$x][$i] != $c)
{
--$i;
break 2;
}
}
}
return $i == 0 ? '' : substr($strings[0], 0, $i);
}
}

Loading…
Cancel
Save