#! /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
buffer='$buffer'
int='$int'
num='$num'
indent='$indent'
indentBefore='$indentBefore'

echo '/**'
awk '{ printf(" * %s\n", $0) }' description
echo ' */'
cat definition

echo '/* @@@-PARSER-CODE-START-@@@ */'
echo "function ${CLASSNAME}() {"
cat << EOF
	this.__state = 0;
	this.__sts = [];
	this.__stk = [];
	this.__stv = [];
	this.__slen = 0;
	this._ = null;
	this._l = null;
	this._initlist = function() {
		this._l = [];
	};
	this._addlist = function(o) {
		this._l.push(o);
	};

	this.unread = null;
	this.yieldObject = null;
	this.befstream = null;
	this.exception = null;

	this.$buffer = null;
	this.$int = null;
	this.$num = null;

EOF

[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
	this.__backtrack = null;
	this.__backtrack_ptr = -1;
	this.__backtrack_len = -1;
	this.__backtrack_wptr = -1;
	this.__backtrack_sym = null;
	this.__backtrack_state = null;
	this.__backtrack_wsym = null;
	this.__backtrack_ptrs = null;
	this.__backtrack_cont = null;
	this.__backtrack_sptr = -1;
	this.__backtrack_flg = false;
EOF

[ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
	this.__lookahead_state = 0;
	this.__lookahead_mark = -1;
	this.__lookahead = null;
	this.__lookahead_ptr = -1;
	this.__lookaheadw = null;
	this.__lookaheadw_ptr = -1;
	this.__lookahead_ok = true;
EOF

[ -n "$ENABLE_INDENT" ] && cat << EOF
	this.$indent = 0;
	this.$indentBefore = 0;
	this.__is_line_begin = true;
	this.__indent_unread = null;
EOF

for i in $SUBAUTOMATA
do
  ch1=`replace_strange_char $i`
  echo "	this.ENGINE_${ch1} = ${CLASSNAME}.ENGINE_${ch1};"
done
echo "	this._unreadl = null;"
echo '}'
echo
echo "function ${CLASSNAME}_TokenException() {"
echo '}'

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

function ${CLASSNAME}__copy(a, len) {
	var k;

	r = [];
	for(k = 0; k < len; k++)  r[k] = a[k];
	return r;
}

function ${CLASSNAME}_Continuation(s, sts, stk, stv, len) {
	this.__state = s;
	this.__sts = ${CLASSNAME}__copy(sts, len);
	this.__stk = ${CLASSNAME}__copy(stk, len);
	this.__stv = ${CLASSNAME}__copy(stv, len);
}

EOF

[ -n "$ENABLE_LOG" ] && cat << EOF
${CLASSNAME}.__LOGFILE = null;
${CLASSNAME}.__PUTTRACE = null;
${CLASSNAME}.__log = null;

EOF

function _putreadf {
  if [ -n "$ENABLE_INDENT" ]; then
    cat << EOF
${CLASSNAME}.prototype.${1} = function(rd) {
	var c;

	if(this.__indent_unread != null) {
		c = this.__indent_unread;
		this.__indent_unread = null;
	} else if(!this.__is_line_begin) {
		c = rd();
	} else {
		this.$indent = 0;
		while((c = rd()) == ' '.charCodeAt(0) || c == '\t'.charCodeAt(0)) {
			this.$indent++;
		}
		this.__indent_unread = c;
		c = c != null ? INDENT : c;
	}

	if(this.__is_line_begin = c == '\n'.charCodeAt(0)) {
		this.$indentBefore = this.$indent;
	}
	return c;
}
EOF
  else
    cat << EOF
${CLASSNAME}.prototype.${1} = function(rd) {
	return rd();
}
EOF
  fi
}

cat field
if [ -z "$LEXER" ]; then
  _putreadf _read1l
  cat << EOF
	
${CLASSNAME}.prototype._read1 = function(stream) {
	var c;

	if(this._unreadl != null) {
		c = this._unreadl;
		this._unreadl = null;
EOF
  if [ -z "$TYPE" -a -z "$NEWLINEMODE" ]; then
    cat << EOF
	} else if((c = this._read1l(stream)) == 13 && (c = this._read1l(stream)) != 10) {
		this._unreadl = c;
		c = 13;
	}
EOF
  else
    cat << EOF
	} else {
		c = this._read1l(stream);
	}
EOF
  fi
  cat << EOF
	return c;
}
EOF
else
  _putreadf _read1l
  nina_template.nfa.js.sub2.sh "$LEXER" "$LEXER" "$CLASSNAME" "$LCTYPE"
  cat << EOF
	
${CLASSNAME}.prototype._read1ul = function(stream) {
	var c;

	if(this._unreadl != null) {
		c = this._unreadl;
		this._unreadl = null;
EOF
  if [ -z "$LTYPE" -a -z "$NEWLINEMODE" ]; then
    cat << EOF
	} else if((c = this._read1l(stream)) == 13 && (c = this._read1l(stream)) != 10) {
		this._unreadl = c;
		c = 13;
	}
EOF
  else
    cat << EOF
	} else {
		c = this._read1l(stream);
	}
EOF
  fi
  cat << EOF
	return c;
}

