Skip to content

Commit 47c2c03

Browse files
committed
Add native parser PHP facade
1 parent 2622441 commit 47c2c03

8 files changed

Lines changed: 3160 additions & 3037 deletions

packages/mysql-on-sqlite/src/mysql/class-wp-mysql-lexer.php

Lines changed: 5 additions & 2990 deletions
Large diffs are not rendered by default.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
class WP_MySQL_Native_Lexer extends WP_MySQL_Polyfill_Lexer {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
class WP_MySQL_Native_Parser extends WP_MySQL_Polyfill_Parser {
4+
}
Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,14 @@
11
<?php
22

3-
class WP_MySQL_Parser extends WP_Parser {
4-
/**
5-
* The current query AST.
6-
*
7-
* @var WP_Parser_Node|null
8-
*/
9-
private $current_ast;
3+
require_once __DIR__ . '/class-wp-mysql-polyfill-parser.php';
104

11-
/**
12-
* Parse the next query from the input SQL string.
13-
*
14-
* This method reads tokens until a query is parsed, or the parsing fails.
15-
* It returns a boolean indicating whether a query was successfully parsed.
16-
*
17-
* Example:
18-
*
19-
* // Parse all queries in the input SQL string.
20-
* $parser = new WP_MySQL_Parser( $sql );
21-
* while ( $parser->next_query() ) {
22-
* $ast = $parser->get_query_ast();
23-
* if ( ! $ast ) {
24-
* // The parsing failed.
25-
* }
26-
* // The query was successfully parsed.
27-
* }
28-
*
29-
* @return bool Whether a query was successfully parsed.
30-
*/
31-
public function next_query(): bool {
32-
if ( $this->position >= count( $this->tokens ) ) {
33-
return false;
34-
}
35-
$this->current_ast = $this->parse();
36-
return true;
5+
if ( class_exists( 'WP_MySQL_Native_Parser', false ) ) {
6+
if ( ! function_exists( 'wp_sqlite_mysql_native_export_grammar' ) ) {
7+
require_once __DIR__ . '/mysql-rust-bridge.php';
378
}
9+
} else {
10+
require_once __DIR__ . '/class-wp-mysql-native-parser.php';
11+
}
3812

39-
/**
40-
* Get the current query AST.
41-
*
42-
* When no query has been parsed yet, the parsing failed, or the end of the
43-
* input was reached, this method returns null.
44-
*
45-
* @see WP_MySQL_Parser::next_query() for usage example.
46-
*
47-
* @return WP_Parser_Node|null The current query AST, or null if no query was parsed.
48-
*/
49-
public function get_query_ast(): ?WP_Parser_Node {
50-
return $this->current_ast;
51-
}
13+
class WP_MySQL_Parser extends WP_MySQL_Native_Parser {
5214
}

packages/mysql-on-sqlite/src/mysql/class-wp-mysql-polyfill-lexer.php

Lines changed: 2997 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
class WP_MySQL_Polyfill_Parser extends WP_Parser {
4+
/**
5+
* The current query AST.
6+
*
7+
* @var WP_Parser_Node|null
8+
*/
9+
private $current_ast;
10+
11+
/**
12+
* Parse the next query from the input SQL string.
13+
*
14+
* This method reads tokens until a query is parsed, or the parsing fails.
15+
* It returns a boolean indicating whether a query was successfully parsed.
16+
*
17+
* Example:
18+
*
19+
* // Parse all queries in the input SQL string.
20+
* $parser = new WP_MySQL_Parser( $sql );
21+
* while ( $parser->next_query() ) {
22+
* $ast = $parser->get_query_ast();
23+
* if ( ! $ast ) {
24+
* // The parsing failed.
25+
* }
26+
* // The query was successfully parsed.
27+
* }
28+
*
29+
* @return bool Whether a query was successfully parsed.
30+
*/
31+
public function next_query(): bool {
32+
if ( $this->position >= count( $this->tokens ) ) {
33+
return false;
34+
}
35+
$this->current_ast = $this->parse();
36+
return true;
37+
}
38+
39+
/**
40+
* Get the current query AST.
41+
*
42+
* When no query has been parsed yet, the parsing failed, or the end of the
43+
* input was reached, this method returns null.
44+
*
45+
* @see WP_MySQL_Parser::next_query() for usage example.
46+
*
47+
* @return WP_Parser_Node|null The current query AST, or null if no query was parsed.
48+
*/
49+
public function get_query_ast(): ?WP_Parser_Node {
50+
return $this->current_ast;
51+
}
52+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/**
4+
* Bridge helpers for the optional Rust MySQL lexer/parser extension.
5+
* PHP keeps the grammar object, while Rust owns the exported parser state.
6+
*/
7+
8+
/**
9+
* Export grammar internals for the native parser.
10+
*
11+
* @param WP_Parser_Grammar $grammar Parser grammar.
12+
* @return array<string, mixed>
13+
*/
14+
function wp_sqlite_mysql_native_export_grammar( WP_Parser_Grammar $grammar ): array {
15+
return array(
16+
'highest_terminal_id' => $grammar->highest_terminal_id,
17+
'rules' => $grammar->rules,
18+
'lookahead_is_match_possible' => $grammar->lookahead_is_match_possible,
19+
'rule_names' => $grammar->rule_names,
20+
'fragment_ids' => $grammar->fragment_ids,
21+
);
22+
}

packages/mysql-on-sqlite/src/parser/class-wp-parser-node.php

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ class WP_Parser_Node {
1515
*/
1616
public $rule_id;
1717
public $rule_name;
18-
private $children = array();
18+
private $children = array();
19+
private $native_ast = null;
20+
private $native_node_index = null;
1921

2022
public function __construct( $rule_id, $rule_name ) {
2123
$this->rule_id = $rule_id;
2224
$this->rule_name = $rule_name;
2325
}
2426

2527
public function append_child( $node ) {
28+
$this->materialize_native_children();
2629
$this->children[] = $node;
2730
}
2831

@@ -99,6 +102,8 @@ public function append_child( $node ) {
99102
* ]
100103
*/
101104
public function merge_fragment( $node ) {
105+
$this->materialize_native_children();
106+
$node->materialize_native_children();
102107
$this->children = array_merge( $this->children, $node->children );
103108
}
104109

@@ -108,6 +113,9 @@ public function merge_fragment( $node ) {
108113
* @return bool True if this node has any child nodes or tokens, false otherwise.
109114
*/
110115
public function has_child(): bool {
116+
if ( $this->has_native_ast() ) {
117+
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
118+
}
111119
return count( $this->children ) > 0;
112120
}
113121

@@ -118,6 +126,9 @@ public function has_child(): bool {
118126
* @return bool True if any child nodes are found, false otherwise.
119127
*/
120128
public function has_child_node( ?string $rule_name = null ): bool {
129+
if ( $this->has_native_ast() ) {
130+
return wp_sqlite_mysql_native_ast_has_child_node( $this->native_ast, $this->native_node_index, $rule_name );
131+
}
121132
foreach ( $this->children as $child ) {
122133
if (
123134
$child instanceof WP_Parser_Node
@@ -136,6 +147,9 @@ public function has_child_node( ?string $rule_name = null ): bool {
136147
* @return bool True if any child tokens are found, false otherwise.
137148
*/
138149
public function has_child_token( ?int $token_id = null ): bool {
150+
if ( $this->has_native_ast() ) {
151+
return wp_sqlite_mysql_native_ast_has_child_token( $this->native_ast, $this->native_node_index, $token_id );
152+
}
139153
foreach ( $this->children as $child ) {
140154
if (
141155
$child instanceof WP_Parser_Token
@@ -154,6 +168,9 @@ public function has_child_token( ?int $token_id = null ): bool {
154168
* null when no children are found.
155169
*/
156170
public function get_first_child() {
171+
if ( $this->has_native_ast() ) {
172+
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
173+
}
157174
return $this->children[0] ?? null;
158175
}
159176

@@ -164,6 +181,9 @@ public function get_first_child() {
164181
* @return WP_Parser_Node|null The first matching child node; null when no children are found.
165182
*/
166183
public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_Node {
184+
if ( $this->has_native_ast() ) {
185+
return wp_sqlite_mysql_native_ast_get_first_child_node( $this->native_ast, $this->native_node_index, $rule_name );
186+
}
167187
foreach ( $this->children as $child ) {
168188
if (
169189
$child instanceof WP_Parser_Node
@@ -182,6 +202,9 @@ public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_No
182202
* @return WP_Parser_Token|null The first matching child token; null when no children are found.
183203
*/
184204
public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token {
205+
if ( $this->has_native_ast() ) {
206+
return wp_sqlite_mysql_native_ast_get_first_child_token( $this->native_ast, $this->native_node_index, $token_id );
207+
}
185208
foreach ( $this->children as $child ) {
186209
if (
187210
$child instanceof WP_Parser_Token
@@ -203,6 +226,9 @@ public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token
203226
* @return WP_Parser_Node|null The first matching descendant node; null when no descendants are found.
204227
*/
205228
public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Parser_Node {
229+
if ( $this->has_native_ast() ) {
230+
return wp_sqlite_mysql_native_ast_get_first_descendant_node( $this->native_ast, $this->native_node_index, $rule_name );
231+
}
206232
for ( $i = 0; $i < count( $this->children ); $i++ ) {
207233
$child = $this->children[ $i ];
208234
if ( ! $child instanceof WP_Parser_Node ) {
@@ -229,6 +255,9 @@ public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Pars
229255
* @return WP_Parser_Token|null The first matching descendant token; null when no descendants are found.
230256
*/
231257
public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_Token {
258+
if ( $this->has_native_ast() ) {
259+
return wp_sqlite_mysql_native_ast_get_first_descendant_token( $this->native_ast, $this->native_node_index, $token_id );
260+
}
232261
for ( $i = 0; $i < count( $this->children ); $i++ ) {
233262
$child = $this->children[ $i ];
234263
if ( $child instanceof WP_Parser_Token ) {
@@ -251,6 +280,9 @@ public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_
251280
* @return array<WP_Parser_Node|WP_Parser_Token> An array of all child nodes and tokens of this node.
252281
*/
253282
public function get_children(): array {
283+
if ( $this->has_native_ast() ) {
284+
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
285+
}
254286
return $this->children;
255287
}
256288

@@ -261,6 +293,9 @@ public function get_children(): array {
261293
* @return WP_Parser_Node[] An array of all matching child nodes.
262294
*/
263295
public function get_child_nodes( ?string $rule_name = null ): array {
296+
if ( $this->has_native_ast() ) {
297+
return wp_sqlite_mysql_native_ast_get_child_nodes( $this->native_ast, $this->native_node_index, $rule_name );
298+
}
264299
$nodes = array();
265300
foreach ( $this->children as $child ) {
266301
if (
@@ -280,6 +315,9 @@ public function get_child_nodes( ?string $rule_name = null ): array {
280315
* @return WP_Parser_Token[] An array of all matching child tokens.
281316
*/
282317
public function get_child_tokens( ?int $token_id = null ): array {
318+
if ( $this->has_native_ast() ) {
319+
return wp_sqlite_mysql_native_ast_get_child_tokens( $this->native_ast, $this->native_node_index, $token_id );
320+
}
283321
$tokens = array();
284322
foreach ( $this->children as $child ) {
285323
if (
@@ -301,6 +339,9 @@ public function get_child_tokens( ?int $token_id = null ): array {
301339
* @return array<WP_Parser_Node|WP_Parser_Token> An array of all descendant nodes and tokens of this node.
302340
*/
303341
public function get_descendants(): array {
342+
if ( $this->has_native_ast() ) {
343+
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
344+
}
304345
$descendants = array();
305346
foreach ( $this->children as $child ) {
306347
if ( $child instanceof WP_Parser_Node ) {
@@ -324,6 +365,9 @@ public function get_descendants(): array {
324365
* @return WP_Parser_Node[] An array of all matching descendant nodes.
325366
*/
326367
public function get_descendant_nodes( ?string $rule_name = null ): array {
368+
if ( $this->has_native_ast() ) {
369+
return wp_sqlite_mysql_native_ast_get_descendant_nodes( $this->native_ast, $this->native_node_index, $rule_name );
370+
}
327371
$nodes = array();
328372
foreach ( $this->children as $child ) {
329373
if ( ! $child instanceof WP_Parser_Node ) {
@@ -348,6 +392,9 @@ public function get_descendant_nodes( ?string $rule_name = null ): array {
348392
* @return WP_Parser_Token[] An array of all matching descendant tokens.
349393
*/
350394
public function get_descendant_tokens( ?int $token_id = null ): array {
395+
if ( $this->has_native_ast() ) {
396+
return wp_sqlite_mysql_native_ast_get_descendant_tokens( $this->native_ast, $this->native_node_index, $token_id );
397+
}
351398
$tokens = array();
352399
foreach ( $this->children as $child ) {
353400
if ( $child instanceof WP_Parser_Token ) {
@@ -367,6 +414,9 @@ public function get_descendant_tokens( ?int $token_id = null ): array {
367414
* @return int The byte offset in the input string where this node begins.
368415
*/
369416
public function get_start(): int {
417+
if ( $this->has_native_ast() ) {
418+
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
419+
}
370420
return $this->get_first_descendant_token()->start;
371421
}
372422

@@ -376,9 +426,26 @@ public function get_start(): int {
376426
* @return int The byte length of this node in the input string.
377427
*/
378428
public function get_length(): int {
429+
if ( $this->has_native_ast() ) {
430+
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
431+
}
379432
$tokens = $this->get_descendant_tokens();
380433
$first_token = $tokens[0];
381434
$last_token = $tokens[ count( $tokens ) - 1 ];
382435
return $last_token->start + $last_token->length - $first_token->start;
383436
}
437+
438+
private function has_native_ast(): bool {
439+
return null !== $this->native_ast;
440+
}
441+
442+
private function materialize_native_children(): void {
443+
if ( ! $this->has_native_ast() ) {
444+
return;
445+
}
446+
447+
$this->children = wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
448+
$this->native_ast = null;
449+
$this->native_node_index = null;
450+
}
384451
}

0 commit comments

Comments
 (0)