#
# Copyright 2013-2014 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.
#
sed "s/@@YEAR@@/$YEAR/g
s/@@OWNER@@/$OWNER/g
s/@@ORGANIZATION@@/$ORGANIZATION/g" /license/$LICENSE

[ -z "$PACKAGE" ] || echo "package $PACKAGE;"
[ -z "$EXTENDS" ] || extends="extends $EXTENDS"
[ -z "$IMPLEMENTS" ] || implements="implements $IMPLEMENTS"
print_imports
cat definition

if [ -z "$TYPE" ]
then
  nullsym='-1'
  streamsym='java.io.Reader'
  rdt='java.io.Reader'
  tre='throws java.io.IOException'
else
  nullsym='null'
  rdt="Iterable<$CTYPE>"
  streamsym='java.util.Iterator'
  tre=''
fi
cat << EOF
public $ABSTRACT class ${CLASSNAME}${TEMPLATE} $extends $implements {

	static class TokenException extends RuntimeException {
	}

	static abstract class Engine {
		abstract int step($streamsym __rd, $CTYPE c) $tre;
		abstract boolean accepted();
		abstract int execaction($CTYPE c);
		abstract boolean isend();
		abstract int recover(Exception e);
		abstract int deadState();
		abstract int stateSize();
		abstract int finallyState();
	}
EOF

[ -n "$ENABLE_BACKTRACK" ] && cat << EOF

	static class Continuation {

		private int STATE;
		private int[] __sts;
		private Engine[] __stk;
		private Object[][] __stv;

		Continuation(int s, int[] sts, Engine[] stk, Object[][] stv,
				int len) {
			STATE = s;
			__sts = new int[len];
			__stk = new Engine[len];
			__stv = new Object[len][];
			System.arraycopy(sts, 0, __sts, 0, len);
			System.arraycopy(stk, 0, __stk, 0, len);
			System.arraycopy(stv, 0, __stv, 0, len);
		}

	}

EOF
cat << EOF
	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_YIELD = -85;
	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 $CTYPE unread = $nullsym;
	Object yieldObject;
	$streamsym befstream = null;
	Throwable exception;

EOF

[ -n "$ENABLE_LOG" ] && cat << EOF
	private static final String __LOGFILE =
			System.getProperty("nina.${CLASSNAME}.log.file");
	private static final boolean __PUTTRACE =
			Boolean.getBoolean("nina.${CLASSNAME}.trace");

	private java.io.PrintStream __log = null;

EOF
[ -z "$TYPE" ] && [ -n "$ENABLE_MATCHER" ] && echo '	private String matched;'

if [ -n "$ENABLE_BACKTRACK" ]
then
  echo "	private $CTYPE[] __backtrack = null;"
  echo "	private int __backtrack_ptr = -1;"
  echo "	private int __backtrack_len = -1;"
  echo "	private int __backtrack_wptr = -1;"
  echo "	private int __backtrack_sym = -1;"
  echo "	private int[] __backtrack_state = null;"
  echo "	private $CTYPE[] __backtrack_wsym = null;"
  echo "	private int[] __backtrack_ptrs = null;"
  echo "	private Continuation[] __backtrack_cont = null;"
  echo "	private int __backtrack_sptr = -1;"
  echo "	private boolean __backtrack_flg = false;"
fi

if [ -n "$ENABLE_LOOKAHEAD" ]
then
  echo "	private int __lookahead_state;"
  echo "	private int __lookahead_mark = -1;"
  echo "	private $CTYPE[] __lookahead = null;"
  echo "	private int __lookahead_ptr = -1;"
  echo "	private $CTYPE[] __lookaheadw = null;"
  echo "	private int __lookaheadw_ptr = -1;"
  echo "	private boolean __lookahead_ok = true;"
fi

echo
cat field
cat fragment
#cat fragment | replace_action
echo

echo "	$CTYPE _read1($streamsym stream) $tre {"
if [ -s read ]; then
  cat read
elif [ -z "$TYPE" ]; then
  echo '		int c;'
  echo
  echo '		c = stream.read();'
  echo '		return c < 0 ? -1 : c;'
else
  echo "		return ($CTYPE)stream.next();"
fi
echo '	}'

cat << EOF
	private int _read($streamsym rd) $tre {
		$CTYPE c;

		while(true) {
EOF
  [ -n "$ENABLE_BACKTRACK" ] && cat << EOF
			if(__backtrack_sym >= 0) {
				c = __backtrack_sym;
				__backtrack_sym = -1;
				__logprint("Read Backtracking: ", c);
			} else if(unread >= 0) {
EOF
  [ -n "$ENABLE_BACKTRACK" ] || echo '			if(unread >= 0) {'
  cat << EOF
				c = unread;
				unread = -1;
				__logprint("Read unread: ", c);
EOF
  [ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
			} else if(__lookahead_ptr >= 0) {
				if(__lookahead_ptr < __lookahead.length) {
					c = __lookahead[__lookahead_ptr++];
				} else {
					__lookahead = null;
					__lookahead_ptr = -1;
					c = _read(rd);
				}
				__logprint("Read Lookahead: ", c);
EOF
  [ -n "$ENABLE_BACKTRACK" ] && cat << EOF
			} else if(__backtrack_ptr >= 0) {
				if(__backtrack_ptr < __backtrack_len) {
					c = __backtrack[__backtrack_ptr++];
				} else {
					__backtrack = null;
					__backtrack_ptr = __backtrack_wptr = -1;
					c = _read(rd);
				}
				__logprint("Read Backtracking: ", c);
EOF
echo "			} else if((c = _read1(rd)) != $nullsym) {"
[ -n "$ENABLE_BACKTRACK" ] && echo '				__write_backtrack(c);'
echo '				__logprint("Read: ", c);'
[ -z "$READSLOW" ] && cat << EOF
			} else {
				__logprint("Read end-of-file");
			}
EOF
[ -z "$READSLEEP" ] || [ $READSLEEP -lt 0 ] || slp="__sleep($READSLEEP);"
[ -z "$READSLOW" ] || cat << EOF
			} else {
				$slp
				continue;
			}
EOF
cat << EOF
			return c;
		}
	}

	void UNGET($CTYPE c) {
		unread = c;
		__logprint("Set unread: ", c);
	}
EOF

[ -z "READSLOW" ] || cat << EOF
	private void __sleep(int m) {
		try {
			Thread.sleep(m);
		} catch(InterruptedException e) {
			throw new RuntimeException(e);
		}
	}
EOF

[ -n "$ENABLE_LOG" ] && cat << EOF

	private void __logprint(String s, $CTYPE c) {
		if(__log != null) {
			__log.print(s);
			if(c == '\n') {
				__log.println("\\n");
			} else if(c == '\t') {
				__log.println("\\t");
			} else if(c == '\r') {
				__log.println("\\r");
			} else if(c >= 0 && c <= 0x1f) {
				__log.print("\\x");
				__log.println(Integer.toString(c));
			} else if(c >= 0x7f && c <= 0x9f) {
				__log.print("\\x");
				__log.println(Integer.toString(c));
			} else {
				__log.println((char)c);
			}
		}
	}
EOF
  [ -n "$ENABLE_LOG" ] || cat << EOF

	private void __logprint(String s, $CTYPE c) {
	}
EOF

[ -n "$ENABLE_LOG" ] && cat << EOF

	private static String __logfile() {
		return __LOGFILE + "." + System.currentTimeMillis() + ".log";
	}

	private void __logopen() {
		try {
			if(__LOGFILE != null) {
				__log = new java.io.PrintStream(__logfile());
			}
		} catch(java.io.IOException e) {
			throw new RuntimeException(e);
		}
	}

	private void __logprint(String s) {
		if(__log != null)  __log.println(s);
	}

	private void __logclose() {
		if(__log != null)  __log.close();
		__log = null;
	}

	private void __puttrace(java.io.PrintStream p) {
		int s = STATE;
		Engine g;

		p.println("Stack trace:");
		for(int k = __stk.length - 1; k >= 0; k--) {
			g = __stk[k];
			p.println("\tAutomaton: " + g + ", state: " + s);
			if(k > 0)  s = __sts[k];
		}
	}

	private void __puttrace() {
		if(__PUTTRACE) {
			__puttrace(System.err);
			if(__log != null)  __puttrace(__log);
		}
	}

EOF
[ -n "$ENABLE_LOG" ] || cat << EOF

	private void __logopen() {
	}

	private void __logprint(String s) {
	}

	private void __logclose() {
	}

	private void __puttrace() {
	}

EOF

if [ -n "$ENABLE_LOOKAHEAD" ]
then
  cat << EOF
	void LOOKAHEAD($CTYPE c) {
		$CTYPE[] a;

		if(__lookaheadw == null) {
			__lookahead_state = STATE;
			__lookaheadw = new $CTYPE[72];
			__lookaheadw_ptr = 0;
			__lookaheadw[__lookaheadw_ptr++] = c;
		} else if(__lookaheadw_ptr < __lookaheadw.length) {
			__lookaheadw[__lookaheadw_ptr++] = c;
		} else {
			a = new $CTYPE[__lookaheadw.length * 2];
			System.arraycopy(__lookaheadw, 0, a, 0,
					__lookaheadw.length);
			__lookaheadw = a;
			__lookaheadw[__lookaheadw_ptr++] = c;
		}
	}

	private void __copy_lookahead(int p) {
		$CTYPE[] a;

		if(__lookahead == null) {
			a = new $CTYPE[__lookaheadw_ptr];
		} else if(__lookaheadw_ptr < __lookahead.length) {
			a = __lookahead;
		} else {
			a = new $CTYPE[__lookaheadw_ptr];
		}
		System.arraycopy(__lookaheadw, 0, a, 0, __lookaheadw_ptr);
		__lookahead = a;
		__lookahead_ptr = p;
		__lookaheadw = null;
		__lookaheadw_ptr = -1;
	}

	void LOOKAHEAD_COMMIT() {
		if(__lookahead_mark < 0) {
			__lookaheadw = null;
			__lookaheadw_ptr = -1;
		} else {
			__copy_lookahead(__lookahead_mark);
		}
		__lookahead_mark = -1;
		__logprint("Commit Lookahead");
	}

	void LOOKAHEAD_RB() {
		__copy_lookahead(0);
		STATE = __lookahead_state;
		__lookahead_ok = false;
		__lookahead_mark = -1;
		__logprint("Rollback Lookahead");
	}

	void LOOKAHEAD_MARK() {
		__lookahead_mark = __lookaheadw_ptr;
	}

EOF
fi

if [ -n "$ENABLE_BACKTRACK" ]
then
  [ -z "$TYPE" ] && bkt='__backtrack_sym != c'
  [ -z "$TYPE" ] || bkt='!c.equals(__backtrack_sym)'
  cat << EOF
	void SET_BACKTRACK($CTYPE c) {
		$CTYPE[] a;
		int[] b;

		if(${bkt}) {
			if(__backtrack_ptr < 0) {
				__backtrack = new $CTYPE[1024];
				__backtrack_wptr = 0;
			} else {
				__backtrack_wptr = __backtrack_ptr;
			}

			if(__backtrack_wsym == null) {
				__backtrack_state = new int[72];
				__backtrack_wsym = new $CTYPE[72];
				__backtrack_ptrs = new int[72];
				__backtrack_cont = new Continuation[72];
				__backtrack_sptr = 0;
			} else if(__backtrack_sptr >= __backtrack_wsym.length) {
				a = __backtrack_wsym;
				b = __backtrack_ptrs;
				__backtrack_wsym = new $CTYPE[a.length];
				__backtrack_ptrs = new int[b.length];
				System.arraycopy(a, 0, __backtrack_wsym, 0, a.length);
				System.arraycopy(b, 0, __backtrack_ptrs, 0, b.length);
			}
			__backtrack_state[__backtrack_sptr] = STATE;
			__backtrack_wsym[__backtrack_sptr] = c;
			__backtrack_ptrs[__backtrack_sptr] = __backtrack_wptr;
			__backtrack_cont[__backtrack_sptr] = GETCC();
			__backtrack_sptr++;
			__backtrack_flg = true;
			__logprint("Set Backtrack: ", c);
		}
	}

	private void __write_backtrack($CTYPE c) {
		$CTYPE[] a;

		if(__backtrack_wptr < 0) {
			// do nothing
		} else if(__backtrack_wptr < __backtrack.length) {
			__backtrack[__backtrack_wptr++] = c;
		} else {
			a = new $CTYPE[__backtrack.length * 2];
			System.arraycopy(__backtrack, 0, a, 0,
					__backtrack.length);
			__backtrack = a;
			__backtrack[__backtrack_wptr++] = c;
		}
	}

	void BACKTRACK_COMMIT() {
		if(--__backtrack_sptr <= 0) {
			__backtrack_state = null;
			__backtrack_wsym = null;
			__backtrack_ptrs = null;
			__backtrack_cont = null;
			__backtrack_sptr = -1;
			__backtrack_wptr = -1;
			__logprint("Commit Backtracking");
		}
	}

	boolean BACKTRACK($CTYPE c) {
		if(__backtrack_wptr >= 0) {
			__backtrack_len = __backtrack_wptr;
			__backtrack_wptr = -1;
			__backtrack_sptr--;
			STATE = __backtrack_state[__backtrack_sptr];
			__backtrack_ptr = __backtrack_ptrs[__backtrack_sptr];
			__backtrack_sym = __backtrack_wsym[__backtrack_sptr];
			SETCC(__backtrack_cont[__backtrack_sptr]);
			__logprint("Rollback Backtracking");
			return true;
		} else {
			return false;
		}
	}

	Continuation GETCC() {
		return new Continuation(STATE, __sts, __stk, __stv, __slen);
	}

	void SETCC(Continuation c) {
		int l;

		STATE = c.STATE;
		l = c.__stk.length;
		l = __stk.length > l ? __stk.length : l * 2;
		__sts = new int[l];
		__stk = new Engine[l];
		System.arraycopy(c.__sts, 0, __sts, 0, c.__sts.length);
		System.arraycopy(c.__stk, 0, __stk, 0, c.__stk.length);
		__slen = c.__sts.length;
	}

EOF
fi

for i in $SUBAUTOMATA
do
  ch1=`replace_strange_char $i`
  echo
  echo "	private int ${ch1}_step($streamsym __rd, $CTYPE" ' $c)' " $tre {"
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '		boolean __l__ = __lookahead_ok;'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '		__lookahead_ok = true;'
  echo '		switch(STATE) {'
  print_states $i
  echo '		}'
  echo '		return 0;'
  echo '	}'
  echo
  echo "	private boolean ${ch1}_accepted() {"
  print_accepts $i
  echo '	}'
  echo
  echo "	int ${ch1}_execaction($CTYPE" ' $c) {'
  echo '		switch(STATE) {'
  print_actions $i
  echo '		}'
  echo '		return 1;'
  echo '	}'
  echo
  echo "	boolean ${ch1}_isend() {"
  print_isend $i
  echo '	}'
  cat << EOF

	private final Engine ENGINE_${ch1} = new Engine() {

		int step($streamsym __rd, $CTYPE c) $tre {
			return ${ch1}_step(__rd, c);
		}

		boolean accepted() {
			return ${ch1}_accepted();
		}

		int execaction($CTYPE c) {
			return ${ch1}_execaction(c);
		}

		boolean isend() {
			return ${ch1}_isend();
		}

		int recover(Exception e) {
EOF
    print_recover $i
    cat << EOF
		}

		int deadState() {
EOF
    print_deadstate $i
    cat << EOF
		}

		int stateSize() {
EOF
    print_statesize $i
    cat << EOF
		}

		int finallyState() {
EOF
    print_finallystate $i
    cat << EOF
		}

		public String toString() {
			return "${ch1}";
		}

	};
EOF
done

cat << EOF

	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()];
	}
