Skip to content

Instantly share code, notes, and snippets.

@Lachee
Last active August 1, 2019 02:44
Show Gist options
  • Save Lachee/de832c924211145233e097beb8c272d7 to your computer and use it in GitHub Desktop.
Save Lachee/de832c924211145233e097beb8c272d7 to your computer and use it in GitHub Desktop.
XVE Argument Resolve.
<?php
function compileTemplate(Template $template, $tokens)
{
return $template->evaluate($tokens);
}
/**
Resolves a input. Returns either a Literal or a string.
The strings are either a variable name or some compiled code. Either way they are safe to 'paste' in place.
*/
function resolve($node, $output)
{
$nodeDefinition = $node->getNodeDefinition();
$isLiteral = false;
$arguments = array();
$tokens = [
'function' => $this->getNodeDefinition()->getFunction(),
'operator' => $this->getNodeDefinition()->getOperator(),
];
for($i = 0; $i < count($node->inputs); $i++)
{
//Prepare the value and link
$slot = $node->getSlotDefinition(Node::INPUT_SLOT, $i); //Returns the SlotDefinition of the INPUT side.
$link = $node->getInputLink($i); //Returns the Link for the input slot at index i.
$value = $node->getBinding($i); //Returns the Literal (or null) for the binding field.
//Connected, so override value.
if ($link->node != null)
$value = $this->resolve($link->node, $link->target); //Recursive call to itself.
//Store data
$tokens["in.{$i}"] = $value;
$arguments[$slot->getName()] = $value;
}
//Return the node dependent resolution.
// If we have overwritten the resolve in the definition of the type, then it will execute that (if able).
// otherwise it will resolve the output in a default manner.
// This means, for event based nodes it will return the variable name.
// For OOP and Operator based nodes, it will return an evaluated template that can be pasted in.
$resolution = $nodeDefinition->resolve($this, $node, $arguments, $output);
if ($resolution instanceof Template)
{
//Async and Event type nodes CANNOT be compiled in this context. They must follow the flow so their results can be compiled.
// The NodeDefinition::resolve should account for this already.
if ($nodeDefinition->getStyle() == FUNCSTYLE_EVENT || $nodeDefinition->getStyle() == FUNCSTYLE_ASYNC)
throw new \Exception("Cannot compile EVENT or ASYNC nodes while resolving argument.");
//We are safe to compile.
return $this->compileTemplate($resolution, $tokens);
}
//Return the final resolution. This is either a variable, some compiled code, or a literal.
// Either way it can be just pasted in.
return $resolution;
}
class Link
{
public $node //Node The node it is connected to.
public $target //int The slot the node is connected to
}
class Template
{
public $text //string The template itself.
function evaluate($parameters)
{
$eval = $this->text;
foreach($parameters as $key => $value)
{
$token = '$' . strtolower($key) . '$';
$eval = str_replace($token, $value, $eval);
}
return $eval;
}
}
class NodeDefinition
{
function resolve($compiler, $node, $arguments, $output)
{
//We have overwritten the resolution function, so lets use that instead.
if ($this->literal != null)
{
//Prepare the value.
$val = null;
//Its not a callable (function) so it must be a raw literal itself.
if (!is_callable($this->literal))
$val = $this->literal;
//Validate we can use the override
$canExecute = true;
foreach($arguments as $arg)
{
if (!($arg instanceof Literal))
{
$canExecute = false;
break;
}
}
//If we can use it, call the function.
if ($canExecute)
$val = $this->literal($compiler, $arguments, $output);
//We have a val, so we can actually return it (otherwise fall through and find it the hard way).
if ($val != null)
{
//Make sure the value is a Literal, otherwise something broke.
if (!($val instanceof Literal))
throw new \Exception("The literal NodeDef override must return a object of type Literal.");
//Return the literal
return $val;
}
}
switch($this->style)
{
//TODO: Make these resolve the template, not just return it.
// There might be an issue with infinite loops.
default:
case self::FUNCSTYLE_OPERATOR:
case self::FUNCSTYLE_OOP:
return $this->getCompileTemplate();
//EVENT based ones are a special case. They cannot resolve early.
case self::FUNCSTYLE_ASYNC:
case self::FUNCSTYLE_EVENT:
return $compiler->getVariableName($node, $output);
}
}
/** Fetches the template. If it doesn't have one, a default one for the style will be returned.
OPERATOR: $in.0$ $operator$ $in.1$ $operator$ $in.2$ $operator$ ...
STATIC: $function$($in.0$, $in.1$, ...)
OOP: $in.0$.$function$($in.1$, $in.2$, ... )
EVENT: $in.0$.$function$($in.1$, $in.2$, ... ); $next$
ASYNC: $in.0$.$function$($in.1$, $in.2$, ... ).then(($out.0$, $out.1$, ...) => { $then$ }); $next
*/
function getCompileTemplate() : Template
{
//Get the template if it exists already.
// Note: When loading this template, it will put it into a Template object for us.
// It will also load any template strings that end in .js and use those instead.
if ($this->template != null)
return $this->template;
switch($this->stlye)
{
//...
}
}
}
/*
Thoughts:
Binding needs to be able to load .js files too. Check that.
Templates are loaded into a Template object. If they end in .js, then load that
Maybe bring loaded definition files inline with templates by changing __prototype to $prototype$
Are FUNCSTYLE_EVENT static or oop? Their async version FUNCSTYLE_ASYNC are oop, however since the FUNCSTYLE_EVENT are really only loading things like foreach and store, should they be static by default?
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment