#! /bin/jsh
#
# 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.
#
putlicense_cstyle.sh

[ -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'
  [ -z "$NEWLINEMODE" ] && streamsym='java.io.Reader'
  [ -z "$NEWLINEMODE" ] || streamsym='java.io.InputStream'
  streamfile='java.io.FileInputStream'
  [ -z "$NEWLINEMODE" ] && streamins='java.io.InputStreamReader'
  tre='throws java.io.IOException'
elif [ -z "$LTYPE" ]
then
  nullsym='null'
  nullsyml='-1'
  [ -z "$NEWLINEMODE" ] && streamsym='java.io.Reader'
  [ -z "$NEWLINEMODE" ] || streamsym='java.io.InputStream'
  streamfile='java.io.FileInputStream'
  [ -z "$NEWLINEMODE" ] && streamins='java.io.InputStreamReader'
  tre='throws java.io.IOException'
else
  nullsym='null'
  nullsyml='null'
  streamsym='java.util.Iterator'
  tre=''
fi

buffer='$buffer'
int='$int'
bigint='$bigint'
num='$num'
indent='$indent'
indentBefore='$indentBefore'

echo '/**'
awk '{ printf(" * %s\n", $0) }' description
echo ' */'
cat << EOF
public $ABSTRACT class ${CLASSNAME}${TEMPLATE} $extends $implements {

	/* @@@-PARSER-CODE-START-@@@ */
	static class TokenException extends RuntimeException {
	}

	static abstract class Engine {
		abstract int step($CTYPE c) $tre;
		abstract boolean accepted();
		abstract boolean isDead();
		abstract boolean isEmptyTransition();
		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

[ -z "$TYPE" ] && echo '	static final int INVALIDTOKEN = 0x7fff7fff;'
[ -z "$TYPE" ] && echo '	private static final int NINA_BEGIN = -2;'
[ -z "$TYPE" ] || echo '	static final Object INVALIDTOKEN = new Object();'
[ -z "$TYPE" ] || echo '	private static final Object NINA_BEGIN = new Object();'
cat << EOF
	private static final int NINA_EOF = -1;
	private static final int NINA_ACCEPT = -8;
	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_STACKLEN = 72;
	static final int NINA_DISCARDSTATE = 0x40000000;
	static final int INITIAL = 0;
	static final int INDENT = 1;

	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;

	$VALTYPE _;
	java.util.List<$VALTYPE> _l;
	Object yieldObject;
	Throwable exception;

	StringBuffer $buffer;
	int $int;
	java.math.BigInteger $bigint;
	Number $num;

	java.util.Stack<$streamsym> streamStack =
			new java.util.Stack<$streamsym>();

	void _initlist() {
		_l = new java.util.ArrayList<$VALTYPE>();
	}

	void _addlist($VALTYPE x) {
		_l.add(x);
	}

EOF

[ -n "$ENABLE_INDENT" ] && cat << EOF
	private int $indent;
	private int $indentBefore;
	private boolean __is_line_begin = true;
	private int __indent_unread = -1;

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
[ -n "$LEXER" ] && echo "	private $LCTYPE _unreadl = $nullsyml;"
[ -n "$LEXER" ] || echo "	private $CTYPE _unreadl = $nullsym;"

function _putreadf {
  echo
  echo "	void INCLUDE($streamsym rd) {"
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '		if(__lookahead_ptr >= 0) {'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '			throw new IllegalStateException();'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '		}'
  [ -n "$ENABLE_BACKTRACK" ] && echo '		if(__backtrack_ptr >= 0) {'
  [ -n "$ENABLE_BACKTRACK" ] && echo '			throw new IllegalStateException();'
  [ -n "$ENABLE_BACKTRACK" ] && echo '		}'
  [ -n "$ENABLE_INDENT"    ] && echo '		if(__indent_unread >= 0) {'
  [ -n "$ENABLE_INDENT"    ] && echo '			throw new IllegalStateException();'
  [ -n "$ENABLE_INDENT"    ] && echo '		}'
  echo "		streamStack.push(rd);"
  echo "	}"
  echo
  if [ -n "$streamfile" ]; then
    echo "	void INCLUDE(String name) $tre {"
    echo '		java.io.InputStream ins;'
    echo
    echo "		ins = new ${streamfile}(name);"
    [ -n "$streamins" ] && echo "		INCLUDE(new ${streamins}(ins));"
    [ -n "$streamins" ] || echo "		INCLUDE(ins);"
    echo '	}'
    echo
  fi

  [ -n "$ENABLE_INDENT" ] || echo "	$2 $1() $tre {"
  [ -n "$ENABLE_INDENT" ] && echo "	$2 ${1}_1() $tre {"
  if [ -s read ]; then
    cat read
  elif [ -z "$3" ]; then
    cat << EOF
		int c;

		while(streamStack.size() > 0) {
			if((c = streamStack.peek().read()) >= 0) {
				return c;
EOF
    [ -n "$streamfile" ] && echo '			} else if(streamStack.size() > 1) {'
    [ -n "$streamfile" ] && echo '				streamStack.pop().close();'
    cat << EOF
			} else {
				streamStack.pop();
			}
		}
		return NINA_EOF;
EOF
  else
    echo "		return ($2)stream.next();"
  fi
  echo '	}'

  if [ -n "$ENABLE_INDENT" ]; then
    cat << EOF
	$2 $1() $tre {
		int c;

		if(__indent_unread >= 0) {
			c = __indent_unread;
			__indent_unread = -1;
		} else if(!__is_line_begin) {
			c = ${1}_1();
		} else {
			$indent = 0;
			while((c = ${1}_1()) == ' ' || c == '\t') {
				$indent++;
			}
			__indent_unread = c;
			c = c >= 0 ? INDENT : c;
		}

		if(__is_line_begin = c == '\n') {
			$indentBefore = $indent;
		}
		return c;
	}
EOF
  fi
}

if [ -z "$LEXER" ]; then
  _putreadf _read1l "$CTYPE" "$TYPE"
  cat << EOF

	$CTYPE _read1() $tre {
		$CTYPE c;

		if(_unreadl != $nullsym) {
			c = _unreadl;
			_unreadl = $nullsym;
EOF
  if [ -z "$TYPE" -a -z "$NEWLINEMODE" ]; then
    cat <<  EOF
		} else if((c = _read1l()) == '\r' && (c = _read1l()) != '\n') {
			_unreadl = c;
			c = '\r';
		}
EOF
  else
    cat << EOF
		} else {
			c = _read1l();
		}
EOF
  fi
  cat << EOF
		return c;
	}
EOF
else
  _putreadf _read1l "$LCTYPE" "$LTYPE"
  nina_template.nfa.java.sub2.sh "$LEXER" "$LCTYPE" "$LTYPE" "$LEXER" "$LCTYPE"
  cat << EOF

	$LCTYPE _read1ul() $tre {
		$LCTYPE c;

		if(_unreadl != $nullsyml) {
			c = _unreadl;
			_unreadl = $nullsyml;
EOF
  if [ -z "$LTYPE" -a -z "$NEWLINEMODE" ]; then
    cat <<  EOF
		} else if((c = _read1l()) == '\r' && (c = _read1l()) != '\n') {
			_unreadl = c;
			c = '\r';
		}
EOF
  else
    cat << EOF
		} else {
			c = _read1l();
		}
EOF
  fi
  cat << EOF
		return c;
	}

	$CTYPE _read1() $tre {
		StringBuffer b = new StringBuffer();
		Object o = null;
		boolean f;
		$LCTYPE a;
		int s;

		s = STATE;  STATE = 0;
		f = ${LEXER}_accepted();
		while(o == null) {
			if((a = _read1ul()) == $nullsyml) {
				o = ${LEXER}_gettoken(b);
				break;
			} else if(${LEXER}_step(a) == 0) {
				if(f) {
					_unreadl = a;
					o = ${LEXER}_gettoken(b);
					b = new StringBuffer();
					STATE = 0;
					f = ${LEXER}_accepted();
				} else {
					o = INVALIDTOKEN;  break;
				}
			} else {
				b.appendCodePoint(a);
				if(f = ${LEXER}_accepted()) {
					if(${LEXER}_isdead()) {
						o = ${LEXER}_gettoken(b);
						b = new StringBuffer();
						STATE = 0;
						f = ${LEXER}_accepted();
					}
				} else if(${LEXER}_isdead()) {
					o = INVALIDTOKEN;  break;
				}
			}
		}
		STATE = s;
		return o;
	}
EOF
fi

cat << EOF

	private $CTYPE _read() $tre {
		$CTYPE c;

		while(true) {
EOF
[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
			if(__backtrack_sym != $nullsym) {
				c = __backtrack_sym;
				__backtrack_sym = -1;
				__logprint("Read Backtracking: ", c);
			} else if(unread != $nullsym) {
EOF
[ -n "$ENABLE_BACKTRACK" ] || echo "			if(unread != $nullsym) {"
cat << EOF

				c = unread;
				unread = $nullsym;
				__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();
				}
				__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();
				}
				__logprint("Read Backtracking: ", c);
EOF
echo "			} else if((c = _read1()) != $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

	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_ptr > 0) {
			a = new $CTYPE[__lookahead.length - __lookahead_ptr];
			System.arraycopy(__lookahead, __lookahead_ptr, a, 0, a.length);
			__lookahead = 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($CTYPE" ' $c)' " $tre {"
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '		boolean __l__ = __lookahead_ok;'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo
  [ -n "$DYNAMICAUTOMATA" ]  && echo '		if((STATE & NINA_DISCARDSTATE) != 0)  return 1;'
  [ -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) {'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '		if((STATE & NINA_DISCARDSTATE) != 0) {'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '			if($c >= 0) {'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '				STATE = STATE & ~NINA_DISCARDSTATE;'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '				__lookahead_ok = false;'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '			}'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '			return 1;'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '		}'
  [ -n "$DYNAMICAUTOMATA" ]  && echo
  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($CTYPE c) $tre {
			return ${ch1}_step(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
		}

		boolean isDead() {
EOF
    print_deads $i
    cat << EOF
		}

		boolean isEmptyTransition() {
EOF
    print_empty_transitions $i
    cat << EOF
		}

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

	};
EOF
done

for i in $LRPARSERS
do
  y.tab.java.sh $i
done

c='$c'
[ -n "$DYNAMICAUTOMATA" ] && cat << EOF

	private boolean __startWith(char[] a, int c) {
		return a.length > 0 && a[0] == c;
	}

	private boolean __startWith(String a, int c) {
		return a.length() > 0 && a.charAt(0) == c;
	}

	private boolean __startWith(StringBuffer a, int c) {
		return a.length() > 0 && a.charAt(0) == c;
	}

	private class DynamicEngine extends Engine {

		private int ptr = 0;
		private int oldst;
		private char[] a;

		DynamicEngine(char[] a, int oldst) {
			this.oldst = oldst;
			this.a = a;
		}

		DynamicEngine(String a, int ost) {
			this(a.toCharArray(), ost);
		}

		DynamicEngine(StringBuffer a, int ost) {
			this(a.toString().toCharArray(), ost);
		}

		int step($CTYPE $c) $tre {
			if(ptr >= a.length) {
				LOOKAHEAD_COMMIT();
				return 0;
			} else if(a[ptr] == $c) {
				LOOKAHEAD($c);
				ptr++;
				return 1;
			} else {
				LOOKAHEAD($c);
				LOOKAHEAD_RB();
				__sts[__slen - 1] = NINA_DISCARDSTATE | oldst;
				return 0;
			}
		}

		boolean accepted() {
			return true;
		}

		boolean isDead() {
			return false;
		}

		boolean isEmptyTransition() {
			return false;
		}

		int execaction($CTYPE c) {
			return 1;
		}

		boolean isend() {
			return ptr >= a.length;
		}

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

		int deadState() {
			return -1;
		}

		int stateSize() {
			return a.length;
		}

		int finallyState() {
			return -1;
		}

		public String toString() {
			return "dynamic:" + new String(a);
		}

	}
EOF

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 $CTYPE _parse($CTYPE x, Boolean rt, boolean skip,
			int[] st) $tre {
		boolean b = false, p = skip;
		$CTYPE c = x;
		Engine en;
		int a;

		b = __stk[__slen - 1].accepted();
		if(rt.booleanValue()) {
			switch(__stk[__slen - 1].execaction(NINA_BEGIN)) {
			case NINA_ACCEPT:
				__logprint("accept " + __stk[__slen - 1]);
				st[0] = NINA_ACCEPT;  return $nullsym;
			case NINA_FAIL:
				__logprint("match failed: begin");
				__puttrace();
				st[0] = NINA_FAIL;  return $nullsym;
			case NINA_HALT_ACCEPT:
				__logprint("machine halted: begin");
				st[0] = NINA_HALT_ACCEPT;  return $nullsym;
			case NINA_HALT_REJECT:
				__logprint("machine halted: begin");
				st[0] = NINA_HALT_REJECT;  return $nullsym;
			case NINA_YIELD:
				__logprint("machine yielded: ", c);
				st[0] = NINA_YIELD;  return $nullsym;
			}
		}

		try {
			do {
				en = __stk[__slen - 1];
				if(p) {
					p = false;
				} else if((a = en.step(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);
						st[0] = NINA_ACCEPT;  return $nullsym;
					case NINA_FAIL:
						__logprint("match failed: ", c);
						__puttrace();
						UNGET(c);
						st[0] = NINA_FAIL;  return $nullsym;
					case NINA_HALT_ACCEPT:
						__logprint("machine halted: ", c);
						st[0] = NINA_HALT_ACCEPT;  return $nullsym;
					case NINA_HALT_REJECT:
						__logprint("machine halted: ", c);
						st[0] = NINA_HALT_REJECT;  return $nullsym;
					case NINA_YIELD:
						__logprint("machine yielded: ", c);
						st[0] = NINA_YIELD;  return $nullsym;
					}
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);
					st[0] = NINA_ACCEPT;  return $nullsym;
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 if(c == $nullsym) {
					if(!b)  throw new ${TOKENERROR}();
					st[0] = NINA_ACCEPT;  return $nullsym;
				} else {
					__logprint("match failed: ", c);
					__puttrace();
					UNGET(c);
					st[0] = NINA_FAIL;  return $nullsym;
				}

				if(__stk[__slen - 1].isEmptyTransition()) {
					// do nothing
				} else if(!__stk[__slen - 1].isDead()) {
					c = _read();
				} else if(b) {
					__logprint("accept " + __stk[__slen - 1]);
					st[0] = NINA_ACCEPT;  return $nullsym;
EOF
  [ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
				} else if(__lookaheadw_ptr >= 0) {
					__logprint("match failed: try 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();
					st[0] = NINA_FAIL;  return $nullsym;
				}
			} while(true);
		} 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(Engine entry) $tre {
		Boolean b = Boolean.FALSE;
		int[] a = new int[1];
		boolean skip = true;
		$CTYPE c = 0;

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

			ot: while(true) {
				try {
					if((c = _parse(c, b, skip, a)) != $nullsym) {
						skip = false;
					} else if(a[0] == NINA_FAIL) {
						while((STATE = getdeadstate()) < 0) {
							if((b = execfinally()) != null)  break ot;
							if(__slen-- <= 1) {
								throw new TokenException();
							}
						}
						skip = true;
					} else if(a[0] == NINA_HALT_ACCEPT) {
						if((b = execfinally()) != null)  break;
						__slen = 0;
						b = Boolean.TRUE;  break;
					} else if(a[0] == NINA_HALT_REJECT) {
						if((b = execfinally()) != null)  break;
						__slen = 0;
						b = Boolean.FALSE;  break;
					} else if(a[0] == NINA_YIELD) {
						return false;
					} else if(__slen > 1) {
						if((b = execfinally()) != null)  break;
						STATE = __sts[--__slen];
						skip = true;
					} 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 {
		streamStack.push(rd);
		return parse(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(streamStack.size() == 0) {
			throw new IllegalStateException();
		}
		yieldObject = rd;
		streamStack.push(rd);
	}

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

		if(streamStack.size() == 0) {
			throw new IllegalStateException();
		} else if(yieldObject == null) {
			return null;
		} else if(parse(ENGINE_${ch1})) {
			if(yieldObject == null)  throw new NullPointerException();
			o = yieldObject;  yieldObject = null;
			return o;
		} else {
			if(yieldObject == null)  throw new NullPointerException();
			return yieldObject;
		}
	}

	static void puts(String s) {
		System.out.println(s);
	}

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" -o -z "$LTYPE" ] && [ -z "$NEWLINEMODE" ] && 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

for i in $SUBAUTOMATA
do
  print_constants $i
done
echo '	/* @@@-PARSER-CODE-END-@@@ */'

cat fragment
#cat fragment | replace_action

[ -n "$PUTMAIN" ] && cat << EOF
	public static void main(String[] args) throws Exception {
		parseAll(System.in);
	}
EOF
[ -n "$PUTPROMPT" ] && cat << EOF
	public static void main(String[] args) throws Exception {
		String s;

		while((s = System.console().readLine("> ")) != null) {
			try {
				parseAll(new java.io.StringReader(s));
			} catch(TokenException e) {
				System.out.println("syntax error");
			}
		}
	}
EOF

echo '}'