EOF

ch1=`replace_strange_char ${MAINNAME}`
echo
cat << EOF
	private int _parse($streamsym rd, int x, Boolean rt) $tre {
		boolean b = false;
		int c = x, a;
		Engine en;

		b = __stk[__slen - 1].accepted();
		if(rd == null) {
			throw new RuntimeException("can not recurse");
		} else if(rt.booleanValue()) {
			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;
			case NINA_YIELD:
				__logprint("machine yielded: ", c);
				return NINA_YIELD;
			}
		}

		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;
					case NINA_YIELD:
						__logprint("machine yielded: ", c);
						return NINA_YIELD;
					}
EOF
  [ -n "$ENABLE_BACKTRACK" ] && cat << EOF
					if(__backtrack_flg) {
						__write_backtrack(c);
						__backtrack_flg = false;
					}
EOF
  cat << EOF
				} else if(a < 0) {
					__logprint("entering " + __stk[__slen - 1]);
					return c;
				} else if(b) {
					__logprint("accept " + __stk[__slen - 1]);
					UNGET(c);
					return NINA_ACCEPT;
EOF
  [ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
				} else if(__lookaheadw_ptr >= 0) {
					__logprint("match failed: try lookahead: ", c);
					LOOKAHEAD(c);
					LOOKAHEAD_RB();
					b = en.accepted();
EOF
  [ -n "$ENABLE_BACKTRACK" ] && cat << EOF
				} else if(BACKTRACK(c)) {
					__logprint("match failed: try backtracking: ", c);
EOF
  cat << EOF
				} else {
					__logprint("match failed: ", c);
					__puttrace();
					UNGET(c);
					return NINA_FAIL;
				}
			} while((c = _read(rd)) >= 0);
			if(!b)  throw new ${TOKENERROR}();
			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);
	}

	${API_ACCESS_MODIFIER} boolean parse($streamsym rd, Engine entry) $tre {
		int c = -1;
		Boolean b = Boolean.FALSE;

		__logopen();
		try {
			if(__slen == 0) {
				b = Boolean.TRUE;
				__stkpush(0, entry);
			}

			ot: while(true) {
				try {
					if((c = _parse(rd, c, b)) >= 0) {
						// do nothing
					} else if(c == NINA_FAIL) {
						while((STATE = getdeadstate()) < 0) {
							if((b = execfinally()) != null)  break ot;
							if(__slen-- <= 1) {
								throw new TokenException();
							}
						}
					} else if(c == NINA_HALT_ACCEPT) {
						if((b = execfinally()) != null)  break;
						__slen = 0;
						b = Boolean.TRUE;  break;
					} else if(c == NINA_HALT_REJECT) {
						if((b = execfinally()) != null)  break;
						__slen = 0;
						b = Boolean.FALSE;  break;
					} else if(c == NINA_YIELD) {
						return false;
					} else if(__slen > 1) {
						if((b = execfinally()) != null)  break;
						STATE = __sts[--__slen];
					} else {
						if((b = execfinally()) != null)  break;
						b = new Boolean(__stk[--__slen].accepted());
						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;
					}
				}
				b = Boolean.TRUE;
			}
			if(!b.booleanValue())  throw new TokenException();
			return b.booleanValue();
		} finally {
			__logclose();
		}
	}

	${API_ACCESS_MODIFIER} boolean parse($streamsym rd) $tre {
		return parse(rd, ENGINE_${ch1});
	}

	${API_ACCESS_MODIFIER} static boolean parseAll($streamsym rd) $tre {
		${CLASSNAME} o = new ${CLASSNAME}();

		return o.parse(rd);
	}

	${API_ACCESS_MODIFIER} void setStream($streamsym rd) {
		if(befstream != null) {
			throw new IllegalStateException();
		}
		yieldObject = befstream = rd;
	}

	${API_ACCESS_MODIFIER} Object parseNext() $tre {
		Object o;

		if(befstream == null) {
			throw new IllegalStateException();
		} else if(yieldObject == null) {
			return null;
		} else if(parse(befstream, ENGINE_${ch1})) {
			if(yieldObject == null)  throw new NullPointerException();
			o = yieldObject;  yieldObject = null;
			return o;
		} else {
			if(yieldObject == null)  throw new NullPointerException();
			return yieldObject;
		}
	}

