using System;
using System.IO;

public class rewrite {
    BuiltinTypes T;
    public static compilation visit(compilation ast, TextWriter w, string[] args, MessageWriter msg) {
        if (msg.Count == 0)
            new rewrite(w).compilation(ast);
        return ast;
    }

    protected TextWriter w;
    public rewrite(TextWriter w) {
        this.w = w;
    }

    static expression wrap(expression ast, int count) {
        if (count > 1)
            ast = new local_expression(ast);
        return ast;
    }

    public virtual void accessor_declaration(accessor_declaration ast) {
        if (ast.body != null)
            statement(ast.body);
    }
    public virtual void argumentList(argumentList args, int lvalue, int rvalue) {
        foreach (argument x in args)
            x.expr = expression(x.expr, lvalue, rvalue);
    }
    public virtual expression array_creation_expression1(array_creation_expression1 ast, int lvalue, int rvalue) {
        for (int i = 0; i < ast.exprs.Count; i++)
            ast.exprs[i] = expression(ast.exprs[i], lvalue, rvalue);
        if (ast.init != null)
            array_initializer(ast.init, lvalue, rvalue);
        return ast;
    }
    public virtual expression array_creation_expression2(array_creation_expression2 ast, int lvalue, int rvalue) {
        array_initializer(ast.init, lvalue, rvalue);
        return ast;
    }
    public virtual void array_initializer(array_initializer ast, int lvalue, int rvalue) {
        foreach (variable_initializer x in ast.a)
            variable_initializer(x, lvalue, rvalue);
    }
    public virtual expression as_expression(as_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
        return wrap(ast, rvalue);
    }
    public virtual expression assignment_expression(assignment_expression ast, int lvalue, int rvalue) {
        ast.e1 = expression(ast.e1, 1, 0);
        ast.e2 = expression(ast.e2, 0, ast.valueUsed ? 2 : 1);
        return wrap(ast, ast.valueUsed ? 2 : 1);
    }
    public virtual void base_initializer(base_initializer ast) {
        argumentList(ast.args, 0, 1);
    }
    public virtual expression binary_expression(binary_expression ast, int lvalue, int rvalue) {
        ast.e1 = expression(ast.e1);
        ast.e2 = expression(ast.e2);
        return wrap(ast, rvalue);
    }
    public virtual void block_statement(block_statement ast) {
        foreach (statement x in ast.stmts)
            statement(x);
    }
    public virtual void break_statement(break_statement ast) {
        ast.exitstry = exitstry(ast, ast.stmt);
    }
    public virtual expression cast_expression(cast_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
        return wrap(ast, rvalue);
    }
    public virtual void catch_clause(catch_clause ast) {
        statement(ast.block);
    }
    public virtual void catch_clauses(catch_clauses ast) {
        if (ast.general != null)
            statement(ast.general);
        foreach (catch_clause x in ast.specifics)
            catch_clause(x);
    }
    public virtual expression checked_expression(checked_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
        return ast;
    }
    public virtual void class_declaration(class_declaration ast) {
        declarationList(ast.body);
    }
    public virtual void compilation(compilation ast) {
        T = ast.global.Types;
        foreach (compilation_unit x in ast.inputs)
            compilation_unit(x);
    }
    public virtual void compilation_unit(compilation_unit ast) {
        declarationList(ast.declarations);
    }
    public virtual expression compound_assignment_expression(compound_assignment_expression ast, int lvalue, int rvalue) {
        ast.e1 = expression(ast.e1, 1, 1);
        ast.e2 = expression(ast.e2, 0, rvalue + 1);
        return ast;
    }
    public virtual expression cond_expression(cond_expression ast, int lvalue, int rvalue) {
        ast.cond = expression(ast.cond);
        ast.failure = expression(ast.failure);
        ast.success = expression(ast.success);
        return wrap(ast, rvalue);
    }
    public virtual void const_declarator(const_declarator ast) {
        ast.expr = expression(ast.expr);
    }
    public virtual void const_statement(const_statement ast) {
        foreach (declarator d in ast.consts)
            declarator(d);
    }
    public virtual void constant_declaration(constant_declaration ast) {
        foreach (declarator d in ast.decls)
            declarator(d);
    }
    public virtual void constructor_declaration(constructor_declaration ast) {
        if (ast.block != null)
            statement(ast.block);
        constructor_declarator(ast.decl);
    }
    public virtual void constructor_declarator(constructor_declarator ast) {
        if (ast.init != null)
            constructor_initializer(ast.init);
    }
    public virtual void constructor_initializer(constructor_initializer ast) {
             if (ast is base_initializer) base_initializer((base_initializer)ast);
        else if (ast is this_initializer) this_initializer((this_initializer)ast);
        else throw new ArgumentException();
    }
    public virtual void continue_statement(continue_statement ast) {
        ast.exitstry = exitstry(ast, ast.stmt);
    }
    public virtual void declaration(declaration ast) {
             if (ast is class_declaration) class_declaration((class_declaration)ast);
        else if (ast is constant_declaration) constant_declaration((constant_declaration)ast);
        else if (ast is constructor_declaration) constructor_declaration((constructor_declaration)ast);
        else if (ast is delegate_declaration) ;
        else if (ast is destructor_declaration) destructor_declaration((destructor_declaration)ast);
        else if (ast is enum_declaration) ;
        else if (ast is event_declaration1) event_declaration1((event_declaration1)ast);
        else if (ast is event_declaration2) event_declaration2((event_declaration2)ast);
        else if (ast is field_declaration) field_declaration((field_declaration)ast);
        else if (ast is indexer_declaration) indexer_declaration((indexer_declaration)ast);
        else if (ast is interface_declaration) ;
        else if (ast is interface_event_declaration) ;
        else if (ast is interface_indexer_declaration) ;
        else if (ast is interface_method_declaration) ;
        else if (ast is interface_property_declaration) ;
        else if (ast is method_declaration) method_declaration((method_declaration)ast);
        else if (ast is namespace_declaration) namespace_declaration((namespace_declaration)ast);
        else if (ast is operator_declaration) operator_declaration((operator_declaration)ast);
        else if (ast is property_declaration) property_declaration((property_declaration)ast);
        else if (ast is struct_declaration) struct_declaration((struct_declaration)ast);
        else throw new ArgumentException();
    }
    public virtual void declarationList(declarationList decls) {
        foreach (declaration d in decls)
            declaration(d);
    }
    public virtual void declarator(declarator ast) {
             if (ast is const_declarator) const_declarator((const_declarator)ast);
        else if (ast is event_declarator) event_declarator((event_declarator)ast);
        else if (ast is field_declarator) field_declarator((field_declarator)ast);
        else if (ast is fixed_declarator) fixed_declarator((fixed_declarator)ast);
        else if (ast is var_declarator) var_declarator((var_declarator)ast);
        else throw new ArgumentException();
    }
    public virtual void destructor_declaration(destructor_declaration ast) {
        if (ast.block != null)
            statement(ast.block);
    }
    public virtual void do_statement(do_statement ast) {
        statement(ast.body);
        ast.expr = expression(ast.expr);
    }
    public virtual expression element_access(element_access ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, lvalue + rvalue);
        argumentList(ast.exprs, lvalue, lvalue + rvalue);
        return ast;
    }
    public virtual void event_accessor(event_accessor ast) {
        statement(ast.block);
    }
    public virtual void event_declaration1(event_declaration1 ast) {
        foreach (declarator d in ast.decls)
            declarator(d);
    }
    public virtual void event_declaration2(event_declaration2 ast) {
        foreach (event_accessor e in ast.accessors)
            event_accessor(e);
    }
    public virtual void event_declarator(event_declarator ast) {
        if (ast.init != null)
            variable_initializer(ast.init, 0, 1);
    }
    public virtual bool exitstry(statement ast, AST lca) {
        for (AST s = ast; s != null && s != lca; s = s.parent)
            if (is_try(s))
                return true;
        return false;
    }
    public virtual expression expr_access(expr_access ast, int lvalue, int rvalue) {
        if (ast.sym is Type)
            return ast;
        ast.expr = expression(ast.expr, lvalue, rvalue);
        return wrap(ast, rvalue);
    }
    public virtual void expr_initializer(expr_initializer ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
    }
    public virtual expression expression(expression ast) {
        return expression(ast, 0, 1);
    }
    public virtual expression expression(expression ast, int lvalue, int rvalue) {
             if (ast is array_creation_expression1) return array_creation_expression1((array_creation_expression1)ast, lvalue, rvalue);
        else if (ast is array_creation_expression2) return array_creation_expression2((array_creation_expression2)ast, lvalue, rvalue);
        else if (ast is as_expression) return as_expression((as_expression)ast, lvalue, rvalue);
        else if (ast is assignment_expression) return assignment_expression((assignment_expression)ast, lvalue, rvalue);
        else if (ast is base_access) return ast;
        else if (ast is binary_expression) return binary_expression((binary_expression)ast, lvalue, rvalue);
        else if (ast is literal) return ast;
        else if (ast is cast_expression) return cast_expression((cast_expression)ast, lvalue, rvalue);
        else if (ast is checked_expression) return checked_expression((checked_expression)ast, lvalue, rvalue);
        else if (ast is compound_assignment_expression) return compound_assignment_expression((compound_assignment_expression)ast, lvalue, rvalue);
        else if (ast is cond_expression) return cond_expression((cond_expression)ast, lvalue, rvalue);
        else if (ast is element_access) return element_access((element_access)ast, lvalue, rvalue);
        else if (ast is member_access) return member_access((member_access)ast, lvalue, rvalue);
        else if (ast is implicit_cast_expression) return implicit_cast_expression((implicit_cast_expression)ast, lvalue, rvalue);
        else if (ast is invocation_expression) return invocation_expression((invocation_expression)ast, lvalue, rvalue);
        else if (ast is is_expression) return is_expression((is_expression)ast, lvalue, rvalue);
        else if (ast is new_expression) return new_expression((new_expression)ast, lvalue, rvalue);
        else if (ast is post_expression) return post_expression((post_expression)ast, lvalue, rvalue);
        else if (ast is pre_expression) return pre_expression((pre_expression)ast, lvalue, rvalue);
        else if (ast is simple_name) return simple_name((simple_name)ast, lvalue, rvalue);
        else if (ast is sizeof_expression) return sizeof_expression((sizeof_expression)ast, lvalue, rvalue);
        else if (ast is this_access) return ast;
        else if (ast is typeof_expression) return ast;
        else if (ast is unary_expression) return unary_expression((unary_expression)ast, lvalue, rvalue);
        else if (ast is unchecked_expression) return unchecked_expression((unchecked_expression)ast, lvalue, rvalue);
        else throw new ArgumentException();
    }
    public virtual void expression_statement(expression_statement ast) {
        ast.expr = expression(ast.expr, 0, 0);
    }
    public virtual void field_declaration(field_declaration ast) {
        foreach (declarator d in ast.decls)
            declarator(d);
    }
    public virtual void field_declarator(field_declarator ast) {
        if (ast.init != null)
            variable_initializer(ast.init, 0, 1);
    }
    public virtual void finally_clause(finally_clause ast) {
        statement(ast.block);
    }
    public virtual void fixed_declarator(fixed_declarator ast) {
        ast.expr = expression(ast.expr);
    }
    public virtual void fixed_statement(fixed_statement ast) {
        statement(ast.body);
        foreach (declarator d in ast.declarators)
            declarator(d);
    }
    public virtual void for_decl(for_decl ast) {
        local_statement(ast.decl);
    }
    public virtual void for_init(for_init ast) {
             if (ast is for_decl) for_decl((for_decl)ast);
        else if (ast is for_list) for_list((for_list)ast);
        else throw new ArgumentException();
    }
    public virtual void for_list(for_list ast) {
        for (int i = 0; i < ast.exprs.Count; i++)
            ast.exprs[i] = expression(ast.exprs[i], 0, 0);
    }
    public virtual void for_statement(for_statement ast) {
        statement(ast.body);
        if (ast.cond != null)
            ast.cond = expression(ast.cond);
        if (ast.init != null)
            for_init(ast.init);
        for (int i = 0; i < ast.iterators.Count; i++)
            ast.iterators[i] = expression(ast.iterators[i], 0, 0);
    }
    public virtual void foreach_statement(foreach_statement ast) {
        statement(ast.body);
        ast.expr = expression(ast.expr);
    }
    public virtual void goto_case_statement(goto_case_statement ast) {
        ast.exitstry = exitstry(ast, ast.stmt);
    }
    public virtual void goto_default_statement(goto_default_statement ast) {
        ast.exitstry = exitstry(ast, ast.stmt);
    }
    public virtual void goto_statement(goto_statement ast) {
        ast.exitstry = exitstry(ast, AST.leastCommonAncestor(ast.target, ast));
    }
    public virtual void if_statement(if_statement ast) {
        if (ast.elsepart != null)
            statement(ast.elsepart);
        ast.expr = expression(ast.expr);
        statement(ast.thenpart);
    }
    public virtual expression implicit_cast_expression(implicit_cast_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
        return wrap(ast, rvalue);
    }
    public virtual void indexer_declaration(indexer_declaration ast) {
        foreach (accessor_declaration d in ast.accessors)
            accessor_declaration(d);
    }
    public virtual expression invocation_expression(invocation_expression ast, int lvalue, int rvalue) {
        argumentList(ast.args, 0, 1);
        ast.expr = expression(ast.expr);
        return wrap(ast, rvalue);
    }
    public virtual expression is_expression(is_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr);
        return wrap(ast, rvalue);
    }
    public virtual bool is_try(AST s) {
        if (s is try_statement   || s is catch_clauses
        ||  s is using_statement || s is lock_statement)
            return true;
        if (s is foreach_statement) {
            foreach_statement ast = (foreach_statement)s;
            if (ast.expr.typ is ArrayType)
                return false;
            if (ast.GetEnumerator.Type.Implements(T.IDisposable)
            || !ast.GetEnumerator.Type.Is("sealed"))
                return true;
        }
        return false;
    }
    public virtual void local_statement(local_statement ast) {
        foreach (declarator d in ast.vars)
            declarator(d);
    }
    public virtual void lock_statement(lock_statement ast) {
        statement(ast.body);
        ast.expr = expression(ast.expr);
    }
    public virtual expression member_access(member_access ast, int lvalue, int rvalue) {
             if (ast is expr_access) return expr_access((expr_access)ast, lvalue, rvalue);
        else if (ast is pointer_access) return pointer_access((pointer_access)ast, lvalue, rvalue);
        else if (ast is predefined_access) return predefined_access((predefined_access)ast, lvalue, rvalue);
        else throw new ArgumentException();
    }
    public virtual void method_declaration(method_declaration ast) {
        if (ast.body != null)
            statement(ast.body);
    }
    public virtual void namespace_declaration(namespace_declaration ast) {
        declarationList(ast.nb.declarations);
    }
    public virtual expression new_expression(new_expression ast, int lvalue, int rvalue) {
        argumentList(ast.args, lvalue, rvalue);
        return wrap(ast, rvalue);
    }
    public virtual void operator_declaration(operator_declaration ast) {
        if (ast.block != null)
            statement(ast.block);
    }
    public virtual expression pointer_access(pointer_access ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
        return ast;
    }
    public virtual expression post_expression(post_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, 1, 1);
        return wrap(ast, rvalue);
    }
    public virtual expression pre_expression(pre_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, 1, 1);
        return wrap(ast, rvalue);
    }
    public virtual expression predefined_access(predefined_access ast, int lvalue, int rvalue) {
        return wrap(ast, rvalue);
    }
    public virtual void property_declaration(property_declaration ast) {
        foreach (accessor_declaration d in ast.decls)
            accessor_declaration(d);
    }
    public virtual void resource(resource ast) {
             if (ast is resource_decl) resource_decl((resource_decl)ast);
        else if (ast is resource_expr) resource_expr((resource_expr)ast);
        else throw new ArgumentException();
    }
    public virtual void resource_decl(resource_decl ast) {
        local_statement(ast.local);
    }
    public virtual void resource_expr(resource_expr ast) {
        ast.expr = expression(ast.expr);
    }
    public virtual void return_statement(return_statement ast) {
        if (ast.expr != null)
            ast.expr = expression(ast.expr);
        ast.exitstry = exitstry(ast, null);
    }
    public virtual expression simple_name(simple_name ast, int lvalue, int rvalue) {
        if (ast.sym is EnumMember || ast.sym is Type)
            return ast;
        return wrap(ast, rvalue);
    }
    public virtual expression sizeof_expression(sizeof_expression ast, int lvalue, int rvalue) {
        return ast;
    }
    public virtual void stackalloc_initializer(stackalloc_initializer ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
    }
    public virtual void statement(statement ast) {
             if (ast is block_statement) block_statement((block_statement)ast);
        else if (ast is break_statement) break_statement((break_statement)ast);
        else if (ast is checked_statement) statement(((checked_statement)ast).stmt);
        else if (ast is const_statement) const_statement((const_statement)ast);
        else if (ast is continue_statement) break_statement((break_statement)ast);
        else if (ast is do_statement) do_statement((do_statement)ast);
        else if (ast is empty_statement) ;
        else if (ast is expression_statement) expression_statement((expression_statement)ast);
        else if (ast is fixed_statement) fixed_statement((fixed_statement)ast);
        else if (ast is for_statement) for_statement((for_statement)ast);
        else if (ast is foreach_statement) foreach_statement((foreach_statement)ast);
        else if (ast is goto_case_statement) goto_case_statement((goto_case_statement)ast);
        else if (ast is goto_default_statement)  goto_default_statement((goto_default_statement)ast);
        else if (ast is goto_statement) goto_statement((goto_statement)ast);
        else if (ast is if_statement) if_statement((if_statement)ast);
        else if (ast is labeled_statement) statement(((labeled_statement)ast).stmt);
        else if (ast is local_statement) local_statement((local_statement)ast);
        else if (ast is lock_statement) lock_statement((lock_statement)ast);
        else if (ast is return_statement) return_statement((return_statement)ast);
        else if (ast is switch_statement) switch_statement((switch_statement)ast);
        else if (ast is throw_statement) throw_statement((throw_statement)ast);
        else if (ast is try_statement) try_statement((try_statement)ast);
        else if (ast is unchecked_statement) statement(((unchecked_statement)ast).stmt);
        else if (ast is unsafe_statement) statement(((unsafe_statement)ast).block);
        else if (ast is using_statement) using_statement((using_statement)ast);
        else if (ast is while_statement) while_statement((while_statement)ast);
        else throw new ArgumentException();
    }
    public virtual void struct_declaration(struct_declaration ast) {
        declarationList(ast.body);
    }
    public virtual void switch_expression(switch_expression ast) {
        ast.expr = expression(ast.expr);
    }
    public virtual void switch_section(switch_section ast) {
        foreach (statement x in ast.stmts)
            statement(x);
    }
    public virtual void switch_statement(switch_statement ast) {
        ast.expr = expression(ast.expr);
        foreach (switch_section s in ast.sections)
            switch_section(s);
    }
    public virtual void this_initializer(this_initializer ast) {
        argumentList(ast.args, 0, 1);
    }
    public virtual void throw_statement(throw_statement ast) {
        if (ast.expr != null)
            ast.expr = expression(ast.expr);
    }
    public virtual void try_statement(try_statement ast) {
        statement(ast.block);
        if (ast.catches != null)
            catch_clauses(ast.catches);
        if (ast.finally_block != null)
            finally_clause(ast.finally_block);
    }
    public virtual expression unary_expression(unary_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr);
        return wrap(ast, rvalue);
    }
    public virtual expression unchecked_expression(unchecked_expression ast, int lvalue, int rvalue) {
        ast.expr = expression(ast.expr, lvalue, rvalue);
        return ast;
    }
    public virtual void using_statement(using_statement ast) {
        statement(ast.body);
        resource(ast.r);
    }
    public virtual void var_declarator(var_declarator ast) {
        if (ast.init != null)
            variable_initializer(ast.init, 0, 1);
    }
    public virtual void variable_initializer(variable_initializer ast, int lvalue, int rvalue) {
             if (ast is array_initializer) array_initializer((array_initializer)ast, lvalue, rvalue);
        else if (ast is expr_initializer) expr_initializer((expr_initializer)ast, lvalue, rvalue);
        else if (ast is stackalloc_initializer) stackalloc_initializer((stackalloc_initializer)ast, lvalue, rvalue);
        else throw new ArgumentException();
    }
    public virtual void while_statement(while_statement ast) {
        statement(ast.body);
        ast.expr = expression(ast.expr);
    }
}
