void parser_dump(mrb_state *mrb, mrb_ast_node *tree, int offset)
{
#ifdef ENABLE_STDIO
    if (!tree) return;
again:
    dump_prefix(offset);
    node_type n = tree->getType();
    mrb_ast_node *orig = tree;
    if(dynamic_cast<UpdatedNode *>(tree)==0)
        tree = tree->right();

    switch (n) {
        case NODE_BEGIN: parser_dump(mrb,(BeginNode *)orig,offset); break;

        case NODE_RESCUE: parser_dump(mrb,(RescueNode *)orig,offset); break;

        case NODE_ENSURE: parser_dump(mrb,(EnsureNode *)orig,offset); break;

        case NODE_LAMBDA:
            printf("NODE_LAMBDA:\n");
            goto block;

        case NODE_BLOCK:
            printf("NODE_BLOCK:\n");
block:
            parser_dump(mrb,(LambdaCommonNode *)orig,offset);
            break;

        case NODE_IF:
        {
            IfNode *in = (IfNode *)orig;
            printf("NODE_IF:\n");
            dump_prefix(offset+1);
            printf("cond:\n");
            parser_dump(mrb, in->cond(), offset+2); //tree->left()
            dump_prefix(offset+1);
            printf("then:\n");
            parser_dump(mrb, in->true_body(), offset+2); //tree->right()->left()
            if (in->false_body()) {
                dump_prefix(offset+1);
                printf("else:\n");
                parser_dump(mrb, in->false_body() , offset+2); //tree->right()->right()->left()
            }
        }
            break;

        case NODE_AND:
        {
            AndNode *an = (AndNode *)orig;
            printf("NODE_AND:\n");
            parser_dump(mrb, an->lhs(), offset+1);
            parser_dump(mrb, an->rhs(), offset+1);
        }
            break;

        case NODE_OR:
        {
            OrNode *an = (OrNode *)orig;
            printf("NODE_OR:\n");
            parser_dump(mrb, an->lhs(), offset+1);
            parser_dump(mrb, an->rhs(), offset+1);
        }
            break;

        case NODE_CASE:  {
            CaseNode *cn = (CaseNode *)orig;
            printf("NODE_CASE:\n");
            if (cn->switched_on()) {
                parser_dump(mrb, cn->switched_on(), offset+1);
            }
            tree = cn->cases();
            while (tree) {
                dump_prefix(offset+1);
                printf("case:\n");
                dump_recur(mrb, tree->left()->left(), offset+2);
                dump_prefix(offset+1);
                printf("body:\n");
                parser_dump(mrb, tree->left()->right(), offset+2);
                tree = tree->right();
            }
        }
            break;

        case NODE_WHILE:
        {
            WhileNode *n = (WhileNode *)orig;
            printf("NODE_WHILE:\n");
            dump_prefix(offset+1);
            printf("cond:\n");
            parser_dump(mrb, n->lhs(), offset+2);
            dump_prefix(offset+1);
            printf("body:\n");
            parser_dump(mrb, n->rhs(), offset+2);
        }
            break;

        case NODE_UNTIL:
        {
            UntilNode *n = (UntilNode *)orig;
            printf("NODE_UNTIL:\n");
            dump_prefix(offset+1);
            printf("cond:\n");
            parser_dump(mrb, n->lhs(), offset+2);
            dump_prefix(offset+1);
            printf("body:\n");
            parser_dump(mrb, n->rhs(), offset+2);
        }
            break;

        case NODE_FOR:
            printf("NODE_FOR:\n");
            dump_prefix(offset+1);
            parser_dump(mrb,(ForNode *)orig,offset);
            break;

        case NODE_SCOPE:
            printf("NODE_SCOPE:\n");
        {
            ScopeNode *ns = (ScopeNode *)orig;
            const tLocals &n2(ns->locals());

            if ( !n2.empty() ) {
                dump_prefix(offset+1);
                printf("local variables:\n");
                dump_prefix(offset+2);
                for(auto iter=n2.begin(); iter!=n2.end(); ++iter) {
                    if ( (iter+1) != n2.end() )
                        printf(", ");
                    printf("%s", mrb_sym2name(mrb, *iter));
                }
                printf("\n");
            }
            tree = ns->body();
        }
            offset++;
            goto again;

        case NODE_FCALL:
        case NODE_CALL:
        {
            CallCommonNode *cn = (CallCommonNode *)orig;
            printf("NODE_CALL:\n");
            parser_dump(mrb, cn->m_receiver, offset+1);
            dump_prefix(offset+1);
            printf("method='%s' (%d)\n",
                   mrb_sym2name(mrb, cn->m_method),
                   cn->m_method);
            CommandArgs *ca = cn->m_cmd_args;
            if (ca) {
                dump_prefix(offset+1);
                printf("args:\n");
                dump_recur(mrb, ca->m_args, offset+2);
                if (ca->m_blk) {
                    dump_prefix(offset+1);
                    printf("block:\n");
                    parser_dump(mrb, ca->m_blk, offset+2);
                }
            }
        }
            break;

        case NODE_DOT2:
        {
            printf("NODE_DOT2:\n");
            Dot2Node *nd = (Dot2Node *)orig;
            parser_dump(mrb, nd->lhs(), offset+1);
            parser_dump(mrb, nd->rhs(), offset+1);
        }
            break;

        case NODE_DOT3:
        {
            printf("NODE_DOT3:\n");
            Dot3Node *nd = (Dot3Node *)orig;
            parser_dump(mrb, nd->lhs(), offset+1);
            parser_dump(mrb, nd->rhs(), offset+1);
        }
            break;

        case NODE_COLON2:
        {
            Colon2Node *cn = (Colon2Node *)orig;
            printf("NODE_COLON2:\n");
            parser_dump(mrb, cn->m_val, offset+1);
            dump_prefix(offset+1);
            printf("::%s\n", mrb_sym2name(mrb, cn->m_sym));
        }
            break;

        case NODE_COLON3:{
            printf("NODE_COLON3:\n");
            dump_prefix(offset+1);
            Colon3Node *n = (Colon3Node *)orig;
            printf("::%s\n", mrb_sym2name(mrb, n->sym()));
        }
            break;

        case NODE_ARRAY:
            printf("NODE_ARRAY:\n");
            dump_recur(mrb, ((ArrayNode *)orig)->child(), offset+1);
            break;

        case NODE_HASH:
        {
            HashNode *nd = (HashNode *)orig;
            printf("NODE_HASH:\n");
            tree = nd->child();
            while (tree) {
                dump_prefix(offset+1);
                printf("key:\n");
                parser_dump(mrb, tree->left()->left(), offset+2);
                dump_prefix(offset+1);
                printf("value:\n");
                parser_dump(mrb, tree->left()->right(), offset+2);
                tree = tree->right();
            }
        }
            break;

        case NODE_SPLAT:
            printf("NODE_SPLAT:\n");
            parser_dump(mrb, ((SplatNode *)orig)->child(), offset+1);
            break;

        case NODE_ASGN:
        {
            AsgnNode *an = (AsgnNode *)orig;
            printf("NODE_ASGN:\n");
            dump_prefix(offset+1);
            printf("lhs:\n");
            parser_dump(mrb, an->lhs(), offset+2);
            dump_prefix(offset+1);
            printf("rhs:\n");
            parser_dump(mrb, an->rhs(), offset+2);
        }
            break;

        case NODE_MASGN:
        {
            printf("NODE_MASGN:\n");
            dump_prefix(offset+1);
            printf("mlhs:\n");
            MAsgnNode *mn = (MAsgnNode *)orig;
            mrb_ast_node *n2 = mn->lhs();

            if (n2->left()) {
                dump_prefix(offset+2);
                printf("pre:\n");
                dump_recur(mrb, n2->left(), offset+3);
            }
            n2 = n2->right();
            if (n2) {
                if (n2->left()) {
                    dump_prefix(offset+2);
                    printf("rest:\n");
                    if (n2->left() == (mrb_ast_node*)-1) {
                        dump_prefix(offset+2);
                        printf("(empty)\n");
                    }
                    else {
                        parser_dump(mrb, n2->left(), offset+3);
                    }
                }
                n2 = n2->right();
                if (n2) {
                    if (n2->left()) {
                        dump_prefix(offset+2);
                        printf("post:\n");
                        dump_recur(mrb, n2->left(), offset+3);
                    }
                }
            }
            dump_prefix(offset+1);
            printf("rhs:\n");
            parser_dump(mrb, mn->rhs(), offset+2);
        }
            break;

        case NODE_OP_ASGN: {
            OpAsgnNode *opasgn = static_cast<OpAsgnNode *>(tree);
            printf("NODE_OP_ASGN:\n");
            dump_prefix(offset+1);
            printf("lhs:\n");
            parser_dump(mrb, opasgn->lhs(), offset+2);
            dump_prefix(offset+1);
            printf("op='%s' (%d)\n", mrb_sym2name(mrb, opasgn->op_sym), opasgn->op_sym);
            parser_dump(mrb, opasgn->rhs(), offset+1);
            break;
        }
        case NODE_SUPER:
        {
            printf("NODE_SUPER:\n");
            SuperNode *x=(SuperNode *)orig;
            if (x->hasParams()) {
                dump_prefix(offset+1);
                printf("args:\n");
                dump_recur(mrb, x->args(), offset+2);
                if (x->block()) {
                    dump_prefix(offset+1);
                    printf("block:\n");
                    parser_dump(mrb, x->block(), offset+2);
                }
            }
        }
            break;

        case NODE_ZSUPER:
            printf("NODE_ZSUPER\n");
            break;

        case NODE_RETURN:
            printf("NODE_RETURN:\n");
            parser_dump(mrb, ((ReturnNode *)orig)->child(), offset+1);
            break;

        case NODE_YIELD:
            printf("NODE_YIELD:\n");
            dump_recur(mrb, ((YieldNode *)orig)->child(), offset+1);
            break;

        case NODE_BREAK:
            printf("NODE_BREAK:\n");
            parser_dump(mrb, ((BreakNode *)orig)->child(), offset+1);
            //parser_dump(mrb, tree, offset+1);
            break;

        case NODE_NEXT:
            printf("NODE_NEXT:\n");
            parser_dump(mrb, ((NextNode *)orig)->child(), offset+1);
            break;

        case NODE_REDO:
            printf("NODE_REDO\n");
            break;

        case NODE_RETRY:
            printf("NODE_RETRY\n");
            break;

        case NODE_LVAR:
        {
            LVarNode * lvar = (LVarNode *)orig;
            printf("NODE_LVAR %s\n", mrb_sym2name(mrb, lvar->sym()));
        }
            break;

        case NODE_GVAR:
        {
            GVarNode * gvar = (GVarNode *)orig;
            printf("NODE_GVAR %s\n", mrb_sym2name(mrb, gvar->sym()));
        }
            break;

        case NODE_IVAR:
        {
            IVarNode * ivar = (IVarNode *)orig;
            printf("NODE_IVAR %s\n", mrb_sym2name(mrb, ivar->sym()));
        }
            break;

        case NODE_CVAR:
        {
            CVarNode * cvar = (CVarNode *)orig;
            printf("NODE_CVAR %s\n", mrb_sym2name(mrb, cvar->sym()));
        }
            break;

        case NODE_CONST:
        {
            ConstNode * cvar = (ConstNode *)orig;
            printf("NODE_CONST %s\n", mrb_sym2name(mrb, cvar->sym()));
        }

            break;

        case NODE_MATCH:
            printf("NODE_MATCH:\n");
            dump_prefix(offset + 1);
            printf("lhs:\n");
            parser_dump(mrb, tree->left(), offset + 2);
            dump_prefix(offset + 1);
            printf("rhs:\n");
            parser_dump(mrb, tree->right(), offset + 2);
            break;

        case NODE_BACK_REF:
            printf("NODE_BACK_REF: $%c\n", ((BackRefNode *)orig)->m_ref);
            break;

        case NODE_NTH_REF:
            printf("NODE_NTH_REF: $%d\n", ((NthRefNode *)orig)->m_ref);
            break;

        case NODE_ARG:
        {
            ArgNode * var = (ArgNode *)orig;
            printf("NODE_ARG %s\n", mrb_sym2name(mrb, var->sym()));
        }
            break;

        case NODE_BLOCK_ARG:
        {

            printf("NODE_BLOCK_ARG:\n");
            parser_dump(mrb, ((BlockArgNode *)orig)->child(), offset+1);
        }
            break;

        case NODE_INT:
        {
            IntLiteralNode *int_lit = (IntLiteralNode *)orig;
            printf("NODE_INT %s base %d\n", int_lit->m_val, int_lit->m_base);
        }
            break;

        case NODE_FLOAT:
            printf("NODE_FLOAT %s\n", ((FloatLiteralNode *)orig)->m_val);
            break;

        case NODE_NEGATE:
            printf("NODE_NEGATE\n");
            parser_dump(mrb, ((NegateNode *)tree)->child(), offset+1);
            break;

        case NODE_STR:
        {
            StrNode *sn=(StrNode *)orig;
            printf("NODE_STR \"%s\" len %zu\n", sn->m_str, sn->m_length);
        }
            break;

        case NODE_DSTR:
        {
            DstrNode *dn = (DstrNode *)orig;
            printf("NODE_DSTR\n");
            dump_recur(mrb, dn->child(), offset+1);
        }
            break;

        case NODE_XSTR:
            printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->left(), (int)(intptr_t)tree->right());
            break;

        case NODE_DXSTR: {
            DxstrNode *dn = (DxstrNode *)orig;
            printf("NODE_DXSTR\n");
            dump_recur(mrb, dn->child(), offset+1);
        }
            break;

        case NODE_REGX:
            printf("NODE_REGX /%s/%s\n", (char*)tree->left(), (char*)tree->right());
            break;

        case NODE_DREGX:
            printf("NODE_DREGX\n");
            dump_recur(mrb, tree->left(), offset+1);
            dump_prefix(offset);
            printf("tail: %s\n", (char*)tree->right()->right()->left());
            dump_prefix(offset);
            printf("opt: %s\n", (char*)tree->right()->right()->right());
            break;

        case NODE_SYM:
        {
            SymNode * cvar = (SymNode *)orig;
            printf("NODE_SYM :%s\n", mrb_sym2name(mrb, cvar->sym()));
        }
            break;

        case NODE_SELF:
            printf("NODE_SELF\n");
            break;

        case NODE_NIL:
            printf("NODE_NIL\n");
            break;

        case NODE_TRUE:
            printf("NODE_TRUE\n");
            break;

        case NODE_FALSE:
            printf("NODE_FALSE\n");
            break;

        case NODE_ALIAS:
        {
            AliasNode *an = (AliasNode *)orig;
            printf("NODE_ALIAS %s %s:\n",
                   mrb_sym2name(mrb, an->m_from),
                   mrb_sym2name(mrb, an->m_to));
        }
            break;

        case NODE_UNDEF:
            printf("NODE_UNDEF");
        {
            UndefNode *und((UndefNode *)tree);
            for(mrb_sym v : und->m_syms) {
                printf(" %s", mrb_sym2name(mrb, v));
            }
        }
            printf(":\n");
            break;

        case NODE_CLASS: {
            ClassNode *cn = static_cast<ClassNode *>(tree);
            printf("NODE_CLASS:\n");
            if (cn->receiver()->left() == (mrb_ast_node*)0) {
                dump_prefix(offset+1);
                printf(":%s\n", mrb_sym2name(mrb, sym(cn->receiver()->right())));
            }
            else if (cn->receiver()->left() == (mrb_ast_node*)1) {
                dump_prefix(offset+1);
                printf("::%s\n", mrb_sym2name(mrb, sym(cn->receiver()->right())));
            }
            else {
                parser_dump(mrb, cn->receiver()->left(), offset+1);
                dump_prefix(offset+1);
                printf("::%s\n", mrb_sym2name(mrb, sym(cn->receiver()->right())));
            }
            if (cn->super()) {
                dump_prefix(offset+1);
                printf("super:\n");
                parser_dump(mrb, cn->super(), offset+2);
            }
            dump_prefix(offset+1);
            printf("body:\n");
            parser_dump(mrb, cn->scope()->body(), offset+2);
            break;
        }

        case NODE_MODULE: {
            ModuleNode *cn = static_cast<ModuleNode *>(tree);
            printf("NODE_MODULE:\n");

            if (cn->receiver()->left() == (mrb_ast_node*)0) {
                dump_prefix(offset+1);
                printf(":%s\n", mrb_sym2name(mrb, sym(cn->receiver()->right())));
            }
            else if (cn->receiver()->left() == (mrb_ast_node*)1) {
                dump_prefix(offset+1);
                printf("::%s\n", mrb_sym2name(mrb, sym(cn->receiver()->right())));
            }
            else {
                parser_dump(mrb, cn->receiver(), offset+1);
                dump_prefix(offset+1);
                printf("::%s\n", mrb_sym2name(mrb, sym(cn->receiver()->right())));
            }
            dump_prefix(offset+1);
            printf("body:\n");
            parser_dump(mrb, cn->scope()->body(), offset+2);
            break;
        }
        case NODE_SCLASS: {
            printf("NODE_SCLASS:\n");
            SclassNode *scn((SclassNode *)tree);

            parser_dump(mrb, scn->scope(), offset+1);
//            dump_prefix(offset+1);
//            printf("body:\n");
//            parser_dump(mrb, scntree->right()->left()->right(), offset+2);
        }
            break;

        case NODE_DEF:
        {
            DefNode *dn = (DefNode *)orig;
            printf("NODE_DEF:\n");
            dump_prefix(offset+1);
            parser_dump(mrb,dn,offset);
        }
            break;

        case NODE_SDEF:
        {
            SdefNode *sn = (SdefNode *)orig;
            printf("NODE_SDEF:\n");
            parser_dump(mrb, sn->receiver(), offset+1);
            dump_prefix(offset+1);
            printf(":"); // prepend name with ':'
            parser_dump(mrb,sn,offset);
        }
            break;

        case NODE_POSTEXE:
            printf("NODE_POSTEXE:\n");
            parser_dump(mrb, ((PostExeNode *)orig)->child(), offset+1);
            break;

        case NODE_HEREDOC:{
            printf("NODE_HEREDOC:\n");
            HeredocNode *hdn((HeredocNode *)tree);

            parser_dump(mrb, hdn->contents()->doc->left(), offset+1);
        }
            break;

        default:
            printf("node type: %d (0x%x)\n", (int)n, (int)n);
            break;
    }
#endif
}