summaryrefslogtreecommitdiff
path: root/src/libfsm/goto.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libfsm/goto.cc')
-rw-r--r--src/libfsm/goto.cc978
1 files changed, 978 insertions, 0 deletions
diff --git a/src/libfsm/goto.cc b/src/libfsm/goto.cc
new file mode 100644
index 00000000..610f44d1
--- /dev/null
+++ b/src/libfsm/goto.cc
@@ -0,0 +1,978 @@
+/*
+ * Copyright 2001-2018 Adrian Thurston <thurston@colm.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ragel.h"
+#include "goto.h"
+#include "redfsm.h"
+#include "bstmap.h"
+#include "gendata.h"
+
+#include <sstream>
+
+using std::ostringstream;
+
+IpLabel *Goto::allocateLabels( IpLabel *labels, IpLabel::Type type, int n )
+{
+ if ( labels == 0 ) {
+ labels = new IpLabel[n];
+ for ( int id = 0; id < n; id++ ) {
+ labels[id].type = type;
+ labels[id].stid = id;
+ }
+ }
+
+ return labels;
+}
+
+void Goto::setTableState( TableArray::State state )
+{
+ for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) {
+ TableArray *tableArray = *i;
+ tableArray->setState( state );
+ }
+}
+
+/* Emit the goto to take for a given transition. */
+std::ostream &Goto::COND_GOTO( RedCondPair *cond )
+{
+ out << "goto " << ctrLabel[cond->id].reference() << ";";
+ return out;
+}
+
+/* Emit the goto to take for a given transition. */
+std::ostream &Goto::TRANS_GOTO( RedTransAp *trans )
+{
+ if ( trans->condSpace == 0 || trans->condSpace->condSet.length() == 0 ) {
+ /* Existing. */
+ assert( trans->numConds() == 1 );
+ RedCondPair *cond = trans->outCond( 0 );
+
+ /* Go to the transition which will go to the state. */
+ out << "goto " << ctrLabel[cond->id].reference() << ";";
+ }
+ else {
+ out << ck << " = 0;\n";
+ for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) {
+ out << "if ( ";
+ CONDITION( out, *csi );
+ Size condValOffset = (1 << csi.pos());
+ out << " )\n" << ck << " += " << condValOffset << ";\n";
+ }
+ CondKey lower = 0;
+ CondKey upper = trans->condFullSize() - 1;
+ COND_B_SEARCH( trans, lower, upper, 0, trans->numConds()-1 );
+
+ if ( trans->errCond() != 0 ) {
+ COND_GOTO( trans->errCond() ) << "\n";
+ }
+ }
+
+ return out;
+}
+
+/* Write out the array of actions. */
+void Goto::taActions()
+{
+ actions.start();
+
+ actions.value( 0 );
+
+ for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) {
+ /* Write out the length, which will never be the last character. */
+ actions.value( act->key.length() );
+
+ for ( GenActionTable::Iter item = act->key; item.lte(); item++ )
+ actions.value( item->value->actionId );
+ }
+
+ actions.finish();
+}
+
+void Goto::GOTO_HEADER( RedStateAp *state )
+{
+ /* Label the state. */
+ out << "case " << state->id << ":\n";
+}
+
+
+void Goto::SINGLE_SWITCH( RedStateAp *state )
+{
+ /* Load up the singles. */
+ int numSingles = state->outSingle.length();
+ RedTransEl *data = state->outSingle.data;
+
+ if ( numSingles == 1 ) {
+ /* If there is a single single key then write it out as an if. */
+ out << "if ( " << GET_KEY() << " == " <<
+ KEY(data[0].lowKey) << " ) {\n";
+
+ /* Virtual function for writing the target of the transition. */
+ TRANS_GOTO(data[0].value) << "\n";
+ out << "}\n";
+ }
+ else if ( numSingles > 1 ) {
+ /* Write out single keys in a switch if there is more than one. */
+ out << "switch( " << GET_KEY() << " ) {\n";
+
+ /* Write out the single indices. */
+ for ( int j = 0; j < numSingles; j++ ) {
+ out << "case " << KEY(data[j].lowKey) << ": {\n";
+ TRANS_GOTO(data[j].value) << "\n";
+ out << "}\n";
+ }
+
+ /* Close off the transition switch. */
+ out << "}\n";
+ }
+}
+
+void Goto::RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high )
+{
+ /* Get the mid position, staying on the lower end of the range. */
+ int mid = (low + high) >> 1;
+ RedTransEl *data = state->outRange.data;
+
+ /* Determine if we need to look higher or lower. */
+ bool anyLower = mid > low;
+ bool anyHigher = mid < high;
+
+ /* Determine if the keys at mid are the limits of the alphabet. */
+ bool limitLow = keyOps->eq( data[mid].lowKey, lower );
+ bool limitHigh = keyOps->eq( data[mid].highKey, upper );
+
+ if ( anyLower && anyHigher ) {
+ /* Can go lower and higher than mid. */
+ out << "if ( " << GET_KEY() << " < " <<
+ KEY(data[mid].lowKey) << " ) {\n";
+ RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 );
+ out << "} else if ( " << GET_KEY() << " > " <<
+ KEY(data[mid].highKey) << " ) {\n";
+ RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high );
+ out << "} else {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ else if ( anyLower && !anyHigher ) {
+ /* Can go lower than mid but not higher. */
+ out << "if ( " << GET_KEY() << " < " <<
+ KEY(data[mid].lowKey) << " ) {\n";
+ RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 );
+
+ /* if the higher is the highest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitHigh ) {
+ out << "} else {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ else {
+ out << "} else if ( " << GET_KEY() << " <= " <<
+ KEY(data[mid].highKey) << " ) {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ }
+ else if ( !anyLower && anyHigher ) {
+ /* Can go higher than mid but not lower. */
+ out << "if ( " << GET_KEY() << " > " <<
+ KEY(data[mid].highKey) << " ) {\n";
+ RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high );
+
+ /* If the lower end is the lowest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitLow ) {
+ out << "} else {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ else {
+ out << "} else if ( " << GET_KEY() << " >= " <<
+ KEY(data[mid].lowKey) << " ) {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ }
+ else {
+ /* Cannot go higher or lower than mid. It's mid or bust. What
+ * tests to do depends on limits of alphabet. */
+ if ( !limitLow && !limitHigh ) {
+ out << "if ( " << KEY(data[mid].lowKey) << " <= " <<
+ GET_KEY() << " && " << GET_KEY() << " <= " <<
+ KEY(data[mid].highKey) << " ) {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ else if ( limitLow && !limitHigh ) {
+ out << "if ( " << GET_KEY() << " <= " <<
+ KEY(data[mid].highKey) << " ) {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ else if ( !limitLow && limitHigh ) {
+ out << "if ( " << KEY(data[mid].lowKey) << " <= " <<
+ GET_KEY() << " ) {\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ else {
+ /* Both high and low are at the limit. No tests to do. */
+ out << "{\n";
+ TRANS_GOTO(data[mid].value) << "\n";
+ out << "}\n";
+ }
+ }
+}
+
+/* Write out a key from the fsm code gen. Depends on wether or not the key is
+ * signed. */
+string Goto::CKEY( CondKey key )
+{
+ ostringstream ret;
+ ret << key.getVal();
+ return ret.str();
+}
+
+void Goto::COND_B_SEARCH( RedTransAp *trans, CondKey lower,
+ CondKey upper, int low, int high )
+{
+ /* Get the mid position, staying on the lower end of the range. */
+ int mid = (low + high) >> 1;
+// RedCondEl *data = trans->outCond(0);
+
+ /* Determine if we need to look higher or lower. */
+ bool anyLower = mid > low;
+ bool anyHigher = mid < high;
+
+ CondKey midKey = trans->outCondKey( mid );
+ RedCondPair *midTrans = trans->outCond( mid );
+
+ /* Determine if the keys at mid are the limits of the alphabet. */
+ bool limitLow = midKey == lower;
+ bool limitHigh = midKey == upper;
+
+ if ( anyLower && anyHigher ) {
+ /* Can go lower and higher than mid. */
+ out << "if ( " << ck << " < " <<
+ CKEY(midKey) << " ) {\n";
+ COND_B_SEARCH( trans, lower, midKey-1, low, mid-1 );
+ out << "} else if ( " << ck << " > " <<
+ CKEY(midKey) << " ) {\n";
+ COND_B_SEARCH( trans, midKey+1, upper, mid+1, high );
+ out << "} else {\n";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ else if ( anyLower && !anyHigher ) {
+ /* Can go lower than mid but not higher. */
+ out << "if ( " << ck << " < " <<
+ CKEY(midKey) << " ) {\n";
+ COND_B_SEARCH( trans, lower, midKey-1, low, mid-1);
+
+ /* if the higher is the highest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitHigh ) {
+ out << "} else {\n";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ else {
+ out << "} else if ( " << ck << " <= " <<
+ CKEY(midKey) << " ) {\n";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ }
+ else if ( !anyLower && anyHigher ) {
+ /* Can go higher than mid but not lower. */
+ out << "if ( " << ck << " > " <<
+ CKEY(midKey) << " ) {\n";
+ COND_B_SEARCH( trans, midKey+1, upper, mid+1, high );
+
+ /* If the lower end is the lowest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitLow ) {
+ out << "} else {\n";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ else {
+ out << "} else if ( " << ck << " >= " <<
+ CKEY(midKey) << " ) {\n";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ }
+ else {
+ /* Cannot go higher or lower than mid. It's mid or bust. What
+ * tests to do depends on limits of alphabet. */
+ if ( !limitLow && !limitHigh ) {
+ out << "if ( " << ck << " == " <<
+ CKEY(midKey) << " ) {\n";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ else if ( limitLow && !limitHigh ) {
+ out << "if ( " << ck << " <= " <<
+ CKEY(midKey) << " ) {\n";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ else if ( !limitLow && limitHigh ) {
+ out << "if ( " << CKEY(midKey) << " <= " << ck << " )\n {";
+ COND_GOTO(midTrans) << "\n";
+ out << "}\n";
+ }
+ else {
+ /* Both high and low are at the limit. No tests to do. */
+ COND_GOTO(midTrans) << "\n";
+ }
+ }
+}
+
+void Goto::STATE_GOTO_ERROR()
+{
+ /* Bail out immediately. */
+ out << " goto " << _again << ";\n";
+}
+
+void Goto::FROM_STATE_ACTION_EMIT( RedStateAp *state )
+{
+ if ( state->fromStateAction != 0 ) {
+ /* Write every action in the list. */
+ for ( GenActionTable::Iter item = state->fromStateAction->key; item.lte(); item++ ) {
+ ACTION( out, item->value, IlOpts( state->id, false,
+ state->fromStateAction->anyNextStmt() ) );
+ out << "\n";
+ }
+ }
+}
+
+std::ostream &Goto::STATE_CASES()
+{
+ bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates();
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Writing code above state gotos. */
+ GOTO_HEADER( st );
+
+ FROM_STATE_ACTION_EMIT( st );
+
+ if ( !noEnd && eof ) {
+ out <<
+ "if ( " << P() << " == " << vEOF() << " ) {\n";
+
+ if ( st->eofTrans != 0 )
+ TRANS_GOTO( st->eofTrans );
+
+ out <<
+ " goto " << _again << ";\n"
+ "}\n"
+ "else {\n";
+ }
+
+ if ( st == redFsm->errState )
+ STATE_GOTO_ERROR();
+ else {
+ /* Try singles. */
+ if ( st->outSingle.length() > 0 )
+ SINGLE_SWITCH( st );
+
+ /* Default case is to binary search for the ranges, if that fails then */
+ if ( st->outRange.length() > 0 ) {
+ RANGE_B_SEARCH( st, keyOps->minKey, keyOps->maxKey,
+ 0, st->outRange.length() - 1 );
+ }
+
+ /* Write the default transition. */
+ TRANS_GOTO( st->defTrans ) << "\n";
+ }
+
+ if ( !noEnd && eof ) {
+ out <<
+ "}\n";
+ }
+ }
+ return out;
+}
+
+std::ostream &Goto::TRANSITION( RedCondPair *pair )
+{
+ /* Write the label for the transition so it can be jumped to. */
+ if ( ctrLabel[pair->id].isReferenced )
+ out << "_ctr" << pair->id << ": ";
+
+ /* Destination state. */
+ if ( pair->action != 0 && pair->action->anyCurStateRef() )
+ out << ps << " = " << vCS() << ";";
+ out << vCS() << " = " << pair->targ->id << "; ";
+
+ if ( pair->action != 0 ) {
+ /* Write out the transition func. */
+ out << "goto f" << pair->action->actListId << ";\n";
+ }
+ else {
+ /* No code to execute, just loop around. */
+ out << "goto " << _again << ";\n";
+ }
+ return out;
+}
+
+std::ostream &Goto::TRANSITIONS()
+{
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
+ if ( trans->condSpace == 0 )
+ TRANSITION( &trans->p );
+ }
+
+ for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ )
+ TRANSITION( &cond->p );
+
+ return out;
+}
+
+unsigned int Goto::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->location+1;
+ return act;
+}
+
+unsigned int Goto::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->location+1;
+ return act;
+}
+
+unsigned int Goto::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->location+1;
+ return act;
+}
+
+void Goto::taToStateActions()
+{
+ toStateActions.start();
+
+ /* Take one off for the psuedo start state. */
+ int numStates = redFsm->stateList.length();
+ unsigned int *vals = new unsigned int[numStates];
+ memset( vals, 0, sizeof(unsigned int)*numStates );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ vals[st->id] = TO_STATE_ACTION(st);
+
+ for ( int st = 0; st < redFsm->nextStateId; st++ ) {
+ /* Write any eof action. */
+ toStateActions.value( vals[st] );
+ }
+ delete[] vals;
+
+ toStateActions.finish();
+}
+
+void Goto::taFromStateActions()
+{
+ fromStateActions.start();
+
+ /* Take one off for the psuedo start state. */
+ int numStates = redFsm->stateList.length();
+ unsigned int *vals = new unsigned int[numStates];
+ memset( vals, 0, sizeof(unsigned int)*numStates );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ vals[st->id] = FROM_STATE_ACTION(st);
+
+ for ( int st = 0; st < redFsm->nextStateId; st++ ) {
+ /* Write any eof action. */
+ fromStateActions.value( vals[st] );
+ }
+ delete[] vals;
+
+ fromStateActions.finish();
+}
+
+void Goto::taEofActions()
+{
+ eofActions.start();
+
+ /* Take one off for the psuedo start state. */
+ int numStates = redFsm->stateList.length();
+ unsigned int *vals = new unsigned int[numStates];
+ memset( vals, 0, sizeof(unsigned int)*numStates );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ vals[st->id] = EOF_ACTION(st);
+
+ for ( int st = 0; st < redFsm->nextStateId; st++ ) {
+ /* Write any eof action. */
+ eofActions.value( vals[st] );
+ }
+ delete[] vals;
+
+ eofActions.finish();
+}
+
+void Goto::taNfaOffsets()
+{
+ nfaOffsets.start();
+
+ /* Offset of zero means no NFA targs, real targs start at 1. */
+ long offset = 1;
+
+ /* Take one off for the psuedo start state. */
+ int numStates = redFsm->stateList.length();
+ unsigned int *vals = new unsigned int[numStates];
+ memset( vals, 0, sizeof(unsigned int)*numStates );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->nfaTargs == 0 ) {
+ vals[st->id] = 0;
+ //nfaOffsets.value( 0 );
+ }
+ else {
+ vals[st->id] = offset;
+ //nfaOffsets.value( offset );
+ offset += 1 + st->nfaTargs->length();
+ }
+ }
+
+ for ( int st = 0; st < redFsm->nextStateId; st++ )
+ nfaOffsets.value( vals[st] );
+ delete[] vals;
+
+ nfaOffsets.finish();
+}
+
+void Goto::taNfaTargs()
+{
+ nfaTargs.start();
+
+ /* Offset of zero means no NFA targs, put a filler there. */
+ nfaTargs.value( 0 );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->nfaTargs != 0 ) {
+ nfaTargs.value( st->nfaTargs->length() );
+ for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ )
+ nfaTargs.value( targ->state->id );
+ }
+ }
+
+ nfaTargs.finish();
+}
+
+/* These need to mirror nfa targs. */
+void Goto::taNfaPushActions()
+{
+ nfaPushActions.start();
+
+ nfaPushActions.value( 0 );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->nfaTargs != 0 ) {
+ nfaPushActions.value( 0 );
+ for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ )
+ NFA_PUSH_ACTION( targ );
+ }
+ }
+
+ nfaPushActions.finish();
+}
+
+void Goto::taNfaPopTrans()
+{
+ nfaPopTrans.start();
+
+ nfaPopTrans.value( 0 );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->nfaTargs != 0 ) {
+ nfaPopTrans.value( 0 );
+ for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ )
+ NFA_POP_TEST( targ );
+ }
+ }
+
+ nfaPopTrans.finish();
+}
+
+void Goto::EOF_CHECK( ostream &ret )
+{
+ ret <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto " << _test_eof << ";\n";
+}
+
+void Goto::GOTO( ostream &ret, int gotoDest, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << "; ";
+
+ if ( inFinish && !noEnd )
+ EOF_CHECK( ret );
+
+ ret << "goto " << _again << ";";
+
+ ret << CLOSE_GEN_BLOCK();
+}
+
+void Goto::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR();
+ INLINE_LIST( ret, ilItem->children, 0, inFinish, false );
+ ret << CLOSE_HOST_EXPR() << ";";
+
+ if ( inFinish && !noEnd )
+ EOF_CHECK( ret );
+
+ ret << " goto " << _again << ";";
+
+ ret << CLOSE_GEN_BLOCK();
+}
+
+void Goto::CURS( ostream &ret, bool inFinish )
+{
+ ret << "(" << ps << ")";
+}
+
+void Goto::TARGS( ostream &ret, bool inFinish, int targState )
+{
+ ret << "(" << vCS() << ")";
+}
+
+void Goto::NEXT( ostream &ret, int nextDest, bool inFinish )
+{
+ ret << vCS() << " = " << nextDest << ";";
+}
+
+void Goto::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
+{
+ ret << vCS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish, false );
+ ret << ");";
+}
+
+void Goto::CALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK();
+
+ if ( red->prePushExpr != 0 ) {
+ ret << OPEN_HOST_BLOCK( red->prePushExpr );
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+ ret << CLOSE_HOST_BLOCK();
+ }
+
+ ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " <<
+ TOP() << " += 1;" << vCS() << " = " <<
+ callDest << ";";
+
+ if ( inFinish && !noEnd )
+ EOF_CHECK( ret );
+
+ ret << " goto " << _again << ";";
+
+ ret << CLOSE_GEN_BLOCK();
+}
+
+void Goto::NCALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK();
+
+ if ( red->prePushExpr != 0 ) {
+ ret << OPEN_HOST_BLOCK( red->prePushExpr );
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+ ret << CLOSE_HOST_BLOCK();
+ }
+
+ ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " <<
+ TOP() << " += 1;" << vCS() << " = " <<
+ callDest << "; " << CLOSE_GEN_BLOCK();
+}
+
+void Goto::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK();
+
+ if ( red->prePushExpr != 0 ) {
+ ret << OPEN_HOST_BLOCK( red->prePushExpr );
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+ ret << CLOSE_HOST_BLOCK();
+ }
+
+ ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" <<
+ vCS() << " = " << OPEN_HOST_EXPR();
+ INLINE_LIST( ret, ilItem->children, targState, inFinish, false );
+ ret << CLOSE_HOST_EXPR() << ";";
+
+ if ( inFinish && !noEnd )
+ EOF_CHECK( ret );
+
+ ret << " goto " << _again << ";";
+
+ ret << CLOSE_GEN_BLOCK();
+}
+
+void Goto::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK();
+
+ if ( red->prePushExpr != 0 ) {
+ ret << OPEN_HOST_BLOCK( red->prePushExpr );
+ INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false );
+ ret << CLOSE_HOST_BLOCK();
+ }
+
+ ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" <<
+ vCS() << " = " << OPEN_HOST_EXPR();
+ INLINE_LIST( ret, ilItem->children, targState, inFinish, false );
+ ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK();
+}
+
+void Goto::RET( ostream &ret, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];";
+
+ if ( red->postPopExpr != 0 ) {
+ ret << OPEN_HOST_BLOCK( red->postPopExpr );
+ INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false );
+ ret << CLOSE_HOST_BLOCK();
+ }
+
+ if ( inFinish && !noEnd )
+ EOF_CHECK( ret );
+
+ ret << "goto " << _again << ";" << CLOSE_GEN_BLOCK();
+}
+
+void Goto::NRET( ostream &ret, bool inFinish )
+{
+ ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];";
+
+ if ( red->postPopExpr != 0 ) {
+ ret << OPEN_HOST_BLOCK( red->postPopExpr );
+ INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false );
+ ret << CLOSE_HOST_BLOCK();
+ }
+
+ ret << CLOSE_GEN_BLOCK();
+}
+
+void Goto::BREAK( ostream &ret, int targState, bool csForced )
+{
+ ret << OPEN_GEN_BLOCK() << P() << " += 1; " << "goto " << _out << "; " << CLOSE_GEN_BLOCK();
+}
+
+void Goto::NBREAK( ostream &ret, int targState, bool csForced )
+{
+ ret << OPEN_GEN_BLOCK() << P() << " += 1; " << nbreak << " = 1; " << CLOSE_GEN_BLOCK();
+}
+
+void Goto::tableDataPass()
+{
+ if ( type == Loop )
+ taActions();
+
+ taToStateActions();
+ taFromStateActions();
+ taEofActions();
+
+ taNfaTargs();
+ taNfaOffsets();
+ taNfaPushActions();
+ taNfaPopTrans();
+}
+
+void Goto::genAnalysis()
+{
+ /* For directly executable machines there is no required state
+ * ordering. Choose a depth-first ordering to increase the
+ * potential for fall-throughs. */
+ redFsm->depthFirstOrdering();
+
+ /* Choose default transitions and the single transition. */
+ redFsm->chooseDefaultSpan();
+
+ /* Choose single. */
+ redFsm->moveSelectTransToSingle();
+
+ /* If any errors have occured in the input file then don't write anything. */
+ if ( red->id->errorCount > 0 )
+ return;
+
+ /* Anlayze Machine will find the final action reference counts, among other
+ * things. We will use these in reporting the usage of fsm directives in
+ * action code. */
+ red->analyzeMachine();
+
+ /* Run the analysis pass over the table data. */
+ setTableState( TableArray::AnalyzePass );
+ tableDataPass();
+
+ /* Switch the tables over to the code gen mode. */
+ setTableState( TableArray::GeneratePass );
+}
+
+void Goto::writeData()
+{
+ if ( type == Loop ) {
+ if ( redFsm->anyActions() )
+ taActions();
+ }
+
+ if ( redFsm->anyToStateActions() )
+ taToStateActions();
+
+ if ( redFsm->anyFromStateActions() )
+ taFromStateActions();
+
+ if ( redFsm->anyEofActions() )
+ taEofActions();
+
+ taNfaTargs();
+ taNfaOffsets();
+ taNfaPushActions();
+ taNfaPopTrans();
+
+ STATE_IDS();
+}
+
+void Goto::writeExec()
+{
+ int maxCtrId = redFsm->nextCondId > redFsm->nextTransId ? redFsm->nextCondId : redFsm->nextTransId;
+ ctrLabel = allocateLabels( ctrLabel, IpLabel::Ctr, maxCtrId );
+
+ out << "{\n";
+
+ DECLARE( INT(), cpc );
+ DECLARE( INT(), ck );
+ DECLARE( INT(), pop_test );
+ DECLARE( INT(), nbreak );
+ DECLARE( INT(), ps, " = 0" );
+ DECLARE( INT(), new_recs );
+ DECLARE( INT(), alt );
+ DECLARE( INDEX( ARR_TYPE( actions ) ), acts );
+ DECLARE( UINT(), nacts );
+
+ out << "\n";
+
+ out << EMIT_LABEL( _resume );
+
+ /* Do we break out on no more input. */
+ bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates();
+ if ( !noEnd ) {
+ if ( eof ) {
+ out <<
+ " if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n"
+ " goto " << _out << ";\n";
+ }
+ else {
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto " << _out << ";\n";
+ }
+ }
+
+ NFA_PUSH( vCS() );
+
+ out <<
+ " switch ( " << vCS() << " ) {\n";
+ STATE_CASES() <<
+ " }\n"
+ "\n";
+ TRANSITIONS() <<
+ "\n";
+
+ if ( redFsm->anyRegActions() )
+ EXEC_FUNCS() << "\n";
+
+ out << EMIT_LABEL( _again );
+
+ if ( !noEnd && eof ) {
+ out <<
+ " if ( " << P() << " == " << vEOF() << " ) {\n"
+ " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n"
+ " goto " << _out << ";\n"
+ " }\n"
+ " else {\n";
+ }
+
+ TO_STATE_ACTIONS();
+
+ if ( redFsm->errState != 0 ) {
+ out <<
+ " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n";
+ }
+
+ out <<
+ " " << P() << " += 1;\n"
+ " goto " << _resume << ";\n";
+
+ if ( redFsm->errState != 0 ) {
+ out <<
+ " }\n";
+ }
+
+ if ( !noEnd && eof ) {
+ out <<
+ " }\n";
+ }
+
+ if ( redFsm->anyNfaStates() ) {
+ out <<
+ " if ( nfa_len == 0 )\n"
+ " goto " << _out << ";\n"
+ "\n"
+ " nfa_count += 1;\n"
+ " nfa_len -= 1;\n"
+ " " << P() << " = nfa_bp[nfa_len].p;\n"
+ ;
+
+ if ( redFsm->bAnyNfaPops ) {
+ NFA_FROM_STATE_ACTION_EXEC();
+
+ NFA_POP_TEST_EXEC();
+
+ out <<
+ " if ( " << pop_test << " )\n"
+ " " << vCS() << " = nfa_bp[nfa_len].state;\n"
+ " else\n"
+ " " << vCS() << " = " << ERROR_STATE() << ";\n";
+ }
+ else {
+ out <<
+ " " << vCS() << " = nfa_bp[nfa_len].state;\n";
+
+ }
+
+ NFA_POST_POP();
+
+ out << "goto " << _resume << ";\n";
+ }
+
+ out << EMIT_LABEL( _out );
+
+ out << "}\n";
+}