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
10 changes: 10 additions & 0 deletions src/AbstractLiteral.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Eventjet\Ausdruck;

abstract class AbstractLiteral extends Expression
{
abstract public function value(): mixed;
}
18 changes: 17 additions & 1 deletion src/ListLiteral.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

use Eventjet\Ausdruck\Parser\Span;
use Override;
use RuntimeException;

use function array_map;
use function implode;
use function sprintf;

final class ListLiteral extends Expression
final class ListLiteral extends AbstractLiteral
{
/**
* @param list<Expression> $elements
Expand Down Expand Up @@ -74,4 +76,18 @@ public function getType(): Type
}
return Type::listOf($elementType ?? Type::any());
}

#[Override]
public function value(): mixed
{
$out = [];
foreach ($this->elements as $index => $element) {
if (!$element instanceof AbstractLiteral) {
throw new RuntimeException(sprintf('Element at index %d is not a literal', $index));
}
/** @psalm-suppress MixedAssignment */
$out[] = $element->value();
}
return $out;
}
}
8 changes: 7 additions & 1 deletion src/Literal.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* @internal
* @psalm-internal Eventjet\Ausdruck
*/
final class Literal extends Expression
final class Literal extends AbstractLiteral
{
use LocationTrait;

Expand Down Expand Up @@ -77,4 +77,10 @@ public function getType(): Type
{
return Type::fromValue($this->value);
}

#[Override]
public function value(): mixed
{
return $this->value;
}
}
8 changes: 8 additions & 0 deletions src/Parser/ExpressionParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ private function parseLazy(Expression|null $left): Expression|null
}
return $this->dot($left);
}
if ($token === 'true') {
$this->tokens->next();
return Expr::literal(true, $parsedToken->location());
}
if ($token === 'false') {
$this->tokens->next();
return Expr::literal(false, $parsedToken->location());
}
if (is_string($token)) {
if ($left !== null) {
throw SyntaxError::create(
Expand Down
4 changes: 2 additions & 2 deletions src/Parser/Literal.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use function sprintf;

/**
* @template-covariant T of string | int | float
* @template-covariant T of string | int | float | bool
* @internal
* @psalm-internal Eventjet\Ausdruck
*/
Expand All @@ -20,7 +20,7 @@ final class Literal implements Stringable
/**
* @param T $value
*/
public function __construct(public readonly string|int|float $value)
public function __construct(public readonly string|int|float|bool $value)
{
}

Expand Down
2 changes: 1 addition & 1 deletion src/Parser/ParsedToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
final class ParsedToken
{
/**
* @param Token | string | Literal<string | int | float> $token
* @param Token | string | Literal<string | int | float | bool> $token
* @param positive-int $line
* @param positive-int $column
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Parser/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum Token: string
case Arrow = '->';

/**
* @param Token | string | Literal<string | int | float> $token
* @param Token | string | Literal<string | int | float | bool> $token
*/
public static function print(self|string|Literal $token): string
{
Expand Down
17 changes: 16 additions & 1 deletion src/StructLiteral.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Eventjet\Ausdruck\Parser\Span;
use Override;
use RuntimeException;
use stdClass;

use function array_key_exists;
use function array_map;
Expand All @@ -17,7 +19,7 @@
* @internal
* @psalm-internal Eventjet\Ausdruck
*/
final class StructLiteral extends Expression
final class StructLiteral extends AbstractLiteral
{
/**
* @param array<string, Expression> $fields
Expand Down Expand Up @@ -70,4 +72,17 @@ public function getType(): Type
{
return Type::struct(array_map(static fn(Expression $value) => $value->getType(), $this->fields));
}

#[Override]
public function value(): mixed
{
$out = new stdClass();
foreach ($this->fields as $name => $value) {
if (!$value instanceof AbstractLiteral) {
throw new RuntimeException(sprintf('Field "%s" is not a literal', $name));
}
$out->$name = $value->value();
}
return $out;
}
}
21 changes: 13 additions & 8 deletions tests/unit/E2eCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

namespace Eventjet\Ausdruck\Test\Unit;

use Eventjet\Ausdruck\AbstractLiteral;
use Eventjet\Ausdruck\Parser\ExpressionParser;
use Eventjet\Ausdruck\Parser\SyntaxError;
use Eventjet\Ausdruck\Parser\TypeError;
use Eventjet\Ausdruck\Parser\TypeParser;
use Eventjet\Ausdruck\Parser\Types;
use Eventjet\Ausdruck\StructLiteral;
use Eventjet\Ausdruck\Type;
use FilesystemIterator;
use RecursiveDirectoryIterator;
Expand All @@ -21,8 +24,6 @@
use function explode;
use function file_get_contents;
use function implode;
use function json_decode;
use function json_last_error;
use function sprintf;
use function str_ends_with;
use function str_replace;
Expand All @@ -32,7 +33,6 @@
use function trim;

use const DIRECTORY_SEPARATOR;
use const JSON_ERROR_NONE;

final readonly class E2eCase
{
Expand Down Expand Up @@ -99,14 +99,19 @@ private static function parse(string $contents): self
foreach ($sectionLines as $name => $lines) {
$sections[$name] = implode("\n", $lines);
}
/** @var mixed $output */
$output = json_decode($sections['Output']);
if ($output === null && json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException('Invalid JSON in output section');
$output = ExpressionParser::parse($sections['Output']);
if (!$output instanceof AbstractLiteral) {
throw new RuntimeException(sprintf('Output section must be a literal, got %s', $output));
}
/** @var mixed $output */
$output = $output->value();
if (array_key_exists('Input', $sections)) {
$inputStruct = ExpressionParser::parse($sections['Input']);
if (!$inputStruct instanceof StructLiteral) {
throw new RuntimeException('Input section must be a struct literal');
}
/** @var array<string, mixed> $input */
$input = (array)json_decode($sections['Input'], associative: false);
$input = (array)$inputStruct->value();
} else {
$input = [];
}
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/cases/atomic/get/with-inline-type.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
foo:string
-- Input --
{ "foo": "bar" }
{ foo: "bar" }
-- Output --
"bar"
3 changes: 3 additions & 0 deletions tests/unit/cases/atomic/literal/false.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
false
-- Output --
false
3 changes: 3 additions & 0 deletions tests/unit/cases/atomic/literal/true.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
true
-- Output --
true
2 changes: 1 addition & 1 deletion tests/unit/cases/eq/int-literal-and-get.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
42 === myint:int
-- Input --
{ "myint": 42 }
{ myint: 42 }
-- Output --
true
2 changes: 1 addition & 1 deletion tests/unit/cases/eq/not/int-literal-and-get.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
42 === myint:int
-- Input --
{ "myint": 69 }
{ myint: 69 }
-- Output --
false
2 changes: 1 addition & 1 deletion tests/unit/cases/map/equals-three.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
numbers:list<int>.map:list<bool>(|n| n:int === 3)
-- Input --
{ "numbers": [1, 2, 3, 4, 3, 5] }
{ numbers: [1, 2, 3, 4, 3, 5] }
-- Output --
[false, false, true, false, true, false]
2 changes: 1 addition & 1 deletion tests/unit/cases/or/get-false-get-false.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
a:bool || b:bool
-- Input --
{ "a": false, "b": false }
{ a: false, b: false }
-- Output --
false
2 changes: 1 addition & 1 deletion tests/unit/cases/or/get-false-get-true.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
a:bool || b:bool
-- Input --
{ "a": false, "b": true }
{ a: false, b: true }
-- Output --
true
2 changes: 1 addition & 1 deletion tests/unit/cases/or/get-true-get-false.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
a:bool || b:bool
-- Input --
{ "a": true, "b": false }
{ a: true, b: false }
-- Output --
true
2 changes: 1 addition & 1 deletion tests/unit/cases/or/get-true-get-true.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
a:bool || b:bool
-- Input --
{ "a": true, "b": true }
{ a: true, b: true }
-- Output --
true
4 changes: 2 additions & 2 deletions tests/unit/cases/struct/equality/equal-structs.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
a:{ foo: string, bar: int } === b:{ foo: string, bar: int }
-- Input --
{
"a": { "foo": "bar", "bar": 123 },
"b": { "foo": "bar", "bar": 123 }
a: { foo: "bar", bar: 123 },
b: { foo: "bar", bar: 123 }
}
-- Output --
true
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
a:{ foo: string, bar: int } === b:{ bar: int, foo: string }
-- Input --
{
"a": { "foo": "bar", "bar": 123 },
"b": { "bar": 123, "foo": "bar" }
a: { foo: "bar", bar: 123 },
b: { bar: 123, foo: "bar" }
}
-- Output --
true
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
a:{ foo: string, bar: int } === b:{ foo: string, bar: int }
-- Input --
{
"a": { "foo": "bar", "bar": 123 },
"b": { "foo": "baz", "bar": 123 }
a: { foo: "bar", bar: 123 },
b: { foo: "baz", bar: 123 }
}
-- Output --
false
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
bag:Bag.items.filter:list<Item>(|i| i:Item.tags.contains("foo"))
-- Input --
{
"bag": {
"items": [
{ "tags": ["foo"] },
{ "tags": ["foo", "bar"] },
{ "tags": ["bar"] }
bag: {
items: [
{ tags: ["foo"] },
{ tags: ["foo", "bar"] },
{ tags: ["bar"] }
]
}
}
-- Output --
[
{ "tags": ["foo"] },
{ "tags": ["foo", "bar"] }
{ tags: ["foo"] },
{ tags: ["foo", "bar"] }
]
-- Types --
Item: {
Expand Down