Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
CRAP | |
100.00% |
1 / 1 |
AFPTreeNode | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
7 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
toDebugString | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
toDebugStringInner | n/a |
0 / 0 |
n/a |
0 / 0 |
5 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\AbuseFilter\Parser; |
4 | |
5 | use MediaWiki\Extension\AbuseFilter\Parser\Exception\InternalException; |
6 | |
7 | /** |
8 | * Represents a node of a parser tree. |
9 | */ |
10 | class AFPTreeNode { |
11 | // Each of the constants below represents a node corresponding to a level |
12 | // of the parser, from the top of the tree to the bottom. |
13 | |
14 | // ENTRY is always one-element and thus does not have its own node. |
15 | |
16 | // SEMICOLON is a many-children node, denoting that the nodes have to be |
17 | // evaluated in order and the last value has to be returned. |
18 | public const SEMICOLON = 'SEMICOLON'; |
19 | |
20 | // ASSIGNMENT (formerly known as SET) is a node which is responsible for |
21 | // assigning values to variables. ASSIGNMENT is a (variable name [string], |
22 | // value [tree node]) tuple, INDEX_ASSIGNMENT (which is used to assign |
23 | // values at array offsets) is a (variable name [string], index [tree node], |
24 | // value [tree node]) tuple, and ARRAY_APPEND has the form of (variable name |
25 | // [string], value [tree node]). |
26 | public const ASSIGNMENT = 'ASSIGNMENT'; |
27 | public const INDEX_ASSIGNMENT = 'INDEX_ASSIGNMENT'; |
28 | public const ARRAY_APPEND = 'ARRAY_APPEND'; |
29 | |
30 | // CONDITIONAL represents both a ternary operator and an if-then-else-end |
31 | // construct. The format is (condition, evaluated-if-true, evaluated-in-false). |
32 | // The first two are tree nodes, the last one can be a node, or null if there's no else. |
33 | public const CONDITIONAL = 'CONDITIONAL'; |
34 | |
35 | // LOGIC is a logic operator accepted by AFPData::boolOp. The format is |
36 | // (operation, left operand, right operand). |
37 | public const LOGIC = 'LOGIC'; |
38 | |
39 | // COMPARE is a comparison operator accepted by AFPData::boolOp. The format is |
40 | // (operation, left operand, right operand). |
41 | public const COMPARE = 'COMPARE'; |
42 | |
43 | // SUM_REL is either '+' or '-'. The format is (operation, left operand, |
44 | // right operand). |
45 | public const SUM_REL = 'SUM_REL'; |
46 | |
47 | // MUL_REL is a multiplication-related operation accepted by AFPData::mulRel. |
48 | // The format is (operation, left operand, right operand). |
49 | public const MUL_REL = 'MUL_REL'; |
50 | |
51 | // POW is an exponentiation operator. The format is (base, exponent). |
52 | public const POW = 'POW'; |
53 | |
54 | // BOOL_INVERT is a boolean inversion operator. The format is (operand). |
55 | public const BOOL_INVERT = 'BOOL_INVERT'; |
56 | |
57 | // KEYWORD_OPERATOR is one of the binary keyword operators supported by the |
58 | // filter language. The format is (keyword, left operand, right operand). |
59 | public const KEYWORD_OPERATOR = 'KEYWORD_OPERATOR'; |
60 | |
61 | // UNARY is either unary minus or unary plus. The format is (operator, operand). |
62 | public const UNARY = 'UNARY'; |
63 | |
64 | // ARRAY_INDEX is an operation of accessing an array by an offset. The format |
65 | // is (array, offset). |
66 | public const ARRAY_INDEX = 'ARRAY_INDEX'; |
67 | |
68 | // Since parenthesis only manipulate precedence of the operators, they are |
69 | // not explicitly represented in the tree. |
70 | |
71 | // FUNCTION_CALL is an invocation of built-in function. The format is a |
72 | // tuple where the first element is a function name, and all subsequent |
73 | // elements are the arguments. |
74 | public const FUNCTION_CALL = 'FUNCTION_CALL'; |
75 | |
76 | // ARRAY_DEFINITION is an array literal. The $children field contains tree |
77 | // nodes for the values of each of the array element used. |
78 | public const ARRAY_DEFINITION = 'ARRAY_DEFINITION'; |
79 | |
80 | // ATOM is a node representing a literal. The only element of $children is a |
81 | // token corresponding to the literal. |
82 | public const ATOM = 'ATOM'; |
83 | |
84 | // BINOP is a combination of LOGIC (^), COMPARE (<=, <, etc.), |
85 | // SUM_REL (+, -), MUL_REL (*, /, %), POW (**), |
86 | // KEYWORD_OPERATOR (like, rlike, etc.), and ARRAY_INDEX ([]). |
87 | // The format is (operator, operand, operand). |
88 | // Currently, it's only used in SyntaxChecker |
89 | // & and | which is in LOGIC is not in BINOP because it affects |
90 | // control flow. |
91 | public const BINOP = 'BINOP'; |
92 | |
93 | /** @var string Type of the node, one of the constants above */ |
94 | public $type; |
95 | /** |
96 | * Parameters of the value. Typically it is an array of children nodes, |
97 | * which might be either strings (for parametrization of the node) or another |
98 | * node. In case of ATOM it's a parser token. |
99 | * @var AFPTreeNode[]|string[]|AFPToken |
100 | */ |
101 | public $children; |
102 | |
103 | /** @var int Position used for error reporting. */ |
104 | public $position; |
105 | |
106 | /** |
107 | * @param string $type |
108 | * @param (AFPTreeNode|null)[]|string[]|AFPToken $children |
109 | * @param int $position |
110 | */ |
111 | public function __construct( $type, $children, $position ) { |
112 | $this->type = $type; |
113 | $this->children = $children; |
114 | $this->position = $position; |
115 | } |
116 | |
117 | /** |
118 | * @return string |
119 | * @codeCoverageIgnore |
120 | */ |
121 | public function toDebugString() { |
122 | return implode( "\n", $this->toDebugStringInner() ); |
123 | } |
124 | |
125 | /** |
126 | * @return array |
127 | * @codeCoverageIgnore |
128 | */ |
129 | private function toDebugStringInner() { |
130 | if ( $this->type === self::ATOM ) { |
131 | return [ "ATOM({$this->children->type} {$this->children->value})" ]; |
132 | } |
133 | |
134 | $align = static function ( $line ) { |
135 | return ' ' . $line; |
136 | }; |
137 | |
138 | $lines = [ $this->type ]; |
139 | // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach children is array here |
140 | foreach ( $this->children as $subnode ) { |
141 | if ( $subnode instanceof AFPTreeNode ) { |
142 | $sublines = array_map( $align, $subnode->toDebugStringInner() ); |
143 | } elseif ( is_string( $subnode ) ) { |
144 | $sublines = [ " {$subnode}" ]; |
145 | } else { |
146 | throw new InternalException( "Each node parameter has to be either a node or a string" ); |
147 | } |
148 | |
149 | $lines = array_merge( $lines, $sublines ); |
150 | } |
151 | return $lines; |
152 | } |
153 | } |