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