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

package net.morilib.range;
import net.morilib.util.CharCodes;

public  class CharsetParser   {

	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();
		abstract int finallyState();
	}
	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;

	Throwable exception;

	private int unread = -1;

private StringBuffer bf;
private RangeAdder add;
private Range rng;
private java.util.Stack<Range> stk = new java.util.Stack<Range>();
private int lh, lz, lp = -1;
private boolean sp = false;
private String cset;

private Range _rng() {
	if(add != null) {
		if(lh >= 0)  add.add(IntervalsInt.newClosedInterval(lh, lh));
		if(lz >= 0 && lh != lz) {
			add.add(IntervalsInt.newClosedInterval(lz, lz));
		}
		rng = add.toRange();
		add = null;
		return rng;
	} else {
		return rng;
	}
}

private void _ini() {
	add = new RangeAdder();
	lh = lz = -1;
}

private void _adr1(int c) {
	if(lh >= 0)  add.add(IntervalsInt.newClosedInterval(lh, lh));
	if(sp) {
		lh = lz = -1;
	} else {
		lh = lz = c;
	}
	sp = false;
}

private void _adr2(int c) {
	add.add(IntervalsInt.newClosedInterval(lh, c));
	lh = lz = -1;
}

private void _adx(String s) {
	int c = Integer.parseInt(s, 16);

	add.add(IntervalsInt.newClosedInterval(c, c));
}

private void _ade(int c) {
	sp = true;
	switch(c) {
	case 't':  add.add(CharSets.parse("\t"));  break;
	case 'n':  add.add(CharSets.parse("\n"));  break;
	case 'r':  add.add(CharSets.parse("\r"));  break;
	case 'f':  add.add(CharSets.parse("\f"));  break;
	case 'd':  add.add(CharSets.NUMBERS);  break;
	case 'D':  add.add(CharSets.NOT_NUMBER);  break;
	case 's':  add.add(CharSets.ASCII_WHITESPACE);  break;
	case 'S':  add.add(CharSets.NOT_ASCII_WHITESPACE);  break;
	case 'w':  add.add(CharSets.ASCII_WORD);  break;
	case 'W':  add.add(CharSets.NOT_ASCII_WORD);  break;
	default:   break;
	}
}

