#! /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='System.IO.TextReader'
  [ -z "$NEWLINEMODE" ] || streamsym='System.IO.Stream'
  [ -z "$NEWLINEMODE" ] && readfn='Read'
  [ -z "$NEWLINEMODE" ] || readfn='ReadByte'
elif [ -z "$LTYPE" ]
then
  nullsym='null'
  nullsyml='-1'
  [ -z "$NEWLINEMODE" ] && streamsym='System.IO.TextReader'
  [ -z "$NEWLINEMODE" ] || streamsym='System.IO.Stream'
  [ -z "$NEWLINEMODE" ] && readfn='Read'
  [ -z "$NEWLINEMODE" ] || readfn='ReadByte'
else
  nullsym='null'
  nullsyml='null'
  streamsym='System.Collections.IEnumerator'
fi

cat << EOF

class LR${1}Engine : Engine {

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

	//
	private ${CLASSNAME}${TEMPLATE} __this__;
	private object[] __synarray = null;
	private int __synptr;
	private System.Text.StringBuilder __buf = new System.Text.StringBuilder();
	private bool __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.cs.sh $lex "LR${1}Lexer"
  cat << EOF

	int StepLex(int state, int a)
	{
		int s = __this__.STATE;
		int r;

		__this__.STATE = state;
		r = _Step(a) != 0 ? __this__.STATE : -1;
		__this__.STATE = s;
		return r;
	}

	bool IsAcceptedLex(int state)
	{
		int s = __this__.STATE;
		bool r;

		__this__.STATE = state;
		r = _Accepted();
		__this__.STATE = s;
		return r;
	}

	bool IsDeadLex(int state)
	{
		int s = __this__.STATE;
		bool r;

		__this__.STATE = state;
		r = _Isdead();
		__this__.STATE = s;
		return r;
	}

	object GetTokenLex(int state, string b)
	{
		int s = __this__.STATE;
		object r;

		__this__.STATE = state;
		r = _Gettoken(new System.Text.StringBuilder(b));
		__this__.STATE = s;
		return r;
	}

EOF
fi
cat_definition $1

cat << EOF

	public LR${1}Engine(${CLASSNAME}${TEMPLATE} o)
	{
		__this__ = o;
	}

	private int GetShift(object __t)
	{
		switch(__this__.STATE >> LRSTATESHIFT)
		{
EOF
put_shift $1
cat << EOF
		}
		return -1;
	}

	private int GetReduce(object __t)
	{
		Object __b__;
		int __k;

		switch(__this__.STATE >> LRSTATESHIFT)
		{
EOF
put_reduce $1
cat << EOF
		}
		return -1;
	}

	private bool IsAccept(object __t)
	{
		switch(__this__.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 System.Exception();
	}

	private bool __eq(object o, object p)
	{
		return o != null && o.Equals(p);
	}

	private bool __eq(object o, char p)
	{
		return o != null && o.Equals(p);
	}

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

		if(__synptr >= __synarray.Length)
		{
			a = new object[INITSIZE * 2 + 1];
			Array.Copy(__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 System.Exception();
		__synptr -= num * 2;
	}

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

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

EOF

[ -z "$lex" ] && cat << EOF
	public int Step($streamsym __rd, $CTYPE a)
	{
		int k;

		if(__this__.STATE == ACCEPTSTATE)
		{
			return 0;
		}
		else if(IsAccept(a))
		{
			__this__.UNGET(a);
			__this__.STATE = ACCEPTSTATE;
			return 1;
		}
		else if((k = GetShift(a)) >= 0)
		{
			_push(k << LRSTATESHIFT, a);
			__this__.STATE = k << LRSTATESHIFT;
			return 1;
		}
		else if(k < -1)
		{
			_push((-k - 2) << LRSTATESHIFT, a);
			__this__.STATE = 0;
			return NinaAccept;
		}
		else if((k = GetReduce(a)) >= 0)
		{
			__this__.UNGET(a);
			__this__.STATE = k << LRSTATESHIFT;
			return 1;
		}
		else
		{
			return 0;
		}
	}
EOF
[ -z "$lex" ] || cat << EOF
	private int _steptoken()
	{
		int k;

		if(IsAccept(__token))
		{
			__this__.STATE = ACCEPTSTATE;
			return 1;
		}
		else if((k = GetShift(__token)) >= 0)
		{
			_push(k << LRSTATESHIFT, __token);
			__this__.STATE = k << LRSTATESHIFT;
			__token = null;
			return 1;
		}
		else if(k < -1)
		{
			_push((-k - 2) << LRSTATESHIFT, __token);
			__this__.STATE = 0;
			__token = null;
			return NinaAccept;
		}
		else if((k = GetReduce(__token)) >= 0)
		{
			__this__.STATE = k << LRSTATESHIFT;
			return 1;
		}
		else
		{
			__token = null;
			return 0;
		}
	}

	public int Step($streamsym __rd, $CTYPE a)
	{
		int k;

		if(__this__.STATE == ACCEPTSTATE)
		{
			return 0;
		}
		else if(__token != null)
		{
			__this__.UNGET(a);
			return _steptoken();
		}
		else if((k = StepLex(__this__.STATE & FASTATEMASK, a)) < 0)
		{
			if(__ok)
			{
				__this__.UNGET(a);
				__token = GetTokenLex(__this__.STATE & FASTATEMASK, __buf.ToString());
				__this__.STATE = __this__.STATE & LRSTATEMASK;
				__buf = new System.Text.StringBuilder();
				__ok = IsAcceptedLex(0);
				return 1;
			}
			else
			{
				return 0;
			}
		}
		else
		{
			__this__.STATE = (__this__.STATE & LRSTATEMASK) | k;
			__buf.Append((char)a);
			if(__ok = IsAcceptedLex(__this__.STATE & FASTATEMASK))
			{
				if(IsDeadLex(__this__.STATE & FASTATEMASK))
				{
					__token = GetTokenLex(__this__.STATE & FASTATEMASK, __buf.ToString());
					__this__.STATE = __this__.STATE & LRSTATEMASK;
					__buf = new System.Text.StringBuilder();
					__ok = IsAcceptedLex(0);
					return _steptoken();
				}
			}
			else if(IsDeadLex(__this__.STATE & FASTATEMASK))
			{
				return 0;
			}
			return 1;
		}
	}
EOF

cat << EOF

		public bool Accepted()
		{
			return __this__.STATE == ACCEPTSTATE;
		}

		public int Execaction($CTYPE c)
		{
			if(NinaBegin.Equals(c) && __synarray == null)
			{
				__synarray = new object[INITSIZE];
EOF
put_grammar_initial $1
cat << EOF
				__synptr = 1;
			}
			return 1;
		}

		public bool Isend()
		{
			return false;
		}

		public int Recover(System.Exception e)
		{
			return -1;
		}

		public int DeadState()
		{
			return -1;
		}

		public int StateSize()
		{
			return 1;
		}

		public int FinallyState()
		{
			return -1;
		}

		public string ToString() {
			return "${1}";
		}

EOF
echo '}'
