Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/FnDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ private function fn_to_array(array $args)

private function fn_map(array $args)
{
$this->validate('map', $args, [['expression'], ['any']]);
$this->validate('map', $args, [['expression'], ['array']]);
$result = [];
foreach ($args[1] as $a) {
$result[] = $args[0]($a);
Expand Down
6 changes: 6 additions & 0 deletions src/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class Parser
T::T_EOF => 0,
T::T_QUOTED_IDENTIFIER => 0,
T::T_IDENTIFIER => 0,
T::T_UNKNOWN => 0,
T::T_LITERAL => 0,
T::T_RBRACKET => 0,
T::T_RPAREN => 0,
T::T_COMMA => 0,
Expand Down Expand Up @@ -272,6 +274,10 @@ private function led_pipe(array $left)

private function led_lparen(array $left)
{
if (!isset($left['value'])) {
throw $this->syntax('Invalid function name');
}

$args = [];
$this->next();

Expand Down
1 change: 1 addition & 0 deletions src/SyntaxErrorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public function __construct(
array $token,
$expression
) {
$token += ['pos' => mb_strlen($expression, 'UTF-8'), 'value' => null];
$message = sprintf("Syntax error at character %d\n", max($token['pos'], 0))
. $expression . "\n" . str_repeat(' ', max($token['pos'], 0)) . "^\n";
$message .= !is_array($expectedTypesOrMessage)
Expand Down
11 changes: 11 additions & 0 deletions tests/FnDispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ public function testConvertsToString(): void
$this->assertEquals('foo', $fn('to_string', [new _TestStringClass()]));
$this->assertEquals('"foo"', $fn('to_string', [new _TestJsonStringClass()]));
}

public function testMapRequiresArraySecondArgument(): void
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage(
'Argument 1 of map must be one of the following types: array. null found'
);

$fn = new FnDispatcher();
$fn('map', [function ($v) { return $v; }, null]);
}
}

class _TestStringClass
Expand Down
46 changes: 45 additions & 1 deletion tests/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,51 @@ public static function invalidExpressionProvider(): array
['=', 'Syntax error at character 0'],
['<', 'Syntax error at character 0'],
['>', 'Syntax error at character 0'],
['|', 'Syntax error at character 0']
['|', 'Syntax error at character 0'],
['@(foo)', 'Invalid function name'],
['@=', 'Did not reach the end of the token stream'],
['`1` `2`', 'Did not reach the end of the token stream'],
['{a: @', 'Syntax error at character 5']
];
}

/**
* @dataProvider invalidLedTokenProvider
*/
public function testInvalidExpressionsThrowCleanSyntaxErrors(string $expr): void
{
$diags = [];
set_error_handler(function ($errno, $errstr) use (&$diags) {
$diags[] = $errstr;
return true;
});

try {
try {
(new Parser())->parse($expr);
$this->fail("Expected SyntaxErrorException for: $expr");
} catch (SyntaxErrorException $e) {
$this->assertStringContainsString('Syntax error at character', $e->getMessage());
}
} finally {
restore_error_handler();
}

$this->assertSame([], $diags, "PHP warnings/notices emitted while parsing: $expr");
}

public static function invalidLedTokenProvider(): array
{
return [
['avg([].size)+'],
['a = b'],
['@ `1`'],
['@``'],
['foo[]+'],
['foo#bar'],
['@ `"`'],
['+foo'],
['"foo'],
];
}
}
7 changes: 7 additions & 0 deletions tests/SyntaxErrorExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,11 @@ public function testCreatesWithArray(): void
EOT;
$this->assertStringContainsString($expected, $e->getMessage());
}

public function testDefaultsMissingTokenKeysToEndOfExpression(): void
{
$e = new SyntaxErrorException(['comma' => true], ['type' => 'eof'], 'abc');

$this->assertStringContainsString('Syntax error at character 3', $e->getMessage());
}
}
Loading