#! /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.
#
lex=`echo_lexer $1`
this='$this'
c='$c'
if [ -z "$lex" ]
then
  echo
elif [ -n "$TYPE" ]
then
  raiseerror 'mustcharacter'
else
  nina_template.nfa.js.sub1.sh $lex "LR${1}Lexer"
  cat << EOF

LR${1}Engine.prototype.stepLex = function($this, state, a) {
	this.__lexer.__state = state;
	return this.__lexer._step(this.__lexer, a) != 0 ? this.__lexer.__state : -1;
}

LR${1}Engine.prototype.isAcceptedLex = function($this, state) {
	this.__lexer.__state = state;
	return this.__lexer._accepted(this.__lexer);
}

LR${1}Engine.prototype.isDeadLex = function($this, state) {
	this.__lexer.__state = state;
	return this.__lexer._isdead(this.__lexer);
}

LR${1}Engine.prototype.getTokenLex = function($this, state, b) {
	this.__lexer.__state = state;
	return this.__lexer._gettoken(this.__lexer, b);
}

EOF
fi

cat << EOF

function LR${1}Engine() {
	this.__synarray = null;
	this.__synptr = 0;
	this.__buf = '';
	this.__ok = false;
	this.__token = null;
EOF
[ -n "$lex" ] && echo "	this.__lexer = new LR${1}Lexer();"
echo '}'
cat_definition $1

cat << EOF

LR${1}Engine.prototype.getShift = function($this, __t) {
	switch($this.__state >> LRSTATESHIFT) {
EOF
put_shift $1
cat << EOF
	}
	return -1;
}

LR${1}Engine.prototype.getReduce = function($this, __t) {
	var __b__, __k;

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

LR${1}Engine.prototype.isAccept = function($this, __t) {
	switch($this.__state >> LRSTATESHIFT) {
EOF
put_accept $1
cat << EOF
	}
	return false;
}

LR${1}Engine.prototype.getGoto = function(__stateID, __n) {
	switch(__stateID >> LRSTATESHIFT) {
EOF
put_goto $1
cat << EOF
	}
	throw "RuntimeException";
}

LR${1}Engine.prototype.__eq = function(o, p) {
	return o == p;
}

LR${1}Engine.prototype._push = function(k, s) {
	this.__synarray[this.__synptr++] = s;
	this.__synarray[this.__synptr++] = k;
}

LR${1}Engine.prototype._pop = function(num) {
	if(this.__synptr < num * 2)  throw "RuntimeException";
	this.__synptr -= num * 2;
}

LR${1}Engine.prototype._refsyn = function() {
	return this.__synarray[this.__synptr - 1];
}

LR${1}Engine.prototype._refv = function(n) {
	return this.__synarray[this.__synptr - n * 2];
}

EOF

[ -z "$lex" ] && cat << EOF
LR${1}Engine.prototype.step = function($this, __rd, a) {
	var k;

	if($this.__state == ACCEPTSTATE) {
		return 0;
	} else if(this.isAccept($this, a)) {
		$this._f_UNGET(a);
		$this.__state = ACCEPTSTATE;
		return 1;
	} else if((k = this.getShift($this, a)) >= 0) {
		this._push(k << LRSTATESHIFT, a);
		$this.__state = k << LRSTATESHIFT;
		return 1;
	} else if((k = this.getReduce($this, a)) >= 0) {
		$this._f_UNGET(a);
		$this.__state = k << LRSTATESHIFT;
		return 1;
	} else {
		return 0;
	}
}
EOF
[ -z "$lex" ] || cat << EOF
LR${1}Engine.prototype._steptoken = function($this) {
	var k;

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

LR${1}Engine.prototype.step = function($this, __rd, a) {
	var k;

	if($this.__state == ACCEPTSTATE) {
		return 0;
	} else if(this.__token != null) {
		$this._f_UNGET(a);
		return this._steptoken($this);
	} else if((k = this.stepLex($this, $this.__state & FASTATEMASK, a)) < 0) {
		if(this.__ok) {
			$this._f_UNGET(a);
			this.__token = this.getTokenLex($this, $this.__state & FASTATEMASK, this.__buf);
			$this.__state = $this.__state & LRSTATEMASK;
			this.__buf = '';
			this.__ok = this.isAcceptedLex($this, 0);
			return 1;
		} else {
			return 0;
		}
	} else {
		$this.__state = ($this.__state & LRSTATEMASK) | k;
		this.__buf += String.fromCharCode(a);
		if(this.__ok = this.isAcceptedLex($this, $this.__state & FASTATEMASK)) {
			if(this.isDeadLex($this, $this.__state & FASTATEMASK)) {
				this.__token = this.getTokenLex($this, $this.__state & FASTATEMASK, this.__buf);
				$this.__state = $this.__state & LRSTATEMASK;
				this.__buf = '';
				this.__ok = this.isAcceptedLex($this, 0);
				return this._steptoken($this);
			}
		} else if(this.isDeadLex($this, $this.__state & FASTATEMASK)) {
			return 0;
		}
		return 1;
	}
}
EOF

cat << EOF

LR${1}Engine.prototype.accepted = function($this) {
	return $this.__state == ACCEPTSTATE;
}

LR${1}Engine.prototype.execaction = function($this, c) {
	if(c == NINA_BEGIN && this.__synarray == null) {
		this.__synarray = [];
EOF
put_grammar_initial $1
cat << EOF
		this.__synptr = 1;
	}
	return 1;
}

LR${1}Engine.prototype.isend = function($this) {
	return false;
}

LR${1}Engine.prototype.recover = function($this, e) {
	return -1;
}

LR${1}Engine.prototype.deadState = function($this) {
	return -1;
}

LR${1}Engine.prototype.stateSize = function($this) {
	return 1;
}

LR${1}Engine.prototype.finallyState = function($this) {
	return -1;
}

LR${1}Engine.prototype.toString = function() {
	return "${1}";
}

EOF
