/** * Visit an element. * * @param \Hoa\Visitor\Element $element Element to visit. * @param mixed &$handle Handle (reference). * @param mixed $eldnah Handle (not reference). * @return mixed */ public function visit(Visitor\Element $element, &$handle = null, $eldnah = null) { $out = null; // Hoa\Praspel. if ($element instanceof Praspel\Model\Specification) { $variable = '$' . $element->getId(); $out = $variable . ' = new \\Hoa\\Praspel\\Model\\Specification();' . "\n"; foreach ($element::getAllowedClauses() as $clause) { if (true === $element->clauseExists($clause)) { $out .= $element->getClause($clause)->accept($this, $handle, $eldnah); } } } elseif ($element instanceof Praspel\Model\Is) { $variable = '$' . $element->getParent()->getId(); $out = "\n" . $variable . '->getClause(\'is\')->setProperty(' . $element->getProperty() . ');' . "\n"; } elseif ($element instanceof Praspel\Model\Declaration) { $variable = '$' . ($eldnah ?: $element->getId()); $out = "\n" . $variable . ' = $' . $element->getParent()->getId() . '->getClause(\'' . $element->getName() . '\');' . "\n"; foreach ($element->getLocalVariables() as $var) { $out .= $var->accept($this, $handle, $eldnah); } foreach ($element->getPredicates() as $predicate) { $out .= $variable . '->predicate(\'' . $predicate . '\');' . "\n"; } } elseif ($element instanceof Praspel\Model\Variable) { $variable = '$' . ($eldnah ?: $element->getClause()->getId()); $name = $element->getName(); $start = $variable . '[\'' . $name . '\']'; if (true === $element->isLocal()) { $out .= $variable . '->let[\'' . $name . '\']'; } else { $out .= $start; } if (null !== ($alias = $element->getAlias())) { $out .= '->domainof(\'' . $alias . '\');' . "\n"; } else { $out .= '->in = ' . $element->getDomains()->accept($this, $handle, $eldnah) . ';' . "\n"; } $constraints = $element->getConstraints(); if (isset($constraints['is'])) { $out .= $start . '->is(\'' . implode('\', \'', $constraints['is']) . '\');' . "\n"; } if (isset($constraints['contains'])) { foreach ($constraints['contains'] as $contains) { $out .= $start . '->contains(' . $contains . ');' . "\n"; } } if (isset($constraints['key'])) { foreach ($constraints['key'] as $pairs) { $out .= $start . '->key(' . $pairs[0] . ')->in = ' . $pairs[1] . ';' . "\n"; } } } elseif ($element instanceof Praspel\Model\Throwable) { $parent = '$' . $element->getParent()->getId(); $_variable = $element->getId(); $variable = '$' . $_variable; $out = "\n" . $variable . ' = ' . $parent . '->getClause(\'throwable\');' . "\n"; foreach ($element as $identifier) { $exception = $element[$identifier]; $start = $variable . '[\'' . $identifier . '\']'; $out .= $start . ' = \'' . $exception->getInstanceName() . '\';' . "\n"; if (false === $element->isDisjointed()) { if (null !== ($with = $element->getWith())) { $temp = $_variable . '_' . $identifier . '_with'; $out .= '$' . $temp . ' = ' . $variable . '->newWith();' . "\n"; foreach ($with->getLocalVariables() as $var) { $out .= $var->accept($this, $handle, $temp); } foreach ($with->getPredicates() as $predicate) { $out .= '$' . $temp . '->predicate(\'' . $predicate . '\');' . "\n"; } $out .= $start . '->setWith($' . $temp . ');' . "\n"; } } else { $out .= $start . '->disjunctionWith(\'' . $exception->getDisjunction() . '\');' . "\n"; } } } elseif ($element instanceof Praspel\Model\DefaultBehavior) { $out = "\n" . '$' . $element->getId() . ' = $' . $element->getParent()->getId() . '->getClause(\'default\')' . "\n"; foreach ($element::getAllowedClauses() as $clause) { if (true === $element->clauseExists($clause)) { $out .= $element->getClause($clause)->accept($this, $handle, $eldnah); } } } elseif ($element instanceof Praspel\Model\Behavior) { $out = "\n" . '$' . $element->getId() . ' = $' . $element->getParent()->getId() . '->getClause(\'behavior\')' . '->get(\'' . $element->getIdentifier() . '\');' . "\n"; foreach ($element::getAllowedClauses() as $clause) { if (true === $element->clauseExists($clause)) { $out .= $element->getClause($clause)->accept($this, $handle, $eldnah); } } } elseif ($element instanceof Praspel\Model\Description) { $parent = '$' . $element->getParent()->getId(); $variable = '$' . $element->getId(); $out = "\n" . $variable . ' = ' . $parent . '->getClause(\'description\');' . "\n"; foreach ($element as $example) { $out .= $variable . '[] = \'' . preg_replace('#(?<!\\\\)\'#', '\\\'', $example) . '\';' . "\n"; } } elseif ($element instanceof Praspel\Model\Collection) { foreach ($element as $el) { $out .= $el->accept($this, $handle, $eldnah); } } elseif ($element instanceof Realdom\Disjunction) { $realdoms = $element->getUnflattenedRealdoms(); if (!empty($realdoms)) { $oout = []; foreach ($realdoms as $realdom) { if ($realdom instanceof Realdom\IRealdom\Constant) { $oout[] = 'const(' . $realdom->accept($this, $handle, $eldnah) . ')'; } else { $oout[] = $realdom->accept($this, $handle, $eldnah); } } $out .= 'realdom()->' . implode('->or->', $oout); } } elseif ($element instanceof Realdom) { if ($element instanceof Realdom\IRealdom\Constant) { if ($element instanceof Realdom\_Array) { $oout = []; foreach ($element['pairs'] as $pair) { $_oout = null; foreach ($pair as $_pair) { if (null !== $_oout) { $_oout .= ', '; } $_oout .= $_pair->accept($this, $handle, $eldnah); } $oout[] = 'array(' . $_oout . ')'; } $out .= 'array(' . implode(', ', $oout) . ')'; } else { $out .= $element->getConstantRepresentation(); } } else { $oout = []; foreach ($element->getArguments() as $argument) { $oout[] = $argument->accept($this, $handle, $eldnah); } $out .= $element->getName() . '(' . implode(', ', $oout) . ')'; } } elseif ($element instanceof Realdom\Crate\Constant) { $holder = $element->getHolder(); $praspel = $element->getPraspelRepresentation(); $out .= '$' . $element->getDeclaration()->getId() . '[\'' . $praspel() . '\']'; } elseif ($element instanceof Realdom\Crate\Variable) { $holder = $element->getVariable(); if ($holder instanceof Praspel\Model\Variable\Implicit) { $out .= 'variable($' . $holder->getClause()->getId() . '->getImplicitVariable(\'' . $holder->getName() . '\'))'; } else { $out .= 'variable($' . $holder->getClause()->getId() . '->getVariable(\'' . $holder->getName() . '\', true))'; } } else { throw new Praspel\Exception\Compiler('%s is not yet implemented.', 0, get_class($element)); } return $out; }
/** * Visit an element. * * @param \Hoa\Visitor\Element $element Element to visit. * @param mixed &$handle Handle (reference). * @param mixed $eldnah Handle (not reference). * @return float */ public function visit(Visitor\Element $element, &$handle = null, $eldnah = null) { $type = $element->getId(); $children = $element->getChildren(); if (null === $handle) { $handle = function ($x) { return $x; }; } $acc =& $handle; switch ($type) { case '#function': $name = array_shift($children)->accept($this, $_, $eldnah); $function = $this->getFunction($name); $arguments = []; foreach ($children as $child) { $child->accept($this, $_, $eldnah); $arguments[] = $_(); unset($_); } $acc = function () use($function, $arguments, $acc) { return $acc($function->distributeArguments($arguments)); }; break; case '#negative': $children[0]->accept($this, $a, $eldnah); $acc = function () use($a, $acc) { return $acc(-$a()); }; break; case '#addition': $children[0]->accept($this, $a, $eldnah); $acc = function ($b) use($a, $acc) { return $acc($a() + $b); }; $children[1]->accept($this, $acc, $eldnah); break; case '#substraction': $children[0]->accept($this, $a, $eldnah); $acc = function ($b) use($a, $acc) { return $acc($a()) - $b; }; $children[1]->accept($this, $acc, $eldnah); break; case '#multiplication': $children[0]->accept($this, $a, $eldnah); $acc = function ($b) use($a, $acc) { return $acc($a() * $b); }; $children[1]->accept($this, $acc, $eldnah); break; case '#division': $children[0]->accept($this, $a, $eldnah); $parent = $element->getParent(); if (null === $parent || $type === $parent->getId()) { $acc = function ($b) use($a, $acc) { if (0 === $b) { throw new \RuntimeException('Division by zero is not possible.'); } return $acc($a()) / $b; }; } else { if ('#fakegroup' !== $parent->getId()) { $classname = get_class($element); $group = new $classname('#fakegroup', null, [$element], $parent); $element->setParent($group); $this->visit($group, $acc, $eldnah); break; } else { $acc = function ($b) use($a, $acc) { if (0 === $b) { throw new \RuntimeException('Division by zero is not possible.'); } return $acc($a() / $b); }; } } $children[1]->accept($this, $acc, $eldnah); break; case '#fakegroup': case '#group': $children[0]->accept($this, $a, $eldnah); $acc = function () use($a, $acc) { return $acc($a()); }; break; case '#variable': $out = $this->getVariable($children[0]->getValueValue()); $acc = function () use($out, $acc) { return $acc($out); }; break; case 'token': $value = $element->getValueValue(); $out = null; if ('constant' === $element->getValueToken()) { if (defined($value)) { $out = constant($value); } else { $out = $this->getConstant($value); } } elseif ('id' === $element->getValueToken()) { return $value; } else { $out = (double) $value; } $acc = function () use($out, $acc) { return $acc($out); }; break; } if (null === $element->getParent()) { return $acc(); } }
/** * Evaluate given AST as CollectiveAccess expression * * @param Visitor\Element $po_element * @param Hoa\Core\Consistency\Xcallable $f_handle * @param Hoa\Core\Consistency\Xcallable $f_eldnah * @return mixed */ public function visit(Visitor\Element $po_element, &$f_handle = null, $f_eldnah = null) { $vs_type = $po_element->getId(); $va_children = $po_element->getChildren(); // if no handle passed, use identity if ($f_handle === null) { $f_handle = function ($x) { return $x; }; } $f_acc =& $f_handle; switch ($vs_type) { case '#function': $vs_name = array_shift($va_children)->accept($this, $_, $f_eldnah); $f_function = $this->getFunction($vs_name); $va_args = array(); foreach ($va_children as $o_child) { $o_child->accept($this, $_, $f_eldnah); $va_args[] = $_(); unset($_); } $f_acc = function () use($f_function, $va_args, $f_acc) { return $f_acc($f_function->distributeArguments($va_args)); }; break; case '#bool_and': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() && $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#bool_or': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() || $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#regex_match': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc((bool) @preg_match($b, $a())); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#regex_nomatch': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc(!(bool) @preg_match($b, $a())); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#comp_gt': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() > $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#comp_gte': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() >= $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#comp_lt': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() < $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#comp_lte': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() <= $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#comp_neq': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() != $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#comp_eq': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() == $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#in_op': $vs_needle = array_shift($va_children)->accept($this, $in, $f_eldnah); $va_haystack = array(); $in = $f_handle; $o_op = array_shift($va_children); if ($o_op->getValueToken() !== 'in_op') { throw new Exception('invalid syntax'); } foreach ($va_children as $o_child) { $o_child->accept($this, $in, $f_eldnah); $va_haystack[] = $in(); unset($in); } $f_acc = function () use($vs_needle, $va_haystack, $f_acc) { return $f_acc(in_array($vs_needle, $va_haystack)); }; break; case '#notin_op': $vs_needle = array_shift($va_children)->accept($this, $in, $f_eldnah); $va_haystack = array(); $in = $f_handle; $o_op = array_shift($va_children); if ($o_op->getValueToken() !== 'notin_op') { throw new Exception('invalid syntax'); } foreach ($va_children as $o_child) { $o_child->accept($this, $in, $f_eldnah); $va_haystack[] = $in(); unset($in); } $f_acc = function () use($vs_needle, $va_haystack, $f_acc) { return $f_acc(!in_array($vs_needle, $va_haystack)); }; break; case '#stradd': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() . $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#negative': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function () use($a, $f_acc) { return $f_acc(-$a()); }; break; case '#addition': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() + $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#substraction': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a()) - $b; }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#multiplication': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function ($b) use($a, $f_acc) { return $f_acc($a() * $b); }; $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#division': $va_children[0]->accept($this, $a, $f_eldnah); $parent = $po_element->getParent(); if (null === $parent || $type === $parent->getId()) { $f_acc = function ($b) use($a, $f_acc) { if (0 === $b) { throw new \RuntimeException('Division by zero is not possible.'); } return $f_acc($a()) / $b; }; } else { if ('#fakegroup' !== $parent->getId()) { $classname = get_class($po_element); $group = new $classname('#fakegroup', null, [$po_element], $parent); $po_element->setParent($group); $this->visit($group, $f_acc, $f_eldnah); break; } else { $f_acc = function ($b) use($a, $f_acc) { if (0 === $b) { throw new \RuntimeException('Division by zero is not possible.'); } return $f_acc($a() / $b); }; } } $va_children[1]->accept($this, $f_acc, $f_eldnah); break; case '#fakegroup': case '#group': $va_children[0]->accept($this, $a, $f_eldnah); $f_acc = function () use($a, $f_acc) { return $f_acc($a()); }; break; case 'token': $value = $po_element->getValueValue(); $token = $po_element->getValueToken(); $out = null; switch ($token) { case 'id': return $value; case 'string': $out = preg_replace('/(^"|"$)/', '', $value); break; case 'regex': // @todo maybe mangle regex? $out = (string) $value; break; case 'variable': $vs_var = mb_substr((string) $value, 1); // get rid of caret ^ $out = $this->getVariable($vs_var); if (is_array($out)) { $out = join("", $out); } // we need to join multiple values break; default: $out = (double) $value; break; } $f_acc = function () use($out, $f_acc) { return $f_acc($out); }; break; } return $f_acc(); }