1 | <?php |
||
8 | abstract class PrettyPrinterAbstract |
||
9 | { |
||
10 | protected $precedenceMap = array( |
||
11 | // [precedence, associativity] where for the latter -1 is %left, 0 is %nonassoc and 1 is %right |
||
12 | 'Expr_BinaryOp_Pow' => array( 0, 1), |
||
13 | 'Expr_BitwiseNot' => array( 10, 1), |
||
14 | 'Expr_PreInc' => array( 10, 1), |
||
15 | 'Expr_PreDec' => array( 10, 1), |
||
16 | 'Expr_PostInc' => array( 10, -1), |
||
17 | 'Expr_PostDec' => array( 10, -1), |
||
18 | 'Expr_UnaryPlus' => array( 10, 1), |
||
19 | 'Expr_UnaryMinus' => array( 10, 1), |
||
20 | 'Expr_Cast_Int' => array( 10, 1), |
||
21 | 'Expr_Cast_Double' => array( 10, 1), |
||
22 | 'Expr_Cast_String' => array( 10, 1), |
||
23 | 'Expr_Cast_Array' => array( 10, 1), |
||
24 | 'Expr_Cast_Object' => array( 10, 1), |
||
25 | 'Expr_Cast_Bool' => array( 10, 1), |
||
26 | 'Expr_Cast_Unset' => array( 10, 1), |
||
27 | 'Expr_ErrorSuppress' => array( 10, 1), |
||
28 | 'Expr_Instanceof' => array( 20, 0), |
||
29 | 'Expr_BooleanNot' => array( 30, 1), |
||
30 | 'Expr_BinaryOp_Mul' => array( 40, -1), |
||
31 | 'Expr_BinaryOp_Div' => array( 40, -1), |
||
32 | 'Expr_BinaryOp_Mod' => array( 40, -1), |
||
33 | 'Expr_BinaryOp_Plus' => array( 50, -1), |
||
34 | 'Expr_BinaryOp_Minus' => array( 50, -1), |
||
35 | 'Expr_BinaryOp_Concat' => array( 50, -1), |
||
36 | 'Expr_BinaryOp_ShiftLeft' => array( 60, -1), |
||
37 | 'Expr_BinaryOp_ShiftRight' => array( 60, -1), |
||
38 | 'Expr_BinaryOp_Smaller' => array( 70, 0), |
||
39 | 'Expr_BinaryOp_SmallerOrEqual' => array( 70, 0), |
||
40 | 'Expr_BinaryOp_Greater' => array( 70, 0), |
||
41 | 'Expr_BinaryOp_GreaterOrEqual' => array( 70, 0), |
||
42 | 'Expr_BinaryOp_Equal' => array( 80, 0), |
||
43 | 'Expr_BinaryOp_NotEqual' => array( 80, 0), |
||
44 | 'Expr_BinaryOp_Identical' => array( 80, 0), |
||
45 | 'Expr_BinaryOp_NotIdentical' => array( 80, 0), |
||
46 | 'Expr_BinaryOp_Spaceship' => array( 80, 0), |
||
47 | 'Expr_BinaryOp_BitwiseAnd' => array( 90, -1), |
||
48 | 'Expr_BinaryOp_BitwiseXor' => array(100, -1), |
||
49 | 'Expr_BinaryOp_BitwiseOr' => array(110, -1), |
||
50 | 'Expr_BinaryOp_BooleanAnd' => array(120, -1), |
||
51 | 'Expr_BinaryOp_BooleanOr' => array(130, -1), |
||
52 | 'Expr_BinaryOp_Coalesce' => array(140, 1), |
||
53 | 'Expr_Ternary' => array(150, -1), |
||
54 | // parser uses %left for assignments, but they really behave as %right |
||
55 | 'Expr_Assign' => array(160, 1), |
||
56 | 'Expr_AssignRef' => array(160, 1), |
||
57 | 'Expr_AssignOp_Plus' => array(160, 1), |
||
58 | 'Expr_AssignOp_Minus' => array(160, 1), |
||
59 | 'Expr_AssignOp_Mul' => array(160, 1), |
||
60 | 'Expr_AssignOp_Div' => array(160, 1), |
||
61 | 'Expr_AssignOp_Concat' => array(160, 1), |
||
62 | 'Expr_AssignOp_Mod' => array(160, 1), |
||
63 | 'Expr_AssignOp_BitwiseAnd' => array(160, 1), |
||
64 | 'Expr_AssignOp_BitwiseOr' => array(160, 1), |
||
65 | 'Expr_AssignOp_BitwiseXor' => array(160, 1), |
||
66 | 'Expr_AssignOp_ShiftLeft' => array(160, 1), |
||
67 | 'Expr_AssignOp_ShiftRight' => array(160, 1), |
||
68 | 'Expr_AssignOp_Pow' => array(160, 1), |
||
69 | 'Expr_YieldFrom' => array(165, 1), |
||
70 | 'Expr_Print' => array(168, 1), |
||
71 | 'Expr_BinaryOp_LogicalAnd' => array(170, -1), |
||
72 | 'Expr_BinaryOp_LogicalXor' => array(180, -1), |
||
73 | 'Expr_BinaryOp_LogicalOr' => array(190, -1), |
||
74 | 'Expr_Include' => array(200, -1), |
||
75 | ); |
||
76 | |||
77 | protected $noIndentToken; |
||
78 | protected $canUseSemicolonNamespaces; |
||
79 | protected $options; |
||
80 | |||
81 | /** |
||
82 | * Creates a pretty printer instance using the given options. |
||
83 | * |
||
84 | * Supported options: |
||
85 | * * bool $shortArraySyntax = false: Whether to use [] instead of array() |
||
86 | * |
||
87 | * @param array $options Dictionary of formatting options |
||
88 | */ |
||
89 | public function __construct(array $options = []) { |
||
90 | $this->noIndentToken = '_NO_INDENT_' . mt_rand(); |
||
91 | |||
92 | $defaultOptions = ['shortArraySyntax' => false]; |
||
93 | $this->options = $options + $defaultOptions; |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Pretty prints an array of statements. |
||
98 | * |
||
99 | * @param Node[] $stmts Array of statements |
||
100 | * |
||
101 | * @return string Pretty printed statements |
||
102 | */ |
||
103 | public function prettyPrint(array $stmts) { |
||
104 | $this->preprocessNodes($stmts); |
||
105 | |||
106 | return ltrim(str_replace("\n" . $this->noIndentToken, "\n", $this->pStmts($stmts, false))); |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Pretty prints an expression. |
||
111 | * |
||
112 | * @param Expr $node Expression node |
||
113 | * |
||
114 | * @return string Pretty printed node |
||
115 | */ |
||
116 | public function prettyPrintExpr(Expr $node) { |
||
119 | |||
120 | /** |
||
121 | * Pretty prints a file of statements (includes the opening <?php tag if it is required). |
||
122 | * |
||
123 | * @param Node[] $stmts Array of statements |
||
124 | * |
||
125 | * @return string Pretty printed statements |
||
126 | */ |
||
127 | public function prettyPrintFile(array $stmts) { |
||
146 | |||
147 | /** |
||
148 | * Preprocesses the top-level nodes to initialize pretty printer state. |
||
149 | * |
||
150 | * @param Node[] $nodes Array of nodes |
||
151 | */ |
||
152 | protected function preprocessNodes(array $nodes) { |
||
161 | |||
162 | /** |
||
163 | * Pretty prints an array of nodes (statements) and indents them optionally. |
||
164 | * |
||
165 | * @param Node[] $nodes Array of nodes |
||
166 | * @param bool $indent Whether to indent the printed nodes |
||
167 | * |
||
168 | * @return string Pretty printed statements |
||
169 | */ |
||
170 | protected function pStmts(array $nodes, $indent = true) { |
||
185 | |||
186 | /** |
||
187 | * Pretty prints a node. |
||
188 | * |
||
189 | * @param Node $node Node to be pretty printed |
||
190 | * |
||
191 | * @return string Pretty printed node |
||
192 | */ |
||
193 | protected function p(Node $node) { |
||
196 | |||
197 | protected function pInfixOp($type, Node $leftNode, $operatorString, Node $rightNode) { |
||
204 | |||
205 | protected function pPrefixOp($type, $operatorString, Node $node) { |
||
209 | |||
210 | protected function pPostfixOp($type, Node $node, $operatorString) { |
||
214 | |||
215 | /** |
||
216 | * Prints an expression node with the least amount of parentheses necessary to preserve the meaning. |
||
217 | * |
||
218 | * @param Node $node Node to pretty print |
||
219 | * @param int $parentPrecedence Precedence of the parent operator |
||
220 | * @param int $parentAssociativity Associativity of parent operator |
||
221 | * (-1 is left, 0 is nonassoc, 1 is right) |
||
222 | * @param int $childPosition Position of the node relative to the operator |
||
223 | * (-1 is left, 1 is right) |
||
224 | * |
||
225 | * @return string The pretty printed node |
||
226 | */ |
||
227 | protected function pPrec(Node $node, $parentPrecedence, $parentAssociativity, $childPosition) { |
||
240 | |||
241 | /** |
||
242 | * Pretty prints an array of nodes and implodes the printed values. |
||
243 | * |
||
244 | * @param Node[] $nodes Array of Nodes to be printed |
||
245 | * @param string $glue Character to implode with |
||
246 | * |
||
247 | * @return string Imploded pretty printed nodes |
||
248 | */ |
||
249 | protected function pImplode(array $nodes, $glue = '') { |
||
257 | |||
258 | /** |
||
259 | * Pretty prints an array of nodes and implodes the printed values with commas. |
||
260 | * |
||
261 | * @param Node[] $nodes Array of Nodes to be printed |
||
262 | * |
||
263 | * @return string Comma separated pretty printed nodes |
||
264 | */ |
||
265 | protected function pCommaSeparated(array $nodes) { |
||
268 | |||
269 | /** |
||
270 | * Signals the pretty printer that a string shall not be indented. |
||
271 | * |
||
272 | * @param string $string Not to be indented string |
||
273 | * |
||
274 | * @return string String marked with $this->noIndentToken's. |
||
275 | */ |
||
276 | protected function pNoIndent($string) { |
||
279 | |||
280 | /** |
||
281 | * Prints reformatted text of the passed comments. |
||
282 | * |
||
283 | * @param Comment[] $comments List of comments |
||
284 | * |
||
285 | * @return string Reformatted text of comments |
||
286 | */ |
||
287 | protected function pComments(array $comments) { |
||
296 | } |
||
297 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.