/*
 * Copyright 2013 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.nina.translate;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import net.morilib.automata.DFA;
import net.morilib.automata.DFAState;
import net.morilib.automata.dfa.ConvertedDFA;
import net.morilib.automata.dfa.ConvertedRangeDFA;
import net.morilib.nina.DFABuilder;
import net.morilib.nina.DFABuilder.DBS;
import net.morilib.nina.NFABuilder;
import net.morilib.nina.Nina;
import net.morilib.nina.NinaAction;
import net.morilib.nina.NinaException;
import net.morilib.nina.NinaNFA;
import net.morilib.nina.NinaParser;
import net.morilib.nina.NinaSubautomata;
import net.morilib.nina.Quadro;
import net.morilib.nina.TMBuilder;
import net.morilib.nina.cmd.NinaInfo;
import net.morilib.nina.cmd.NinatOptions;
import net.morilib.nina.metalang.NinaMetaLanguage;
import net.morilib.nina.translate.sh.ShNinatBuiltInCommands;
import net.morilib.nina.translate.sh.ShNinatFileSystem;
import net.morilib.range.Interval;
import net.morilib.range.Range;
import net.morilib.sh.DefaultShRuntime;
import net.morilib.sh.ShEnvironment;
import net.morilib.sh.ShFacade;
import net.morilib.sh.ShFileSystem;
import net.morilib.sh.ShProcess;
import net.morilib.sh.ShRootEnvironment;
import net.morilib.sh.ShRootShellEnvironment;
import net.morilib.sh.ShRuntime;
import net.morilib.sh.ShSyntaxException;
import net.morilib.sh.misc.XtraceStream;
import net.morilib.util.Tuple2;

/**
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/10/23
 */
