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