diff options
Diffstat (limited to 'src/libfsm/ipgoto.cc')
-rw-r--r-- | src/libfsm/ipgoto.cc | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/src/libfsm/ipgoto.cc b/src/libfsm/ipgoto.cc new file mode 100644 index 00000000..3718ea91 --- /dev/null +++ b/src/libfsm/ipgoto.cc @@ -0,0 +1,764 @@ +/* + * 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 "ipgoto.h" +#include "redfsm.h" +#include "gendata.h" +#include "bstmap.h" +#include "parsedata.h" + +#include <sstream> + +using std::ostringstream; + +void IpGoto::tableDataPass() +{ + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void IpGoto::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; + + redFsm->setInTrans(); + + /* 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 ); +} + +bool IpGoto::useAgainLabel() +{ + return redFsm->anyActionRets() || + redFsm->anyActionByValControl() || + redFsm->anyRegNextStmt(); +} + +void IpGoto::GOTO( ostream &ret, int gotoDest, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + ret << "goto " << stLabel[gotoDest].reference() << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::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() << ";"; + + ret << " goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::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() << "] = " << targState << + "; " << TOP() << "+= 1; "; + + ret << "goto " << stLabel[callDest].reference() << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::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() << "] = " << targState << + "; " << TOP() << "+= 1; " << vCS() << " = " << callDest << "; " << + CLOSE_GEN_BLOCK(); +} + +void IpGoto::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() << "] = " << targState << "; " << TOP() << "+= 1;" << + vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + ret << " goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::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() << "] = " << targState << "; " << TOP() << "+= 1;" << + vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); +} + +void IpGoto::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(); + } + + ret << "goto " << _again << ";" << CLOSE_GEN_BLOCK(); +} + +void IpGoto::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 IpGoto::NEXT( ostream &ret, int nextDest, bool inFinish ) +{ + ret << vCS() << " = " << nextDest << ";"; +} + +void IpGoto::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << vCS() << " = ("; + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << ");"; +} + +void IpGoto::CURS( ostream &ret, bool inFinish ) +{ + ret << "(" << ps << ")"; +} + +void IpGoto::TARGS( ostream &ret, bool inFinish, int targState ) +{ + ret << targState; +} + +void IpGoto::BREAK( ostream &ret, int targState, bool csForced ) +{ + ret << OPEN_GEN_BLOCK() << P() << "+= 1; "; + if ( !csForced ) + ret << vCS() << " = " << targState << "; "; + ret << "goto " << _out << ";" << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NBREAK( ostream &ret, int targState, bool csForced ) +{ + ret << OPEN_GEN_BLOCK() << P() << "+= 1; "; + if ( !csForced ) + ret << vCS() << " = " << targState << "; "; + ret << nbreak << " = 1;" << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NFA_PUSH_ACTION( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->push != 0 ) + act = targ->push->actListId+1; + nfaPushActions.value( act ); +} + +void IpGoto::NFA_POP_TEST( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->popTest != 0 ) + act = targ->popTest->actListId+1; + nfaPopTrans.value( act ); +} + + +bool IpGoto::IN_TRANS_ACTIONS( RedStateAp *state ) +{ + bool anyWritten = false; + + /* Emit any transitions that have actions and that go to this state. */ + for ( int it = 0; it < state->numInConds; it++ ) { + RedCondPair *trans = state->inConds[it]; + if ( trans->action != 0 ) { + /* Remember that we wrote an action so we know to write the + * line directive for going back to the output. */ + anyWritten = true; + + /* Write the label for the transition so it can be jumped to. */ + if ( ctrLabel[trans->id].isReferenced ) + out << "_ctr" << trans->id << ":\n"; + + /* If the action contains a next, then we must preload the current + * state since the action may or may not set it. */ + if ( trans->action->anyNextStmt() ) + out << " " << vCS() << " = " << trans->targ->id << ";\n"; + + if ( redFsm->anyRegNbreak() ) + out << nbreak << " = 0;\n"; + + /* Write each action in the list. */ + for ( GenActionTable::Iter item = trans->action->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( trans->targ->id, false, + trans->action->anyNextStmt() ) ); + out << "\n"; + } + + if ( redFsm->anyRegNbreak() ) { + out << + "if ( " << nbreak << " == 1 )\n" + " goto " << _out << ";\n"; + } + + + /* If the action contains a next then we need to reload, otherwise + * jump directly to the target state. */ + if ( trans->action->anyNextStmt() ) + out << "goto " << _again << ";\n"; + else + out << "goto " << stLabel[trans->targ->id].reference() << ";\n"; + } + } + + + return anyWritten; +} + +void IpGoto::GOTO_HEADER( RedStateAp *state ) +{ +} + +void IpGoto::STATE_GOTO_ERROR() +{ +} + + +/* Emit the goto to take for a given transition. */ +std::ostream &IpGoto::TRANS_GOTO( RedTransAp *trans ) +{ + if ( trans->condSpace == 0 || trans->condSpace->condSet.length() == 0 ) { + /* Existing. */ + assert( trans->numConds() == 1 ); + RedCondPair *cond = trans->outCond( 0 ); + if ( cond->action != 0 ) { + /* Go to the transition which will go to the state. */ + out << "goto " << ctrLabel[trans->p.id].reference() << ";"; + } + else { + /* Go directly to the target state. */ + out << "goto " << stLabel[cond->targ->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; +} + +/* Emit the goto to take for a given transition. */ +std::ostream &IpGoto::COND_GOTO( RedCondPair *cond ) +{ + /* Existing. */ + if ( cond->action != 0 ) { + /* Go to the transition which will go to the state. */ + out << "goto " << ctrLabel[cond->id].reference() << ";"; + } + else { + /* Go directly to the target state. */ + out << "goto " << stLabel[cond->targ->id].reference() << ";"; + } + + return out; +} + +std::ostream &IpGoto::EXIT_STATES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( outLabel[st->id].isReferenced ) { + out << outLabel[st->id].define() << ": " << vCS() << " = " << + st->id << "; goto " << _out << "; \n"; + } + if ( popLabel[st->id].isReferenced ) { + out << popLabel[st->id].define() << ": " << vCS() << " = " << + st->id << "; goto " << _pop << "; \n"; + } + } + return out; +} + +std::ostream &IpGoto::AGAIN_CASES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + out << + "case " << st->id << ": goto " << stLabel[st->id].reference() << ";\n"; + } + return out; +} + +std::ostream &IpGoto::STATE_GOTO_CASES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + out << "case " << st->id << ":\n"; + out << "goto st_case_" << st->id << ";\n"; + } + return out; +} + +void IpGoto::NFA_PUSH_ST( RedStateAp *state ) +{ + std::stringstream ss; + ss << state->id; + std::string _state = ss.str(); + + if ( redFsm->anyNfaStates() ) { + + if ( state->nfaTargs != 0 ) { + out << + "if ( " << ARR_REF( nfaOffsets ) << "[" << _state << "] != 0 ) {\n"; + + if ( red->nfaPrePushExpr != 0 ) { + out << + new_recs << " = " << state->nfaTargs->length() << ";\n"; + } + + if ( red->nfaPrePushExpr != 0 ) { + out << OPEN_HOST_BLOCK( red->nfaPrePushExpr ); + INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false ); + out << CLOSE_HOST_BLOCK(); + } + + int alt = 0; + for ( RedNfaTargs::Iter nt = *state->nfaTargs; nt.lte(); nt++ ) { + out << + " nfa_bp[nfa_len].state = " << nt->state->id << ";\n" + " nfa_bp[nfa_len].p = " << P() << ";\n"; + + if ( nt->popTest != 0 ) { + out << + " nfa_bp[nfa_len].popTrans = " << (nt->popTest->actListId+1) << ";\n"; + } + else if ( redFsm->bAnyNfaPops ) { + out << + " nfa_bp[nfa_len].popTrans = 0;\n"; + } + + if ( nt->push != 0 ) { + for ( GenActionTable::Iter item = nt->push->key; item.lte(); item++ ) + ACTION( out, item->value, IlOpts( 0, false, false ) ); + } + + out << + " nfa_len += 1;\n"; + + alt += 1; + } + + out << + "}\n"; + } + } +} + +std::ostream &IpGoto::STATE_GOTOS() +{ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + IN_TRANS_ACTIONS( st ); + + if ( stLabel[st->id].isReferenced ) + out << "_st" << st->id << ":\n"; + + /* Need to do this if the transition is an eof transition, or if + * the action contains fexec. Otherwise, no need. */ + if ( eof ) { + out << + "if ( " << P() << " == " << vEOF() << " )\n"; + + if ( st->isFinal || !redFsm->anyNfaStates() ) + out << "goto " << outLabel[st->id].reference() << ";\n"; + else + out << "goto " << popLabel[st->id].reference() << ";\n"; + } + + if ( st->toStateAction != 0 ) { + /* Write every action in the list. */ + for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( st->id, false, + st->toStateAction->anyNextStmt() ) ); + out << "\n"; + } + } + + if ( st == redFsm->errState ) { + out << "st_case_" << st->id << ":\n"; + + /* Break out here. */ + if ( !redFsm->anyNfaStates() ) + out << "goto " << outLabel[st->id].reference() << ";\n"; + else + out << "goto " << popLabel[st->id].reference() << ";\n"; + } + else { + + /* Advance and test buffer pos. */ + if ( st->labelNeeded ) { + out << + P() << "+= 1;\n"; + } + + /* Give the st a switch case. */ + out << "st_case_" << st->id << ":\n"; + + if ( !noEnd ) { + if ( eof ) { + out << + "if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n" + " goto " << outLabel[st->id].reference() << ";\n"; + } + else { + out << + "if ( " << P() << " == " << PE() << " )\n" + " goto " << outLabel[st->id].reference() << ";\n"; + } + } + + + NFA_PUSH_ST( st ); + + if ( st->fromStateAction != 0 ) { + /* Write every action in the list. */ + for ( GenActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( st->id, false, + st->fromStateAction->anyNextStmt() ) ); + out << "\n"; + } + } + + if ( !noEnd && eof ) { + out << + "if ( " << P() << " == " << vEOF() << " ) {\n"; + + if ( st->eofTrans != 0 ) + TRANS_GOTO( st->eofTrans ); + else { + if ( st->isFinal || !redFsm->anyNfaStates() ) + out << "goto " << outLabel[st->id].reference() << ";\n"; + else + out << "goto " << popLabel[st->id].reference() << ";\n"; + } + + out << + "}\n" + "else {\n"; + } + + /* Record the prev st if necessary. */ + if ( st->anyRegCurStateRef() ) + out << ps << " = " << st->id << ";\n"; + + /* 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 &IpGoto::FINISH_CASES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + out << + "case " << st->id << ":\n"; + + TRANS_GOTO( st->eofTrans ); + } + } + + return out; +} + +void IpGoto::setLabelsNeeded( GenInlineList *inlineList ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + switch ( item->type ) { + case GenInlineItem::Goto: + case GenInlineItem::Call: + case GenInlineItem::Ncall: { + /* Mark the target as needing a label. */ + item->targState->labelNeeded = true; + break; + } + default: break; + } + + if ( item->children != 0 ) + setLabelsNeeded( item->children ); + } +} + +void IpGoto::setLabelsNeeded( RedCondPair *pair ) +{ + /* If there is no action with a next statement, then the label will be + * needed. */ + if ( pair->action == 0 || !pair->action->anyNextStmt() ) + pair->targ->labelNeeded = true; + + /* Need labels for states that have goto or calls in action code + * invoked on characters (ie, not from out action code). */ + if ( pair->action != 0 ) { + /* Loop the actions. */ + for ( GenActionTable::Iter act = pair->action->key; act.lte(); act++ ) { + /* Get the action and walk it's tree. */ + setLabelsNeeded( act->value->inlineList ); + } + } +} + +/* Set up labelNeeded flag for each state. */ +void IpGoto::setLabelsNeeded() +{ + /* If we use the _again label, then we generate the _again switch, which + * uses all labels. */ + if ( useAgainLabel() ) { + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->labelNeeded = true; + } + else { + /* Do not use all labels by default, init all labelNeeded vars to false. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->labelNeeded = false; + + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + if ( trans->condSpace == 0 ) + setLabelsNeeded( &trans->p ); + } + + for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ ) + setLabelsNeeded( &cond->p ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofAction != 0 ) { + for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) + setLabelsNeeded( item->value->inlineList ); + } + } + } +} + +void IpGoto::writeData() +{ + STATE_IDS(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void IpGoto::NFA_FROM_STATE_ACTION_EXEC() +{ +// if ( redFsm->anyFromStateActions() ) { +// /* Unimplemented feature. Don't have the from state actions array in +// * this mode. Need to add it, or to alter the NFA pop codegen to be +// * consistent with the mode. */ +// assert( false ); +// } +} + +void IpGoto::writeExec() +{ + int maxCtrId = redFsm->nextCondId > redFsm->nextTransId ? redFsm->nextCondId : redFsm->nextTransId; + + stLabel = allocateLabels( stLabel, IpLabel::St, redFsm->nextStateId ); + ctrLabel = allocateLabels( ctrLabel, IpLabel::Ctr, maxCtrId ); + outLabel = allocateLabels( outLabel, IpLabel::Out, redFsm->nextStateId ); + popLabel = allocateLabels( popLabel, IpLabel::Pop, redFsm->nextStateId ); + + /* Must set labels immediately before writing because we may depend on the + * noend write option. */ + setLabelsNeeded(); + + out << "{\n"; + + DECLARE( INT(), cpc ); + DECLARE( INT(), ck ); + DECLARE( INT(), pop_test ); + DECLARE( INT(), nbreak ); + DECLARE( INT(), ps ); + DECLARE( INT(), new_recs ); + DECLARE( INT(), alt ); + + if ( _again.isReferenced ) { + out << + " goto " << _resume << ";\n" + "\n"; + + out << EMIT_LABEL( _again ); + + out << + " switch ( " << vCS() << " ) {\n"; + AGAIN_CASES() << + " }\n" + "\n"; + + } + + out << EMIT_LABEL( _resume ); + + out << "switch ( " << vCS() << " ) {\n"; + + STATE_GOTO_CASES(); + + out << "}\n"; + + STATE_GOTOS(); + + EXIT_STATES(); + + out << EMIT_LABEL( _pop ); + + if ( redFsm->anyNfaStates() ) { + out << + "if ( nfa_len == 0 )\n" + " goto " << _out << ";\n" + "\n"; + + out << + "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"; +} |