PluginProbe
SQLite Database Integration / trunk
SQLite Database Integration vtrunk
3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 2.2.3 2.2.4 All 31 releases
sqlite-database-integration / wp-includes / database / parser / class-wp-parser-node.php

class-wp-parser-node.php in SQLite Database Integration trunk, at wp-includes/database/parser/class-wp-parser-node.php

387 lines 11.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * A node in parse tree.
5 *
6 * This class represents a node in the parse tree that is produced by WP_Parser.
7 * A node corresponds to the related grammar rule that was matched by the parser.
8 * Each node can contain children, consisting of other nodes and grammar tokens.
9 * In this way, a parser node constitutes a recursive structure that represents
10 * a parse (sub)tree at each level of the full grammar tree.
11 *
12 * @access private
13 */
14 class WP_Parser_Node {
15 /**
16 * @TODO: Review and document these properties and their visibility.
17 */
18 public $rule_id;
19 public $rule_name;
20 protected $children = array();
21
22 public function __construct( $rule_id, $rule_name ) {
23 $this->rule_id = $rule_id;
24 $this->rule_name = $rule_name;
25 }
26
27 public function append_child( $node ) {
28 $this->children[] = $node;
29 }
30
31 /**
32 * Flatten the matched rule fragments as if their children were direct
33 * descendants of the current rule.
34 *
35 * What are rule fragments?
36 *
37 * When we initially parse the grammar file, it has compound rules such
38 * as this one:
39 *
40 * query ::= EOF | ((simpleStatement | beginWork) ((SEMICOLON_SYMBOL EOF?) | EOF))
41 *
42 * Building a parser that can understand such rules is way more complex than building
43 * a parser that only follows simple rules, so we flatten those compound rules into
44 * simpler ones. The above rule would be flattened to:
45 *
46 * query ::= EOF | %query0
47 * %query0 ::= %%query01 %%query02
48 * %%query01 ::= simpleStatement | beginWork
49 * %%query02 ::= SEMICOLON_SYMBOL EOF_zero_or_one | EOF
50 * EOF_zero_or_one ::= EOF | ε
51 *
52 * This factorization happens in "convert-grammar.php".
53 *
54 * "Fragments" are intermediate artifacts whose names are not in the original grammar.
55 * They are extremely useful for the parser, but the API consumer should never have to
56 * worry about them. Fragment names start with a percent sign ("%").
57 *
58 * The code below inlines every fragment back in its parent rule.
59 *
60 * We could optimize this. The current $match may be discarded later on so any inlining
61 * effort here would be wasted. However, inlining seems cheap and doing it bottom-up here
62 * is **much** easier than reprocessing the parse tree top-down later on.
63 *
64 * The following parse tree:
65 *
66 * [
67 * 'query' => [
68 * [
69 * '%query01' => [
70 * [
71 * 'simpleStatement' => [
72 * MySQLToken(MySQLLexer::WITH_SYMBOL, 'WITH')
73 * ],
74 * '%query02' => [
75 * [
76 * 'simpleStatement' => [
77 * MySQLToken(MySQLLexer::WITH_SYMBOL, 'WITH')
78 * ]
79 * ],
80 * ]
81 * ]
82 * ]
83 * ]
84 * ]
85 *
86 * Would be inlined as:
87 *
88 * [
89 * 'query' => [
90 * [
91 * 'simpleStatement' => [
92 * MySQLToken(MySQLLexer::WITH_SYMBOL, 'WITH')
93 * ]
94 * ],
95 * [
96 * 'simpleStatement' => [
97 * MySQLToken(MySQLLexer::WITH_SYMBOL, 'WITH')
98 * ]
99 * ]
100 * ]
101 * ]
102 */
103 public function merge_fragment( $node ) {
104 $this->children = array_merge( $this->children, $node->children );
105 }
106
107 /**
108 * Check if this node has any child nodes or tokens.
109 *
110 * @return bool True if this node has any child nodes or tokens, false otherwise.
111 */
112 public function has_child(): bool {
113 return count( $this->children ) > 0;
114 }
115
116 /**
117 * Check if this node has any child nodes.
118 *
119 * @param string|null $rule_name Optional. A node rule name to check for.
120 * @return bool True if any child nodes are found, false otherwise.
121 */
122 public function has_child_node( ?string $rule_name = null ): bool {
123 foreach ( $this->children as $child ) {
124 if (
125 $child instanceof WP_Parser_Node
126 && ( null === $rule_name || $child->rule_name === $rule_name )
127 ) {
128 return true;
129 }
130 }
131 return false;
132 }
133
134 /**
135 * Check if this node has any child tokens.
136 *
137 * @param int|null $token_id Optional. A token ID to check for.
138 * @return bool True if any child tokens are found, false otherwise.
139 */
140 public function has_child_token( ?int $token_id = null ): bool {
141 foreach ( $this->children as $child ) {
142 if (
143 $child instanceof WP_Parser_Token
144 && ( null === $token_id || $child->id === $token_id )
145 ) {
146 return true;
147 }
148 }
149 return false;
150 }
151
152 /**
153 * Get the first child node or token of this node.
154 *
155 * @return WP_Parser_Node|WP_Parser_Token|null The first child node or token;
156 * null when no children are found.
157 */
158 public function get_first_child() {
159 return $this->children[0] ?? null;
160 }
161
162 /**
163 * Get the first child node of this node.
164 *
165 * @param string|null $rule_name Optional. A node rule name to check for.
166 * @return WP_Parser_Node|null The first matching child node; null when no children are found.
167 */
168 public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_Node {
169 foreach ( $this->children as $child ) {
170 if (
171 $child instanceof WP_Parser_Node
172 && ( null === $rule_name || $child->rule_name === $rule_name )
173 ) {
174 return $child;
175 }
176 }
177 return null;
178 }
179
180 /**
181 * Get the first child token of this node.
182 *
183 * @param int|null $token_id Optional. A token ID to check for.
184 * @return WP_Parser_Token|null The first matching child token; null when no children are found.
185 */
186 public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token {
187 foreach ( $this->children as $child ) {
188 if (
189 $child instanceof WP_Parser_Token
190 && ( null === $token_id || $child->id === $token_id )
191 ) {
192 return $child;
193 }
194 }
195 return null;
196 }
197
198 /**
199 * Get the first descendant node of this node.
200 *
201 * The node children are traversed recursively in a depth-first order until
202 * a matching descendant node is found, or the entire subtree is searched.
203 *
204 * @param string|null $rule_name Optional. A node rule name to check for.
205 * @return WP_Parser_Node|null The first matching descendant node; null when no descendants are found.
206 */
207 public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Parser_Node {
208 for ( $i = 0; $i < count( $this->children ); $i++ ) {
209 $child = $this->children[ $i ];
210 if ( ! $child instanceof WP_Parser_Node ) {
211 continue;
212 }
213 if ( null === $rule_name || $child->rule_name === $rule_name ) {
214 return $child;
215 }
216 $node = $child->get_first_descendant_node( $rule_name );
217 if ( $node ) {
218 return $node;
219 }
220 }
221 return null;
222 }
223
224 /**
225 * Get the first descendant token of this node.
226 *
227 * The node children are traversed recursively in a depth-first order until
228 * a matching descendant token is found, or the entire subtree is searched.
229 *
230 * @param int|null $token_id Optional. A token ID to check for.
231 * @return WP_Parser_Token|null The first matching descendant token; null when no descendants are found.
232 */
233 public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_Token {
234 for ( $i = 0; $i < count( $this->children ); $i++ ) {
235 $child = $this->children[ $i ];
236 if ( $child instanceof WP_Parser_Token ) {
237 if ( null === $token_id || $child->id === $token_id ) {
238 return $child;
239 }
240 } else {
241 $token = $child->get_first_descendant_token( $token_id );
242 if ( $token ) {
243 return $token;
244 }
245 }
246 }
247 return null;
248 }
249
250 /**
251 * Get all children of this node.
252 *
253 * @return array<WP_Parser_Node|WP_Parser_Token> An array of all child nodes and tokens of this node.
254 */
255 public function get_children(): array {
256 return $this->children;
257 }
258
259 /**
260 * Get all child nodes of this node.
261 *
262 * @param string|null $rule_name Optional. A node rule name to check for.
263 * @return WP_Parser_Node[] An array of all matching child nodes.
264 */
265 public function get_child_nodes( ?string $rule_name = null ): array {
266 $nodes = array();
267 foreach ( $this->children as $child ) {
268 if (
269 $child instanceof WP_Parser_Node
270 && ( null === $rule_name || $child->rule_name === $rule_name )
271 ) {
272 $nodes[] = $child;
273 }
274 }
275 return $nodes;
276 }
277
278 /**
279 * Get all child tokens of this node.
280 *
281 * @param int|null $token_id Optional. A token ID to check for.
282 * @return WP_Parser_Token[] An array of all matching child tokens.
283 */
284 public function get_child_tokens( ?int $token_id = null ): array {
285 $tokens = array();
286 foreach ( $this->children as $child ) {
287 if (
288 $child instanceof WP_Parser_Token
289 && ( null === $token_id || $child->id === $token_id )
290 ) {
291 $tokens[] = $child;
292 }
293 }
294 return $tokens;
295 }
296
297 /**
298 * Get all descendants of this node.
299 *
300 * The descendants are collected using a depth-first pre-order NLR traversal.
301 * This produces a natural ordering that corresponds to the original input.
302 *
303 * @return array<WP_Parser_Node|WP_Parser_Token> An array of all descendant nodes and tokens of this node.
304 */
305 public function get_descendants(): array {
306 $descendants = array();
307 foreach ( $this->children as $child ) {
308 if ( $child instanceof WP_Parser_Node ) {
309 $descendants[] = $child;
310 $descendants = array_merge( $descendants, $child->get_descendants() );
311 } else {
312 $descendants[] = $child;
313 }
314 }
315 return $descendants;
316 }
317
318 /**
319 * Get all descendant nodes of this node.
320 *
321 * The descendants are collected using a depth-first pre-order NLR traversal.
322 * This produces a natural ordering that corresponds to the original input.
323 * All matching nodes are collected during the traversal.
324 *
325 * @param string|null $rule_name Optional. A node rule name to check for.
326 * @return WP_Parser_Node[] An array of all matching descendant nodes.
327 */
328 public function get_descendant_nodes( ?string $rule_name = null ): array {
329 $nodes = array();
330 foreach ( $this->children as $child ) {
331 if ( ! $child instanceof WP_Parser_Node ) {
332 continue;
333 }
334 if ( null === $rule_name || $child->rule_name === $rule_name ) {
335 $nodes[] = $child;
336 }
337 $nodes = array_merge( $nodes, $child->get_descendant_nodes( $rule_name ) );
338 }
339 return $nodes;
340 }
341
342 /**
343 * Get all descendant tokens of this node.
344 *
345 * The descendants are collected using a depth-first pre-order NLR traversal.
346 * This produces a natural ordering that corresponds to the original input.
347 * All matching tokens are collected during the traversal.
348 *
349 * @param int|null $token_id Optional. A token ID to check for.
350 * @return WP_Parser_Token[] An array of all matching descendant tokens.
351 */
352 public function get_descendant_tokens( ?int $token_id = null ): array {
353 $tokens = array();
354 foreach ( $this->children as $child ) {
355 if ( $child instanceof WP_Parser_Token ) {
356 if ( null === $token_id || $child->id === $token_id ) {
357 $tokens[] = $child;
358 }
359 } else {
360 $tokens = array_merge( $tokens, $child->get_descendant_tokens( $token_id ) );
361 }
362 }
363 return $tokens;
364 }
365
366 /**
367 * Get the byte offset in the input string where this node begins.
368 *
369 * @return int The byte offset in the input string where this node begins.
370 */
371 public function get_start(): int {
372 return $this->get_first_descendant_token()->start;
373 }
374
375 /**
376 * Get the byte length of this node in the input string.
377 *
378 * @return int The byte length of this node in the input string.
379 */
380 public function get_length(): int {
381 $tokens = $this->get_descendant_tokens();
382 $first_token = $tokens[0];
383 $last_token = $tokens[ count( $tokens ) - 1 ];
384 return $last_token->start + $last_token->length - $first_token->start;
385 }
386 }
387