public abstract class AbstractNinaTranslator
implements NinaTranslator {

	//
	private static final SimpleDateFormat YEAR =
			new SimpleDateFormat("yyyy");

	/**
	 * 
	 */
	protected DFA<Object, ?, Void> dfa;

	/**
	 * 
	 */
	protected Quadro quadro;

	/**
	 * 
	 */
	protected NinaAction builder;

	/**
	 * 
	 */
	protected NinaInfo options;

	/**
	 * 
	 */
	protected AbstractNinaTranslator parent;

	//
	private int stateNo = 0;
	private Map<DFAState<Object, ?, Void>, Integer> states =
			new IdentityHashMap
			<DFAState<Object, ?, Void>, Integer>();
	private Set<Integer> accepts = new HashSet<Integer>();
	private Set<Integer> deads = new HashSet<Integer>();
	private Set<Integer> emptyTransitions = new HashSet<Integer>();
	private Stack<DFAState<Object, ?, Void>> trz =
			new Stack<DFAState<Object, ?, Void>>();

	//
	private Map<String, AbstractNinaTranslator> subfas;
	private String fragment;
	private String machine;
	private String mainname;
	private String name;

	//
	NinaMetaLanguage meta;

	/**
	 * 
	 * @param lang
	 * @return
	 */
	public static AbstractNinaTranslator getTranslator(String lang) {
		Class<?> c;
		String p;

		try {
			p = AbstractNinaTranslator.class.getPackage().getName();
			c = Class.forName(p + ".NinaTranslator" + lang);
			return (AbstractNinaTranslator)c.newInstance();
		} catch(ClassNotFoundException e) {
			return null;
		} catch(InstantiationException e) {
			throw new RuntimeException(e);
		} catch(IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	//
	String escapeprint(String s) {
		StringBuffer b = new StringBuffer();

		for(int i = 0; i < s.length(); i++) {
			switch(s.charAt(i)) {
			case '\n':  b.append("\\n");  break;
			case '\r':  b.append("\\r");  break;
			case '\t':  b.append("\\t");  break;
			default:    b.append(s.charAt(i));  break;
			}
		}
		return b.toString();
	}

	/**
	 * 
	 * @param state
	 * @return
	 */
	protected int getStateNo(DFAState<Object, ?, Void> state) {
		if(states.containsKey(state)) {
			return states.get(state);
		} else {
			states.put(state, stateNo);
			if(state.isAccepted())  accepts.add(stateNo);
			if(state.isDead())      deads.add(stateNo);
			if(state instanceof DBS &&
					((DBS)state).isOnlyEmptyTransition()) {
				emptyTransitions.add(stateNo);
			}
			trz.push(state);
			return stateNo++;
		}
	}

	/**
	 * 
	 * @return
	 */
	protected boolean isStackEmpty() {
		return trz.isEmpty();
	}

	/**
	 * 
	 * @return
	 */
	protected DFAState<Object, ?, Void> popStack() {
		return trz.pop();
	}

	/**
	 * 
	 * @return
	 */
	protected int acceptsSize() {
		return accepts.size();
	}

	/**
	 * 
	 * @return
	 */
	protected int deadsSize() {
		return deads.size();
	}

	/**
	 * 
	 * @return
	 */
	protected Set<Integer> acceptsIterable() {
		return accepts;
	}

	/**
	 * 
	 * @return
	 */
	protected Set<Integer> deadsIterable() {
		return deads;
	}

	/**
	 * 
	 * @return
	 */
	protected Set<Integer> emptyTransitionsIterable() {
		return emptyTransitions;
	}

	/**
	 * 
	 * @param state
	 * @return
	 */
	protected boolean containsState(Object state) {
		return states.containsKey(state);
	}

	/**
	 * 
	 * @param state
	 * @return
	 */
	protected Integer stateNo(Object state) {
		return states.get(state);
	}

	/**
	 * 
	 * @return
	 */
	protected Set<DFAState<Object, ?, Void>> stateKeys() {
		return states.keySet();
	}

	/**
	 * 
	 * @return
	 */
	protected int stateSize() {
		return states.size();
	}

	/**
	 * 
	 * @return
	 */
	protected NinaInfo getOptions() {
		return options;
	}

	/**
	 * 
	 * @return
	 */
	protected String getMachine() {
		return machine;
	}

	/**
	 * 
	 * @return
	 */
	protected String getName() {
		return name;
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	protected Object getAutomaton(String s) {
		return subfas.get(s);
	}

	/**
	 * 
	 * @return
	 */
	protected Map<String, ?> getAutomata() {
		return subfas;
	}

	/**
	 * 
	 * @param out
	 */
	public abstract void printStates(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printObjectStates(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printClassStates(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printAcceptStates(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printDeadStates(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printEmptyTransitionStates(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printAcceptToken(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printActions(PrintStream out);

	/**
	 * 
	 * @param imp
	 * @param out
	 */
	public abstract void printImports(List<String> imp,
			PrintStream out);

	/**
	 * 
	 * @param out
	 */
	public abstract void printIsEnd(PrintStream out);

	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	protected abstract InputStream openScript(
			ShEnvironment env) throws IOException;

	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	protected abstract PrintStream openOutput() throws IOException;

	/**
	 * 
	 * @return
	 */
	protected abstract AbstractNinaTranslator newPrototype();

	/**
	 * 
	 * @param out
	 */
	protected abstract void printRecover(PrintStream out);

	/**
	 * 
	 * @param n
	 * @param out
	 */
	protected abstract void printDeadState(String n, PrintStream out);

	/**
	 * 
	 * @param n
	 * @param out
	 */
	protected abstract void printAttrs(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	protected abstract void printFinallyState(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	protected abstract void printInitTrap(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	protected abstract void printNFADeadState(PrintStream out);

	/**
	 * 
	 * @param out
	 */
	protected abstract void printConstants(PrintStream out);

	/**
	 * 
	 * @param ot
	 * @param b1
	 */
	protected abstract void appendValue(StringBuffer ot,
			StringBuffer b1);

	/**
	 * 
	 * @param ot
	 * @param b1
	 */
	protected abstract void appendLvalue(StringBuffer ot,
			StringBuffer b1);

	/**
	 * 
	 * @param ot
	 * @param cn
	 */
	protected abstract void appendMyPosition(StringBuffer ot, String ln,
			int cn);

	/**
	 * 
	 * @param ot
	 */
	protected abstract void appendReturn(StringBuffer ot);

	/**
	 * 
	 * @param ot
	 * @param b
	 */
	protected abstract void appendYield(StringBuffer ot, String b);

	/**
	 * 
	 * @param ot
	 */
	protected abstract String getConstName(String b);

	/**
	 * 
	 * @param q
	 * @return
	 */
	protected abstract LRTranslator getLRTranslator(Quadro q);

	/**
	 * 
	 * @param env
	 */
	protected abstract void setLocalenv(Quadro q, ShEnvironment env);

	//
	private void printStateSize(String n, PrintStream out) {
		out.printf("\t\t\treturn %d;\n", stateSize());
	}

	//
	private static int infimumbound(Interval a) {
		Object o;

		o = a.getInfimumBound();
		return o == Interval.FROM_INFIMUM ? 0 : ((Integer)o).intValue();
	}

	//
	static Map<Object, Range> classifyEdges(
			DFAState<Object, ?, Void> dfa) {
		Map<Object, Range> z = new HashMap<Object, Range>();
		int c;

		for(Interval v : dfa.getAlphabetRanges()) {
			Tuple2<Object, DFAState<Object, ?, Void>> t;
			Range g;

			if(v.isEmpty())  continue;
			c = infimumbound(v);
			t = new Tuple2<Object, DFAState<Object, ?, Void>>(
					dfa.getLabelInt(c),
					dfa.goInt(c));
			if((g = z.get(t)) == null) {
				z.put(t, v);
			} else {
				z.put(t, g.join(v));
			}
		}
		return z;
	}

	/**
	 * 
	 * @param o
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public AbstractNinaTranslator prototype(String name, Object o,
			String s) {
		AbstractNinaTranslator x;

		x = newPrototype();
		x.parent   = parent;
		x.fragment = fragment;
		x.machine  = machine;
		x.options  = options;
		x.subfas   = subfas;
		x.name     = name;
		x.meta     = meta;
		if(o instanceof DFABuilder) {
			x.builder = (DFABuilder)o;
			x.dfa = (DFA<Object, ?, Void>)x.builder.getMachine();
		} else if(o instanceof NFABuilder) {
			x.builder = (NFABuilder)o;
			if(s == null || s.equals("int")) {
				x.dfa = ConvertedRangeDFA.convertDFA(
						(NinaNFA)x.builder.getMachine());
			} else if(quadro.getType() == null) {
				x.dfa = ConvertedRangeDFA.convertDFA(
						(NinaNFA)x.builder.getMachine());
			} else {
				x.dfa = ConvertedDFA.convertDFA(
						(NinaNFA)x.builder.getMachine());
			}
			// not minimize
//			x.dfa = MinimizedRangeDFA.newInstance(x.dfa);
		}
		return x;
	}

	String executeMeta(String s) {
		if(meta == null) {
			return s;
		} else {
			return meta.execute(s).toString();
		}
	}

	//
	private AbstractNinaTranslator getsubfa(String[] args) {
		if(args.length > 1) {
			return subfas.get(args[1]);
		} else {
			return this;
		}
	}

	//
	private void process(String p, Quadro q,
			ShNinatBuiltInCommands cmd, ShEnvironment env,
			PrintStream ot) throws IOException {
		ByteArrayOutputStream bos;
		PrintStream ous = null;
		InputStream ins = null;
		ShFileSystem fs;
		XtraceStream qs;
		ShRuntime run;

		try {
			ins = openScript(env);
			bos = new ByteArrayOutputStream();
			ous = new PrintStream(bos);
			fs  = new ShNinatFileSystem(fragment, q, options);
			run = new DefaultShRuntime(cmd);
			qs  = new XtraceStream(System.out);
			ShFacade.execute(env, fs, cmd, run, ins, System.in, ous,
					System.err, qs);
		} catch(ShSyntaxException e) {
			throw new RuntimeException(e);
		} finally {
			if(ins != null)  ins.close();
			if(ous != null)  ous.close();
		}

		try {
			ous = ot != null ? ot : openOutput();
			ous.write(bos.toByteArray());
			options.postprocess();
		} finally {
			if(ous != null)  ous.close();
		}
	}

	private ShEnvironment getenv(String p, Quadro q,
			NinaSubautomata sub) {
		ShEnvironment env;
		StringBuffer b;
		String s, d;

		env  = new ShRootEnvironment();
		env.bind("IFS", " \t");
		env.bind("CLASSNAME", options.getClassName());
		env.bind("PACKAGE", p);
		env.bind("FILENAME", options.getFilename());
		env.bind("OUTPUT_FILENAME", options.getOutputFilename());
		env.bind("BUFSIZE", options.getBufSize());
		env.bind("TEMPLATE", q.getOption("template"));
		env.bind("EXTENDS", q.getOption("extends"));
		env.bind("IMPLEMENTS", q.getOption("implements"));
		env.bind("ABSTRACT", q.isOptionDefined("abstract") ?
				"abstract" : "");
		env.bind("TOKENERROR", q.isOptionDefined("tokenerror") ?
				q.getOption("tokenerror") : "TokenException");
		env.bind("ITERATOR", q.getOptionBoolean("iterator") ?
				"true" : "");
		env.bind("MAINNAME", mainname);
		env.bind("DEFAULTAT", q.getOption("default"));
		env.bind("ENABLE_LOOKAHEAD", q.getOption("enableLookahead"));
		env.bind("ENABLE_BACKTRACK", q.getOption("enableBacktrack"));
		env.bind("ENABLE_MATCHER", q.getOption("enableMatcher"));
		env.bind("ENABLE_LOG", q.getOption("enableLog"));
		env.bind("API_ACCESS_MODIFIER",
				q.isOptionDefined("APIAccessModifier") ?
						q.getOption("APIAccessModifier") + " " : "");
		env.bind("READER", q.getOption("reader"));
		env.bind("OWNER", q.getOptionNvl("owner", ""));
		env.bind("YEAR", q.getOptionNvl("year",
				YEAR.format(new java.util.Date())));
		env.bind("ORGANIZATION", q.getOptionNvl("organization",
				q.getOptionNvl("owner", "")));
		env.bind("NEWLINEMODE",
				"binary".equals(q.getOption("streammode")) ? "on" : "");
		env.bind("READSLOW", q.getOptionBoolean("readSlow") ?
				"true" : "");
		env.bind("READSLEEP", q.getOptionInt("readSleep", -1) + "");
		env.bind("EXEC_LOOKAHEAD",
				q.getLanguageOption().getLookahead());
		env.bind("EXEC_LOOKAHEAD_COMMIT",
				q.getLanguageOption().getLookaheadCommit());
		env.bind("TAPETYPE", q.getOption("tapetype"));
		env.bind("NINA_VERSION",  Nina.VERSION);
		env.bind("NINA_REVISION", Nina.getRevision());

		if("prompt".equals(q.getOption("main"))) {
			env.bind("PUTPROMPT", "true");
		} else if(q.isOptionDefined("main")) {
			env.bind("PUTMAIN", "true");
		}

		if((s = q.getLicense()).trim().length() > 0) {
			env.bind("LICENSE", "userlicense");
		} else {
			s = q.getOptionNvl("license", "default").toLowerCase();
			env.bind("LICENSE", "/license/" + s);
		}

		if(q.isOptionDefined("enableIndent")) {
			if((s = q.getType()) == null ||
					s.equals("") ||
					s.equals("char")) {
				env.bind("ENABLE_INDENT", "true");
			} else if((s = q.getLexerType()) != null &&
					(s.equals("") || s.equals("char"))) {
				env.bind("ENABLE_INDENT", "true");
			} else {
				throw new NinaException("indentnotsupported");
			}
		}

		if((s = q.getType()) == null || s.equals("char")) {
			env.bind("TYPE", "");
			env.bind("CTYPE", "int");
		} else if(s.equals("string")) {
			env.bind("TYPE", s);
			env.bind("CTYPE", "String");
		} else if(s.equals("class")) {
			env.bind("TYPE", s);
			env.bind("CTYPE", "Object");
		} else {
			env.bind("TYPE", s);
			env.bind("CTYPE", s);
		}

		if((s = q.getLexerType()) != null) {
			env.bind("LEXER", q.getLexer());
			if(s.equals("char")) {
				env.bind("LTYPE", "");
				env.bind("LCTYPE", "int");
			} else if(s.equals("string")) {
				env.bind("LTYPE", s);
				env.bind("LCTYPE", "String");
			} else if(s.equals("class")) {
				env.bind("LTYPE", s);
				env.bind("LCTYPE", "Object");
			} else {
				env.bind("LTYPE", s);
				env.bind("LCTYPE", s);
			}
		}

		b = new StringBuffer();  d = "";
		for(Map.Entry<String, Object> x : sub.entrySet()) {
			if(x.getValue() instanceof DFABuilder) {
				b.append(d).append(x.getKey());
				d = " ";
			}
		}
		env.bind("SUBAUTOMATA", b.toString());

		b = new StringBuffer();  d = "";
		for(String x : q.getLRObjects().keySet()) {
			b.append(d).append(x);
			d = " ";
		}
		env.bind("LRPARSERS", b.toString());
		setLocalenv(q, env);
		return new ShRootShellEnvironment(env);
	}

	private void setenv(ShEnvironment env, NinaAction builder) {
		StringBuffer b;
		String d;

		b = new StringBuffer();  d = "";
		for(String x : builder.getDynamicTransitMap().keySet()) {
			b.append(d).append(x);
			d = " ";
		}
		env.bind("DYNAMICAUTOMATA", b.toString());
		if(b.length() > 0)  env.bind("ENABLE_LOOKAHEAD", "true");
	}

	/**
	 * 
	 * @param name
	 * @param o
	 * @param opts
	 * @param prms
	 * @param sub
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	protected void translateQuadro(String name, Object o,
			NinaInfo opts, Map<String, String> prms,
			final NinaSubautomata sub,
			PrintStream ot) throws IOException {
		ShNinatBuiltInCommands cmd;
		ShEnvironment env;
		String p, q, m;
		NinaNFA n;

		mainname = name;
		subfas   = new HashMap<String, AbstractNinaTranslator>();
		options  = opts;
		if((m = quadro.getOption("machine")) == null) {
			// do nothing
		} else {
			m = "unknown";
		}

		fragment = quadro.getFragment();
		if((p = quadro.getRootPackage()) != null &&
				!p.equals("")) {
			// do nothing
		} else {
			p = "";
		}

		if((q = quadro.getOption("metaLanguage")) != null &&
				!q.equals("")) {
			meta = new NinaMetaLanguage(q);
		}

		if((q = quadro.getFragmentByName("meta")) != null &&
				!q.equals("") &&
				meta != null) {
			meta.execute(q);
		}

		env = getenv(p, quadro, sub);
		cmd = new ShNinatBuiltInCommands();
		cmd.putCommand("print_states", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				AbstractNinaTranslator t;
				String s;

				if(args.length > 1) {
					s = args.length > 2 ? args[2] : null;
					t = prototype(args[1], sub.get(args[1]), s);
					subfas.put(args[1], t);
				} else {
					t = AbstractNinaTranslator.this;
				}

				s = args.length > 2 ? args[2] : quadro.getType();
				if(s == null || s.equals("") || s.equals("char") ||
						s.equals("int")) {
					t.printStates(stdout);
				} else if(s.equals("class")) {
					t.printClassStates(stdout);
				} else {
					t.printObjectStates(stdout);
				}
				return 0;
			}

		});
		cmd.putCommand("print_accepts", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printAcceptStates(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_deads", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printDeadStates(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_empty_transitions", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printEmptyTransitionStates(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_imports", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printImports(quadro.getImports(),
						stdout);
				return 0;
			}

		});
		cmd.putCommand("print_isend", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printIsEnd(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_actions", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printActions(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_recover", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printRecover(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_deadstate", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printDeadState(args[1], stdout);
				return 0;
			}

		});
		cmd.putCommand("print_statesize", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printStateSize(args[1], stdout);
				return 0;
			}

		});
		cmd.putCommand("print_attrs", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printAttrs(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_finallystate", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printFinallyState(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_inittrap", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				printInitTrap(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_constants", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printConstants(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_token", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printAcceptToken(stdout);
				return 0;
			}

		});
		cmd.putCommand("print_nfadead", new ShProcess() {

			@Override
			public int main(ShEnvironment env, ShFileSystem fs,
					InputStream stdin, PrintStream stdout,
					PrintStream stderr,
					String... args) throws IOException {
				getsubfa(args).printNFADeadState(stdout);
				return 0;
			}

		});

		if(o instanceof NFABuilder) {
			machine = "nfa";
			builder = (NFABuilder)o;
			n = (NinaNFA)builder.getMachine();
			if(quadro.getType() == null) {
				dfa = ConvertedRangeDFA.convertDFA(n);
			} else {
				dfa = ConvertedDFA.convertDFA(n);
			}
			// not minimize
//			dfa = MinimizedRangeDFA.newInstance(dfa);
			process(p, quadro, cmd, env, ot);
		} else if(o instanceof DFABuilder) {
			machine = "dfa";
			builder = (DFABuilder)o;
			setenv(env.getEnvironment(), builder);
			dfa = (DFA<Object, Object, Void>)builder.getMachine();
			getLRTranslator(quadro).setCommands(cmd);
			process(p, quadro, cmd, env, ot);
		} else if(o instanceof TMBuilder) {
			machine = "tm";
			builder = (TMBuilder)o;
			dfa = (DFA<Object, Object, Void>)builder.getMachine();
			process(p, quadro, cmd, env, ot);
		} else {
			opts.perror("machinenotsupport", m);
		}
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.compiler.NinaTranslater#translate(java.io.Reader, java.io.PrintWriter)
	 */
	@Override
	public void translate(String name, Reader rd, NinaInfo opts,
			List<String> libs, Map<String, String> prms,
			PrintStream ot) throws IOException {
		NinaSubautomata s;
		String n;
		Object o;

		n = Nina.getFAName(null, name);
		s = new NinaSubautomata(n);
		s.put(n, null);
		quadro = Quadro.read(n, rd, libs);
		o = NinaParser.complie(quadro, libs, prms, s,
				opts.getConsole(), opts);
		translateQuadro(n, o, opts, prms, s, ot);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.translate.NinaTranslator#compile(java.lang.String, java.io.Reader, net.morilib.nina.cmd.NinatOptions, java.util.Map)
	 */
	@Override
	public Object compile(String name, Reader rd, NinatOptions opts,
			List<String> libs,
			Map<String, String> prms) throws IOException {
		NinaSubautomata s;
		Quadro q;
		String n;

		n = Nina.getFAName(null, name);
		s = new NinaSubautomata(n);
		s.put(n, null);
		q = Quadro.read(n, rd, libs);
		return NinaParser.complie(q, opts.getLibraryList(), prms, s,
				opts.getConsole(), opts);
	}

}