${CLASSNAME}.prototype._read1 = function(stream) {
	var b = '';
	var o = null;
	var f, a, s;

	s = this.__state;  this.__state = 0;
	f = this.${LEXER}_accepted(this);
	while(o == null) {
		if((a = this._read1ul(stream)) == null) {
			o = this.${LEXER}_gettoken(this, b);
			break;
		} else if(this.${LEXER}_step(this, a) == 0) {
			if(f) {
				this._unreadl = a;
				o = this.${LEXER}_gettoken(this, b);
				b = '';
				this.__state = 0;
				f = this.${LEXER}_accepted(this);
			} else {
				o = INVALIDTOKEN;  break;
			}
		} else {
			b += String.fromCharCode(a);
			if(f = this.${LEXER}_accepted(this)) {
				if(this.${LEXER}_isdead(this)) {
					o = this.${LEXER}_gettoken(this, b);
					b = '';
					this.__state = 0;
					f = this.${LEXER}_accepted(this);
				}
			} else if(this.${LEXER}_isdead(this)) {
				o = INVALIDTOKEN;  break;
			}
		}
	}
	this.__state = s;
	return o;
}
EOF
fi

cat << EOF

${CLASSNAME}.__SKIP__ = {};
${CLASSNAME}.__DUM__  = {};

