// 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.Symbol;
import bdc.scheme.exception.SyntaxException;
import bdc.scheme.expression.Begin;
import bdc.scheme.expression.Procedure1;

/**
    Cond2If converts reduces derived Cond syntax into another form.
*/
public class Cond2If extends Procedure1
{
    public Object apply1 (Stack stack) throws SchemeException
    {
        return recur(stack.array[stack.inUse-1]);
    }

    private Object recur (Object o1) throws SchemeException
    {
        Pair source = Scheme.pair(o1, this);
        Pair pair = source;
        Object clauses = source.cdr;

            // (cond)
        if (clauses == Scheme.Null) {
            return Scheme.Unspecified;
        }

        Pair   clause  = Scheme.pair(((Pair)clauses).car, this);
        Object rest    =             ((Pair)clauses).cdr;

            // (cond (else ...))
        if (clause.car instanceof Symbol) {
            Symbol symbol = (Symbol)clause.car;
            if (symbol == Compiler.Else) {
                    // make sure this is the last clause
                if (rest == Scheme.Null) {
                    return new Pair(Compiler.Begin, clause.cdr);
                }
                else {
                    throw
                        new SyntaxException(
                            "else clause ins't last in cond",
                            source);
                }
            }
        }

            // (cond (foo) ...)
        if (clause.cdr == Scheme.Null) {
            return new Pair(Compiler.Or,
                            new Pair(clause.car,
                                     new Pair(recur(new Pair(pair.car,
                                                             rest)),
                                              Scheme.Null)));
        }

            // (cond (foo bar) ...)
        return new Pair(Compiler.If,
                        new Pair(clause.car,
                                 new Pair(new Pair(Compiler.Begin,
                                                   clause.cdr),
                                          new Pair(recur(new Pair(pair.car,
                                                                  rest)),
                                                   Scheme.Null))));
    }
}