public static Range parse(String s, String cset) {
	CharsetParser p = new CharsetParser();
	Range r;

	try {
		p.cset = cset;
		p.parse(new java.io.StringReader(s));
		r = p.stk.pop();
		if(p.lp >= 0) {
			r = r.join(IntervalsInt.newClosedInterval(p.lp, p.lp));
		}
		return r;
	} catch(java.io.IOException e) {
		throw new RuntimeException(e);
	}
}


	int _read1(java.io.Reader stream) throws java.io.IOException {

	int c;
	c = stream.read();
	if(c < 0) {
		return -1;
	} else {
		return CharCodes.unicodeToOtherCode((char)c, cset);
	}

	}
	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 = _read1(rd)) == -1) {
			__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 charsetParser_step(java.io.Reader __rd, int  $c)  throws java.io.IOException {
		switch(STATE) {
		case 0:
			__stkpush(1, ENGINE_andt);
			STATE = 0;
			return -1;
		case 1:
			if($c == '[') {
				UNGET($c);
				STATE = 2;
				return 1;
			}
			return 0;
		case 2:
			__stkpush(3, ENGINE_andt);
			STATE = 0;
			return -1;
		case 3:
			UNGET($c);
			STATE = 1;
			return 1;
		}
		return 0;
	}

	private boolean charsetParser_accepted() {
		return (STATE == 1 ||
				STATE == 3);
	}

	int charsetParser_execaction(int  $c) {
		switch(STATE) {
		case 2:
			break;
		case 3:
			stk.push(stk.pop().join(stk.pop()));
			break;
		case 1:
			break;
		case 0:
			break;
		}
		return 1;
	}

	boolean charsetParser_isend() {
		return false;
	}

	private final Engine ENGINE_charsetParser = new Engine() {

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

		boolean accepted() {
			return charsetParser_accepted();
		}

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

		boolean isend() {
			return charsetParser_isend();
		}

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

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 4;
		}

		int finallyState() {
			return -1;
		}

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

	};

	private int parn_step(java.io.Reader __rd, int  $c)  throws java.io.IOException {
		switch(STATE) {
		case 0:
			if($c == '[') {
				STATE = 1;
				return 1;
			} else if($c == '^') {
				STATE = 2;
				return 1;
			} else {
				__stkpush(3, ENGINE_cset);
				STATE = 0;
				return -1;
			}
		case 3:
			return 0;
		case 2:
			__stkpush(4, ENGINE_cset);
			STATE = 0;
			return -1;
		case 4:
			return 0;
		case 1:
			__stkpush(5, ENGINE_charsetParser);
			STATE = 0;
			return -1;
		case 5:
			if($c == ']') {
				STATE = 6;
				return 1;
			}
			return 0;
		case 6:
			return 0;
		}
		return 0;
	}

	private boolean parn_accepted() {
		return (STATE == 3 ||
				STATE == 4 ||
				STATE == 6);
	}

	int parn_execaction(int  $c) {
		switch(STATE) {
		case 2:
			break;
		case 5:
			break;
		case 3:
			stk.push(_rng());
			break;
		case 6:
			break;
		case 0:
			_ini();
			break;
		case 4:
			stk.push(CharSets.complement(_rng()));
			break;
		case 1:
			break;
		}
		return 1;
	}

	boolean parn_isend() {
		return false;
	}

	private final Engine ENGINE_parn = new Engine() {

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

		boolean accepted() {
			return parn_accepted();
		}

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

		boolean isend() {
			return parn_isend();
		}

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

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 7;
		}

		int finallyState() {
			return -1;
		}

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

	};

	private int cset_step(java.io.Reader __rd, int  $c)  throws java.io.IOException {
		switch(STATE) {
		case 0:
			UNGET($c);
			STATE = 1;
			return 1;
		case 1:
			if($c == '\\') {
				STATE = 2;
				return 1;
			} else {
				STATE = 3;
				return 1;
			}
		case 3:
			if($c >= 0 && $c <= '%') {
				STATE = 3;
				return 1;
			} else if($c == '&') {
				UNGET($c);
				STATE = 4;
				return 1;
			} else if($c >= '\'' && $c <= ',') {
				STATE = 3;
				return 1;
			} else if($c == '-') {
				STATE = 5;
				return 1;
			} else if($c >= '.' && $c <= 'Z') {
				STATE = 3;
				return 1;
			} else if($c == '[') {
				UNGET($c);
				STATE = 4;
				return 1;
			} else if($c == '\\') {
				STATE = 2;
				return 1;
			} else if($c >= '^' && $c <= 2147483647) {
				STATE = 3;
				return 1;
			}
			return 0;
		case 5:
			if($c >= 0 && $c <= '%') {
				STATE = 6;
				return 1;
			} else if($c >= '\'' && $c <= 'Z') {
				STATE = 6;
				return 1;
			} else if($c == '\\') {
				STATE = 6;
				return 1;
			} else if($c >= '^' && $c <= 2147483647) {
				STATE = 6;
				return 1;
			}
			return 0;
		case 6:
			if($c >= 0 && $c <= '%') {
				STATE = 3;
				return 1;
			} else if($c == '&') {
				UNGET($c);
				STATE = 4;
				return 1;
			} else if($c >= '\'' && $c <= 'Z') {
				STATE = 3;
				return 1;
			} else if($c == '[') {
				UNGET($c);
				STATE = 4;
				return 1;
			} else if($c == '\\') {
				STATE = 3;
				return 1;
			} else if($c >= '^' && $c <= 2147483647) {
				STATE = 3;
				return 1;
			}
			return 0;
		case 4:
			return 0;
		case 2:
			if($c == 'D') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'S') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'f') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'n') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'r') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 's') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 't') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'u') {
				STATE = 7;
				return 1;
			} else if($c == 'w') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'x') {
				STATE = 7;
				return 1;
			} else if($c == 'W') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'd') {
				_ade($c);
				STATE = 3;
				return 1;
			} else if($c == 'D') {
				_ade($c);
				STATE = 3;
				return 1;
			} else {
				STATE = 3;
				return 1;
			}
		case 7:
			if($c >= '0' && $c <= '9') {
				STATE = 8;
				return 1;
			} else if($c >= 'A' && $c <= 'F') {
				STATE = 8;
				return 1;
			} else if($c >= 'a' && $c <= 'f') {
				STATE = 8;
				return 1;
			}
			return 0;
		case 8:
			if($c >= '0' && $c <= '9') {
				STATE = 8;
				return 1;
			} else if($c >= 'A' && $c <= 'F') {
				STATE = 8;
				return 1;
			} else if($c >= 'a' && $c <= 'f') {
				STATE = 8;
				return 1;
			} else {
				_adx(bf.toString());
				STATE = 3;
				return 1;
			}
		}
		return 0;
	}

	private boolean cset_accepted() {
		return (STATE == 2 ||
				STATE == 3 ||
				STATE == 4 ||
				STATE == 5 ||
				STATE == 6 ||
				STATE == 8);
	}

	int cset_execaction(int  $c) {
		switch(STATE) {
		case 2:
			_adr1(-1);          
lz = '\\';
			break;
		case 6:
			_adr2($c);
			break;
		case 3:
			_adr1($c);
			break;
		case 0:
			_ini();
			break;
		case 4:
			break;
		case 5:
			lz = '-';
			break;
		case 7:
			bf = new StringBuffer();
			break;
		case 1:
			break;
		case 8:
			bf.append((char)$c);
			break;
		}
		return 1;
	}

	boolean cset_isend() {
		return false;
	}

	private final Engine ENGINE_cset = new Engine() {

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

		boolean accepted() {
			return cset_accepted();
		}

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

		boolean isend() {
			return cset_isend();
		}

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

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 9;
		}

		int finallyState() {
			return -1;
		}

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

	};

	private int andt_step(java.io.Reader __rd, int  $c)  throws java.io.IOException {
		switch(STATE) {
		case 0:
			__stkpush(1, ENGINE_parn);
			STATE = 0;
			return -1;
		case 1:
			if($c == '&') {
				STATE = 2;
				return 1;
			}
			return 0;
		case 2:
			if($c == '&') {
				STATE = 3;
				return 1;
			}
			return 0;
		case 3:
			__stkpush(4, ENGINE_parn);
			STATE = 0;
			return -1;
		case 4:
			UNGET($c);
			STATE = 1;
			return 1;
		}
		return 0;
	}

	private boolean andt_accepted() {
		return (STATE == 1 ||
				STATE == 2 ||
				STATE == 4);
	}

	int andt_execaction(int  $c) {
		switch(STATE) {
		case 0:
			break;
		case 4:
			stk.push(stk.pop().meet(stk.pop()));
			break;
		case 1:
			break;
		case 2:
			lp = '&';
			break;
		case 3:
			lp = -1;
			break;
		}
		return 1;
	}

	boolean andt_isend() {
		return false;
	}

	private final Engine ENGINE_andt = new Engine() {

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

		boolean accepted() {
			return andt_accepted();
		}

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

		boolean isend() {
			return andt_isend();
		}

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

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 5;
		}

		int finallyState() {
			return -1;
		}

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

	};

	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;
		}
	}

	private Boolean execfinally() {
		int a, b;

		if((a = __stk[__slen - 1].finallyState()) >= 0) {
			b = STATE;  STATE = a;
			switch(__stk[__slen - 1].execaction(NINA_BEGIN)) {
			case NINA_HALT_ACCEPT:
				__slen = 0;
				return Boolean.TRUE;
			case NINA_HALT_REJECT:
				__slen = 0;
				return Boolean.FALSE;
			}
			STATE = b;
		}
		return null;
	}

	private int getdeadstate() {
		return __stk[__slen - 1].deadState();
	}

	private int getrecover(Exception e) {
		return __stk[__slen - 1].recover(e);
	}

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

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

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

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

		return o.parse(rd);
	}

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

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

}
