Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ExpressionLanguage] Add a way to hook on each node when dumping the AST #19060

Merged
merged 1 commit into from Jun 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/Symfony/Component/ExpressionLanguage/Node/ArgumentsNode.php
Expand Up @@ -25,14 +25,16 @@ public function compile(Compiler $compiler)
$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;
}
}
40 changes: 20 additions & 20 deletions src/Symfony/Component/ExpressionLanguage/Node/ArrayNode.php
Expand Up @@ -58,34 +58,34 @@ public function evaluate($functions, $values)
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, ', ').'}';
}

$str = '[';

foreach ($array as $key => $value) {
$str .= sprintf('%s, ', $value);
$array[0] = '{';
$array[] = '}';
} else {
foreach ($value as $v) {
$array[] = ', ';
$array[] = $v;
}
$array[0] = '[';
$array[] = ']';
}

return rtrim($str, ', ').']';
return $array;
}

protected function getKeyValuePairs()
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php
Expand Up @@ -155,8 +155,8 @@ public function evaluate($functions, $values)
}
}

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'], ')');
}
}
Expand Up @@ -49,8 +49,8 @@ public function evaluate($functions, $values)
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'], ')');
}
}
76 changes: 32 additions & 44 deletions src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php
Expand Up @@ -38,51 +38,39 @@ public function evaluate($functions, $values)
return $this->attributes['value'];
}

public function dump()
public function toArray()
{
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));
$array = array();
$value = $this->attributes['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;
}
}
14 changes: 8 additions & 6 deletions src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php
Expand Up @@ -50,16 +50,18 @@ public function evaluate($functions, $values)
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;
}
}
16 changes: 8 additions & 8 deletions src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php
Expand Up @@ -39,15 +39,15 @@ public function compile(Compiler $compiler)
$compiler
->compile($this->nodes['node'])
->raw('->')
->raw($this->nodes['attribute']->attributes['value'])
->raw($this->nodes['attribute']->attributes['name'])
;
break;

case self::METHOD_CALL:
$compiler
->compile($this->nodes['node'])
->raw('->')
->raw($this->nodes['attribute']->attributes['value'])
->raw($this->nodes['attribute']->attributes['name'])
->raw('(')
->compile($this->nodes['arguments'])
->raw(')')
Expand All @@ -73,7 +73,7 @@ public function evaluate($functions, $values)
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;

Expand All @@ -83,7 +83,7 @@ public function evaluate($functions, $values)
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);
Expand All @@ -95,17 +95,17 @@ public function evaluate($functions, $values)
}
}

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'], ']');
}
}
}
4 changes: 2 additions & 2 deletions src/Symfony/Component/ExpressionLanguage/Node/NameNode.php
Expand Up @@ -38,8 +38,8 @@ public function evaluate($functions, $values)
return $values[$this->attributes['name']];
}

public function dump()
public function toArray()
{
return $this->attributes['name'];
return array($this->attributes['name']);
}
}
6 changes: 3 additions & 3 deletions src/Symfony/Component/ExpressionLanguage/Node/Node.php
Expand Up @@ -76,14 +76,14 @@ public function evaluate($functions, $values)
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)
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php
Expand Up @@ -59,8 +59,8 @@ public function evaluate($functions, $values)
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'], ')');
}
}
13 changes: 12 additions & 1 deletion src/Symfony/Component/ExpressionLanguage/ParsedExpression.php
Expand Up @@ -42,6 +42,17 @@ public function getNodes()

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;
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Component/ExpressionLanguage/Parser.php
Expand Up @@ -330,7 +330,7 @@ public function parsePostfixExpression($node)
throw new SyntaxError('Expected name', $token->cursor);
}

$arg = new Node\ConstantNode($token->value);
$arg = new Node\NameNode($token->value);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AST is more correct when using NameNode instead of ConstantNode for representing GetAttrNode::PROPERTY_CALL and GetAttrNode::METHOD_CALL (but keep ConstantNodefor GetAttrNode::ARRAY_CALL).
This is enlighten by the "unescaping" (this trim) that was required otherwise when dumping a GetAttrNode.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to agree that this semantically more correct. Is it a BC break? I don't think so.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's a BC break. I created a melody script to reproduce it.

@fabpot it's because of this change your gist your sent me yesterday does not work anymore.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arf, any change is a BC break after all :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we need to revert this change, that's two issues as of now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted now


$arguments = new Node\ArgumentsNode();
if ($this->stream->current->test(Token::PUNCTUATION_TYPE, '(')) {
Expand Down
Expand Up @@ -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
{
Expand Down Expand Up @@ -42,7 +43,8 @@ abstract public function getCompileData();
*/
public function testDump($expected, $node)
{
$this->assertSame($expected, $node->dump());
$expr = new ParsedExpression($expected, $node);
$this->assertSame($expected, $expr->dump());
}

abstract public function getDumpData();
Expand Down
Expand Up @@ -47,8 +47,8 @@ public function getDumpData()
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'))),
Expand Down
Expand Up @@ -24,9 +24,9 @@ public function getEvaluateData()
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')),
);
}
Expand All @@ -37,9 +37,9 @@ public function getCompileData()
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)),
);
}
Expand All @@ -50,9 +50,9 @@ public function getDumpData()
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)),
);
}
Expand Down