Skip to content

Commit

Permalink
[ExpressionLanguage] Add a way to hook on each node when dumping the AST
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Jun 15, 2016
1 parent f400f01 commit 82177f4
Show file tree
Hide file tree
Showing 16 changed files with 105 additions and 98 deletions.
Expand Up @@ -27,12 +27,14 @@ public function compile(Compiler $compiler)

public function dump()
{
$str = '';
$tokens = array();

foreach ($this->getKeyValuePairs() as $pair) {
$str .= sprintf('%s, ', $pair['value']->dump());
$tokens[] = $pair['value'];
$tokens[] = ', ';
}
array_pop($tokens);

return rtrim($str, ', ');
return $tokens;
}
}
36 changes: 18 additions & 18 deletions src/Symfony/Component/ExpressionLanguage/Node/ArrayNode.php
Expand Up @@ -62,30 +62,30 @@ public function dump()
{
$array = array();
foreach ($this->getKeyValuePairs() as $pair) {
$array[$pair['key']->attributes['value']] = $pair['value']->dump();
$array[$pair['key']->attributes['value']] = $pair['value'];
}

$tokens = array();

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);
}
foreach ($array as $k => $v) {
$tokens[] = ', ';
$tokens[] = new ConstantNode($k);
$tokens[] = ': ';
$tokens[] = $v;
}

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

$str = '[';

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

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

protected function getKeyValuePairs()
Expand Down
Expand Up @@ -157,6 +157,6 @@ public function evaluate($functions, $values)

public function dump()
{
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 @@ -51,6 +51,6 @@ public function evaluate($functions, $values)

public function dump()
{
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'], ')');
}
}
74 changes: 31 additions & 43 deletions src/Symfony/Component/ExpressionLanguage/Node/ConstantNode.php
Expand Up @@ -40,49 +40,37 @@ public function evaluate($functions, $values)

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));
$tokens = array();
$value = $this->attributes['value'];

if (true === $value) {
$tokens[] = 'true';
} elseif (false === $value) {
$tokens[] = 'false';
} elseif (null === $value) {
$tokens[] = 'null';
} elseif (is_numeric($value)) {
$tokens[] = $value;
} elseif (!is_array($value)) {
$tokens[] = $this->dumpString($value);
} elseif ($this->isHash($value)) {
foreach ($value as $k => $v) {
$tokens[] = ', ';
$tokens[] = new self($k);
$tokens[] = ': ';
$tokens[] = new self($v);
}
$tokens[0] = '{';
$tokens[] = '}';
} else {
foreach ($value as $v) {
$tokens[] = ', ';
$tokens[] = new self($v);
}
$tokens[0] = '[';
$tokens[] = ']';
}

return $tokens;
}
}
12 changes: 7 additions & 5 deletions src/Symfony/Component/ExpressionLanguage/Node/FunctionNode.php
Expand Up @@ -52,14 +52,16 @@ public function evaluate($functions, $values)

public function dump()
{
$str = $this->attributes['name'];

$str .= '(';
$tokens = array();
$tokens[] = $this->attributes['name'];

foreach ($this->nodes['arguments']->nodes as $node) {
$str .= $node->dump().', ';
$tokens[] = ', ';
$tokens[] = $node;
}
$tokens[1] = '(';
$tokens[] = ')';

return rtrim($str, ', ').')';
return $tokens;
}
}
14 changes: 7 additions & 7 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 @@ -99,13 +99,13 @@ public function dump()
{
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'], ']');
}
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Component/ExpressionLanguage/Node/NameNode.php
Expand Up @@ -40,6 +40,6 @@ public function evaluate($functions, $values)

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

public function dump()
{
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->dump() as $token) {
$dump .= is_scalar($token) ? $token : $this->dumpNode($token);
}

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);

$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
12 changes: 7 additions & 5 deletions src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php
Expand Up @@ -98,24 +98,24 @@ public function getParseData()
'(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
),
Expand Down Expand Up @@ -159,7 +159,9 @@ public function getParseData()

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);
}

/**
Expand Down

0 comments on commit 82177f4

Please sign in to comment.