xref: /php-ast/scripts/generate_ast_data.php (revision 71f6a54d)
1<?php error_reporting(E_ALL);
2
3$outCodeFile = __DIR__ . '/../ast_data.c';
4$strDefsFile = __DIR__ . '/../ast_str_defs.h';
5
6$code = <<<EOC
7#include "php_ast.h"
8
9const zend_ast_kind ast_kinds[] = {
10{KINDS}
11};
12
13const size_t ast_kinds_count = sizeof(ast_kinds) / sizeof(ast_kinds[0]);
14
15const char *ast_kind_to_name(zend_ast_kind kind) {
16\tswitch (kind) {
17{STRS}
18\t}
19
20\treturn NULL;
21}
22
23zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
24\tswitch (kind) {
25{CHILD_NAMES}
26\t}
27
28\treturn NULL;
29}
30
31void ast_register_kind_constants(INIT_FUNC_ARGS) {
32{CONSTS}
33}
34
35EOC;
36
37$strDefsHeader = <<<EOC
38#ifndef AST_STR_DEFS_H
39#define AST_STR_DEFS_H
40
41#define AST_STR_DEFS \
42{STR_DEFS}
43
44#endif
45
46EOC;
47
48$funcNames = ['params', 'uses', 'stmts', 'returnType', 'attributes'];
49
50$names = [
51    /* special nodes */
52    'AST_NAME' => ['name'],
53    'AST_CLOSURE_VAR' => ['name'],
54    'AST_NULLABLE_TYPE' => ['type'],
55
56    /* declaration nodes */
57    'ZEND_AST_FUNC_DECL' => $funcNames,
58    'ZEND_AST_CLOSURE' => $funcNames,
59    'ZEND_AST_METHOD' => $funcNames,
60    'ZEND_AST_ARROW_FUNC' => $funcNames,
61    'ZEND_AST_CLASS' => ['extends', 'implements', 'stmts', 'attributes', 'type'],
62
63    /* 0 child nodes */
64    'ZEND_AST_MAGIC_CONST' => [],
65    'ZEND_AST_TYPE' => [],
66    'ZEND_AST_CALLABLE_CONVERT' => [],
67
68    /* 1 child node */
69    'ZEND_AST_VAR' => ['name'],
70    'ZEND_AST_CONST' => ['name'],
71    'ZEND_AST_UNPACK' => ['expr'],
72    'ZEND_AST_CAST' => ['expr'],
73    'ZEND_AST_EMPTY' => ['expr'],
74    'ZEND_AST_ISSET' => ['var'],
75    'ZEND_AST_SHELL_EXEC' => ['expr'],
76    'ZEND_AST_CLONE' => ['expr'],
77    'ZEND_AST_EXIT' => ['expr'],
78    'ZEND_AST_PRINT' => ['expr'],
79    'ZEND_AST_INCLUDE_OR_EVAL' => ['expr'],
80    'ZEND_AST_UNARY_OP' => ['expr'],
81    'ZEND_AST_PRE_INC' => ['var'],
82    'ZEND_AST_PRE_DEC' => ['var'],
83    'ZEND_AST_POST_INC' => ['var'],
84    'ZEND_AST_POST_DEC' => ['var'],
85    'ZEND_AST_YIELD_FROM' => ['expr'],
86
87    'ZEND_AST_GLOBAL' => ['var'],
88    'ZEND_AST_UNSET' => ['var'],
89    'ZEND_AST_RETURN' => ['expr'],
90    'ZEND_AST_LABEL' => ['name'],
91    'ZEND_AST_REF' => ['var'],
92    'ZEND_AST_HALT_COMPILER' => ['offset'],
93    'ZEND_AST_ECHO' => ['expr'],
94    'ZEND_AST_THROW' => ['expr'],
95    'ZEND_AST_GOTO' => ['label'],
96    'ZEND_AST_BREAK' => ['depth'],
97    'ZEND_AST_CONTINUE' => ['depth'],
98    'ZEND_AST_CLASS_NAME' => ['class'],
99
100    /* 2 child nodes */
101    'ZEND_AST_CLASS_CONST_GROUP' => ['const', 'attributes', 'type'],
102    'ZEND_AST_DIM' => ['expr', 'dim'],
103    'ZEND_AST_PROP' => ['expr', 'prop'],
104    'ZEND_AST_NULLSAFE_PROP' => ['expr', 'prop'],
105    'ZEND_AST_STATIC_PROP' => ['class', 'prop'],
106    'ZEND_AST_CALL' => ['expr', 'args'],
107    'ZEND_AST_CLASS_CONST' => ['class', 'const'],
108    'ZEND_AST_ASSIGN' => ['var', 'expr'],
109    'ZEND_AST_ASSIGN_REF' => ['var', 'expr'],
110    'ZEND_AST_ASSIGN_OP' => ['var', 'expr'],
111    'ZEND_AST_BINARY_OP' => ['left', 'right'],
112    'ZEND_AST_ARRAY_ELEM' => ['value', 'key'],
113    'ZEND_AST_NEW' => ['class', 'args'],
114    'ZEND_AST_INSTANCEOF' => ['expr', 'class'],
115    'ZEND_AST_YIELD' => ['value', 'key'],
116
117    'ZEND_AST_STATIC' => ['var', 'default'],
118    'ZEND_AST_WHILE' => ['cond', 'stmts'],
119    'ZEND_AST_DO_WHILE' => ['stmts', 'cond'],
120    'ZEND_AST_IF_ELEM' => ['cond', 'stmts'],
121    'ZEND_AST_SWITCH' => ['cond', 'stmts'],
122    'ZEND_AST_SWITCH_CASE' => ['cond', 'stmts'],
123    'ZEND_AST_DECLARE' => ['declares', 'stmts'],
124    'ZEND_AST_PROP_ELEM' => ['name', 'default', 'docComment'],
125    'ZEND_AST_PROP_GROUP' => ['type', 'props', 'attributes'],
126    'ZEND_AST_CONST_ELEM' => ['name', 'value', 'docComment'],
127    'ZEND_AST_USE_TRAIT' => ['traits', 'adaptations'],
128    'ZEND_AST_TRAIT_PRECEDENCE' => ['method', 'insteadof'],
129    'ZEND_AST_METHOD_REFERENCE' => ['class', 'method'],
130    'ZEND_AST_NAMESPACE' => ['name', 'stmts'],
131    'ZEND_AST_USE_ELEM' => ['name', 'alias'],
132    'ZEND_AST_TRAIT_ALIAS' => ['method', 'alias'],
133    'ZEND_AST_GROUP_USE' => ['prefix', 'uses'],
134    'ZEND_AST_ATTRIBUTE' => ['class', 'args'],
135    'ZEND_AST_MATCH' => ['cond', 'stmts'],
136    'ZEND_AST_MATCH_ARM' => ['cond', 'expr'],
137    'ZEND_AST_NAMED_ARG' => ['name', 'expr'],
138
139    /* 3 child nodes */
140    'ZEND_AST_METHOD_CALL' => ['expr', 'method', 'args'],
141    'ZEND_AST_NULLSAFE_METHOD_CALL' => ['expr', 'method', 'args'],
142    'ZEND_AST_STATIC_CALL' => ['class', 'method', 'args'],
143    'ZEND_AST_CONDITIONAL' => ['cond', 'true', 'false'],
144
145    'ZEND_AST_TRY' => ['try', 'catches', 'finally'],
146    'ZEND_AST_CATCH' => ['class', 'var', 'stmts'],
147
148    /* 4 child nodes */
149    'ZEND_AST_FOR' => ['init', 'cond', 'loop', 'stmts'],
150    'ZEND_AST_FOREACH' => ['expr', 'value', 'key', 'stmts'],
151    'ZEND_AST_ENUM_CASE' => ['name', 'expr', 'docComment', 'attributes'],
152
153    /* 5 child nodes */
154    'ZEND_AST_PARAM' => ['type', 'name', 'default', 'attributes', 'docComment'],
155];
156
157$listNodes = [
158	'ZEND_AST_ARG_LIST',
159	'ZEND_AST_LIST',
160	'ZEND_AST_ARRAY',
161	'ZEND_AST_ENCAPS_LIST',
162	'ZEND_AST_EXPR_LIST',
163	'ZEND_AST_STMT_LIST',
164	'ZEND_AST_IF',
165	'ZEND_AST_SWITCH_LIST',
166	'ZEND_AST_CATCH_LIST',
167	'ZEND_AST_PARAM_LIST',
168	'ZEND_AST_CLOSURE_USES',
169	'ZEND_AST_PROP_DECL',
170	'ZEND_AST_CONST_DECL',
171	'ZEND_AST_CLASS_CONST_DECL',
172	'ZEND_AST_NAME_LIST',
173	'ZEND_AST_TRAIT_ADAPTATIONS',
174	'ZEND_AST_USE',
175	'ZEND_AST_TYPE_UNION',
176	'ZEND_AST_TYPE_INTERSECTION',
177	'ZEND_AST_ATTRIBUTE_LIST',
178	'ZEND_AST_ATTRIBUTE_GROUP',
179	'ZEND_AST_MATCH_ARM_LIST',
180];
181
182$data = [];
183foreach ($listNodes as $name) {
184    $shortName = str_replace('ZEND_', '', $name);
185    $data[$name] = $shortName;
186}
187foreach ($names as $name => $_) {
188    $shortName = str_replace('ZEND_', '', $name);
189    $data[$name] = $shortName;
190}
191
192if ($argc > 1) {
193    /* Optional: Check against zend_ast.h */
194    $inFile = $argv[1];
195
196    if (!is_readable($inFile)) {
197	die("Input file not readable\n");
198    }
199
200    $inCode = file_get_contents($inFile);
201    if (!preg_match('/enum _zend_ast_kind \{(.*?)\};/s', $inCode, $matches)) {
202	die("Malformed input file\n");
203    }
204
205    $lines = explode("\n", $matches[1]);
206    foreach ($lines as $line) {
207	if (!preg_match('/\s*(ZEND_([A-Z_]+))/', $line, $matches)) {
208	    continue;
209	}
210
211	list(, $zend_name, $name) = $matches;
212	if ($name == 'AST_ZNODE' || $name == 'AST_ZVAL') {
213	    continue;
214	}
215
216	if (!isset($data[$zend_name])) {
217	    echo "Missing $zend_name\n";
218	}
219    }
220}
221
222$kinds = [];
223$strs = [];
224$consts = [];
225foreach ($data as $zend_name => $name) {
226    $kinds[] = "\t$zend_name,";
227    $strs[] = "\t\tcase $zend_name: return \"$name\";";
228    $consts[] = "\tREGISTER_NS_LONG_CONSTANT(\"ast\", \"$name\", $zend_name,"
229        . " CONST_CS | CONST_PERSISTENT);";
230}
231
232$code = str_replace('{KINDS}', implode("\n", $kinds), $code);
233$code = str_replace('{STRS}', implode("\n", $strs), $code);
234$code = str_replace('{CONSTS}', implode("\n", $consts), $code);
235
236$implementations = [];
237foreach ($names as $kind => $children) {
238    if (empty($children)) {
239        $implementations["\t\t\treturn NULL;"][] = $kind;
240        continue;
241    }
242
243    $kindChildNames = [];
244    foreach ($children as $index => $name) {
245        $kindChildNames[] = "\t\t\t\tcase $index: return AST_STR(str_$name);";
246    }
247    $body = "\t\t\tswitch (child) {\n"
248        . implode("\n", $kindChildNames) . "\n\t\t\t}\n\t\t\treturn NULL;";
249    $implementations[$body][] = $kind;
250}
251$childNames = [];
252foreach ($implementations as $body => $kindList) {
253    $codeForGroup = '';
254    foreach ($kindList as $kind) {
255        $codeForGroup .= "\t\tcase $kind:\n";
256    }
257    $codeForGroup .= $body;
258    $childNames[] = $codeForGroup;
259}
260
261$code = str_replace('{CHILD_NAMES}', implode("\n", $childNames), $code);
262
263file_put_contents($outCodeFile, $code);
264
265$strings = get_possible_strings($names);
266$strDefs = [];
267foreach ($strings as $name) {
268    $strDefs[] .= "\tX($name) \\";
269}
270
271$strDefsHeader = str_replace('{STR_DEFS}', implode("\n", $strDefs), $strDefsHeader);
272file_put_contents($strDefsFile, $strDefsHeader);
273
274function get_possible_strings(array $spec) {
275    $strings = array_fill_keys([
276        'kind', 'flags', 'lineno', 'children',
277        'name', 'docComment', 'endLineno', '__declId',
278        'flagsCombinable',
279    ], true);
280
281    foreach ($spec as $kind => $children) {
282        foreach ($children as $childName) {
283            $strings[$childName] = true;
284        }
285    }
286    return array_keys($strings);
287}
288