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