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

Query: parse as AST #6788

Draft
wants to merge 35 commits into
base: v5/develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5e1c65c
Add new query parsers / compilers / runners
rasteiner Nov 12, 2024
061cdc3
Merge branch 'getkirby:main' into v5/compiled-queries
rasteiner Nov 12, 2024
f23a29a
Fix missing imports of Exception
rasteiner Nov 12, 2024
ea6025c
Fix missing imports of Exception
rasteiner Nov 12, 2024
3ceab61
Merge branch 'v5/compiled-queries' of https://github.com/rasteiner/ki…
rasteiner Nov 12, 2024
54f66b4
query parser fixes
rasteiner Nov 12, 2024
fa2ed19
Implement `intercept`, rename AST classes with a "Node" suffix
rasteiner Nov 12, 2024
d1dba29
fix type annotations for arrays in CodeGen and Interpreter classes
rasteiner Nov 12, 2024
dd6eb99
rename parameter in run method from bindings to context to be fully c…
rasteiner Nov 12, 2024
d56e4d6
don't use option helper
rasteiner Nov 13, 2024
67aa780
Update token type constants to include "T_" prefix for safety
rasteiner Nov 13, 2024
f46ab0a
allow identifiers to start with a number and contain (escaped) dots
rasteiner Nov 13, 2024
c68b85f
coding style
rasteiner Nov 13, 2024
52d94fd
Remove unused imports in Query.php
rasteiner Nov 13, 2024
a512a8e
First set of CS fixes
distantnative Nov 13, 2024
2e9186a
Merge pull request #106 from rasteiner/queries/first-cs-cleanup
rasteiner Nov 13, 2024
d82b385
set "interpreted" as default query runner
rasteiner Nov 13, 2024
a25e37d
Add new query parsers / compilers / runners
rasteiner Nov 12, 2024
85dd0e0
Fix missing imports of Exception
rasteiner Nov 12, 2024
46b6755
query parser fixes
rasteiner Nov 12, 2024
9392a43
Implement `intercept`, rename AST classes with a "Node" suffix
rasteiner Nov 12, 2024
b599e66
fix type annotations for arrays in CodeGen and Interpreter classes
rasteiner Nov 12, 2024
90b1715
rename parameter in run method from bindings to context to be fully c…
rasteiner Nov 12, 2024
42fbd4f
don't use option helper
rasteiner Nov 13, 2024
96b8a41
Update token type constants to include "T_" prefix for safety
rasteiner Nov 13, 2024
0c8b8cd
allow identifiers to start with a number and contain (escaped) dots
rasteiner Nov 13, 2024
045867c
coding style
rasteiner Nov 13, 2024
f62695d
Remove unused imports in Query.php
rasteiner Nov 13, 2024
2bf1ffc
First set of CS fixes
distantnative Nov 13, 2024
738674e
set "interpreted" as default query runner
rasteiner Nov 13, 2024
c7da7b7
Merge branch 'v5/compiled-queries' of https://github.com/rasteiner/ki…
rasteiner Dec 3, 2024
a22d4ca
fix query: sequential member access was not resetting method arguments
rasteiner Dec 3, 2024
ae94934
code style
rasteiner Dec 3, 2024
d9d6e8d
only intercept accessed objects
rasteiner Dec 4, 2024
0026642
query fixes, more tests, inject cache as dependency
rasteiner Dec 4, 2024
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
38 changes: 27 additions & 11 deletions src/Query/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
use Kirby\Cms\User;
use Kirby\Image\QrCode;
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Query\Runner;
use Kirby\Toolkit\Query\Runners\Interpreted;
use Kirby\Toolkit\Query\Runners\Transpiled;
use Kirby\Toolkit\Query\Visitor;

/**
* The Query class can be used to query arrays and objects,
Expand Down Expand Up @@ -60,24 +64,16 @@
/**
* Method to help classes that extend Query
* to intercept a segment's result.
*
* @deprecated 5.0.0 Will be removed in 6.0.0
*/
public function intercept(mixed $result): mixed
{
return $result;
}

