[ExpressionLanguage] Add a way to hook on each node when dumping the AST
This commit is contained in:
parent
f400f01ecc
commit
66d23dbef6
|
@ -25,14 +25,16 @@ class ArgumentsNode extends ArrayNode
|
|||
$this->compileArguments($compiler, false);
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
$str = '';
|
||||
$array = array();
|
||||
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
$str .= sprintf('%s, ', $pair['value']->dump());
|
||||
$array[] = $pair['value'];
|
||||
$array[] = ', ';
|
||||
}
|
||||
array_pop($array);
|
||||
|
||||
return rtrim($str, ', ');
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,34 +58,34 @@ class ArrayNode extends Node
|
|||
return $result;
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
$array = array();
|
||||
$value = array();
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
$array[$pair['key']->attributes['value']] = $pair['value']->dump();
|
||||
$value[$pair['key']->attributes['value']] = $pair['value'];
|
||||
}
|
||||
|
||||
if ($this->isHash($array)) {
|
||||
$str = '{';
|
||||
$array = array();
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
if (is_int($key)) {
|
||||
$str .= sprintf('%s: %s, ', $key, $value);
|
||||
} else {
|
||||
$str .= sprintf('"%s": %s, ', $this->dumpEscaped($key), $value);
|
||||
}
|
||||
if ($this->isHash($value)) {
|
||||
foreach ($value as $k => $v) {
|
||||
$array[] = ', ';
|
||||
$array[] = new ConstantNode($k);
|
||||
$array[] = ': ';
|
||||
$array[] = $v;
|
||||
}
|
||||
|
||||
return rtrim($str, ', ').'}';
|
||||
$array[0] = '{';
|
||||
$array[] = '}';
|
||||
} else {
|
||||
foreach ($value as $v) {
|
||||
$array[] = ', ';
|
||||
$array[] = $v;
|
||||
}
|
||||
$array[0] = '[';
|
||||
$array[] = ']';
|
||||
}
|
||||
|
||||
$str = '[';
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
$str .= sprintf('%s, ', $value);
|
||||
}
|
||||
|
||||
return rtrim($str, ', ').']';
|
||||
return $array;
|
||||
}
|
||||
|
||||
protected function getKeyValuePairs()
|
||||
|
|
|
@ -155,8 +155,8 @@ class BinaryNode extends Node
|
|||
}
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
return sprintf('(%s %s %s)', $this->nodes['left']->dump(), $this->attributes['operator'], $this->nodes['right']->dump());
|
||||
return array('(', $this->nodes['left'], ' '.$this->attributes['operator'].' ', $this->nodes['right'], ')');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,8 +49,8 @@ class ConditionalNode extends Node
|
|||
return $this->nodes['expr3']->evaluate($functions, $values);
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
return sprintf('(%s ? %s : %s)', $this->nodes['expr1']->dump(), $this->nodes['expr2']->dump(), $this->nodes['expr3']->dump());
|
||||
return array('(', $this->nodes['expr1'], ' ? ', $this->nodes['expr2'], ' : ', $this->nodes['expr3'], ')');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,51 +38,39 @@ class ConstantNode extends Node
|
|||
return $this->attributes['value'];
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
return $this->dumpValue($this->attributes['value']);
|
||||
}
|
||||
$array = array();
|
||||
$value = $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));
|
||||
if (true === $value) {
|
||||
$array[] = 'true';
|
||||
} elseif (false === $value) {
|
||||
$array[] = 'false';
|
||||
} elseif (null === $value) {
|
||||
$array[] = 'null';
|
||||
} elseif (is_numeric($value)) {
|
||||
$array[] = $value;
|
||||
} elseif (!is_array($value)) {
|
||||
$array[] = $this->dumpString($value);
|
||||
} elseif ($this->isHash($value)) {
|
||||
foreach ($value as $k => $v) {
|
||||
$array[] = ', ';
|
||||
$array[] = new self($k);
|
||||
$array[] = ': ';
|
||||
$array[] = new self($v);
|
||||
}
|
||||
$array[0] = '{';
|
||||
$array[] = '}';
|
||||
} else {
|
||||
foreach ($value as $v) {
|
||||
$array[] = ', ';
|
||||
$array[] = new self($v);
|
||||
}
|
||||
$array[0] = '[';
|
||||
$array[] = ']';
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,16 +50,18 @@ class FunctionNode extends Node
|
|||
return call_user_func_array($functions[$this->attributes['name']]['evaluator'], $arguments);
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
$str = $this->attributes['name'];
|
||||
|
||||
$str .= '(';
|
||||
$array = array();
|
||||
$array[] = $this->attributes['name'];
|
||||
|
||||
foreach ($this->nodes['arguments']->nodes as $node) {
|
||||
$str .= $node->dump().', ';
|
||||
$array[] = ', ';
|
||||
$array[] = $node;
|
||||
}
|
||||
$array[1] = '(';
|
||||
$array[] = ')';
|
||||
|
||||
return rtrim($str, ', ').')';
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class GetAttrNode extends Node
|
|||
$compiler
|
||||
->compile($this->nodes['node'])
|
||||
->raw('->')
|
||||
->raw($this->nodes['attribute']->attributes['value'])
|
||||
->raw($this->nodes['attribute']->attributes['name'])
|
||||
;
|
||||
break;
|
||||
|
||||
|
@ -47,7 +47,7 @@ class GetAttrNode extends Node
|
|||
$compiler
|
||||
->compile($this->nodes['node'])
|
||||
->raw('->')
|
||||
->raw($this->nodes['attribute']->attributes['value'])
|
||||
->raw($this->nodes['attribute']->attributes['name'])
|
||||
->raw('(')
|
||||
->compile($this->nodes['arguments'])
|
||||
->raw(')')
|
||||
|
@ -73,7 +73,7 @@ class GetAttrNode extends Node
|
|||
throw new \RuntimeException('Unable to get a property on a non-object.');
|
||||
}
|
||||
|
||||
$property = $this->nodes['attribute']->attributes['value'];
|
||||
$property = $this->nodes['attribute']->attributes['name'];
|
||||
|
||||
return $obj->$property;
|
||||
|
||||
|
@ -83,7 +83,7 @@ class GetAttrNode extends Node
|
|||
throw new \RuntimeException('Unable to get a property on a non-object.');
|
||||
}
|
||||
|
||||
return call_user_func_array(array($obj, $this->nodes['attribute']->attributes['value']), $this->nodes['arguments']->evaluate($functions, $values));
|
||||
return call_user_func_array(array($obj, $this->nodes['attribute']->attributes['name']), $this->nodes['arguments']->evaluate($functions, $values));
|
||||
|
||||
case self::ARRAY_CALL:
|
||||
$array = $this->nodes['node']->evaluate($functions, $values);
|
||||
|
@ -95,17 +95,17 @@ class GetAttrNode extends Node
|
|||
}
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
switch ($this->attributes['type']) {
|
||||
case self::PROPERTY_CALL:
|
||||
return sprintf('%s.%s', $this->nodes['node']->dump(), trim($this->nodes['attribute']->dump(), '"'));
|
||||
return array($this->nodes['node'], '.', $this->nodes['attribute']);
|
||||
|
||||
case self::METHOD_CALL:
|
||||
return sprintf('%s.%s(%s)', $this->nodes['node']->dump(), trim($this->nodes['attribute']->dump(), '"'), $this->nodes['arguments']->dump());
|
||||
return array($this->nodes['node'], '.', $this->nodes['attribute'], '(', $this->nodes['arguments'], ')');
|
||||
|
||||
case self::ARRAY_CALL:
|
||||
return sprintf('%s[%s]', $this->nodes['node']->dump(), $this->nodes['attribute']->dump());
|
||||
return array($this->nodes['node'], '[', $this->nodes['attribute'], ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ class NameNode extends Node
|
|||
return $values[$this->attributes['name']];
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
return $this->attributes['name'];
|
||||
return array($this->attributes['name']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,14 +76,14 @@ class Node
|
|||
return $results;
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
throw new \BadMethodCallException(sprintf('Dumping a "%s" instance is not supported yet.', get_class($this)));
|
||||
}
|
||||
|
||||
protected function dumpEscaped($value)
|
||||
protected function dumpString($value)
|
||||
{
|
||||
return str_replace(array('\\', '"'), array('\\\\', '\"'), $value);
|
||||
return sprintf('"%s"', addcslashes($value, "\0\t\"\\"));
|
||||
}
|
||||
|
||||
protected function isHash(array $value)
|
||||
|
|
|
@ -59,8 +59,8 @@ class UnaryNode extends Node
|
|||
return $value;
|
||||
}
|
||||
|
||||
public function dump()
|
||||
public function toArray()
|
||||
{
|
||||
return sprintf('(%s %s)', $this->attributes['operator'], $this->nodes['node']->dump());
|
||||
return array('(', $this->attributes['operator'].' ', $this->nodes['node'], ')');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,17 @@ class ParsedExpression extends Expression
|
|||
|
||||
public function dump()
|
||||
{
|
||||
return $this->nodes->dump();
|
||||
return $this->dumpNode($this->nodes);
|
||||
}
|
||||
|
||||
private function dumpNode(Node $node)
|
||||
{
|
||||
$dump = '';
|
||||
|
||||
foreach($node->toArray() as $v) {
|
||||
$dump .= is_scalar($v) ? $v : $this->dumpNode($v);
|
||||
}
|
||||
|
||||
return $dump;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -330,7 +330,7 @@ class Parser
|
|||
throw new SyntaxError('Expected name', $token->cursor);
|
||||
}
|
||||
|
||||
$arg = new Node\ConstantNode($token->value);
|
||||
$arg = new Node\NameNode($token->value);
|
||||
|
||||
$arguments = new Node\ArgumentsNode();
|
||||
if ($this->stream->current->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
namespace Symfony\Component\ExpressionLanguage\Tests\Node;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\Compiler;
|
||||
use Symfony\Component\ExpressionLanguage\ParsedExpression;
|
||||
|
||||
abstract class AbstractNodeTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
@ -42,7 +43,8 @@ abstract class AbstractNodeTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testDump($expected, $node)
|
||||
{
|
||||
$this->assertSame($expected, $node->dump());
|
||||
$expr = new ParsedExpression($expected, $node);
|
||||
$this->assertSame($expected, $expr->dump());
|
||||
}
|
||||
|
||||
abstract public function getDumpData();
|
||||
|
|
|
@ -47,8 +47,8 @@ class ConstantNodeTest extends AbstractNodeTest
|
|||
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('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'))),
|
||||
|
|
|
@ -24,9 +24,9 @@ class GetAttrNodeTest extends AbstractNodeTest
|
|||
array('b', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), array('foo' => array('b' => 'a', 'b'))),
|
||||
array('a', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), array('foo' => array('b' => 'a', 'b'))),
|
||||
|
||||
array('bar', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), array('foo' => new Obj())),
|
||||
array('bar', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), array('foo' => new Obj())),
|
||||
|
||||
array('baz', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), array('foo' => new Obj())),
|
||||
array('baz', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), array('foo' => new Obj())),
|
||||
array('a', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), array('foo' => array('b' => 'a', 'b'), 'index' => 'b')),
|
||||
);
|
||||
}
|
||||
|
@ -37,9 +37,9 @@ class GetAttrNodeTest extends AbstractNodeTest
|
|||
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', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), array('foo' => new Obj())),
|
||||
|
||||
array('$foo->foo(array("b" => "a", 0 => "b"))', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), array('foo' => new Obj())),
|
||||
array('$foo->foo(array("b" => "a", 0 => "b"))', new GetAttrNode(new NameNode('foo'), new NameNode('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)),
|
||||
);
|
||||
}
|
||||
|
@ -50,9 +50,9 @@ class GetAttrNodeTest extends AbstractNodeTest
|
|||
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', new GetAttrNode(new NameNode('foo'), new NameNode('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.foo({"b": "a", 0: "b"})', new GetAttrNode(new NameNode('foo'), new NameNode('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)),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -98,24 +98,24 @@ class ParserTest extends \PHPUnit_Framework_TestCase
|
|||
'(3 - 3) * 2',
|
||||
),
|
||||
array(
|
||||
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar'), new Node\ArgumentsNode(), Node\GetAttrNode::PROPERTY_CALL),
|
||||
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\NameNode('bar'), new Node\ArgumentsNode(), Node\GetAttrNode::PROPERTY_CALL),
|
||||
'foo.bar',
|
||||
array('foo'),
|
||||
),
|
||||
array(
|
||||
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar'), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
|
||||
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\NameNode('bar'), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
|
||||
'foo.bar()',
|
||||
array('foo'),
|
||||
),
|
||||
array(
|
||||
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('not'), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
|
||||
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\NameNode('not'), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
|
||||
'foo.not()',
|
||||
array('foo'),
|
||||
),
|
||||
array(
|
||||
new Node\GetAttrNode(
|
||||
new Node\NameNode('foo'),
|
||||
new Node\ConstantNode('bar'),
|
||||
new Node\NameNode('bar'),
|
||||
$arguments,
|
||||
Node\GetAttrNode::METHOD_CALL
|
||||
),
|
||||
|
@ -159,7 +159,9 @@ class ParserTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
private function createGetAttrNode($node, $item, $type)
|
||||
{
|
||||
return new Node\GetAttrNode($node, new Node\ConstantNode($item), new Node\ArgumentsNode(), $type);
|
||||
$attr = Node\GetAttrNode::ARRAY_CALL === $type ? new Node\ConstantNode($item) : new Node\NameNode($item);
|
||||
|
||||
return new Node\GetAttrNode($node, $attr, new Node\ArgumentsNode(), $type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Reference in New Issue