${CLASSNAME}.prototype._read = function(rd) {
	var c;

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

${CLASSNAME}.prototype._f_UNGET = function(c) {
	this.unread = c;
	this.__logprint("Set unread: ", c);
}

EOF

[ -n "$ENABLE_LOG" ] && cat << EOF
${CLASSNAME}.prototype.__logprint = function(s, c) {
}

${CLASSNAME}.prototype.__logopen = function() {
}

${CLASSNAME}.prototype.__logclose = function() {
}

${CLASSNAME}.prototype.__puttrace = function() {
}

EOF
[ -n "$ENABLE_LOG" ] || cat << EOF
${CLASSNAME}.prototype.__logprint = function(s, c) {
}

${CLASSNAME}.prototype.__logopen = function() {
}

${CLASSNAME}.prototype.__logclose = function() {
}

${CLASSNAME}.prototype.__puttrace = function() {
}

EOF

[ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
${CLASSNAME}.prototype._f_LOOKAHEAD = function(c) {
	if(this.__lookaheadw == null) {
		this.__lookahead_state = this.__state;
		this.__lookaheadw = [];
		this.__lookaheadw_ptr = 0;
		this.__lookaheadw[this.__lookaheadw_ptr++] = c;
	} else {
		this.__lookaheadw[this.__lookaheadw_ptr++] = c;
	}
}

${CLASSNAME}.prototype.__copy_lookahead = function(p) {
	var a, k;

	if(this.__lookahead_ptr > 0) {
		a = [];
		for(k = 0; k < a.length; k++) {
			a[k] = this.__lookaheadw[k + this.__lookahead_ptr];
		}
		__lookahead = a;
	}

	if(this.__lookahead == null) {
		a = [];
	} else if(this.__lookaheadw_ptr < this.__lookahead.length - this.__lookahead_ptr) {
		a = this.__lookahead;
	} else {
		a = [];
	}

	for(k = 0; k < this.__lookaheadw_ptr; k++) {
		a[k] = this.__lookaheadw[k];
	}
	this.__lookahead = a;
	this.__lookahead_ptr = p;
	this.__lookaheadw = null;
	this.__lookaheadw_ptr = -1;
}

${CLASSNAME}.prototype._f_LOOKAHEAD_COMMIT = function() {
	if(this.__lookahead_mark < 0) {
		this.__lookaheadw = null;
		this.__lookaheadw_ptr = -1;
	} else {
		this.__copy_lookahead(this.__lookahead_mark);
	}
	this.__lookahead_mark = -1;
	this.__logprint("Commit Lookahead");
}

${CLASSNAME}.prototype._f_LOOKAHEAD_RB = function() {
	this.__copy_lookahead(0);
	this.__state = this.__lookahead_state;
	this.__lookahead_ok = false;
	this.__lookahead_mark = -1;
	this.__logprint("Rollback Lookahead");
}

${CLASSNAME}.prototype._f_LOOKAHEAD_MARK = function() {
	this.__lookahead_mark = this.__lookaheadw_ptr;
}

${CLASSNAME}.prototype._f_LOOKAHEAD_MARK_INIT = function() {
	this.__lookahead_mark = 0;
}

EOF

[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
${CLASSNAME}.prototype._f_SET_BACKTRACK = function(c) {
	if(this.__backtrack_sym != c) {
		if(this.__backtrack_ptr < 0) {
			this.__backtrack = [];
			this.__backtrack_wptr = 0;
		} else {
			this.__backtrack_wptr = this.__backtrack_ptr;
		}

		if(this.__backtrack_wsym == null) {
			this.__backtrack_state = [];
			this.__backtrack_wsym  = [];
			this.__backtrack_ptrs  = [];
			this.__backtrack_cont  = [];
			this.__backtrack_sptr  = 0;
		} else if(this.__backtrack_sptr >= this.__backtrack_wsym.length) {
			this.__backtrack_wsym = [].concat(this.__backtrack_wsym);
			this.__backtrack_ptrs = [].concat(this.__backtrack_ptrs);
		}
		this.__backtrack_state[this.__backtrack_sptr] = this.__state;
		this.__backtrack_wsym[this.__backtrack_sptr] = c;
		this.__backtrack_ptrs[this.__backtrack_sptr] = this.__backtrack_wptr;
		this.__backtrack_cont[this.__backtrack_sptr] = this.GETCC();
		this.__backtrack_sptr++;
		this.__backtrack_flg = true;
		this.__logprint("Set Backtrack: ", c);
	}
}

${CLASSNAME}.prototype.__write_backtrack = function(c) {
	if(this.__backtrack_wptr < 0) {
		// do nothing
	} else {
		this.__backtrack[this.__backtrack_wptr++] = c;
	}
}

${CLASSNAME}.prototype._f_BACKTRACK_COMMIT = function() {
	if(--this.__backtrack_sptr <= 0) {
		this.__backtrack_state = null;
		this.__backtrack_wsym = null;
		this.__backtrack_ptrs = null;
		this.__backtrack_cont = null;
		this.__backtrack_sptr = -1;
		this.__backtrack_wptr = -1;
		this.__logprint("Commit Backtracking");
	}
}

${CLASSNAME}.prototype._f_BACKTRACK = function(c) {
	if(this.__backtrack_wptr >= 0) {
		this.__backtrack_len = this.__backtrack_wptr;
		this.__backtrack_wptr = -1;
		this.__backtrack_sptr--;
		this.__state = this.__backtrack_state[this.__backtrack_sptr];
		this.__backtrack_ptr = this.__backtrack_ptrs[this.__backtrack_sptr];
		this.__backtrack_sym = this.__backtrack_wsym[this.__backtrack_sptr];
		SETCC(this.__backtrack_cont[this.__backtrack_sptr]);
		this.__logprint("Rollback Backtracking");
		return true;
	} else {
		return false;
	}
}

${CLASSNAME}.prototype._f_GETCC = function() {
	return new ${CLASSNAME}_Continuation(this.__state, this.__sts, this.__stk, this.__stv, this.__slen);
}

${CLASSNAME}.prototype._f_SETCC = function(c) {
	this.__state = c.__state;
	this.__sts = [].concat(c.__sts);
	this.__stk = [].concat(c.__stk);
	this.__slen = c.__sts.length;
}
EOF

this='$this'
for i in $SUBAUTOMATA
do
  ch1=`replace_strange_char $i`
  echo
  echo "${CLASSNAME}.ENGINE_${ch1} = {};"
  echo
  echo "${CLASSNAME}.ENGINE_${ch1}.step = function($this, __rd, " '$c)' " {"
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '	var __l__ = $this.__lookahead_ok;'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo
  [ -n "$DYNAMICAUTOMATA" ]  && echo '	if((STATE & NINA_DISCARDSTATE) != 0)  return 1;'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '	$this.__lookahead_ok = true;'
  echo '	switch(STATE) {'
  print_states $i
  echo '	}'
  echo '	return 0;'
  echo '}'
  echo
  echo "${CLASSNAME}.ENGINE_${ch1}.accepted = function($this) {"
  print_accepts $i
  echo '}'
  echo
  echo "${CLASSNAME}.ENGINE_${ch1}.execaction = function($this, " '$c) {'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '	if((STATE & NINA_DISCARDSTATE) != 0) {'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '		if($c >= 0) {'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '			$this.__state = $this.__state & ~NINA_DISCARDSTATE;'
  [ -n "$DYNAMICAUTOMATA" ]  && echo '			$this.__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 "${CLASSNAME}.ENGINE_${ch1}.isend = function($this) {"
  print_isend $i
  echo '}'
  cat << EOF

${CLASSNAME}.ENGINE_${ch1}.recover = function($this, e) {
EOF
  print_recover $i
  cat << EOF
}

${CLASSNAME}.ENGINE_${ch1}.deadState = function($this) {
EOF
  print_deadstate $i
  cat << EOF
}

${CLASSNAME}.ENGINE_${ch1}.stateSize = function($this) {
EOF
  print_statesize $i
  cat << EOF
}

${CLASSNAME}.ENGINE_${ch1}.finallyState = function($this) {
EOF
  print_finallystate $i
  cat << EOF
}

${CLASSNAME}.ENGINE_${ch1}.dead = function($this) {
EOF
  print_deads $i
  cat << EOF
}

${CLASSNAME}.ENGINE_${ch1}.emptyTransition = function($this) {
EOF
  print_empty_transitions $i
  cat << EOF
}

${CLASSNAME}.ENGINE_${ch1}.toString = function() {
	return "${ch1}";
}
EOF
done

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

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

${CLASSNAME}.prototype.__startWith = function(a, c) {
	return a.length > 0 && a.charCodeAt(0) == c;
}

DynamicEngine = function(a, oldst) {
	this.ptr = 0;
	this.oldst = oldst;
	this.a = a;
}

DynamicEngine.prototype.step = function($this, __rd, $c) {
	if(this.ptr >= this.a.length) {
		LOOKAHEAD_COMMIT();
		return 0;
	} else if(this.a.charCodeAt(this.ptr) == $c) {
		LOOKAHEAD($c);
		this.ptr++;
		return 1;
	} else {
		LOOKAHEAD($c);
		LOOKAHEAD_RB();
		$this.__sts[$this.__slen - 1] = NINA_DISCARDSTATE | this.oldst;
		return 0;
	}
}

DynamicEngine.prototype.accepted = function($this) {
	return true;
}

DynamicEngine.prototype.dead = function($this) {
	return false;
}

DynamicEngine.prototype.emptyTransition = function($this) {
	return false;
}

DynamicEngine.prototype.execaction = function($this, c) {
	return 1;
}

DynamicEngine.prototype.isend = function($this) {
	return this.ptr >= this.a.length;
}

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

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

DynamicEngine.prototype.stateSize = function($this) {
	return a.length;
}

DynamicEngine.prototype.finallyState = function($this) {
	return -1;
}
EOF

cat << EOF

${CLASSNAME}.prototype.__stkpush = function(st, en) {
	this.__sts[this.__slen] = st;
	this.__stk[this.__slen] = en;
	this.__stv[this.__slen++] = [];
}
EOF

ch1=`replace_strange_char ${MAINNAME}`
echo
cat << EOF
${CLASSNAME}.prototype._parse = function(rd, x, rt, st) {
	var b = false;
	var c = x, a;
	var en;

	b = this.__stk[this.__slen - 1].accepted(this);
	if(rd == null) {
		throw new ${CLASSNAME}_TokenException("can not recurse");
	} else if(rt) {
		switch(this.__stk[this.__slen - 1].execaction(this, NINA_BEGIN)) {
		case NINA_ACCEPT:
			this.__logprint("accept " + this.__stk[this.__slen - 1]);
			st[0] = NINA_ACCEPT;  return null;
		case NINA_FAIL:
			this.__logprint("match failed: begin");
			this._puttrace();
			st[0] = NINA_FAIL;  return null;
		case NINA_HALT_ACCEPT:
			this.__logprint("machine halted: begin");
			st[0] = NINA_HALT_ACCEPT;  return null;
		case NINA_HALT_REJECT:
			this.__logprint("machine halted: begin");
			st[0] = NINA_HALT_REJECT;  return null;
		case NINA_YIELD:
			this.__logprint("machine yielded: ", c);
			st[0] = NINA_YIELD;  return null;
		}
	}

	try {
		do {
			en = this.__stk[this.__slen - 1];
			if(c === ${CLASSNAME}.__SKIP__) {
				c = ${CLASSNAME}.__DUM__;
			} else if((a = en.step(this, rd, c)) > 0) {
				this.__logprint("transit to state " + this.__state + ": ", c);
				b = en.accepted(this);
				switch(en.execaction(this, c)) {
				case NINA_ACCEPT:
					this.__logprint("accept " + this.__stk[this.__slen - 1].toString());
					this._f_UNGET(c);
					st[0] = NINA_ACCEPT;  return null;
				case NINA_FAIL:
					this.__logprint("match failed: ", c);
					this.__puttrace();
					this._f_UNGET(c);
					st[0] = NINA_FAIL;  return null;
				case NINA_HALT_ACCEPT:
					this.__logprint("machine halted: ", c);
					st[0] = NINA_HALT_ACCEPT;  return null;
				case NINA_HALT_REJECT:
					this.__logprint("machine halted: ", c);
					st[0] = NINA_HALT_REJECT;  return null;
				case NINA_YIELD:
					this.__logprint("machine yielded: ", c);
					st[0] = NINA_YIELD;  return null;
				}
EOF
[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
				if(this.__backtrack_flg) {
					this.__write_backtrack(c);
					this.__backtrack_flg = false;
				}
EOF
cat << EOF
			} else if(a < 0) {
				this.__logprint("entering " + this.__stk[this.__slen - 1].toString());
				return c;
			} else if(b) {
				this.__logprint("accept " + this.__stk[this.__slen - 1].toString());
				this._f_UNGET(c);
				st[0] = NINA_ACCEPT;  return null;
EOF
[ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
			} else if(this.__lookaheadw_ptr >= 0) {
				this.__logprint("match failed: try lookahead: ", c);
				this._f_LOOKAHEAD(c);
				this._f_LOOKAHEAD_RB();
				b = en.accepted(this);
EOF
[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
			} else if(this._f_BACKTRACK(c)) {
				this.__logprint("match failed: try backtracking: ", c);
EOF
cat << EOF
			} else if(c == null) {
				if(!b)  throw "${TOKENERROR}";
				st[0] = NINA_ACCEPT;  return null;
			} else {
				this.__logprint("match failed: ", c);
				this.__puttrace();
				this._f_UNGET(c);
				st[0] = NINA_FAIL;  return null;
			}

			if(this.__stk[this.__slen - 1].emptyTransition(this)) {
				// do nothing
			} else if(!this.__stk[this.__slen - 1].dead(this)) {
				c = this._read(rd);
			} else if(b) {
				this.__logprint("accept " + this.__stk[this.__slen - 1].toString());
				st[0] = NINA_ACCEPT;  return null;
EOF
[ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
			} else if(this.__lookaheadw_ptr >= 0) {
				this.__logprint("match failed: try lookahead: ", c);
				this._f_LOOKAHEAD_RB();
				b = en.accepted(this);
EOF
[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
			} else if(this._f_BACKTRACK(c)) {
				this.__logprint("match failed: try backtracking: ", c);
EOF
cat << EOF
			} else if(c == null) {
				if(!b)  throw "${TOKENERROR}";
				st[0] = NINA_ACCEPT;  return null;
			} else {
				this.__logprint("match failed: ", c);
				this.__puttrace();
				st[0] = NINA_FAIL;  return null;
			}
		} while(true);
	} catch(e) {
		this._f_UNGET(c);
		throw e;
	}
}

${CLASSNAME}.prototype.execfinally = function() {
	var a, b;

	if((a = this.__stk[this.__slen - 1].finallyState(this)) >= 0) {
		b = this.__state;  this.__state = a;
		switch(this.__stk[this.__slen - 1].execaction(this, NINA_BEGIN)) {
		case NINA_HALT_ACCEPT:
			this.__slen = 0;
			return true;
		case NINA_HALT_REJECT:
			this.__slen = 0;
			return false;
		}
		this.__state = b;
	}
	return null;
}

${CLASSNAME}.prototype.getdeadstate = function() {
	return this.__stk[this.__slen - 1].deadState(this);
}

${CLASSNAME}.prototype.getrecover = function(e) {
	return this.__stk[this.__slen - 1].recover(this, e);
}

${CLASSNAME}.prototype.parse_generic = function(rd, entry) {
	var c = ${CLASSNAME}.__SKIP__;
	var b = false;
	var a = [];

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

		ot: while(true) {
			try {
				if((c = this._parse(rd, c, b, a)) != null) {
					// do nothing
				} else if(a[0] == NINA_FAIL) {
					while((this.__state = this.getdeadstate()) < 0) {
						if((b = this.execfinally()) != null)  break ot;
						if(this.__slen-- <= 1) {
							throw new ${CLASSNAME}_TokenException();
						}
					}
					c = ${CLASSNAME}.__SKIP__;
				} else if(a[0] == NINA_HALT_ACCEPT) {
					if((b = this.execfinally()) != null)  break;
					this.__slen = 0;
					b = true;  break;
				} else if(a[0] == NINA_HALT_REJECT) {
					if((b = this.execfinally()) != null)  break;
					this.__slen = 0;
					b = false;  break;
				} else if(a[0] == NINA_YIELD) {
					return false;
				} else if(this.__slen > 1) {
					if((b = this.execfinally()) != null)  break;
					this.__state = this.__sts[--this.__slen];
					c = ${CLASSNAME}.__SKIP__;
				} else {
					if((b = this.execfinally()) != null)  break;
					b = this.__stk[--this.__slen].accepted(this);
					break;
				}
			} catch(e) {
				this.exception = e;
				if(this.__slen <= 0)  throw e;
				while((this.__state = this.getrecover(e)) < 0) {
					if((b = this.execfinally()) != null)  return b;
					if(this.__slen-- <= 1)  throw e;
				}
			}
			b = true;
		}
		if(!b)  throw new ${CLASSNAME}_TokenException();
		return b;
	} finally {
		this.__logclose();
	}
}

${CLASSNAME}.prototype.parse = function(rd) {
	return this.parse_generic(rd, this.ENGINE_${ch1});
}

${CLASSNAME}.prototype.setStream = function(rd) {
	if(this.befstream != null) {
		throw "IllegalStateException";
	}
	this.yieldObject = this.befstream = rd;
}

${CLASSNAME}.prototype.parseNext = function() {
	var o;

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

${CLASSNAME}.parseAll = function(rd) {
	return new ${CLASSNAME}().parse(rd);
}

${CLASSNAME}.parseStream = function(stream) {
	return ${CLASSNAME}.parseAll(function () {
		var c;

		c = stream.read();
		return c < 0 ? null : c;
	});
}

${CLASSNAME}.parseString = function(s) {
	var p = 0;

	return ${CLASSNAME}.parseAll(function () {
		return p < s.length ? s.charCodeAt(p++) : null;
	});
}

${CLASSNAME}.parseStdin = function() {
	return ${CLASSNAME}.parseStream(
			new java.io.InputStreamReader(java.lang.System['in']));
}

function puts(s) {
	CONSOLE.println(s);
}
EOF

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

cat fragment
#cat fragment | replace_action

[ -n "$PUTMAIN" ] && cat << EOF
${CLASSNAME}.parseStdin();
EOF
[ -n "$PUTPROMPT" ] && cat << EOF
var s;

while((s = java.lang.System.console().readLine("> ")) != null) {
	${CLASSNAME}.parseStream(new java.io.StringReader(s));
}
EOF
