// Translated by Nina
// This source code is under public domain

package net.morilib.lisp.nano;
import java.math.BigInteger;

public  class NanoNumberParser   {

	static class TokenException extends RuntimeException {
	}

	static abstract class Engine {
		abstract int step(java.io.Reader __rd, int c) throws java.io.IOException;
		abstract boolean accepted();
		abstract int execaction(int c);
		abstract boolean isend();
		abstract int recover(Exception e);
		abstract int deadState();
		abstract int stateSize();
	}
	private static final int NINA_ACCEPT = -1;
	private static final int NINA_FAIL = -9;
	private static final int NINA_HALT_ACCEPT = -91;
	private static final int NINA_HALT_REJECT = -72;
	private static final int NINA_BEGIN = -2;
	private static final int NINA_STACKLEN = 72;
	static final int INITIAL = 0;

	private int STATE;
	private int[] __sts = new int[NINA_STACKLEN];
	private Engine[] __stk = new Engine[NINA_STACKLEN];
	private Object[][] __stv = new Object[NINA_STACKLEN][];
	private int __slen = 0;

	private int unread = -1;

private StringBuffer nbf, dbf, fbf;
private LispReal real;
private Datum num;

private void ib() {
	real = LispInteger.ONE;
	num = null;
}

private void ia() {
	nbf = new StringBuffer();
	dbf = fbf = null;
}

private void an(int c) {
	if(c != '+')  nbf.append((char)c);
}

private void ad(int c) {
	dbf.append((char)c);
}

private void af(int c) {
	if(fbf == null)  fbf = nbf;
	fbf.append((char)c);
}

private LispReal getr() {
	if(dbf != null) {
		return LispRational.newRational(new BigInteger(nbf.toString()),
				new BigInteger(dbf.toString()));
	} else if(fbf != null) {
		return new LispDouble(Double.parseDouble(fbf.toString()));
	} else {
		return LispInteger.valueOf(new BigInteger(nbf.toString()));
	}
}

private LispReal getr(int rdx) {
	if(dbf != null) {
		return LispRational.newRational(new BigInteger(nbf.toString(), rdx),
				new BigInteger(dbf.toString(), rdx));
	} else {
		return LispInteger.valueOf(new BigInteger(nbf.toString(), rdx));
	}
}

private static Datum _exec(String s) {
	NanoNumberParser r;

	try {
		r = new NanoNumberParser();
		r.parse(new java.io.StringReader(s));
		return r.num;
	} catch(java.io.IOException e) {
		throw new RuntimeException();
	}
}

public static Datum parseNumber(String s, int rdx) {
	if(s.length() == 0) {
		return LispBoolean.FALSE;
	} else if(s.charAt(0) == '#') {
		return _exec(s);
	} else if(rdx == 10) {
		return _exec("#d" + s);
	} else if(rdx == 8) {
		return _exec("#o" + s);
	} else if(rdx == 16) {
		return _exec("#x" + s);
	} else if(rdx == 2) {
		return _exec("#b" + s);
	} else {
		return LispBoolean.FALSE;
	}
}


	private int _read(java.io.Reader rd) throws java.io.IOException {
		int c;

		if(unread >= 0) {
			c = unread;
			unread = -1;
			__logprint("Read unread: ", c);
		} else if((c = rd.read()) >= 0) {
			__logprint("Read: ", c);
		} else {
			__logprint("Read end-of-file");
		}
		return c;
	}

	void UNGET(int c) {
		unread = c;
		__logprint("Set unread: ", c);
	}

	private void __logprint(String s, int c) {
	}

	private void __logopen() {
	}

	private void __logprint(String s) {
	}

	private void __logclose() {
	}

	private void __puttrace() {
	}


