Skip to content

Commit

Permalink
Merge pull request #106 from rasteiner/queries/first-cs-cleanup
Browse files Browse the repository at this point in the history
First set  of CS fixes
  • Loading branch information
rasteiner authored Nov 13, 2024
2 parents 52d94fd + a512a8e commit 2e9186a
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 157 deletions.
2 changes: 1 addition & 1 deletion src/Query/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public function resolve(array|object $data = []): mixed

$mode = App::instance()->option('query.runner', 'transpiled');

if($mode === 'legacy') {
if ($mode === 'legacy') {
return $this->resolve_legacy($data);
}

Expand Down
19 changes: 9 additions & 10 deletions src/Toolkit/Query/BaseParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ abstract class BaseParser
public function __construct(
Tokenizer|Iterator $source,
) {
if($source instanceof Tokenizer) {
$this->tokens = $source->tokenize();
} else {
$this->tokens = $source;
if ($source instanceof Tokenizer) {
$source = $source->tokenize();
}

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

if ($first === null) {
throw new Exception('No tokens found.');
Expand All @@ -36,7 +35,7 @@ public function __construct(

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

Expand All @@ -45,7 +44,7 @@ protected function consume(TokenType $type, string $message): Token

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

Expand All @@ -54,7 +53,7 @@ protected function check(TokenType $type): bool

protected function advance(): Token|null
{
if (!$this->isAtEnd()) {
if ($this->isAtEnd() === false) {
$this->previous = $this->current;
$this->tokens->next();
$this->current = $this->tokens->current();
Expand All @@ -71,7 +70,7 @@ protected function isAtEnd(): bool

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

Expand All @@ -81,7 +80,7 @@ protected function match(TokenType $type): Token|false
protected function matchAny(array $types): Token|false
{
foreach ($types as $type) {
if ($this->check($type)) {
if ($this->check($type) === true) {
return $this->advance();
}
}
Expand Down
63 changes: 33 additions & 30 deletions src/Toolkit/Query/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Kirby\Toolkit\Query;

use Exception;
use Iterator;
use Kirby\Toolkit\Query\AST\ArgumentListNode;
use Kirby\Toolkit\Query\AST\ArrayListNode;
use Kirby\Toolkit\Query\AST\ClosureNode;
Expand All @@ -17,18 +16,12 @@

class Parser extends BaseParser
{
public function __construct(
Tokenizer|Iterator $source,
) {
parent::__construct($source);
}

public function parse(): Node
{
$expression = $this->expression();

// ensure that we consumed all tokens
if(!$this->isAtEnd()) {
if ($this->isAtEnd() === false) {
$this->consume(TokenType::T_EOF, 'Expect end of expression.');
}

Expand All @@ -46,7 +39,7 @@ private function coalesce(): Node

while ($this->match(TokenType::T_COALESCE)) {
$right = $this->ternary();
$left = new CoalesceNode($left, $right);
$left = new CoalesceNode($left, $right);
}

return $left;
Expand All @@ -57,18 +50,23 @@ private function ternary(): Node
$left = $this->memberAccess();

if ($tok = $this->matchAny([TokenType::T_QUESTION_MARK, TokenType::T_TERNARY_DEFAULT])) {
if($tok->type === TokenType::T_TERNARY_DEFAULT) {
if ($tok->type === TokenType::T_TERNARY_DEFAULT) {
$trueIsDefault = true;
$trueBranch = null;
$falseBranch = $this->expression();
$trueBranch = null;
} else {
$trueIsDefault = false;
$trueBranch = $this->expression();
$trueBranch = $this->expression();
$this->consume(TokenType::T_COLON, 'Expect ":" after true branch.');
$falseBranch = $this->expression();
}

return new TernaryNode($left, $trueBranch, $falseBranch, $trueIsDefault);
$falseBranch = $this->expression();

return new TernaryNode(
$left,
$trueBranch,
$falseBranch,
$trueIsDefault
);
}

return $left;
Expand All @@ -81,20 +79,24 @@ private function memberAccess(): Node
while ($tok = $this->matchAny([TokenType::T_DOT, TokenType::T_NULLSAFE])) {
$nullSafe = $tok->type === TokenType::T_NULLSAFE;

if($right = $this->match(TokenType::T_IDENTIFIER)) {
if ($right = $this->match(TokenType::T_IDENTIFIER)) {
$right = $right->lexeme;
} elseif($right = $this->match(TokenType::T_INTEGER)) {
} elseif ($right = $this->match(TokenType::T_INTEGER)) {
$right = $right->literal;
} else {
throw new Exception('Expect property name after ".".');
}

if($this->match(TokenType::T_OPEN_PAREN)) {
if ($this->match(TokenType::T_OPEN_PAREN)) {
$arguments = $this->argumentList();
$left = new MemberAccessNode($left, $right, $arguments, $nullSafe);
} else {
$left = new MemberAccessNode($left, $right, null, $nullSafe);
}

$left = new MemberAccessNode(
$left,
$right,
$arguments ?? null,
$nullSafe
);
}

return $left;
Expand All @@ -104,10 +106,10 @@ private function listUntil(TokenType $until): array
{
$elements = [];

while (!$this->isAtEnd() && !$this->check($until)) {
while ($this->isAtEnd() === false && $this->check($until) === false) {
$elements[] = $this->expression();

if (!$this->match(TokenType::T_COMMA)) {
if ($this->match(TokenType::T_COMMA) == false) {
break;
}
}
Expand All @@ -129,10 +131,11 @@ private function atomic(): Node
{
// float numbers
if ($integer = $this->match(TokenType::T_INTEGER)) {
if($this->match(TokenType::T_DOT)) {
if ($this->match(TokenType::T_DOT)) {
$fractional = $this->match(TokenType::T_INTEGER);
return new LiteralNode((float)($integer->literal . '.' . $fractional->literal));
}

return new LiteralNode($integer->literal);
}

Expand All @@ -155,7 +158,7 @@ private function atomic(): Node

// global functions and variables
if ($token = $this->match(TokenType::T_IDENTIFIER)) {
if($this->match(TokenType::T_OPEN_PAREN)) {
if ($this->match(TokenType::T_OPEN_PAREN)) {
$arguments = $this->argumentList();
return new GlobalFunctionNode($token->lexeme, $arguments);
}
Expand All @@ -167,29 +170,29 @@ private function atomic(): Node
if ($token = $this->match(TokenType::T_OPEN_PAREN)) {
$list = $this->listUntil(TokenType::T_CLOSE_PAREN);

if($this->match(TokenType::T_ARROW)) {
if ($this->match(TokenType::T_ARROW)) {
$expression = $this->expression();

/**
* Assert that all elements are VariableNodes
* @var VariableNode[] $list
*/
foreach($list as $element) {
if(!$element instanceof VariableNode) {
if ($element instanceof VariableNode === false) {
throw new Exception('Expecting only variables in closure argument list.');
}
}

$arguments = array_map(fn ($element) => $element->name, $list);
return new ClosureNode($arguments, $expression);
}
if(count($list) > 1) {

if (count($list) > 1) {
throw new Exception('Expecting \"=>\" after closure argument list.');
}

// this is just a grouping
return $list[0];


}

throw new Exception('Expect expression.');
Expand Down
15 changes: 9 additions & 6 deletions src/Toolkit/Query/Runners/Interpreted.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Closure;
use Kirby\Toolkit\Query\Parser;
use Kirby\Toolkit\Query\Runner;
use Kirby\Toolkit\Query\Runners\Visitors\Interpreter;
use Kirby\Toolkit\Query\Tokenizer;

class Interpreted extends Runner
Expand All @@ -20,22 +21,24 @@ public function __construct(
protected function getResolver(string $query): Closure
{
// load closure from process cache
if(isset(self::$cache[$query])) {
if (isset(self::$cache[$query])) {
return self::$cache[$query];
}

// on cache miss, parse query and generate closure
$t = new Tokenizer($query);
$parser = new Parser($t);
$node = $parser->parse();
$tokenizer = new Tokenizer($query);
$parser = new Parser($tokenizer);
$node = $parser->parse();

$self = $this;

return self::$cache[$query] = function (array $binding) use ($node, $self) {
$interpreter = new Visitors\Interpreter($self->allowedFunctions, $binding);
if($self->interceptor !== null) {
$interpreter = new Interpreter($self->allowedFunctions, $binding);

if ($self->interceptor !== null) {
$interpreter->setInterceptor($self->interceptor);
}

return $node->accept($interpreter);
};
}
Expand Down
44 changes: 30 additions & 14 deletions src/Toolkit/Query/Runners/Transpiled.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Exception;
use Kirby\Toolkit\Query\Parser;
use Kirby\Toolkit\Query\Runner;
use Kirby\Toolkit\Query\Runners\Visitors\CodeGen;
use Kirby\Toolkit\Query\Tokenizer;

class Transpiled extends Runner
Expand All @@ -24,7 +25,6 @@ public function __construct(
) {
}


/**
* Retrieves the executor closure for a given query.
* If the closure is not already cached, it will be generated and stored in `Runner::$cacheFolder`.
Expand All @@ -35,33 +35,43 @@ public function __construct(
protected function getResolver(string $query): Closure
{
// load closure from process memory
if(isset(self::$cache[$query])) {
if (isset(self::$cache[$query])) {
return self::$cache[$query];
}

// load closure from file-cache / opcache
$hash = crc32($query);
$hash = crc32($query);
$filename = self::$cacheFolder . '/' . $hash . '.php';
if(file_exists($filename)) {

if (file_exists($filename)) {
return self::$cache[$query] = include $filename;
}

// on cache miss, parse query and generate closure
$t = new Tokenizer($query);
$parser = new Parser($t);
$node = $parser->parse();
$codeGen = new Visitors\CodeGen($this->allowedFunctions);
$tokenizer = new Tokenizer($query);
$parser = new Parser($tokenizer);
$node = $parser->parse();
$codeGen = new CodeGen($this->allowedFunctions);

$functionBody = $node->accept($codeGen);

$mappings = join("\n", array_map(fn ($k, $v) => "$k = $v;", array_keys($codeGen->mappings), $codeGen->mappings)) . "\n";
$comment = join("\n", array_map(fn ($l) => "// $l", explode("\n", $query)));
$mappings = array_map(
fn ($k, $v) => "$k = $v;",
array_keys($codeGen->mappings),
$codeGen->mappings
);
$mappings = join("\n", $mappings) . "\n";

$comment = array_map(fn ($l) => "// $l", explode("\n", $query));
$comment = join("\n", $comment);

$uses = array_map(fn ($k) => "use $k;", array_keys($codeGen->uses));
$uses = join("\n", $uses) . "\n";

$uses = join("\n", array_map(fn ($k) => "use $k;", array_keys($codeGen->uses))) . "\n";
$function = "<?php\n$uses\n$comment\nreturn function(array \$context, array \$functions, Closure \$intercept) {\n$mappings\nreturn $functionBody;\n};";

// store closure in file-cache
if(!is_dir(self::$cacheFolder)) {
if (is_dir(self::$cacheFolder) === false) {
mkdir(self::$cacheFolder, 0777, true);
}

Expand All @@ -83,9 +93,15 @@ protected function getResolver(string $query): Closure
public function run(string $query, array $context = []): mixed
{
$function = $this->getResolver($query);
if(!is_callable($function)) {

if (is_callable($function) === false) {
throw new Exception('Query is not valid');
}
return $function($context, $this->allowedFunctions, $this->interceptor ?? fn ($v) => $v);

return $function(
$context,
$this->allowedFunctions,
$this->interceptor ?? fn ($v) => $v
);
}
}
Loading

0 comments on commit 2e9186a

Please sign in to comment.