// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.scheme.compiler;

import bdc.scheme.Pair;
import bdc.scheme.Scheme;
import bdc.scheme.SchemeException;
import bdc.scheme.Stack;
import bdc.scheme.expression.Procedure1;

/**
    And2If converts reduces derived And syntax into another form.
*/
public class And2If extends Procedure1
{
    public Object apply1 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Object e = Scheme.pair(o1, this).cdr;

        /*
            (and)
            =>
            #t
        */

        if (e == Scheme.Null) {
            return Boolean.TRUE;
        }

        /*
            (and foo)
            =>
            foo
        */
        Pair clauses = Scheme.pair(e, this);
        if (clauses.cdr == Scheme.Null) {
            return clauses.car;
        }

        /*
            (and foo ...)
            =>
            (let ((-x foo)
                  (-thunk (lambda () ...)))
              (if -x
                (-thunk)
                -x))
        */
        Pair if_ =
            new Pair(Compiler.If,
                     new Pair(Compiler.X,
                              new Pair(new Pair(Compiler.Thunk,
                                                Scheme.Null),
                                       new Pair(Compiler.X,
                                                Scheme.Null))));
        Pair lambda =
            new Pair(Compiler.Lambda,
                     new Pair(Scheme.Null,
                              new Pair(new Pair(Compiler.And,
                                                clauses.cdr),
                                       Scheme.Null)));
        Pair letClauses =
            new Pair(new Pair(Compiler.X,
                              new Pair(clauses.car,
                                       Scheme.Null)),
                     new Pair(new Pair(Compiler.Thunk,
                                       new Pair(lambda,
                                                Scheme.Null)),
                              Scheme.Null));


        Pair let =
            new Pair(Compiler.Let,
                     new Pair(letClauses,
                              new Pair(if_,
                                       Scheme.Null)));
        return let;
    }
}
