]> granicus.if.org Git - php/commitdiff
Implemented AST pretty-printer
authorDmitry Stogov <dmitry@zend.com>
Thu, 19 Feb 2015 08:11:17 +0000 (11:11 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 19 Feb 2015 08:11:17 +0000 (11:11 +0300)
Zend/zend_ast.c
Zend/zend_ast.h

index 242c99ff7f52857c1a2896931bfd7239da4ecec9..ee3ae69a34c3bfb33a76affc5f328414118a2c06 100644 (file)
@@ -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;
+}
index a9d58428ec923ac0b22f4c49011f97a0d25aa8a3..bc26ea2168a6a0b70c9ce0eded84c67f34b17dea 100644 (file)
@@ -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);