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