#! /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.
#
if [ -z "$TYPE" ]
then
  nullsym='-1'
  [ -z "$NEWLINEMODE" ] && streamsym='java.io.Reader'
  [ -z "$NEWLINEMODE" ] || streamsym='java.io.InputStream'
  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'
  tre='throws java.io.IOException'
else
  nullsym='null'
  nullsyml='null'
  streamsym='java.util.Iterator'
  tre=''
fi

cat << EOF

private class LR${1}Engine extends Engine {

	//
	private static final int INITSIZE = 765;
	private static final int ACCEPTSTATE  = 0x40000001;
	private static final int LRSTATEMASK  = 0x3fff0000;
	private static final int LRSTATESHIFT = 16;
	private static final int FASTATEMASK  = 0x0000ffff;

	//
	private Object[] __synarray = null;
	private int __synptr;
	private boolean __ok = false;
	private Object __token = null;

EOF
lex=`echo_lexer $1`
if [ -z "$lex" ]
then
  echo
elif [ -n "$TYPE" ]
then
  raiseerror 'mustcharacter'
else
  nina_template.nfa.java.sh $lex "LR${1}Lexer"
  cat << EOF
	private LR${1}Lexer __lexer = new LR${1}Lexer();
	private StringBuffer __buf = new StringBuffer();

	int stepLex(int state, $CTYPE a) {
		__lexer.STATE = state;
		return __lexer._step(a) != 0 ? __lexer.STATE : -1;
	}

	boolean isAcceptedLex(int state) {
		__lexer.STATE = state;
		return __lexer._accepted();
	}

	boolean isDeadLex(int state) {
		__lexer.STATE = state;
		return __lexer._isdead();
	}

	void appendBufferLex($CTYPE a) {
		__buf.appendCodePoint(a);
	}

	void initBufferLex() {
		__buf = new StringBuffer();
		__ok = isAcceptedLex(0);
	}

	Object getTokenLex(int state) {
		__lexer.STATE = state;
		return __lexer._gettoken(__buf);
	}

EOF
fi

cat_definition $1

echo '	private int getShift(Object __t) {'
echo '		switch(STATE >> LRSTATESHIFT) {'
put_shift $1
echo '		}'
echo '		return -1;'
echo '	}'
echo

echo '	@SuppressWarnings("unchecked")'
echo '	private int getReduce(Object __t) {'
echo '		Object $$;'
echo '		int __k;'
echo
echo '		switch(STATE >> LRSTATESHIFT) {'
put_reduce $1
cat << EOF
		}
		return -1;
	}

	private boolean isAccept(Object __t) {
		switch(STATE >> LRSTATESHIFT) {
EOF
put_accept $1
cat << EOF
		}
		return false;
	}

	private int getGoto(int __stateID, int __n) {
		switch(__stateID >> LRSTATESHIFT) {
EOF
put_goto $1
cat << EOF
		}
		throw new RuntimeException();
	}

	private boolean __eq(Object o, Object p) {
		return o != null && o.equals(p);
	}

	boolean __eq(Object o, char p) {
		return o != null && o.equals(Character.valueOf(p));
	}

	private void _push(int k, Object s) {
		Object[] a;

		if(__synptr >= __synarray.length) {
			a = new Object[INITSIZE * 2 + 1];
			System.arraycopy(__synarray, 0, a, 0, __synarray.length);
			__synarray = a;
		}
		__synarray[__synptr++] = s;
		__synarray[__synptr++] = k;
	}

	private void _pop(int num) {
		if(__synptr < num * 2)  throw new RuntimeException();
		__synptr -= num * 2;
	}

	private int _refsyn() {
		return ((Integer)__synarray[__synptr - 1]).intValue();
	}

	private Object _refv(int n) {
		return __synarray[__synptr - n * 2];
	}

EOF

[ -z "$lex" ] && cat << EOF
	int step($CTYPE a) $tre {
		int k;

		if(STATE == ACCEPTSTATE) {
			return 0;
		} else if(isAccept(a)) {
			UNGET(a);
			STATE = ACCEPTSTATE;
			return 1;
		} else if((k = getShift(a)) >= 0) {
			_push(k << LRSTATESHIFT, a);
			STATE = k << LRSTATESHIFT;
			return 1;
		} else if((k = getReduce(a)) >= 0) {
			UNGET(a);
			STATE = k << LRSTATESHIFT;
			return 1;
		} else {
			return 0;
		}
	}
EOF
[ -z "$lex" ] || cat << EOF
	private int _steptoken() {
		int k;

		if(isAccept(__token)) {
			STATE = ACCEPTSTATE;
			return 1;
		} else if((k = getShift(__token)) >= 0) {
			_push(k << LRSTATESHIFT, __token);
			STATE = k << LRSTATESHIFT;
			__token = null;
			return 1;
		} else if(k < -1) {
			_push((-k - 2) << LRSTATESHIFT, __token);
			STATE = 0;
			__token = null;
			return NINA_ACCEPT;
		} else if((k = getReduce(__token)) >= 0) {
			STATE = k << LRSTATESHIFT;
			return 1;
		} else {
			__token = null;
			return 0;
		}
	}

	int step($CTYPE a) $tre {
		int k;

		if(STATE == ACCEPTSTATE) {
			return 0;
		} else if(__token != null) {
			UNGET(a);
			return _steptoken();
		} else if((k = stepLex(STATE & FASTATEMASK, a)) < 0) {
			if(__ok) {
				UNGET(a);
				__token = getTokenLex(STATE & FASTATEMASK);
				STATE = STATE & LRSTATEMASK;
				initBufferLex();
				return 1;
			} else {
				return 0;
			}
		} else {
			STATE = (STATE & LRSTATEMASK) | k;
			appendBufferLex(a);
			if(__ok = isAcceptedLex(STATE & FASTATEMASK)) {
				if(isDeadLex(STATE & FASTATEMASK)) {
					__token = getTokenLex(STATE & FASTATEMASK);
					STATE = STATE & LRSTATEMASK;
					initBufferLex();
					return _steptoken();
				}
			} else if(isDeadLex(STATE & FASTATEMASK)) {
				return 0;
			}
			return 1;
		}
	}
EOF

cat << EOF

		boolean accepted() {
			return STATE == ACCEPTSTATE;
		}

		int execaction($CTYPE c) {
			if(c == NINA_BEGIN && __synarray == null) {
				__synarray = new Object[INITSIZE];
EOF
put_grammar_initial $1
cat << EOF
				__synptr = 1;
			}
			return 1;
		}

		boolean isend() {
			return false;
		}

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

		int deadState() {
			return -1;
		}

		int stateSize() {
			return 1;
		}

		int finallyState() {
			return -1;
		}

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

EOF
echo '};'
