PluginProbe
SQLite Database Integration / 2.2.13
SQLite Database Integration v2.2.13
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.php

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

125 lines 3.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * A recursive descent parser.
5 *
6 * This is a dynamic recursive descent parser that can parse LL grammars.
7 *
8 * @TODO: Add a detailed description and list the properties that a grammar must
9 * satisfy in order to be supported by this parser (e.g., no left recursion).
10 */
11 class WP_Parser {
12 protected $grammar;
13 protected $tokens;
14 protected $position;
15
16 public function __construct( WP_Parser_Grammar $grammar, array $tokens ) {
17 $this->grammar = $grammar;
18 $this->tokens = $tokens;
19 $this->position = 0;
20 }
21
22 public function parse() {
23 // @TODO: Make the starting rule lookup non-grammar-specific.
24 $query_rule_id = $this->grammar->get_rule_id( 'query' );
25 $ast = $this->parse_recursive( $query_rule_id );
26 return false === $ast ? null : $ast;
27 }
28
29 private function parse_recursive( $rule_id ) {
30 $is_terminal = $rule_id <= $this->grammar->highest_terminal_id;
31 if ( $is_terminal ) {
32 if ( $this->position >= count( $this->tokens ) ) {
33 return false;
34 }
35
36 if ( WP_Parser_Grammar::EMPTY_RULE_ID === $rule_id ) {
37 return true;
38 }
39
40 if ( $this->tokens[ $this->position ]->id === $rule_id ) {
41 ++$this->position;
42 return $this->tokens[ $this->position - 1 ];
43 }
44 return false;
45 }
46
47 $branches = $this->grammar->rules[ $rule_id ];
48 if ( ! count( $branches ) ) {
49 return false;
50 }
51
52 // Bale out from processing the current branch if none of its rules can
53 // possibly match the current token.
54 if ( isset( $this->grammar->lookahead_is_match_possible[ $rule_id ] ) ) {
55 $token_id = $this->tokens[ $this->position ]->id;
56 if (
57 ! isset( $this->grammar->lookahead_is_match_possible[ $rule_id ][ $token_id ] ) &&
58 ! isset( $this->grammar->lookahead_is_match_possible[ $rule_id ][ WP_Parser_Grammar::EMPTY_RULE_ID ] )
59 ) {
60 return false;
61 }
62 }
63
64 $rule_name = $this->grammar->rule_names[ $rule_id ];
65 $starting_position = $this->position;
66 foreach ( $branches as $branch ) {
67 $this->position = $starting_position;
68 $node = new WP_Parser_Node( $rule_id, $rule_name );
69 $branch_matches = true;
70 foreach ( $branch as $subrule_id ) {
71 $subnode = $this->parse_recursive( $subrule_id );
72 if ( false === $subnode ) {
73 $branch_matches = false;
74 break;
75 } elseif ( true === $subnode ) {
76 /*
77 * The subrule was matched without actually matching a token.
78 * This means a special empty "ε" (epsilon) rule was matched.
79 * An "ε" rule in a grammar matches an empty input of 0 bytes.
80 * It is used to represent optional grammar productions.
81 */
82 continue;
83 } elseif ( is_array( $subnode ) && 0 === count( $subnode ) ) {
84 continue;
85 }
86 if ( is_array( $subnode ) && ! count( $subnode ) ) {
87 continue;
88 }
89 if ( isset( $this->grammar->fragment_ids[ $subrule_id ] ) ) {
90 $node->merge_fragment( $subnode );
91 } else {
92 $node->append_child( $subnode );
93 }
94 }
95
96 // Negative lookahead for INTO after a valid SELECT statement.
97 // If we match a SELECT statement, but there is an INTO keyword after it,
98 // we're in the wrong branch and need to leave matching to a later rule.
99 // @TODO: Extract this to the "WP_MySQL_Parser" class, or add support
100 // for right-associative rules, which could solve this.
101 // See: https://github.com/mysql/mysql-workbench/blob/8.0.38/library/parsers/grammars/MySQLParser.g4#L994
102 // See: https://github.com/antlr/antlr4/issues/488
103 $la = $this->tokens[ $this->position ] ?? null;
104 if ( $la && 'selectStatement' === $rule_name && WP_MySQL_Lexer::INTO_SYMBOL === $la->id ) {
105 $branch_matches = false;
106 }
107
108 if ( true === $branch_matches ) {
109 break;
110 }
111 }
112
113 if ( ! $branch_matches ) {
114 $this->position = $starting_position;
115 return false;
116 }
117
118 if ( ! $node->has_child() ) {
119 return true;
120 }
121
122 return $node;
123 }
124 }
125