EOF

[ -z "$TYPE" ] && [ -n "$ENABLE_MATCHER" ] && cat << EOF
	${API_ACCESS_MODIFIER} boolean matches(String s) {
		int n;

		try {
			STATE = 0;  matched = null;
			for(n = 0; n < s.length(); n++) {
				if(${ch1}_step(null, s.charAt(n)) == 0) {
					return false;
				}
			}
		} catch(java.io.IOException e) {
			throw new RuntimeException(e);
		}

		if(${ch1}_accepted()) {
			matched = s;
			return true;
		} else {
			return false;
		}
	}

	${API_ACCESS_MODIFIER} boolean lookingAt(String s) {
		StringBuffer b = new StringBuffer();
		int n;

		try {
			STATE = 0;  matched = null;
			for(n = 0; n < s.length(); n++) {
				if(${ch1}_accepted()) {
					matched = b.toString();
				}

				if(${ch1}_step(null, s.charAt(n)) > 0) {
					// do nothing
				} else {
					return matched != null;
				}
				b.append(s.charAt(n));
			}
		} catch(java.io.IOException e) {
			throw new RuntimeException(e);
		}

		if(${ch1}_accepted()) {
			matched = b.toString();
			return true;
		} else {
			return matched != null;
		}
	}

	${API_ACCESS_MODIFIER} boolean find(String s) {
		StringBuffer b;
		int n, k;

		try {
			STATE = 0;  matched = null;
			for(k = 0; k < s.length(); k++) {
				b = new StringBuffer();
				for(n = k; n < s.length(); n++) {
					if(${ch1}_accepted()) {
						matched = b.toString();
					}

					if(${ch1}_step(null, s.charAt(n)) > 0) {
						// do noting
					} else if(matched == null) {
						break;
					} else {
						return true;
					}
					b.append(s.charAt(n));
				}

				if(${ch1}_accepted()) {
					matched = b.toString();
					return true;
				} else if(matched != null) {
					return true;
				} else {
					STATE = 0;
				}
			}
			return false;
		} catch(java.io.IOException e) {
			throw new RuntimeException(e);
		}
	}

	${API_ACCESS_MODIFIER} String group() {
		return matched;
	}

EOF

[ -z "$TYPE" ] && cat << EOF
	${API_ACCESS_MODIFIER} boolean parse(java.io.InputStream rd) $tre {
		return parse(new java.io.InputStreamReader(rd));
	}

	${API_ACCESS_MODIFIER} static boolean parseAll(
			java.io.InputStream rd) $tre {
		return parseAll(new java.io.InputStreamReader(rd));
	}

EOF
echo '}'
