From: Dmitry Stogov Date: Thu, 19 Feb 2015 08:11:17 +0000 (+0300) Subject: Implemented AST pretty-printer X-Git-Tag: PRE_PHP7_EREG_MYSQL_REMOVALS~112 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6b779596d7ece59f5cb9ec6c507ac8b1c7794270;p=php Implemented AST pretty-printer --- diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 242c99ff7f..ee3ae69a34 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -22,6 +22,9 @@ #include "zend_ast.h" #include "zend_API.h" #include "zend_operators.h" +#include "zend_language_parser.h" +#include "zend_smart_str.h" +#include "zend_exceptions.h" ZEND_API zend_ast_process_t zend_ast_process = NULL; @@ -441,3 +444,1142 @@ ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn) { } } } + +/* + * Operator Precendence + * ==================== + * priority associativity operators + * ---------------------------------- + * 10 left include, include_once, eval, require, require_once + * 20 left , + * 30 left or + * 40 left xor + * 50 left and + * 60 right print + * 70 right yield + * 80 right => + * 90 right = += -= *= /= .= %= &= |= ^= <<= >>= **= + * 100 left ? : + * 110 right ?? + * 120 left || + * 130 left && + * 140 left | + * 150 left ^ + * 160 left & + * 170 non-associative == != === !== + * 180 non-associative < <= > >= <=> + * 190 left << >> + * 200 left + - . + * 210 left * / % + * 220 right ! + * 230 non-associative instanceof + * 240 right + - ++ -- ~ (type) @ + * 250 right ** + * 260 left [ + * 270 non-associative clone new + */ + +static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int indent); + +static void zend_ast_export_str(smart_str *str, zend_string *s) +{ + size_t i; + + for (i = 0; i < s->len; i++) { + unsigned char c = s->val[i]; + if (c == '\'' || c == '\\') { + smart_str_appendc(str, '\\'); + smart_str_appendc(str, c); + } else { + smart_str_appendc(str, c); + } + } +} + +static void zend_ast_export_qstr(smart_str *str, char quote, zend_string *s) +{ + size_t i; + + for (i = 0; i < s->len; i++) { + unsigned char c = s->val[i]; + if (c < ' ') { + switch (c) { + case '\n': + smart_str_appends(str, "\\n"); + break; + case '\r': + smart_str_appends(str, "\\r"); + break; + case '\t': + smart_str_appends(str, "\\t"); + break; + case '\f': + smart_str_appends(str, "\\f"); + break; + case '\v': + smart_str_appends(str, "\\v"); + break; +#ifdef PHP_WIN32 + case VK_ESCAPE: +#else + case '\e': +#endif + smart_str_appends(str, "\\e"); + break; + default: + smart_str_appends(str, "\\0"); + smart_str_appendc(str, '0' + (c / 8)); + smart_str_appendc(str, '0' + (c % 8)); + break; + } + } else { + if (c == quote || c == '$' || c == '\\') { + smart_str_appendc(str, '\\'); + } + smart_str_appendc(str, c); + } + } +} + +static void zend_ast_export_indent(smart_str *str, int indent) +{ + while (indent > 0) { + smart_str_appends(str, " "); + indent--; + } +} + +static void zend_ast_export_name(smart_str *str, zend_ast *ast, int priority, int indent) +{ + if (ast->kind == ZEND_AST_ZVAL) { + zval *zv = zend_ast_get_zval(ast); + + if (Z_TYPE_P(zv) == IS_STRING) { + smart_str_append(str, Z_STR_P(zv)); + return; + } + } + zend_ast_export_ex(str, ast, priority, indent); +} + +static void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int priority, int indent) +{ + if (ast->kind == ZEND_AST_ZVAL) { + zval *zv = zend_ast_get_zval(ast); + + if (Z_TYPE_P(zv) == IS_STRING) { + if (ast->attr == ZEND_NAME_FQ) { + smart_str_appendc(str, '\\'); + } else if (ast->attr == ZEND_NAME_RELATIVE) { + smart_str_appends(str, "namespace\\"); + } + smart_str_append(str, Z_STR_P(zv)); + return; + } + } + zend_ast_export_ex(str, ast, priority, indent); +} + +static int zend_ast_valid_var_char(char ch) +{ + unsigned char c = (unsigned char)ch; + + if (c != '_' && c < 127 && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) { + return 0; + } + return 1; +} + +static int zend_ast_valid_var_name(const char *s, size_t len) +{ + unsigned char c; + size_t i; + + if (len == 0) { + return 0; + } + c = (unsigned char)s[0]; + if (c != '_' && c < 127 && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) { + return 0; + } + for (i = 1; i < len; i++) { + c = (unsigned char)s[i]; + if (c != '_' && c < 127 && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) { + return 0; + } + } + return 1; +} + +static void zend_ast_export_var(smart_str *str, zend_ast *ast, int priority, int indent) +{ + if (ast->kind == ZEND_AST_ZVAL) { + zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) == IS_STRING && + zend_ast_valid_var_name(Z_STRVAL_P(zv), Z_STRLEN_P(zv))) { + smart_str_append(str, Z_STR_P(zv)); + return; + } + } else if (ast->kind == ZEND_AST_VAR) { + zend_ast_export_ex(str, ast, 0, indent); + return; + } + smart_str_appendc(str, '{'); + zend_ast_export_name(str, ast, 0, indent); + smart_str_appendc(str, '}'); +} + +static void zend_ast_export_list(smart_str *str, zend_ast_list *list, int separator, int priority, int indent) +{ + uint32_t i = 0; + + while (i < list->children) { + if (i != 0 && separator) { + smart_str_appends(str, ", "); + } + zend_ast_export_ex(str, list->child[i], priority, indent); + i++; + } +} + +static void zend_ast_export_encaps_list(smart_str *str, char quote, zend_ast_list *list, int indent) +{ + uint32_t i = 0; + zend_ast *ast; + + while (i < list->children) { + ast = list->child[i]; + if (ast->kind == ZEND_AST_ZVAL) { + zval *zv = zend_ast_get_zval(ast); + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); + zend_ast_export_qstr(str, quote, Z_STR_P(zv)); + } else if (ast->kind == ZEND_AST_VAR && + ast->child[0]->kind == ZEND_AST_ZVAL && + (i + 1 == list->children || + list->child[i + 1]->kind != ZEND_AST_ZVAL || + !zend_ast_valid_var_char( + *Z_STRVAL_P( + zend_ast_get_zval(list->child[i + 1]))))) { + zend_ast_export_ex(str, ast, 0, indent); + } else { + smart_str_appendc(str, '{'); + zend_ast_export_ex(str, ast, 0, indent); + smart_str_appendc(str, '}'); + } + i++; + } +} + +static void zend_ast_export_name_list(smart_str *str, zend_ast_list *list, int indent) +{ + uint32_t i = 0; + + while (i < list->children) { + if (i != 0) { + smart_str_appends(str, ", "); + } + zend_ast_export_name(str, list->child[i], 0, indent); + i++; + } +} + +static void zend_ast_export_var_list(smart_str *str, zend_ast_list *list, int indent) +{ + uint32_t i = 0; + + while (i < list->children) { + if (i != 0) { + smart_str_appends(str, ", "); + } + if (list->child[i]->attr) { + smart_str_appendc(str, '&'); + } + smart_str_appendc(str, '$'); + zend_ast_export_name(str, list->child[i], 20, indent); + i++; + } +} + +static void zend_ast_export_stmt(smart_str *str, zend_ast *ast, int indent) +{ + if (ast->kind == ZEND_AST_STMT_LIST || + ast->kind == ZEND_AST_TRAIT_ADAPTATIONS) { + zend_ast_list *list = (zend_ast_list*)ast; + uint32_t i = 0; + + while (i < list->children) { + ast = list->child[i]; + zend_ast_export_stmt(str, ast, indent); + i++; + } + } else { + zend_ast_export_indent(str, indent); + zend_ast_export_ex(str, ast, 0, indent); + switch (ast->kind) { + case ZEND_AST_LABEL: + case ZEND_AST_IF: + case ZEND_AST_SWITCH: + case ZEND_AST_WHILE: + case ZEND_AST_TRY: + case ZEND_AST_FOR: + case ZEND_AST_FOREACH: + case ZEND_AST_FUNC_DECL: + case ZEND_AST_METHOD: + case ZEND_AST_CLASS: + case ZEND_AST_USE_TRAIT: + case ZEND_AST_NAMESPACE: + case ZEND_AST_DECLARE: + break; + default: + smart_str_appendc(str, ';'); + break; + } + smart_str_appendc(str, '\n'); + } +} + +static void zend_ast_export_if_stmt(smart_str *str, zend_ast_list *list, int indent) +{ + uint32_t i; + zend_ast *ast; + +tail_call: + i = 0; + while (i < list->children) { + ast = list->child[i]; + ZEND_ASSERT(ast->kind == ZEND_AST_IF_ELEM); + if (ast->child[0]) { + if (i == 0) { + smart_str_appends(str, "if ("); + } else { + zend_ast_export_indent(str, indent); + smart_str_appends(str, "} elseif ("); + } + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + } else { + zend_ast_export_indent(str, indent); + smart_str_appends(str, "} else "); + if (ast->child[1]->kind == ZEND_AST_IF) { + list = (zend_ast_list*)ast->child[1]; + goto tail_call; + } else { + smart_str_appends(str, "{\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + } + } + i++; + } + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); +} + +static void zend_ast_export_zval(smart_str *str, zval *zv, int priority, int indent) +{ + zend_long idx; + zend_string *key; + zval *val; + int first; + + ZVAL_DEREF(zv); + switch (Z_TYPE_P(zv)) { + case IS_NULL: + smart_str_appends(str, "null"); + break; + case IS_FALSE: + smart_str_appends(str, "false"); + break; + case IS_TRUE: + smart_str_appends(str, "true"); + break; + case IS_LONG: + smart_str_append_long(str, Z_LVAL_P(zv)); + break; + case IS_DOUBLE: + key = zend_strpprintf(0, "%.*G", (int) EG(precision), Z_DVAL_P(zv)); + smart_str_appendl(str, key->val, key->len); + zend_string_release(key); + break; + case IS_STRING: + smart_str_appendc(str, '\''); + zend_ast_export_str(str, Z_STR_P(zv)); + smart_str_appendc(str, '\''); + break; + case IS_ARRAY: + smart_str_appendc(str, '['); + first = 1; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zv), idx, key, val) { + if (first) { + first = 0; + } else { + smart_str_appends(str, ", "); + } + if (key) { + smart_str_appendc(str, '\''); + zend_ast_export_str(str, key); + smart_str_appends(str, "' => "); + } else { + smart_str_append_long(str, idx); + smart_str_appends(str, " => "); + } + zend_ast_export_zval(str, val, 0, indent); + } ZEND_HASH_FOREACH_END(); + smart_str_appendc(str, ']'); + break; + case IS_CONSTANT: + smart_str_appendl(str, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + break; + case IS_CONSTANT_AST: + zend_ast_export_ex(str, Z_ASTVAL_P(zv), priority, indent); + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } +} + +#define BINARY_OP(_op, _p, _pl, _pr) do { \ + op = _op; \ + p = _p; \ + pl = _pl; \ + pr = _pr; \ + goto binary_op; \ + } while (0) + +#define PREFIX_OP(_op, _p, _pl) do { \ + op = _op; \ + p = _p; \ + pl = _pl; \ + goto prefix_op; \ + } while (0) + +#define FUNC_OP(_op) do { \ + op = _op; \ + goto func_op; \ + } while (0) + +#define POSTFIX_OP(_op, _p, _pl) do { \ + op = _op; \ + p = _p; \ + pl = _pl; \ + goto postfix_op; \ + } while (0) + +#define APPEND_NODE_1(_op) do { \ + op = _op; \ + goto append_node_1; \ + } while (0) + +#define APPEND_STR(_op) do { \ + op = _op; \ + goto append_str; \ + } while (0) + +#define APPEND_DEFAULT_VALUE(n) do { \ + p = n; \ + goto append_default_value; \ + } while (0) + +static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int indent) +{ + zval *zv; + zend_ast_decl *decl; + int p, pl, pr; + const char *op; + +tail_call: + if (!ast) { + return; + } + switch (ast->kind) { + /* special nodes */ + case ZEND_AST_ZVAL: + zend_ast_export_zval(str, zend_ast_get_zval(ast), priority, indent); + break; +//??? case ZEND_AST_ZNODE: + + /* declaration nodes */ + case ZEND_AST_FUNC_DECL: + case ZEND_AST_CLOSURE: + case ZEND_AST_METHOD: + decl = (zend_ast_decl *) ast; + if (decl->flags & ZEND_ACC_PUBLIC) { + smart_str_appends(str, "public "); + } else if (decl->flags & ZEND_ACC_PROTECTED) { + smart_str_appends(str, "protected "); + } else if (decl->flags & ZEND_ACC_PRIVATE) { + smart_str_appends(str, "private "); + } + if (decl->flags & ZEND_ACC_STATIC) { + smart_str_appends(str, "static "); + } + if (decl->flags & ZEND_ACC_ABSTRACT) { + smart_str_appends(str, "abstract "); + } + if (decl->flags & ZEND_ACC_FINAL) { + smart_str_appends(str, "final "); + } + smart_str_appends(str, "function "); + if (decl->flags & ZEND_ACC_RETURN_REFERENCE) { + smart_str_appendc(str, '&'); + } + if (ast->kind != ZEND_AST_CLOSURE) { + smart_str_appendl(str, decl->name->val, decl->name->len); + } + smart_str_appendc(str, '('); + zend_ast_export_ex(str, decl->child[0], 0, indent); + smart_str_appendc(str, ')'); + zend_ast_export_ex(str, decl->child[1], 0, indent); + if (decl->child[3]) { + smart_str_appends(str, ": "); + zend_ast_export_ns_name(str, decl->child[3], 0, indent); + } + if (decl->child[2]) { + smart_str_appends(str, " {\n"); + zend_ast_export_stmt(str, decl->child[2], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + if (ast->kind != ZEND_AST_CLOSURE) { + smart_str_appendc(str, '\n'); + } + } else { + smart_str_appends(str, ";\n"); + } + break; + case ZEND_AST_CLASS: + decl = (zend_ast_decl *) ast; + if (decl->flags & ZEND_ACC_INTERFACE) { + smart_str_appends(str, "interface "); + } else if (decl->flags & ZEND_ACC_TRAIT) { + smart_str_appends(str, "trait "); + } else { + if (decl->flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) { + smart_str_appends(str, "abstract "); + } + if (decl->flags & ZEND_ACC_FINAL) { + smart_str_appends(str, "final "); + } + smart_str_appends(str, "class "); + } + smart_str_appendl(str, decl->name->val, decl->name->len); + if (decl->child[0]) { + smart_str_appends(str, " extends "); + zend_ast_export_ns_name(str, decl->child[0], 0, indent); + } + if (decl->child[1]) { + smart_str_appends(str, " implements "); + zend_ast_export_ex(str, decl->child[1], 0, indent); + } + smart_str_appends(str, " {\n"); + zend_ast_export_stmt(str, decl->child[2], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "}\n"); + break; + + /* list nodes */ + case ZEND_AST_ARG_LIST: + case ZEND_AST_EXPR_LIST: + case ZEND_AST_PARAM_LIST: +simple_list: + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + break; + case ZEND_AST_LIST: + smart_str_appends(str, "list("); + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_ARRAY: + smart_str_appendc(str, '['); + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + smart_str_appendc(str, ']'); + break; + case ZEND_AST_ENCAPS_LIST: + smart_str_appendc(str, '"'); + zend_ast_export_encaps_list(str, '"', (zend_ast_list*)ast, indent); + smart_str_appendc(str, '"'); + break; + case ZEND_AST_STMT_LIST: + case ZEND_AST_TRAIT_ADAPTATIONS: + zend_ast_export_stmt(str, ast, indent); + break; + case ZEND_AST_IF: + zend_ast_export_if_stmt(str, (zend_ast_list*)ast, indent); + break; + case ZEND_AST_SWITCH_LIST: + case ZEND_AST_CATCH_LIST: + zend_ast_export_list(str, (zend_ast_list*)ast, 0, 0, indent); + break; + case ZEND_AST_CLOSURE_USES: + smart_str_appends(str, " use("); + zend_ast_export_var_list(str, (zend_ast_list*)ast, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_PROP_DECL: + if (ast->attr & ZEND_ACC_PUBLIC) { + smart_str_appends(str, "public "); + } else if (ast->attr & ZEND_ACC_PROTECTED) { + smart_str_appends(str, "protected "); + } else if (ast->attr & ZEND_ACC_PRIVATE) { + smart_str_appends(str, "private "); + } + if (ast->attr & ZEND_ACC_STATIC) { + smart_str_appends(str, "static "); + } + goto simple_list; + case ZEND_AST_CONST_DECL: + case ZEND_AST_CLASS_CONST_DECL: + smart_str_appends(str, "const "); + goto simple_list; + case ZEND_AST_NAME_LIST: + zend_ast_export_name_list(str, (zend_ast_list*)ast, indent); + break; + case ZEND_AST_USE: + smart_str_appends(str, "use "); + if (ast->attr == T_FUNCTION) { + smart_str_appends(str, "function "); + } else if (ast->attr == T_CONST) { + smart_str_appends(str, "const "); + } + goto simple_list; + + /* 0 child nodes */ + case ZEND_AST_MAGIC_CONST: + switch (ast->attr) { + case T_LINE: APPEND_STR("__LINE__"); + case T_FILE: APPEND_STR("__FILE__"); + case T_DIR: APPEND_STR("__DIR__"); + case T_TRAIT_C: APPEND_STR("__TRAIT__"); + case T_METHOD_C: APPEND_STR("__METHOD__"); + case T_FUNC_C: APPEND_STR("__FUNCTION__"); + case T_NS_C: APPEND_STR("__NAMESPACE__"); + case T_CLASS_C: APPEND_STR("__CLASS__"); + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + case ZEND_AST_TYPE: + switch (ast->attr) { + case IS_ARRAY: APPEND_STR("array"); + case IS_CALLABLE: APPEND_STR("callable"); + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + + /* 1 child node */ + case ZEND_AST_VAR: + smart_str_appendc(str, '$'); + zend_ast_export_var(str, ast->child[0], 0, indent); + break; + case ZEND_AST_CONST: + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + break; + case ZEND_AST_RESOLVE_CLASS_NAME: + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + APPEND_STR("::class"); + case ZEND_AST_UNPACK: + smart_str_appends(str, "..."); + ast = ast->child[0]; + goto tail_call; + case ZEND_AST_UNARY_PLUS: PREFIX_OP("+", 240, 241); + case ZEND_AST_UNARY_MINUS: PREFIX_OP("-", 240, 241); + case ZEND_AST_CAST: + switch (ast->attr) { + case IS_NULL: PREFIX_OP("(unset)", 240, 241); + case _IS_BOOL: PREFIX_OP("(bool)", 240, 241); + case IS_LONG: PREFIX_OP("(int)", 240, 241); + case IS_DOUBLE: PREFIX_OP("(double)", 240, 241); + case IS_STRING: PREFIX_OP("(string)", 240, 241); + case IS_ARRAY: PREFIX_OP("(array)", 240, 241); + case IS_OBJECT: PREFIX_OP("(object)", 240, 241); + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + case ZEND_AST_EMPTY: + FUNC_OP("empty"); + case ZEND_AST_ISSET: + FUNC_OP("isset"); + case ZEND_AST_SILENCE: + PREFIX_OP("@", 240, 241); + case ZEND_AST_SHELL_EXEC: + smart_str_appendc(str, '`'); + if (ast->child[0]->kind == ZEND_AST_ENCAPS_LIST) { + zend_ast_export_encaps_list(str, '`', (zend_ast_list*)ast->child[0], indent); + } else { + zend_ast_export_ex(str, ast->child[0], 0, indent); + } + smart_str_appendc(str, '`'); + break; + case ZEND_AST_CLONE: + PREFIX_OP("clone ", 270, 271); + case ZEND_AST_EXIT: + if (ast->child[0]) { + FUNC_OP("exit"); + } else { + APPEND_STR("exit"); + } + break; + case ZEND_AST_PRINT: + PREFIX_OP("print ", 60, 61); + case ZEND_AST_INCLUDE_OR_EVAL: + switch (ast->attr) { + case ZEND_INCLUDE_ONCE: FUNC_OP("include_once"); + case ZEND_INCLUDE: FUNC_OP("include"); + case ZEND_REQUIRE_ONCE: FUNC_OP("require_once"); + case ZEND_REQUIRE: FUNC_OP("require"); + case ZEND_EVAL: FUNC_OP("eval"); + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + case ZEND_AST_UNARY_OP: + switch (ast->attr) { + case ZEND_BW_NOT: PREFIX_OP("~", 240, 241); + case ZEND_BOOL_NOT: PREFIX_OP("!", 240, 241); + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + case ZEND_AST_PRE_INC: + PREFIX_OP("++", 240, 241); + case ZEND_AST_PRE_DEC: + PREFIX_OP("--", 240, 241); + case ZEND_AST_POST_INC: + POSTFIX_OP("++", 240, 241); + case ZEND_AST_POST_DEC: + POSTFIX_OP("--", 240, 241); + + case ZEND_AST_GLOBAL: + APPEND_NODE_1("global"); + case ZEND_AST_UNSET: + FUNC_OP("unset"); + case ZEND_AST_RETURN: + APPEND_NODE_1("return"); + case ZEND_AST_LABEL: + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, ':'); + break; + case ZEND_AST_REF: + smart_str_appendc(str, '&'); + ast = ast->child[0]; + goto tail_call; + case ZEND_AST_HALT_COMPILER: + APPEND_STR("__HALT_COMPILER()"); + case ZEND_AST_ECHO: + APPEND_NODE_1("echo"); + case ZEND_AST_THROW: + APPEND_NODE_1("throw"); + case ZEND_AST_GOTO: + smart_str_appends(str, "goto "); + zend_ast_export_name(str, ast->child[0], 0, indent); + break; + case ZEND_AST_BREAK: + APPEND_NODE_1("break"); + case ZEND_AST_CONTINUE: + APPEND_NODE_1("continue"); + + /* 2 child nodes */ + case ZEND_AST_DIM: + zend_ast_export_ex(str, ast->child[0], 260, indent); + smart_str_appendc(str, '['); + if (ast->child[1]) { + zend_ast_export_ex(str, ast->child[1], 0, indent); + } + smart_str_appendc(str, ']'); + break; + case ZEND_AST_PROP: + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, "->"); + zend_ast_export_var(str, ast->child[1], 0, indent); + break; + case ZEND_AST_STATIC_PROP: + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::$"); + zend_ast_export_var(str, ast->child[1], 0, indent); + break; + case ZEND_AST_CALL: + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_CLASS_CONST: + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::"); + zend_ast_export_name(str, ast->child[1], 0, indent); + break; + case ZEND_AST_ASSIGN: BINARY_OP(" = ", 90, 91, 90); + case ZEND_AST_ASSIGN_REF: BINARY_OP(" =& ", 90, 91, 90); + case ZEND_AST_ASSIGN_OP: + switch (ast->attr) { + case ZEND_ASSIGN_ADD: BINARY_OP(" += ", 90, 91, 90); + case ZEND_ASSIGN_SUB: BINARY_OP(" -= ", 90, 91, 90); + case ZEND_ASSIGN_MUL: BINARY_OP(" *= ", 90, 91, 90); + case ZEND_ASSIGN_DIV: BINARY_OP(" /= ", 90, 91, 90); + case ZEND_ASSIGN_MOD: BINARY_OP(" %= ", 90, 91, 90); + case ZEND_ASSIGN_SL: BINARY_OP(" <<= ", 90, 91, 90); + case ZEND_ASSIGN_SR: BINARY_OP(" >>= ", 90, 91, 90); + case ZEND_ASSIGN_CONCAT: BINARY_OP(" .= ", 90, 91, 90); + case ZEND_ASSIGN_BW_OR: BINARY_OP(" |= ", 90, 91, 90); + case ZEND_ASSIGN_BW_AND: BINARY_OP(" &= ", 90, 91, 90); + case ZEND_ASSIGN_BW_XOR: BINARY_OP(" ^= ", 90, 91, 90); + case ZEND_POW: BINARY_OP(" **= ", 90, 91, 90); + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + case ZEND_AST_BINARY_OP: + switch (ast->attr) { + case ZEND_ADD: BINARY_OP(" + ", 200, 200, 201); + case ZEND_SUB: BINARY_OP(" - ", 200, 200, 201); + case ZEND_MUL: BINARY_OP(" * ", 210, 210, 211); + case ZEND_DIV: BINARY_OP(" / ", 210, 210, 211); + case ZEND_MOD: BINARY_OP(" % ", 210, 210, 211); + case ZEND_SL: BINARY_OP(" << ", 190, 190, 191); + case ZEND_SR: BINARY_OP(" >> ", 190, 190, 191); + case ZEND_CONCAT: BINARY_OP(" . ", 200, 200, 201); + case ZEND_BW_OR: BINARY_OP(" | ", 140, 140, 141); + case ZEND_BW_AND: BINARY_OP(" & ", 160, 160, 161); + case ZEND_BW_XOR: BINARY_OP(" ^ ", 150, 150, 151); + case ZEND_IS_IDENTICAL: BINARY_OP(" === ", 170, 171, 171); + case ZEND_IS_NOT_IDENTICAL: BINARY_OP(" !== ", 170, 171, 171); + case ZEND_IS_EQUAL: BINARY_OP(" == ", 170, 171, 171); + case ZEND_IS_NOT_EQUAL: BINARY_OP(" != ", 170, 171, 171); + case ZEND_IS_SMALLER: BINARY_OP(" < ", 180, 181, 181); + case ZEND_IS_SMALLER_OR_EQUAL: BINARY_OP(" <= ", 180, 181, 181); + case ZEND_POW: BINARY_OP(" ** ", 250, 251, 250); + case ZEND_BOOL_XOR: BINARY_OP(" xor ", 40, 40, 41); + case ZEND_SPACESHIP: BINARY_OP(" <=> ", 180, 181, 181); + EMPTY_SWITCH_DEFAULT_CASE(); + } + break; + case ZEND_AST_GREATER: BINARY_OP(" > ", 180, 181, 181); + case ZEND_AST_GREATER_EQUAL: BINARY_OP(" >= ", 180, 181, 181); + case ZEND_AST_AND: BINARY_OP(" && ", 130, 130, 131); + case ZEND_AST_OR: BINARY_OP(" || ", 120, 120, 121); + case ZEND_AST_ARRAY_ELEM: + if (ast->child[1]) { + zend_ast_export_ex(str, ast->child[1], 80, indent); + smart_str_appends(str, " => "); + } + zend_ast_export_ex(str, ast->child[0], 80, indent); + break; + case ZEND_AST_NEW: + smart_str_appends(str, "new "); + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_INSTANCEOF: BINARY_OP(" instanceof ", 230, 231, 231); + case ZEND_AST_YIELD: + if (priority > 70) smart_str_appendc(str, '('); + smart_str_appends(str, "yield "); + if (ast->child[0]) { + if (ast->child[1]) { + zend_ast_export_ex(str, ast->child[1], 70, indent); + smart_str_appends(str, " => "); + } + zend_ast_export_ex(str, ast->child[0], 70, indent); + } + if (priority > 70) smart_str_appendc(str, ')'); + break; + case ZEND_AST_COALESCE: BINARY_OP(" ?? ", 110, 111, 110); + case ZEND_AST_STATIC: + smart_str_appends(str, "static $"); + zend_ast_export_name(str, ast->child[0], 0, indent); + APPEND_DEFAULT_VALUE(1); + case ZEND_AST_WHILE: + smart_str_appends(str, "while ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + case ZEND_AST_DO_WHILE: + smart_str_appends(str, "do {\n"); + zend_ast_export_stmt(str, ast->child[0], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "} while ("); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appendc(str, ')'); + break; + + case ZEND_AST_IF_ELEM: + if (ast->child[0]) { + smart_str_appends(str, "if ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + } else { + smart_str_appends(str, "else {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + } + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + case ZEND_AST_SWITCH: + smart_str_appends(str, "switch ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_ex(str, ast->child[1], 0, indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + case ZEND_AST_SWITCH_CASE: + zend_ast_export_indent(str, indent); + if (ast->child[0]) { + smart_str_appends(str, "case "); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ":\n"); + } else { + smart_str_appends(str, "default:\n"); + } + zend_ast_export_stmt(str, ast->child[1], indent + 1); + break; + case ZEND_AST_DECLARE: + smart_str_appends(str, "declare("); + ZEND_ASSERT(ast->child[0]->kind == ZEND_AST_CONST_DECL); + zend_ast_export_list(str, (zend_ast_list*)ast->child[0], 1, 0, indent); + smart_str_appendc(str, ')'); + if (ast->child[1]) { + smart_str_appends(str, " {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + } else { + smart_str_appendc(str, ';'); + } + break; + case ZEND_AST_PROP_ELEM: + smart_str_appendc(str, '$'); + /* break missing intentionally */ + case ZEND_AST_CONST_ELEM: + zend_ast_export_name(str, ast->child[0], 0, indent); + APPEND_DEFAULT_VALUE(1); + case ZEND_AST_USE_TRAIT: + smart_str_appends(str, "use "); + zend_ast_export_ex(str, ast->child[0], 0, indent); + if (ast->child[1]) { + smart_str_appends(str, " {\n"); + zend_ast_export_ex(str, ast->child[1], 0, indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "}"); + } else { + smart_str_appends(str, ";"); + } + break; + case ZEND_AST_TRAIT_PRECEDENCE: + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, " insteadof "); + zend_ast_export_ex(str, ast->child[1], 0, indent); + break; + case ZEND_AST_METHOD_REFERENCE: + if (ast->child[0]) { + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::"); + } + zend_ast_export_name(str, ast->child[1], 0, indent); + break; + case ZEND_AST_NAMESPACE: + smart_str_appends(str, "namespace"); + if (ast->child[0]) { + smart_str_appendc(str, ' '); + zend_ast_export_name(str, ast->child[0], 0, indent); + } + if (ast->child[1]) { + smart_str_appends(str, " {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "}\n"); + } else { + smart_str_appendc(str, ';'); + } + break; + case ZEND_AST_USE_ELEM: + case ZEND_AST_TRAIT_ALIAS: + zend_ast_export_name(str, ast->child[0], 0, indent); + if (ast->attr & ZEND_ACC_PUBLIC) { + smart_str_appends(str, " as public"); + } else if (ast->attr & ZEND_ACC_PROTECTED) { + smart_str_appends(str, " as protected"); + } else if (ast->attr & ZEND_ACC_PRIVATE) { + smart_str_appends(str, " as private"); + } else if (ast->child[1]) { + smart_str_appends(str, " as"); + } + if (ast->child[1]) { + smart_str_appendc(str, ' '); + zend_ast_export_name(str, ast->child[1], 0, indent); + } + break; + + /* 3 child nodes */ + case ZEND_AST_METHOD_CALL: + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, "->"); + zend_ast_export_var(str, ast->child[1], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[2], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_STATIC_CALL: + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::"); + zend_ast_export_var(str, ast->child[1], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[2], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_CONDITIONAL: + if (priority > 100) smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], 100, indent); + if (ast->child[1]) { + smart_str_appends(str, " ? "); + zend_ast_export_ex(str, ast->child[1], 101, indent); + smart_str_appends(str, " : "); + } else { + smart_str_appends(str, " ?: "); + } + zend_ast_export_ex(str, ast->child[2], 101, indent); + if (priority > 100) smart_str_appendc(str, ')'); + break; + + case ZEND_AST_TRY: + smart_str_appends(str, "try {\n"); + zend_ast_export_stmt(str, ast->child[0], indent + 1); + zend_ast_export_indent(str, indent); + zend_ast_export_ex(str, ast->child[1], 0, indent); + if (ast->child[2]) { + smart_str_appends(str, "} finally {\n"); + zend_ast_export_stmt(str, ast->child[2], indent + 1); + zend_ast_export_indent(str, indent); + } + smart_str_appendc(str, '}'); + break; + case ZEND_AST_CATCH: + smart_str_appends(str, "} catch ("); + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appends(str, " $"); + zend_ast_export_var(str, ast->child[1], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[2], indent + 1); + zend_ast_export_indent(str, indent); + break; + case ZEND_AST_PARAM: + if (ast->child[0]) { + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, ' '); + } + if (ast->attr & ZEND_PARAM_REF) { + smart_str_appendc(str, '&'); + } + if (ast->attr & ZEND_PARAM_VARIADIC) { + smart_str_appends(str, "..."); + } + smart_str_appendc(str, '$'); + zend_ast_export_name(str, ast->child[1], 0, indent); + APPEND_DEFAULT_VALUE(2); + + /* 4 child nodes */ + case ZEND_AST_FOR: + smart_str_appends(str, "for ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appendc(str, ';'); + if (ast->child[1]) { + smart_str_appendc(str, ' '); + zend_ast_export_ex(str, ast->child[1], 0, indent); + } + smart_str_appendc(str, ';'); + if (ast->child[2]) { + smart_str_appendc(str, ' '); + zend_ast_export_ex(str, ast->child[2], 0, indent); + } + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[3], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + case ZEND_AST_FOREACH: + smart_str_appends(str, "foreach ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, " as "); + if (ast->child[2]) { + zend_ast_export_ex(str, ast->child[2], 0, indent); + smart_str_appends(str, " => "); + } + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[3], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + EMPTY_SWITCH_DEFAULT_CASE(); + } + return; + +binary_op: + if (priority > p) smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], pl, indent); + smart_str_appends(str, op); + zend_ast_export_ex(str, ast->child[1], pr, indent); + if (priority > p) smart_str_appendc(str, ')'); + return; + +prefix_op: + if (priority > p) smart_str_appendc(str, '('); + smart_str_appends(str, op); + zend_ast_export_ex(str, ast->child[0], pl, indent); + if (priority > p) smart_str_appendc(str, ')'); + return; + +postfix_op: + if (priority > p) smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], pl, indent); + smart_str_appends(str, op); + if (priority > p) smart_str_appendc(str, ')'); + return; + +func_op: + smart_str_appends(str, op); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appendc(str, ')'); + return; + +append_node_1: + smart_str_appends(str, op); + if (ast->child[0]) { + smart_str_appendc(str, ' '); + ast = ast->child[0]; + goto tail_call; + } + return; + +append_str: + smart_str_appends(str, op); + return; + +append_default_value: + if (ast->child[p]) { + smart_str_appends(str, " = "); + ast = ast->child[p]; + goto tail_call; + } + return; +} + +ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix) +{ + smart_str str = {0}; + + smart_str_appends(&str, prefix); + zend_ast_export_ex(&str, ast, 0, 0); + smart_str_appends(&str, suffix); + smart_str_0(&str); + return str.s; +} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index a9d58428ec..bc26ea2168 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -203,6 +203,7 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki ZEND_API zend_ast *zend_ast_list_add(zend_ast *list, zend_ast *op); ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope); +ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix); ZEND_API zend_ast *zend_ast_copy(zend_ast *ast); ZEND_API void zend_ast_destroy(zend_ast *ast);