PluginProbe
SQLite Database Integration / 2.2.8
SQLite Database Integration v2.2.8
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 / parser / class-wp-parser-node.php

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

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