/**
* Returns the query result if anything
* can be found, otherwise returns null
*
* @throws \Kirby\Exception\BadMethodCallException If an invalid method is accessed by the query
*/
public function resolve(array|object $data = []): mixed
private function resolve_legacy(array|object $data = []): mixed
{
if (empty($this->query) === true) {
return $data;
}

// merge data with default entries
if (is_array($data) === true) {
$data = [...static::$entries, ...$data];
Expand All @@ -99,6 +95,26 @@

// loop through all segments to resolve query
return Expression::factory($this->query, $this)->resolve($data);

}

/**
* Returns the query result if anything
* can be found, otherwise returns null
*
* @throws \Kirby\Exception\BadMethodCallException If an invalid method is accessed by the query
*/
public function resolve(array|object $data = []): mixed
{
if (empty($this->query) === true) {
return $data;
}

return match(option('query.runner', 'interpreted')) {

Check failure on line 113 in src/Query/Query.php

View workflow job for this annotation

GitHub Actions / Unit tests - PHP 8.2

HelperFunctionUse

src/Query/Query.php:113:16: HelperFunctionUse: Use of user-overridable Kirby helper "option"

Check failure on line 113 in src/Query/Query.php

View workflow job for this annotation

GitHub Actions / Unit tests - PHP 8.3

HelperFunctionUse

src/Query/Query.php:113:16: HelperFunctionUse: Use of user-overridable Kirby helper "option"
'transpiled' => (new Transpiled(static::$entries))->run($this->query, $data),
'interpreted' => (new Interpreted(static::$entries))->run($this->query, $data),
default => $this->resolve_legacy($data)
};
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/Toolkit/Query/AST/ArgumentList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class ArgumentList extends Node {
public function __construct(
public array $arguments,
) {}
}
9 changes: 9 additions & 0 deletions src/Toolkit/Query/AST/ArrayList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class ArrayList extends Node {
public function __construct(
public array $elements,
) {}
}
12 changes: 12 additions & 0 deletions src/Toolkit/Query/AST/Closure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class Closure extends Node
{
public function __construct(
public ArgumentList $arguments,
public Node $body,
) {
}
}
10 changes: 10 additions & 0 deletions src/Toolkit/Query/AST/Coalesce.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class Coalesce extends Node {
public function __construct(
public Node $left,
public Node $right,
) {}
}
10 changes: 10 additions & 0 deletions src/Toolkit/Query/AST/GlobalFunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class GlobalFunction extends Node {
public function __construct(
public string $name,
public ArgumentList $arguments,
) {}
}
9 changes: 9 additions & 0 deletions src/Toolkit/Query/AST/Literal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class Literal extends Node {
public function __construct(
public mixed $value,
) {}
}
12 changes: 12 additions & 0 deletions src/Toolkit/Query/AST/MemberAccess.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class MemberAccess extends Node {
public function __construct(
public Node $object,
public string $member,
public ?ArgumentList $arguments = null,
public bool $nullSafe = false,
) {}
}
11 changes: 11 additions & 0 deletions src/Toolkit/Query/AST/Node.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Kirby\Toolkit\Query\AST;

use Kirby\Toolkit\Query\Visitor;

class Node {
public function accept(Visitor $visitor) {
return $visitor->visitNode($this);
}
}
13 changes: 13 additions & 0 deletions src/Toolkit/Query/AST/Ternary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class Ternary extends Node {
public function __construct(
public Node $condition,
public ?Node $trueBranch,
public Node $falseBranch,

public bool $trueBranchIsDefault = false,
) {}
}
9 changes: 9 additions & 0 deletions src/Toolkit/Query/AST/Variable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Kirby\Toolkit\Query\AST;

class Variable extends Node {
public function __construct(
public string $name,
) {}
}
83 changes: 83 additions & 0 deletions src/Toolkit/Query/BaseParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace Kirby\Toolkit\Query;

use Iterator;

abstract class BaseParser {
protected ?Token $previous;
protected Token $current;

/**
* @var Iterator<Token>
*/
protected Iterator $tokens;


public function __construct(
Tokenizer|Iterator $source,
) {
if($source instanceof Tokenizer) {
$this->tokens = $source->tokenize();
} else {
$this->tokens = $source;
}

$first = $this->tokens->current();

if ($first === null) {
throw new \Exception('No tokens found.');

Check warning on line 29 in src/Toolkit/Query/BaseParser.php

View workflow job for this annotation

GitHub Actions / Code Quality

Missing class import via use statement (line '29', column '14').
Fixed Show fixed Hide fixed
}

$this->current = $first;
}

protected function consume(TokenType $type, string $message): Token {
if ($this->check($type)) {
return $this->advance();
}

throw new \Exception($message);

Check warning on line 40 in src/Toolkit/Query/BaseParser.php

View workflow job for this annotation

GitHub Actions / Code Quality

Missing class import via use statement (line '40', column '13').
Fixed Show fixed Hide fixed
}

protected function check(TokenType $type): bool {
if ($this->isAtEnd()) {
return false;
}

return $this->current->type === $type;
}

protected function advance(): ?Token {
if (!$this->isAtEnd()) {
$this->previous = $this->current;
$this->tokens->next();
$this->current = $this->tokens->current();
}

return $this->previous;
}

protected function isAtEnd(): bool {
return $this->current->type === TokenType::EOF;
}


protected function match(TokenType $type): Token|false {
if ($this->check($type)) {
return $this->advance();
}

return false;
}

protected function matchAny(array $types): Token|false {
foreach ($types as $type) {
if ($this->check($type)) {
return $this->advance();
}
}

return false;
}
}
Loading
Loading