[ExpressionLanguage] Added a way to dump AST
This commit is contained in:
parent
e7077605bb
commit
87af6e5ae3
@ -24,4 +24,15 @@ class ArgumentsNode extends ArrayNode
|
|||||||
{
|
{
|
||||||
$this->compileArguments($compiler, false);
|
$this->compileArguments($compiler, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
$str = '';
|
||||||
|
|
||||||
|
foreach ($this->getKeyValuePairs() as $pair) {
|
||||||
|
$str .= sprintf('%s, ', $pair['value']->dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($str, ', ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,36 @@ class ArrayNode extends Node
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
$array = array();
|
||||||
|
foreach ($this->getKeyValuePairs() as $pair) {
|
||||||
|
$array[$pair['key']->attributes['value']] = $pair['value']->dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isHash($array)) {
|
||||||
|
$str = '{';
|
||||||
|
|
||||||
|
foreach ($array as $key => $value) {
|
||||||
|
if (is_int($key)) {
|
||||||
|
$str .= sprintf('%s: %s, ', $key, $value);
|
||||||
|
} else {
|
||||||
|
$str .= sprintf('"%s": %s, ', $this->dumpEscaped($key), $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($str, ', ').'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
$str = '[';
|
||||||
|
|
||||||
|
foreach ($array as $key => $value) {
|
||||||
|
$str .= sprintf('%s, ', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($str, ', ').']';
|
||||||
|
}
|
||||||
|
|
||||||
protected function getKeyValuePairs()
|
protected function getKeyValuePairs()
|
||||||
{
|
{
|
||||||
$pairs = array();
|
$pairs = array();
|
||||||
|
@ -154,4 +154,9 @@ class BinaryNode extends Node
|
|||||||
return preg_match($right, $left);
|
return preg_match($right, $left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
return sprintf('(%s %s %s)', $this->nodes['left']->dump(), $this->attributes['operator'], $this->nodes['right']->dump());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,4 +48,9 @@ class ConditionalNode extends Node
|
|||||||
|
|
||||||
return $this->nodes['expr3']->evaluate($functions, $values);
|
return $this->nodes['expr3']->evaluate($functions, $values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
return sprintf('(%s ? %s : %s)', $this->nodes['expr1']->dump(), $this->nodes['expr2']->dump(), $this->nodes['expr3']->dump());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,4 +37,52 @@ class ConstantNode extends Node
|
|||||||
{
|
{
|
||||||
return $this->attributes['value'];
|
return $this->attributes['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
return $this->dumpValue($this->attributes['value']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dumpValue($value)
|
||||||
|
{
|
||||||
|
switch (true) {
|
||||||
|
case true === $value:
|
||||||
|
return 'true';
|
||||||
|
|
||||||
|
case false === $value:
|
||||||
|
return 'false';
|
||||||
|
|
||||||
|
case null === $value:
|
||||||
|
return 'null';
|
||||||
|
|
||||||
|
case is_numeric($value):
|
||||||
|
return $value;
|
||||||
|
|
||||||
|
case is_array($value):
|
||||||
|
if ($this->isHash($value)) {
|
||||||
|
$str = '{';
|
||||||
|
|
||||||
|
foreach ($value as $key => $v) {
|
||||||
|
if (is_int($key)) {
|
||||||
|
$str .= sprintf('%s: %s, ', $key, $this->dumpValue($v));
|
||||||
|
} else {
|
||||||
|
$str .= sprintf('"%s": %s, ', $this->dumpEscaped($key), $this->dumpValue($v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($str, ', ').'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
$str = '[';
|
||||||
|
|
||||||
|
foreach ($value as $key => $v) {
|
||||||
|
$str .= sprintf('%s, ', $this->dumpValue($v));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($str, ', ').']';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return sprintf('"%s"', $this->dumpEscaped($value));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,4 +49,17 @@ class FunctionNode extends Node
|
|||||||
|
|
||||||
return call_user_func_array($functions[$this->attributes['name']]['evaluator'], $arguments);
|
return call_user_func_array($functions[$this->attributes['name']]['evaluator'], $arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
$str = $this->attributes['name'];
|
||||||
|
|
||||||
|
$str .= '(';
|
||||||
|
|
||||||
|
foreach ($this->nodes['arguments']->nodes as $node) {
|
||||||
|
$str .= $node->dump().', ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($str, ', ').')';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,4 +94,18 @@ class GetAttrNode extends Node
|
|||||||
return $array[$this->nodes['attribute']->evaluate($functions, $values)];
|
return $array[$this->nodes['attribute']->evaluate($functions, $values)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
switch ($this->attributes['type']) {
|
||||||
|
case self::PROPERTY_CALL:
|
||||||
|
return sprintf('%s.%s', $this->nodes['node']->dump(), trim($this->nodes['attribute']->dump(), '"'));
|
||||||
|
|
||||||
|
case self::METHOD_CALL:
|
||||||
|
return sprintf('%s.%s(%s)', $this->nodes['node']->dump(), trim($this->nodes['attribute']->dump(), '"'), $this->nodes['arguments']->dump());
|
||||||
|
|
||||||
|
case self::ARRAY_CALL:
|
||||||
|
return sprintf('%s[%s]', $this->nodes['node']->dump(), $this->nodes['attribute']->dump());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,4 +37,9 @@ class NameNode extends Node
|
|||||||
{
|
{
|
||||||
return $values[$this->attributes['name']];
|
return $values[$this->attributes['name']];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
return $this->attributes['name'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,4 +75,27 @@ class Node
|
|||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException(sprintf('Dumping a "%s" instance is not supported yet.', get_class($this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dumpEscaped($value)
|
||||||
|
{
|
||||||
|
return str_replace(array('\\', '"'), array('\\\\', '\"'), $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isHash(array $value)
|
||||||
|
{
|
||||||
|
$expectedKey = 0;
|
||||||
|
|
||||||
|
foreach ($value as $key => $val) {
|
||||||
|
if ($key !== $expectedKey++) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,4 +58,9 @@ class UnaryNode extends Node
|
|||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
return sprintf('(%s %s)', $this->attributes['operator'], $this->nodes['node']->dump());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,4 +39,9 @@ class ParsedExpression extends Expression
|
|||||||
{
|
{
|
||||||
return $this->nodes;
|
return $this->nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
return $this->nodes->dump();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,4 +36,14 @@ abstract class AbstractNodeTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract public function getCompileData();
|
abstract public function getCompileData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getDumpData
|
||||||
|
*/
|
||||||
|
public function testDump($expected, $node)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, $node->dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getDumpData();
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,13 @@ class ArgumentsNodeTest extends ArrayNodeTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('"a", "b"', $this->getArrayNode()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected function createArrayNode()
|
protected function createArrayNode()
|
||||||
{
|
{
|
||||||
return new ArgumentsNode();
|
return new ArgumentsNode();
|
||||||
|
@ -42,6 +42,21 @@ class ArrayNodeTest extends AbstractNodeTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
yield array('{"b": "a", 0: "b"}', $this->getArrayNode());
|
||||||
|
|
||||||
|
$array = $this->createArrayNode();
|
||||||
|
$array->addElement(new ConstantNode('c'), new ConstantNode('a"b'));
|
||||||
|
$array->addElement(new ConstantNode('d'), new ConstantNode('a\b'));
|
||||||
|
yield array('{"a\\"b": "c", "a\\\\b": "d"}', $array);
|
||||||
|
|
||||||
|
$array = $this->createArrayNode();
|
||||||
|
$array->addElement(new ConstantNode('c'));
|
||||||
|
$array->addElement(new ConstantNode('d'));
|
||||||
|
yield array('["c", "d"]', $array);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getArrayNode()
|
protected function getArrayNode()
|
||||||
{
|
{
|
||||||
$array = $this->createArrayNode();
|
$array = $this->createArrayNode();
|
||||||
|
@ -114,4 +114,53 @@ class BinaryNodeTest extends AbstractNodeTest
|
|||||||
array('preg_match("/^[a-z]+/i\$/", "abc")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))),
|
array('preg_match("/^[a-z]+/i\$/", "abc")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
$array = new ArrayNode();
|
||||||
|
$array->addElement(new ConstantNode('a'));
|
||||||
|
$array->addElement(new ConstantNode('b'));
|
||||||
|
|
||||||
|
return array(
|
||||||
|
array('(true or false)', new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))),
|
||||||
|
array('(true || false)', new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))),
|
||||||
|
array('(true and false)', new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))),
|
||||||
|
array('(true && false)', new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))),
|
||||||
|
|
||||||
|
array('(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))),
|
||||||
|
array('(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))),
|
||||||
|
array('(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))),
|
||||||
|
|
||||||
|
array('(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
array('(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
array('(1 <= 1)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(1))),
|
||||||
|
|
||||||
|
array('(1 > 2)', new BinaryNode('>', new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
array('(1 >= 2)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
array('(1 >= 1)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(1))),
|
||||||
|
|
||||||
|
array('(true === true)', new BinaryNode('===', new ConstantNode(true), new ConstantNode(true))),
|
||||||
|
array('(true !== true)', new BinaryNode('!==', new ConstantNode(true), new ConstantNode(true))),
|
||||||
|
|
||||||
|
array('(2 == 1)', new BinaryNode('==', new ConstantNode(2), new ConstantNode(1))),
|
||||||
|
array('(2 != 1)', new BinaryNode('!=', new ConstantNode(2), new ConstantNode(1))),
|
||||||
|
|
||||||
|
array('(1 - 2)', new BinaryNode('-', new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
array('(1 + 2)', new BinaryNode('+', new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
array('(2 * 2)', new BinaryNode('*', new ConstantNode(2), new ConstantNode(2))),
|
||||||
|
array('(2 / 2)', new BinaryNode('/', new ConstantNode(2), new ConstantNode(2))),
|
||||||
|
array('(5 % 2)', new BinaryNode('%', new ConstantNode(5), new ConstantNode(2))),
|
||||||
|
array('(5 ** 2)', new BinaryNode('**', new ConstantNode(5), new ConstantNode(2))),
|
||||||
|
array('("a" ~ "b")', new BinaryNode('~', new ConstantNode('a'), new ConstantNode('b'))),
|
||||||
|
|
||||||
|
array('("a" in ["a", "b"])', new BinaryNode('in', new ConstantNode('a'), $array)),
|
||||||
|
array('("c" in ["a", "b"])', new BinaryNode('in', new ConstantNode('c'), $array)),
|
||||||
|
array('("c" not in ["a", "b"])', new BinaryNode('not in', new ConstantNode('c'), $array)),
|
||||||
|
array('("a" not in ["a", "b"])', new BinaryNode('not in', new ConstantNode('a'), $array)),
|
||||||
|
|
||||||
|
array('(1 .. 3)', new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))),
|
||||||
|
|
||||||
|
array('("abc" matches "/^[a-z]+/i$/")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,4 +31,12 @@ class ConditionalNodeTest extends AbstractNodeTest
|
|||||||
array('((false) ? (1) : (2))', new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))),
|
array('((false) ? (1) : (2))', new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('(true ? 1 : 2)', new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
array('(false ? 1 : 2)', new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,4 +40,20 @@ class ConstantNodeTest extends AbstractNodeTest
|
|||||||
array('array(0 => 1, "b" => "a")', new ConstantNode(array(1, 'b' => 'a'))),
|
array('array(0 => 1, "b" => "a")', new ConstantNode(array(1, 'b' => 'a'))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('false', new ConstantNode(false)),
|
||||||
|
array('true', new ConstantNode(true)),
|
||||||
|
array('null', new ConstantNode(null)),
|
||||||
|
array(3, new ConstantNode(3)),
|
||||||
|
array(3.3, new ConstantNode(3.3)),
|
||||||
|
array('"foo"', new ConstantNode('foo')),
|
||||||
|
array('{0: 1, "b": "a", 1: true}', new ConstantNode(array(1, 'b' => 'a', true))),
|
||||||
|
array('{"a\\"b": "c", "a\\\\b": "d"}', new ConstantNode(array('a"b' => 'c', 'a\\b' => 'd'))),
|
||||||
|
array('["c", "d"]', new ConstantNode(array('c', 'd'))),
|
||||||
|
array('{"a": ["b"]}', new ConstantNode(array('a' => array('b')))),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,13 @@ class FunctionNodeTest extends AbstractNodeTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('foo("bar")', new FunctionNode('foo', new Node(array(new ConstantNode('bar')))), array('foo' => $this->getCallables())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getCallables()
|
protected function getCallables()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
|
@ -44,6 +44,19 @@ class GetAttrNodeTest extends AbstractNodeTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('foo[0]', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)),
|
||||||
|
array('foo["b"]', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)),
|
||||||
|
|
||||||
|
array('foo.foo', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), array('foo' => new Obj())),
|
||||||
|
|
||||||
|
array('foo.foo({"b": "a", 0: "b"})', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), array('foo' => new Obj())),
|
||||||
|
array('foo[index]', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getArrayNode()
|
protected function getArrayNode()
|
||||||
{
|
{
|
||||||
$array = new ArrayNode();
|
$array = new ArrayNode();
|
||||||
|
@ -28,4 +28,11 @@ class NameNodeTest extends AbstractNodeTest
|
|||||||
array('$foo', new NameNode('foo')),
|
array('$foo', new NameNode('foo')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('foo', new NameNode('foo')),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,4 +35,14 @@ class UnaryNodeTest extends AbstractNodeTest
|
|||||||
array('(!true)', new UnaryNode('not', new ConstantNode(true))),
|
array('(!true)', new UnaryNode('not', new ConstantNode(true))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDumpData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('(- 1)', new UnaryNode('-', new ConstantNode(1))),
|
||||||
|
array('(+ 3)', new UnaryNode('+', new ConstantNode(3))),
|
||||||
|
array('(! true)', new UnaryNode('!', new ConstantNode(true))),
|
||||||
|
array('(not true)', new UnaryNode('not', new ConstantNode(true))),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user