/*
 * Copyright 2009 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.lisp.nano;

import java.util.ArrayList;
import java.util.List;

/**
 * 
 *
 *
 * @author MORIGUCHI, Yuichiro 2009
 */
/*package*/ final class SyntaxUtils {

	//
	private SyntaxUtils() {
		// do nothing
	}

	//
	/*package*/ static boolean isValidSymbolList(Datum d) {
		Datum p = d;

		if(d.isTypeSymbol()) {
			return true;
		}

		while(p != Nil.NIL) {
			if(p instanceof Cons) {
				Cons c = (Cons)p;

				if(!c.getCar().isTypeSymbol()) {
					return false;
				}
				p = c.getCdr();
			} else {
				return p.isTypeSymbol();
			}
		}
		return true;
	}

	//
	/*package*/ static void compileBind(
			Datum sym,
			Datum bcdr,
			Environment env,
			LispCompiler comp,
			CompiledCode.Builder build,
			Cons callsym,
			boolean toplevel,
			LispMessage mesg,
			String ercd,
			List<Cons> symlist,
			CodeExecutor exec,
			IntStack memento) {
		if(bcdr instanceof Cons) {
			Datum d1 = ((Cons)bcdr).getCar();

			// create a temporary Closure
			CompiledCode.Builder nbuild = new CompiledCode.Builder();
			Environment  nenv = new Environment(env);

			comp.compile(d1, nenv, nbuild, callsym, true,
					new ArrayList<Cons>(), exec, memento);
			nbuild.addReturnOp();

			// create a temporary Closure
			ClosureClass cl = new ClosureClass(
					Nil.NIL, nbuild.getCodeRef());

			// call the temporary Closure
			build.addPush(cl);
			build.addBeginList();
			build.addEndList();
			build.addCall();

			if(toplevel) {
				Symbol r;

				if(sym instanceof SymbolName) {
					r = ((SymbolName)sym).getSymbol();
					r = r.getEnclosedSymbol();
				} else {
					throw mesg.getError("err.require.symbol");
				}
				build.addBind(r);
				build.addPush(sym);
			} else {
				build.addBind(sym);
				build.addPush(Undef.UNDEF);
			}
		} else {
			throw mesg.getError(ercd);
		}
	}

	//
	/*package*/ static void compileList(
			Datum b,
			Environment env,
			LispCompiler comp,
			CompiledCode.Builder nbuild,
			Cons callsym,
			boolean istail,
			LispMessage mesg,
			List<Cons> symlist,
			CodeExecutor exec,
			IntStack memento,
			boolean toplv) {
		Datum bcdr = b;

		nbuild.addPush(Undef.UNDEF);
		while(bcdr != Nil.NIL) {
			if(bcdr instanceof Cons) {
				Cons bc = (Cons)bcdr;
				boolean tl = istail && (bc.getCdr() == Nil.NIL);

				// dispose previous value
				nbuild.addPop();
				comp.compile(bc.getCar(), env, nbuild, toplv,
						callsym, tl, symlist, exec, memento);
				bcdr = bc.getCdr();
			} else {
				throw mesg.getError("err.explist.malform");
			}
		}
	}

	//
	/*package*/ static void compileList(
			Datum b,
			Environment env,
			LispCompiler comp,
			CompiledCode.Builder nbuild,
			Cons callsym,
			boolean istail,
			LispMessage mesg,
			List<Cons> symlist,
			CodeExecutor exec,
			IntStack memento) {
		compileList(b, env, comp, nbuild, callsym, istail, mesg,
				symlist, exec, memento, false);
	}

	//
	/*package*/ static boolean equalsReserved(Symbol rword, Datum d) {
		if(d instanceof SymbolName) {
			Symbol s1 = ((SymbolName)d).getSymbol();

			return Symbol.DEFAULT_NAMESPACE.getSymbol(
					s1.getName()).equals(rword);
		} else {
			return rword.equals(d);
		}
	}

}
