/**
 * Compiler implementation of the
 * $(LINK2 http://www.dlang.org, D programming language).
 *
 * Copyright:   Copyright (c) 1999-2017 by The D Language Foundation, All Rights Reserved
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/apply.d, _apply.d)
 */

module dmd.apply;

// Online documentation: https://dlang.org/phobos/dmd_apply.html

import dmd.arraytypes;
import dmd.dtemplate;
import dmd.expression;
import dmd.visitor;

/**************************************
 * An Expression tree walker that will visit each Expression e in the tree,
 * in depth-first evaluation order, and call fp(e,param) on it.
 * fp() signals whether the walking continues with its return value:
 * Returns:
 *      0       continue
 *      1       done
 * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
 * Creating an iterator for this would be much more complex.
 */
private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor
{
    alias visit = super.visit;
public:
    StoppableVisitor v;

    extern (D) this(StoppableVisitor v)
    {
        this.v = v;
    }

    bool doCond(Expression e)
    {
        if (!stop && e)
            e.accept(this);
        return stop;
    }

    bool doCond(Expressions* e)
    {
        if (!e)
            return false;
        for (size_t i = 0; i < e.dim && !stop; i++)
            doCond((*e)[i]);
        return stop;
    }

    bool applyTo(Expression e)
    {
        e.accept(v);
        stop = v.stop;
        return true;
    }

    override void visit(Expression e)
    {
        applyTo(e);
    }

    override void visit(NewExp e)
    {
        //printf("NewExp::apply(): %s\n", toChars());
        doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e);
    }

    override void visit(NewAnonClassExp e)
    {
        //printf("NewAnonClassExp::apply(): %s\n", toChars());
        doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e);
    }

    override void visit(TypeidExp e)
    {
        doCond(isExpression(e.obj)) || applyTo(e);
    }

    override void visit(UnaExp e)
    {
        doCond(e.e1) || applyTo(e);
    }

    override void visit(BinExp e)
    {
        doCond(e.e1) || doCond(e.e2) || applyTo(e);
    }

    override void visit(AssertExp e)
    {
        //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
        doCond(e.e1) || doCond(e.msg) || applyTo(e);
    }

    override void visit(CallExp e)
    {
        //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
        doCond(e.e1) || doCond(e.arguments) || applyTo(e);
    }

    override void visit(ArrayExp e)
    {
        //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
        doCond(e.e1) || doCond(e.arguments) || applyTo(e);
    }

    override void visit(SliceExp e)
    {
        doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e);
    }

    override void visit(ArrayLiteralExp e)
    {
        doCond(e.basis) || doCond(e.elements) || applyTo(e);
    }

    override void visit(AssocArrayLiteralExp e)
    {
        doCond(e.keys) || doCond(e.values) || applyTo(e);
    }

    override void visit(StructLiteralExp e)
    {
        if (e.stageflags & stageApply)
            return;
        int old = e.stageflags;
        e.stageflags |= stageApply;
        doCond(e.elements) || applyTo(e);
        e.stageflags = old;
    }

    override void visit(TupleExp e)
    {
        doCond(e.e0) || doCond(e.exps) || applyTo(e);
    }

    override void visit(CondExp e)
    {
        doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e);
    }
}

extern (C++) bool walkPostorder(Expression e, StoppableVisitor v)
{
    scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v);
    e.accept(pv);
    return v.stop;
}