	private int intx_step(java.io.Reader __rd,
			int  $c) throws java.io.IOException {
		boolean __g = true;

		switch(STATE) {
		case 0:
			if(__g && $c == '+') {
				STATE = 1;
				return 1;
			} else if(__g && $c == '-') {
				STATE = 1;
				return 1;
			} else if(__g && $c >= '0' && $c <= '9') {
				STATE = 2;
				return 1;
			} else if(__g && $c >= 'A' && $c <= 'F') {
				STATE = 2;
				return 1;
			} else if(__g && $c >= 'a' && $c <= 'f') {
				STATE = 2;
				return 1;
			}
			return 0;
		case 2:
			if(__g && $c == '/') {
				STATE = 3;
				return 1;
			} else if(__g && $c >= '0' && $c <= '9') {
				STATE = 2;
				return 1;
			} else if(__g && $c >= 'A' && $c <= 'F') {
				STATE = 2;
				return 1;
			} else if(__g && $c >= 'a' && $c <= 'f') {
				STATE = 2;
				return 1;
			}
			return 0;
		case 3:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 4;
				return 1;
			} else if(__g && $c >= 'A' && $c <= 'F') {
				STATE = 4;
				return 1;
			} else if(__g && $c >= 'a' && $c <= 'f') {
				STATE = 4;
				return 1;
			}
			return 0;
		case 4:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 4;
				return 1;
			} else if(__g && $c >= 'A' && $c <= 'F') {
				STATE = 4;
				return 1;
			} else if(__g && $c >= 'a' && $c <= 'f') {
				STATE = 4;
				return 1;
			}
			return 0;
		case 1:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 2;
				return 1;
			}
			return 0;
		}
		return 0;
	}
	private boolean intx_accepted() {
		return (STATE == 2 ||
				STATE == 4);
	}

	int intx_execaction(int  $c) {
		switch(STATE) {
		case 3:
			dbf = new StringBuffer();
			break;
		case 0:
			ia();
			break;
		case 4:
			ad($c);
			break;
		case 2:
			an($c);
			break;
		case 1:
			an($c);
			break;
		}
		return 1;
	}

	boolean intx_isend() {
		return false;
	}

	private final Engine ENGINE_intx = new Engine() {

		int step(java.io.Reader __rd,
				int c) throws java.io.IOException {
			return intx_step(__rd, c);
		}

		boolean accepted() {
			return intx_accepted();
		}

		int execaction(int c) {
			return intx_execaction(c);
		}

		boolean isend() {
			return intx_isend();
		}

		int recover(Exception e) {
			return -1;
		}

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 5;
		}

		public String toString() {
			return "intx";
		}

	};

	private int real_step(java.io.Reader __rd,
			int  $c) throws java.io.IOException {
		boolean __g = true;

		switch(STATE) {
		case 0:
			if(__g && $c == '+') {
				STATE = 1;
				return 1;
			} else if(__g && $c == '-') {
				STATE = 1;
				return 1;
			} else if(__g && $c == '.') {
				STATE = 2;
				return 1;
			} else if(__g && $c >= '0' && $c <= '9') {
				STATE = 3;
				return 1;
			}
			return 0;
		case 3:
			if(__g && $c == '.') {
				STATE = 2;
				return 1;
			} else if(__g && $c == '/') {
				STATE = 4;
				return 1;
			} else if(__g && $c >= '0' && $c <= '9') {
				STATE = 3;
				return 1;
			} else if(__g && $c == 'E') {
				STATE = 5;
				return 1;
			} else if(__g && $c == 'F') {
				STATE = 5;
				return 1;
			} else if(__g && $c == 'e') {
				STATE = 5;
				return 1;
			} else if(__g && $c == 'f') {
				STATE = 5;
				return 1;
			}
			return 0;
		case 5:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 6;
				return 1;
			}
			return 0;
		case 6:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 6;
				return 1;
			}
			return 0;
		case 4:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 7;
				return 1;
			}
			return 0;
		case 7:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 7;
				return 1;
			}
			return 0;
		case 2:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 8;
				return 1;
			}
			return 0;
		case 8:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 8;
				return 1;
			} else if(__g && $c == 'E') {
				STATE = 5;
				return 1;
			} else if(__g && $c == 'F') {
				STATE = 5;
				return 1;
			} else if(__g && $c == 'e') {
				STATE = 5;
				return 1;
			} else if(__g && $c == 'f') {
				STATE = 5;
				return 1;
			}
			return 0;
		case 1:
			if(__g && $c >= '0' && $c <= '9') {
				STATE = 3;
				return 1;
			}
			return 0;
		}
		return 0;
	}
	private boolean real_accepted() {
		return (STATE == 3 ||
				STATE == 6 ||
				STATE == 7 ||
				STATE == 8);
	}

	int real_execaction(int  $c) {
		switch(STATE) {
		case 8:
			af($c);
			break;
		case 3:
			an($c);
			break;
		case 7:
			ad($c);
			break;
		case 2:
			af($c);
			break;
		case 0:
			ia();
			break;
		case 1:
			an($c);
			break;
		case 5:
			af($c);
			break;
		case 4:
			dbf = new StringBuffer();
			break;
		case 6:
			af($c);
			break;
		}
		return 1;
	}

	boolean real_isend() {
		return false;
	}

	private final Engine ENGINE_real = new Engine() {

		int step(java.io.Reader __rd,
				int c) throws java.io.IOException {
			return real_step(__rd, c);
		}

		boolean accepted() {
			return real_accepted();
		}

		int execaction(int c) {
			return real_execaction(c);
		}

		boolean isend() {
			return real_isend();
		}

		int recover(Exception e) {
			return -1;
		}

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 9;
		}

		public String toString() {
			return "real";
		}

	};

	private int nanoNumberParser_step(java.io.Reader __rd,
			int  $c) throws java.io.IOException {
		boolean __g = true;

		switch(STATE) {
		case 0:
			if(__g && $c == '#') {
				STATE = 1;
				return 1;
			}
			return 0;
		case 1:
			if(__g && $c == 'b') {
				STATE = 2;
				return 1;
			} else if(__g && $c == 'd') {
				STATE = 3;
				return 1;
			} else if(__g && $c == 'o') {
				STATE = 4;
				return 1;
			} else if(__g && $c == 'x') {
				STATE = 5;
				return 1;
			}
			return 0;
		case 5:
			__stkpush(6, ENGINE_intx);
			STATE = 0;
			return -1;
		case 6:
			return 0;
		case 4:
			__stkpush(7, ENGINE_intx);
			STATE = 0;
			return -1;
		case 7:
			return 0;
		case 3:
			__stkpush(8, ENGINE_number);
			STATE = 0;
			return -1;
		case 8:
			return 0;
		case 2:
			__stkpush(9, ENGINE_intx);
			STATE = 0;
			return -1;
		case 9:
			return 0;
		case 10:
			return 0;
		}
		return 0;
	}
	private boolean nanoNumberParser_accepted() {
		return (STATE == 0 ||
				STATE == 6 ||
				STATE == 7 ||
				STATE == 8 ||
				STATE == 9 ||
				STATE == 10);
	}

	int nanoNumberParser_execaction(int  $c) {
		switch(STATE) {
		case 8:
			num = num != null ? num : real;
			break;
		case 0:
			break;
		case 1:
			break;
		case 10:
			num = LispBoolean.FALSE;
			break;
		case 7:
			num = getr(8);
			break;
		case 3:
			break;
		case 9:
			num = getr(2);
			break;
		case 2:
			break;
		case 5:
			break;
		case 6:
			num = getr(16);
			break;
		case 4:
			break;
		}
		return 1;
	}

	boolean nanoNumberParser_isend() {
		return false;
	}

	private final Engine ENGINE_nanoNumberParser = new Engine() {

		int step(java.io.Reader __rd,
				int c) throws java.io.IOException {
			return nanoNumberParser_step(__rd, c);
		}

		boolean accepted() {
			return nanoNumberParser_accepted();
		}

		int execaction(int c) {
			return nanoNumberParser_execaction(c);
		}

		boolean isend() {
			return nanoNumberParser_isend();
		}

		int recover(Exception e) {
			return -1;
		}

		int deadState() {
			return 10;
		}

		int stateSize() {
			return 11;
		}

		public String toString() {
			return "nanoNumberParser";
		}

	};

	private int number_step(java.io.Reader __rd,
			int  $c) throws java.io.IOException {
		boolean __g = true;

		switch(STATE) {
		case 0:
			if(__g && $c == '-') {
				UNGET($c);
				STATE = 1;
				return 1;
			} else {
				__stkpush(2, ENGINE_real);
				STATE = 0;
				return -1;
			}
		case 2:
			if(__g && $c == '+') {
				STATE = 1;
				return 1;
			} else if(__g && $c == '-') {
				UNGET($c);
				STATE = 1;
				return 1;
			} else if(__g && $c == '@') {
				STATE = 3;
				return 1;
			}
			return 0;
		case 3:
			__stkpush(4, ENGINE_real);
			STATE = 0;
			return -1;
		case 4:
			return 0;
		case 1:
			if(__g && $c == '-') {
				STATE = 5;
				return 1;
			} else if(__g && $c == 'i') {
				STATE = 6;
				return 1;
			} else {
				__stkpush(7, ENGINE_real);
				STATE = 0;
				return -1;
			}
		case 7:
			if(__g && $c == 'i') {
				STATE = 8;
				return 1;
			}
			return 0;
		case 8:
			return 0;
		case 6:
			return 0;
		case 5:
			if(__g && $c == 'i') {
				STATE = 9;
				return 1;
			}
			return 0;
		case 9:
			return 0;
		}
		return 0;
	}
	private boolean number_accepted() {
		return (STATE == 2 ||
				STATE == 4 ||
				STATE == 6 ||
				STATE == 8 ||
				STATE == 9);
	}

	int number_execaction(int  $c) {
		switch(STATE) {
		case 7:
			break;
		case 5:
			break;
		case 6:
			num = LispComplex.newComplex(real, LispInteger.ONE);
			break;
		case 0:
			ib();
			break;
		case 3:
			break;
		case 1:
			break;
		case 4:
			num = LispComplex.newPolar(real.getRealDouble(), getr().getRealDouble());
			break;
		case 2:
			real = getr();
			break;
		case 9:
			num = LispComplex.newComplex(real, LispInteger.valueOf(-1));
			break;
		case 8:
			num = LispComplex.newComplex(real, getr());
			break;
		}
		return 1;
	}

	boolean number_isend() {
		return false;
	}

	private final Engine ENGINE_number = new Engine() {

		int step(java.io.Reader __rd,
				int c) throws java.io.IOException {
			return number_step(__rd, c);
		}

		boolean accepted() {
			return number_accepted();
		}

		int execaction(int c) {
			return number_execaction(c);
		}

		boolean isend() {
			return number_isend();
		}

		int recover(Exception e) {
			return -1;
		}

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 10;
		}

		public String toString() {
			return "number";
		}

	};

	void __stkpush(int st, Engine en) {
		Object[][] c;
		Engine[] b;
		int[] a;

		if(__slen >= __sts.length) {
			a = new int[__sts.length * 2];
			b = new Engine[__stk.length * 2];
			c = new Object[__stk.length * 2][];
			System.arraycopy(__sts, 0, a, 0, __sts.length);
			System.arraycopy(__stk, 0, b, 0, __stk.length);
			System.arraycopy(__stv, 0, c, 0, __stv.length);
			__sts = a;
			__stk = b;
			__stv = c;
		}
		__sts[__slen] = st;
		__stk[__slen] = en;
		__stv[__slen++] = new Object[en.stateSize()];
	}

	private int _parse(java.io.Reader rd,
			int x) throws java.io.IOException {
		boolean b = false;
		int c = x, a;
		Engine en;

		b = __stk[__slen - 1].accepted();
		if(rd == null) {
			throw new RuntimeException("can not recurse");
		} else {
			switch(__stk[__slen - 1].execaction(NINA_BEGIN)) {
			case NINA_ACCEPT:
				__logprint("accept " + __stk[__slen - 1]);
				return NINA_ACCEPT;
			case NINA_FAIL:
				__logprint("match failed: begin");
				__puttrace();
				return NINA_FAIL;
			case NINA_HALT_ACCEPT:
				__logprint("machine halted: begin");
				return NINA_HALT_ACCEPT;
			case NINA_HALT_REJECT:
				__logprint("machine halted: begin");
				return NINA_HALT_REJECT;
			}
		}

		try {
			do {
				en = __stk[__slen - 1];
				if(c < 0) {
					// do nothing
				} else if((a = en.step(rd, c)) > 0) {
					__logprint("transit to state " + STATE + ": ", c);
					b = en.accepted();
					switch(en.execaction(c)) {
					case NINA_ACCEPT:
						__logprint("accept " + __stk[__slen - 1]);
						UNGET(c);
						return NINA_ACCEPT;
					case NINA_FAIL:
						__logprint("match failed: ", c);
						__puttrace();
						UNGET(c);
						return NINA_FAIL;
					case NINA_HALT_ACCEPT:
						__logprint("machine halted: ", c);
						return NINA_HALT_ACCEPT;
					case NINA_HALT_REJECT:
						__logprint("machine halted: ", c);
						return NINA_HALT_REJECT;
					}
				} else if(a < 0) {
					__logprint("entering " + __stk[__slen - 1]);
					return c;
				} else if(b) {
					__logprint("accept " + __stk[__slen - 1]);
					UNGET(c);
					return NINA_ACCEPT;
				} else {
					__logprint("match failed: ", c);
					__puttrace();
					UNGET(c);
					return NINA_FAIL;
				}
			} while((c = _read(rd)) >= 0);
			if(!b)  throw new TokenException();
			return NINA_ACCEPT;
		} catch(RuntimeException e) {
			UNGET(c);
			throw e;
		}
	}

	 boolean parse(java.io.Reader rd,
			Engine entry) throws java.io.IOException {
		int c = -1;

		__logopen();
		try {
			__stkpush(0, entry);
			while(true) {
				try {
					if((c = _parse(rd, c)) >= 0) {
						// do nothing
					} else if(c == NINA_FAIL) {
						while((STATE = __stk[__slen - 1].deadState()) < 0) {
							if(__slen-- <= 1) {
								throw new TokenException();
							}
						}
					} else if(c == NINA_HALT_ACCEPT) {
						__slen = 0;
						return true;
					} else if(c == NINA_HALT_REJECT) {
						__slen = 0;
						return false;
					} else if(__slen > 1) {
						STATE = __sts[--__slen];
					} else {
						break;
					}
				} catch(RuntimeException e) {
					while((STATE = __stk[__slen - 1].recover(e)) < 0) {
						if(__slen-- <= 1) {
							throw new TokenException();
						}
					}
				}
			}
			return __stk[__slen - 1].accepted();
		} finally {
			__logclose();
		}
	}

	 boolean parse(
			java.io.Reader rd) throws java.io.IOException {
		return parse(rd, ENGINE_nanoNumberParser);
	}

	 boolean parse(
			java.io.InputStream rd) throws java.io.IOException {
		return parse(new java.io.InputStreamReader(rd));
	}

	 static boolean parseAll(
			java.io.Reader rd) throws java.io.IOException {
		NanoNumberParser o = new NanoNumberParser();

		return o.parse(rd);
	}

	 static boolean parseAll(
			java.io.InputStream rd) throws java.io.IOException {
		return parseAll(new java.io.InputStreamReader(rd));
	}

}
