/* * Copyright 2002-2018 Adrian Thurston * * 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 "fsmgraph.h" #include using std::endl; /* Insert an action into an action table. */ void ActionTable::setAction( int ordering, Action *action ) { /* Multi-insert in case specific instances of an action appear in a * transition more than once. */ insertMulti( ordering, action ); } /* Set all the action from another action table in this table. */ void ActionTable::setActions( const ActionTable &other ) { for ( ActionTable::Iter action = other; action.lte(); action++ ) insertMulti( action->key, action->value ); } void ActionTable::setActions( int *orderings, Action **actions, int nActs ) { for ( int a = 0; a < nActs; a++ ) insertMulti( orderings[a], actions[a] ); } bool ActionTable::hasAction( Action *action ) { for ( int a = 0; a < length(); a++ ) { if ( data[a].value == action ) return true; } return false; } /* Insert an action into an action table. */ void LmActionTable::setAction( int ordering, LongestMatchPart *action ) { /* Multi-insert in case specific instances of an action appear in a * transition more than once. */ insertMulti( ordering, action ); } /* Set all the action from another action table in this table. */ void LmActionTable::setActions( const LmActionTable &other ) { for ( LmActionTable::Iter action = other; action.lte(); action++ ) insertMulti( action->key, action->value ); } void ErrActionTable::setAction( int ordering, Action *action, int transferPoint ) { insertMulti( ErrActionTableEl( action, ordering, transferPoint ) ); } void ErrActionTable::setActions( const ErrActionTable &other ) { for ( ErrActionTable::Iter act = other; act.lte(); act++ ) insertMulti( ErrActionTableEl( act->action, act->ordering, act->transferPoint ) ); } /* Insert a priority into this priority table. Looks out for priorities on * duplicate keys. */ void PriorTable::setPrior( int ordering, PriorDesc *desc ) { PriorEl *lastHit = 0; PriorEl *insed = insert( PriorEl(ordering, desc), &lastHit ); if ( insed == 0 ) { /* This already has a priority on the same key as desc. Overwrite the * priority if the ordering is larger (later in time). */ if ( ordering >= lastHit->ordering ) *lastHit = PriorEl( ordering, desc ); } } /* Set all the priorities from a priorTable in this table. */ void PriorTable::setPriors( const PriorTable &other ) { /* Loop src priorities once to overwrite duplicates. */ PriorTable::Iter priorIt = other; for ( ; priorIt.lte(); priorIt++ ) setPrior( priorIt->ordering, priorIt->desc ); } /* Set the priority of starting transitions. Isolates the start state so it has * no other entry points, then sets the priorities of all the transitions out * of the start state. If the start state is final, then the outPrior of the * start state is also set. The idea is that a machine that accepts the null * string can still specify the starting trans prior for when it accepts the * null word. */ void FsmAp::startFsmPrior( int ordering, PriorDesc *prior ) { /* Make sure the start state has no other entry points. */ isolateStartState( this ); /* Walk all transitions out of the start state. */ for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState != 0 ) trans->tdap()->priorTable.setPrior( ordering, prior ); } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState != 0 ) cond->priorTable.setPrior( ordering, prior ); } } } if ( startState->nfaOut != 0 ) { for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) na->priorTable.setPrior( ordering, prior ); } /* If the new start state is final then set the out priority. This follows * the same convention as setting start action in the out action table of * a final start state. */ if ( startState->stateBits & STB_ISFINAL ) startState->outPriorTable.setPrior( ordering, prior ); /* Start fsm priorities are a special case that may require * minimization afterwards. */ afterOpMinimize( true ); } /* Set the priority of all transitions in a graph. Walks all transition lists * and all def transitions. */ void FsmAp::allTransPrior( int ordering, PriorDesc *prior ) { /* Walk the list of all states. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) { /* Walk the out list of the state. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState != 0 ) trans->tdap()->priorTable.setPrior( ordering, prior ); } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState != 0 ) cond->priorTable.setPrior( ordering, prior ); } } } if ( state->nfaOut != 0 ) { for ( NfaTransList::Iter na = *state->nfaOut; na.lte(); na++ ) na->priorTable.setPrior( ordering, prior ); } } } /* Set the priority of all transitions that go into a final state. Note that if * any entry states are final, we will not be setting the priority of any * transitions that may go into those states in the future. The graph does not * support pending in transitions in the same way pending out transitions are * supported. */ void FsmAp::finishFsmPrior( int ordering, PriorDesc *prior ) { /* Walk all final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { /* Walk all in transitions of the final state. */ for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) t->priorTable.setPrior( ordering, prior ); for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) t->priorTable.setPrior( ordering, prior ); if ( (*state)->nfaIn != 0 ) { for ( NfaInList::Iter na = *(*state)->nfaIn; na.lte(); na++ ) na->priorTable.setPrior( ordering, prior ); } } } /* Set the priority of any future out transitions that may be made going out of * this state machine. */ void FsmAp::leaveFsmPrior( int ordering, PriorDesc *prior ) { /* Set priority in all final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) (*state)->outPriorTable.setPrior( ordering, prior ); } /* Set actions to execute on starting transitions. Isolates the start state * so it has no other entry points, then adds to the transition functions * of all the transitions out of the start state. If the start state is final, * then the func is also added to the start state's out func list. The idea is * that a machine that accepts the null string can execute a start func when it * matches the null word, which can only be done when leaving the start/final * state. */ void FsmAp::startFsmAction( int ordering, Action *action ) { /* Make sure the start state has no other entry points. */ isolateStartState( this ); /* Walk the start state's transitions, setting functions. */ for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState != 0 ) trans->tdap()->actionTable.setAction( ordering, action ); } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState != 0 ) cond->actionTable.setAction( ordering, action ); } } } /* If start state is final then add the action to the out action table. * This means that when the null string is accepted the start action will * not be bypassed. */ if ( startState->stateBits & STB_ISFINAL ) startState->outActionTable.setAction( ordering, action ); if ( startState->nfaOut != 0 ) { for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) { StateAp *state = na->toState; /* Walk the start state's transitions, setting functions. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState != 0 ) trans->tdap()->actionTable.setAction( ordering, action ); } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState != 0 ) cond->actionTable.setAction( ordering, action ); } } } /* If start state is final then add the action to the out action table. * This means that when the null string is accepted the start action will * not be bypassed. */ if ( state->stateBits & STB_ISFINAL ) state->outActionTable.setAction( ordering, action ); } } afterOpMinimize( true ); } /* Set functions to execute on all transitions. Walks the out lists of all * states. */ void FsmAp::allTransAction( int ordering, Action *action ) { /* Walk all states. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) { /* Walk the out list of the state. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState != 0 ) trans->tdap()->actionTable.setAction( ordering, action ); } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState != 0 ) cond->actionTable.setAction( ordering, action ); } } } } } /* Specify functions to execute upon entering final states. If the start state * is final we can't really specify a function to execute upon entering that * final state the first time. So function really means whenever entering a * final state from within the same fsm. */ void FsmAp::finishFsmAction( int ordering, Action *action ) { /* Walk all final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { /* Walk the final state's in list. */ for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) t->actionTable.setAction( ordering, action ); for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) t->actionTable.setAction( ordering, action ); } } /* Add functions to any future out transitions that may be made going out of * this state machine. */ void FsmAp::leaveFsmAction( int ordering, Action *action ) { /* Insert the action in the outActionTable of all final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) (*state)->outActionTable.setAction( ordering, action ); } /* Add functions to the longest match action table for constructing scanners. */ void FsmAp::longMatchAction( int ordering, LongestMatchPart *lmPart ) { /* Walk all final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { /* Walk the final state's in list. */ for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) t->lmActionTable.setAction( ordering, lmPart ); for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) t->lmActionTable.setAction( ordering, lmPart ); } } void FsmAp::fillGaps( StateAp *state ) { /* * First pass fills in the the caps between transitions. */ if ( state->outList.length() == 0 ) { /* Add the range on the lower and upper bound. */ attachNewTrans( state, 0, ctx->keyOps->minKey, ctx->keyOps->maxKey ); } else { TransList srcList; srcList.transfer( state->outList ); /* Check for a gap at the beginning. */ TransList::Iter trans = srcList, next; if ( ctx->keyOps->lt( ctx->keyOps->minKey, trans->lowKey ) ) { /* Make the high key and append. */ Key highKey = trans->lowKey; ctx->keyOps->decrement( highKey ); attachNewTrans( state, 0, ctx->keyOps->minKey, highKey ); } /* Write the transition. */ next = trans.next(); state->outList.append( trans ); /* Keep the last high end. */ Key lastHigh = trans->highKey; /* Loop each source range. */ for ( trans = next; trans.lte(); trans = next ) { /* Make the next key following the last range. */ Key nextKey = lastHigh; ctx->keyOps->increment( nextKey ); /* Check for a gap from last up to here. */ if ( ctx->keyOps->lt( nextKey, trans->lowKey ) ) { /* Make the high end of the range that fills the gap. */ Key highKey = trans->lowKey; ctx->keyOps->decrement( highKey ); attachNewTrans( state, 0, nextKey, highKey ); } /* Reduce the transition. If it reduced to anything then add it. */ next = trans.next(); state->outList.append( trans ); /* Keep the last high end. */ lastHigh = trans->highKey; } /* Now check for a gap on the end to fill. */ if ( ctx->keyOps->lt( lastHigh, ctx->keyOps->maxKey ) ) { /* Get a copy of the default. */ ctx->keyOps->increment( lastHigh ); attachNewTrans( state, 0, lastHigh, ctx->keyOps->maxKey ); } } /* * Second pass fills in gaps in condition lists. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) continue; CondList srcList; srcList.transfer( trans->tcap()->condList ); CondList::Iter cond = srcList, next; /* Check for gap at the beginning. */ if ( cond->key > 0 ) { for ( CondKey key = 0; key < cond->key; key.increment() ) attachNewCond( trans, state, 0, key ); } next = cond.next(); trans->tcap()->condList.append( cond ); CondKey lastKey = cond->key; for ( cond = next; cond.lte(); cond = next ) { /* Make the next key following the last range. */ CondKey nextKey = lastKey; nextKey.increment(); /* Check for a gap from last up to here. */ if ( nextKey < cond->key ) { for ( CondKey key = nextKey; key < cond->key; key.increment() ) attachNewCond( trans, state, 0, key ); } next = cond.next(); trans->tcap()->condList.append( cond ); lastKey = cond->key; } CondKey high = (trans->condSpace == 0) ? 0 : (1 << trans->condSpace->condSet.length()); /* Now check for a gap on the end to fill. */ if ( lastKey < high ) { /* Get a copy of the default. */ lastKey.increment(); for ( CondKey key = lastKey; key < high; key.increment() ) attachNewCond( trans, state, 0, key ); } } } void FsmAp::setErrorActions( StateAp *state, const ActionTable &other ) { /* Fill any gaps in the out list with an error transition. */ fillGaps( state ); /* Set error transitions in the transitions that go to error. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState == 0 ) trans->tdap()->actionTable.setActions( other ); } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState == 0 ) cond->actionTable.setActions( other ); } } } } void FsmAp::setErrorAction( StateAp *state, int ordering, Action *action ) { /* Fill any gaps in the out list with an error transition. */ fillGaps( state ); /* Set error transitions in the transitions that go to error. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState == 0 ) trans->tdap()->actionTable.setAction( ordering, action ); } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState == 0 ) cond->actionTable.setAction( ordering, action ); } } } } /* Give a target state for error transitions. */ void FsmAp::setErrorTarget( StateAp *state, StateAp *target, int *orderings, Action **actions, int nActs ) { /* Fill any gaps in the out list with an error transition. */ fillGaps( state ); /* Set error target in the transitions that go to error. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { if ( trans->tdap()->toState == 0 ) { /* The trans goes to error, redirect it. */ redirectErrorTrans( trans->tdap()->fromState, target, trans->tdap() ); trans->tdap()->actionTable.setActions( orderings, actions, nActs ); } } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { if ( cond->toState == 0 ) { /* The trans goes to error, redirect it. */ redirectErrorTrans( cond->fromState, target, cond ); cond->actionTable.setActions( orderings, actions, nActs ); } } } } } void FsmAp::transferOutActions( StateAp *state ) { for ( ActionTable::Iter act = state->outActionTable; act.lte(); act++ ) state->eofActionTable.setAction( act->key, act->value ); state->outActionTable.empty(); } void FsmAp::transferErrorActions( StateAp *state, int transferPoint ) { for ( int i = 0; i < state->errActionTable.length(); ) { ErrActionTableEl *act = state->errActionTable.data + i; if ( act->transferPoint == transferPoint ) { /* Transfer the error action and remove it. */ setErrorAction( state, act->ordering, act->action ); if ( ! state->isFinState() ) state->eofActionTable.setAction( act->ordering, act->action ); state->errActionTable.vremove( i ); } else { /* Not transfering and deleting, skip over the item. */ i += 1; } } } /* Set error actions in the start state. */ void FsmAp::startErrorAction( int ordering, Action *action, int transferPoint ) { /* Make sure the start state has no other entry points. */ isolateStartState( this ); /* Add the actions. */ startState->errActionTable.setAction( ordering, action, transferPoint ); afterOpMinimize( true ); } /* Set error actions in all states where there is a transition out. */ void FsmAp::allErrorAction( int ordering, Action *action, int transferPoint ) { /* Insert actions in the error action table of all states. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) state->errActionTable.setAction( ordering, action, transferPoint ); } /* Set error actions in final states. */ void FsmAp::finalErrorAction( int ordering, Action *action, int transferPoint ) { /* Add the action to the error table of final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) (*state)->errActionTable.setAction( ordering, action, transferPoint ); } void FsmAp::notStartErrorAction( int ordering, Action *action, int transferPoint ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState ) state->errActionTable.setAction( ordering, action, transferPoint ); } } void FsmAp::notFinalErrorAction( int ordering, Action *action, int transferPoint ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( ! state->isFinState() ) state->errActionTable.setAction( ordering, action, transferPoint ); } } /* Set error actions in the states that have transitions into a final state. */ void FsmAp::middleErrorAction( int ordering, Action *action, int transferPoint ) { /* Isolate the start state in case it is reachable from in inside the * machine, in which case we don't want it set. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState && ! state->isFinState() ) state->errActionTable.setAction( ordering, action, transferPoint ); } } /* Set EOF actions in the start state. */ void FsmAp::startEOFAction( int ordering, Action *action ) { /* Make sure the start state has no other entry points. */ isolateStartState( this ); /* Add the actions. */ startState->eofActionTable.setAction( ordering, action ); afterOpMinimize( true ); } /* Set EOF actions in all states where there is a transition out. */ void FsmAp::allEOFAction( int ordering, Action *action ) { /* Insert actions in the EOF action table of all states. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) state->eofActionTable.setAction( ordering, action ); } /* Set EOF actions in final states. */ void FsmAp::finalEOFAction( int ordering, Action *action ) { /* Add the action to the error table of final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) (*state)->eofActionTable.setAction( ordering, action ); } void FsmAp::notStartEOFAction( int ordering, Action *action ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState ) state->eofActionTable.setAction( ordering, action ); } } void FsmAp::notFinalEOFAction( int ordering, Action *action ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( ! state->isFinState() ) state->eofActionTable.setAction( ordering, action ); } } /* Set EOF actions in the states that have transitions into a final state. */ void FsmAp::middleEOFAction( int ordering, Action *action ) { /* Set the actions in all states that are not the start state and not final. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState && ! state->isFinState() ) state->eofActionTable.setAction( ordering, action ); } } /* * Set To State Actions. */ /* Set to state actions in the start state. */ void FsmAp::startToStateAction( int ordering, Action *action ) { /* Make sure the start state has no other entry points. */ isolateStartState( this ); startState->toStateActionTable.setAction( ordering, action ); afterOpMinimize( true ); } /* Set to state actions in all states. */ void FsmAp::allToStateAction( int ordering, Action *action ) { /* Insert the action on all states. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) state->toStateActionTable.setAction( ordering, action ); } /* Set to state actions in final states. */ void FsmAp::finalToStateAction( int ordering, Action *action ) { /* Add the action to the error table of final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) (*state)->toStateActionTable.setAction( ordering, action ); } void FsmAp::notStartToStateAction( int ordering, Action *action ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState ) state->toStateActionTable.setAction( ordering, action ); } } void FsmAp::notFinalToStateAction( int ordering, Action *action ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( ! state->isFinState() ) state->toStateActionTable.setAction( ordering, action ); } } /* Set to state actions in states that are not final and not the start state. */ void FsmAp::middleToStateAction( int ordering, Action *action ) { /* Set the action in all states that are not the start state and not final. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState && ! state->isFinState() ) state->toStateActionTable.setAction( ordering, action ); } } /* * Set From State Actions. */ void FsmAp::startFromStateAction( int ordering, Action *action ) { /* Make sure the start state has no other entry points. */ isolateStartState( this ); startState->fromStateActionTable.setAction( ordering, action ); afterOpMinimize( true ); } void FsmAp::allFromStateAction( int ordering, Action *action ) { /* Insert the action on all states. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) state->fromStateActionTable.setAction( ordering, action ); } void FsmAp::finalFromStateAction( int ordering, Action *action ) { /* Add the action to the error table of final states. */ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) (*state)->fromStateActionTable.setAction( ordering, action ); } void FsmAp::notStartFromStateAction( int ordering, Action *action ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState ) state->fromStateActionTable.setAction( ordering, action ); } } void FsmAp::notFinalFromStateAction( int ordering, Action *action ) { for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( ! state->isFinState() ) state->fromStateActionTable.setAction( ordering, action ); } } void FsmAp::middleFromStateAction( int ordering, Action *action ) { /* Set the action in all states that are not the start state and not final. */ for ( StateList::Iter state = stateList; state.lte(); state++ ) { if ( state != startState && ! state->isFinState() ) state->fromStateActionTable.setAction( ordering, action ); } } /* Shift the function ordering of the start transitions to start * at fromOrder and increase in units of 1. Useful before staring. * Returns the maximum number of order numbers used. */ int FsmAp::shiftStartActionOrder( int fromOrder ) { int maxUsed = 0; /* Walk the start state's transitions, shifting function ordering. */ for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { int curFromOrder = fromOrder; ActionTable::Iter action = trans->tdap()->actionTable; for ( ; action.lte(); action++ ) action->key = curFromOrder++; /* Keep track of the max number of orders used. */ if ( curFromOrder - fromOrder > maxUsed ) maxUsed = curFromOrder - fromOrder; } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { /* Walk the function data for the transition and set the keys to * increasing values starting at fromOrder. */ int curFromOrder = fromOrder; ActionTable::Iter action = cond->actionTable; for ( ; action.lte(); action++ ) action->key = curFromOrder++; /* Keep track of the max number of orders used. */ if ( curFromOrder - fromOrder > maxUsed ) maxUsed = curFromOrder - fromOrder; } } } return maxUsed; } /* Remove all priorities. */ void FsmAp::clearAllPriorities() { for ( StateList::Iter state = stateList; state.lte(); state++ ) { /* Clear out priority data. */ state->outPriorTable.empty(); /* Clear transition data from the out transitions. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) trans->tdap()->priorTable.empty(); else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) cond->priorTable.empty(); } } if ( state->nfaIn != 0 ) { for ( NfaInList::Iter na = *state->nfaIn; na.lte(); na++ ) na->priorTable.empty(); } } } /* Zeros out the function ordering keys. This may be called before minimization * when it is known that no more fsm operations are going to be done. This * will achieve greater reduction as states will not be separated on the basis * of function ordering. */ void FsmAp::nullActionKeys( ) { /* For each state... */ for ( StateList::Iter state = stateList; state.lte(); state++ ) { /* Walk the transitions for the state. */ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { if ( trans->plain() ) { /* Walk the action table for the transition. */ for ( ActionTable::Iter action = trans->tdap()->actionTable; action.lte(); action++ ) action->key = 0; /* Walk the action table for the transition. */ for ( LmActionTable::Iter action = trans->tdap()->lmActionTable; action.lte(); action++ ) action->key = 0; } else { for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { /* Walk the action table for the transition. */ for ( ActionTable::Iter action = cond->actionTable; action.lte(); action++ ) action->key = 0; /* Walk the action table for the transition. */ for ( LmActionTable::Iter action = cond->lmActionTable; action.lte(); action++ ) action->key = 0; } } } /* Null the action keys of the to state action table. */ for ( ActionTable::Iter action = state->toStateActionTable; action.lte(); action++ ) action->key = 0; /* Null the action keys of the from state action table. */ for ( ActionTable::Iter action = state->fromStateActionTable; action.lte(); action++ ) action->key = 0; /* Null the action keys of the out transtions. */ for ( ActionTable::Iter action = state->outActionTable; action.lte(); action++ ) action->key = 0; /* Null the action keys of the error action table. */ for ( ErrActionTable::Iter action = state->errActionTable; action.lte(); action++ ) action->ordering = 0; /* Null the action keys eof action table. */ for ( ActionTable::Iter action = state->eofActionTable; action.lte(); action++ ) action->key = 0; } } /* Walk the list of states and verify that non final states do not have out * data, that all stateBits are cleared, and that there are no states with * zero foreign in transitions. */ void FsmAp::verifyStates() { for ( StateList::Iter state = stateList; state.lte(); state++ ) { /* Non final states should not have leaving data. */ if ( ! (state->stateBits & STB_ISFINAL) ) { assert( state->outActionTable.length() == 0 ); assert( state->outCondSpace == 0 ); assert( state->outCondKeys.length() == 0 ); assert( state->outPriorTable.length() == 0 ); } /* Data used in algorithms should be cleared. */ assert( (state->stateBits & STB_BOTH) == 0 ); assert( state->foreignInTrans > 0 ); } } /* Compare two transitions according to their relative priority. Since the * base transition has no priority associated with it, the default is to * return equal. */ int FsmAp::comparePrior( const PriorTable &priorTable1, const PriorTable &priorTable2 ) { /* Looking for differing priorities on same keys. Need to concurrently * scan the priority lists. */ PriorTable::Iter pd1 = priorTable1; PriorTable::Iter pd2 = priorTable2; while ( pd1.lte() && pd2.lte() ) { /* Check keys. */ if ( pd1->desc->key < pd2->desc->key ) pd1.increment(); else if ( pd1->desc->key > pd2->desc->key ) pd2.increment(); /* Keys are the same, check priorities. */ else if ( pd1->desc->priority < pd2->desc->priority ) { if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { if ( ! priorInteraction ) { priorInteraction = true; guardId = pd1->desc->guardId; } } return -1; } else if ( pd1->desc->priority > pd2->desc->priority ) { if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { if ( ! priorInteraction ) { priorInteraction = true; guardId = pd1->desc->guardId; } } return 1; } else { /* Keys and priorities are equal, advance both. */ pd1.increment(); pd2.increment(); } } /* No differing priorities on the same key. */ return 0; } int FsmAp::compareCondListBitElim( const CondList &condList1, const CondList &condList2 ) { typedef ValPairIter< PiList > ValPairIterPiListCondAp; ValPairIterPiListCondAp outPair( condList1, condList2 ); for ( ; !outPair.end(); outPair++ ) { switch ( outPair.userState ) { case ValPairIterPiListCondAp::RangeInS1: { int compareRes = FsmAp::compareCondBitElimPtr( outPair.s1Tel.trans, 0 ); if ( compareRes != 0 ) return compareRes; break; } case ValPairIterPiListCondAp::RangeInS2: { int compareRes = FsmAp::compareCondBitElimPtr( 0, outPair.s2Tel.trans ); if ( compareRes != 0 ) return compareRes; break; } case ValPairIterPiListCondAp::RangeOverlap: { int compareRes = FsmAp::compareCondBitElimPtr( outPair.s1Tel.trans, outPair.s2Tel.trans ); if ( compareRes != 0 ) return compareRes; break; }} } return 0; } /* Compares two transitions according to priority and functions. Pointers * should not be null. Does not consider to state or from state. Compare two * transitions according to the data contained in the transitions. Data means * any properties added to user transitions that may differentiate them. Since * the base transition has no data, the default is to return equal. */ int FsmAp::compareTransData( TransAp *trans1, TransAp *trans2 ) { if ( trans1->condSpace < trans2->condSpace ) return -1; else if ( trans2->condSpace < trans1->condSpace ) return 1; if ( trans1->plain() ) { int compareRes = FsmAp::compareCondDataPtr( trans1->tdap(), trans2->tdap() ); if ( compareRes != 0 ) return compareRes; } else { typedef ValPairIter< PiList > ValPairIterPiListCondAp; ValPairIterPiListCondAp outPair( trans1->tcap()->condList, trans2->tcap()->condList ); for ( ; !outPair.end(); outPair++ ) { switch ( outPair.userState ) { case ValPairIterPiListCondAp::RangeInS1: { int compareRes = FsmAp::compareCondDataPtr( outPair.s1Tel.trans, 0 ); if ( compareRes != 0 ) return compareRes; break; } case ValPairIterPiListCondAp::RangeInS2: { int compareRes = FsmAp::compareCondDataPtr( 0, outPair.s2Tel.trans ); if ( compareRes != 0 ) return compareRes; break; } case ValPairIterPiListCondAp::RangeOverlap: { int compareRes = FsmAp::compareCondDataPtr( outPair.s1Tel.trans, outPair.s2Tel.trans ); if ( compareRes != 0 ) return compareRes; break; }} } } return 0; } /* Compares two transitions according to priority and functions. Pointers * should not be null. Does not consider to state or from state. Compare two * transitions according to the data contained in the transitions. Data means * any properties added to user transitions that may differentiate them. Since * the base transition has no data, the default is to return equal. */ template< class Trans > int FsmAp::compareCondData( Trans *trans1, Trans *trans2 ) { /* Compare the prior table. */ int cmpRes = CmpPriorTable::compare( trans1->priorTable, trans2->priorTable ); if ( cmpRes != 0 ) return cmpRes; /* Compare longest match action tables. */ cmpRes = CmpLmActionTable::compare(trans1->lmActionTable, trans2->lmActionTable); if ( cmpRes != 0 ) return cmpRes; /* Compare action tables. */ return CmpActionTable::compare(trans1->actionTable, trans2->actionTable); } /* Compares two transitions according to priority and functions. Pointers * should not be null. Does not consider to state or from state. Compare two * transitions according to the data contained in the transitions. Data means * any properties added to user transitions that may differentiate them. Since * the base transition has no data, the default is to return equal. */ template< class Trans > int FsmAp::compareCondBitElim( Trans *trans1, Trans *trans2 ) { if ( trans1->toState < trans2->toState ) return -1; else if ( trans1->toState > trans2->toState ) return 1; /* Compare the prior table. */ int cmpRes = CmpPriorTable::compare( trans1->priorTable, trans2->priorTable ); if ( cmpRes != 0 ) return cmpRes; /* Compare longest match action tables. */ cmpRes = CmpLmActionTable::compare(trans1->lmActionTable, trans2->lmActionTable); if ( cmpRes != 0 ) return cmpRes; /* Compare action tables. */ return CmpActionTable::compare(trans1->actionTable, trans2->actionTable); } /* Compare the properties of states that are embedded by users. Compares out * priorities, out transitions, to, from, out, error and eof action tables. */ int FsmAp::compareStateData( const StateAp *state1, const StateAp *state2 ) { /* Compare the out priority table. */ int cmpRes = CmpPriorTable:: compare( state1->outPriorTable, state2->outPriorTable ); if ( cmpRes != 0 ) return cmpRes; /* Test to state action tables. */ cmpRes = CmpActionTable::compare( state1->toStateActionTable, state2->toStateActionTable ); if ( cmpRes != 0 ) return cmpRes; /* Test from state action tables. */ cmpRes = CmpActionTable::compare( state1->fromStateActionTable, state2->fromStateActionTable ); if ( cmpRes != 0 ) return cmpRes; /* Test out action tables. */ cmpRes = CmpActionTable::compare( state1->outActionTable, state2->outActionTable ); if ( cmpRes != 0 ) return cmpRes; /* Out condition space and set of vals. */ if ( state1->outCondSpace < state2->outCondSpace ) return -1; else if ( state1->outCondSpace > state2->outCondSpace ) return 1; cmpRes = CmpTable::compare( state1->outCondKeys, state2->outCondKeys ); if ( cmpRes != 0 ) return cmpRes; /* Test out error action tables. */ cmpRes = CmpErrActionTable::compare( state1->errActionTable, state2->errActionTable ); if ( cmpRes != 0 ) return cmpRes; /* Test eof action tables. */ cmpRes = CmpActionTable::compare( state1->eofActionTable, state2->eofActionTable ); if ( cmpRes != 0 ) return cmpRes; return CmpTable::compare( state1->lmNfaParts, state2->lmNfaParts ); } /* Invoked when a state looses its final state status and the leaving * transition embedding data should be deleted. */ void FsmAp::clearOutData( StateAp *state ) { /* Kill the out actions and priorities. */ state->outCondSpace = 0; state->outCondKeys.empty(); state->outActionTable.empty(); state->outPriorTable.empty(); } bool FsmAp::hasOutData( StateAp *state ) { return ( state->outActionTable.length() > 0 || state->outCondSpace != 0 || state->outCondKeys.length() > 0 || state->outPriorTable.length() > 0 || state->outCondSpace != 0 ); } /* * Setting Conditions. */ FsmRes FsmAp::startFsmCondition( Action *condAction, bool sense ) { CondSet set; CondKeySet vals; set.insert( condAction ); vals.append( sense ? 1 : 0 ); /* Make sure the start state has no other entry points. */ isolateStartState( this ); FsmRes res = embedCondition( this, startState, set, vals ); if ( !res.success() ) return res; if ( startState->nfaOut != 0 ) { /* Only one level. */ for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) { res = embedCondition( this, startState, set, vals ); if ( !res.success() ) return res; } } afterOpMinimize( true ); return FsmRes( FsmRes::Fsm(), this ); } void FsmAp::allTransCondition( Action *condAction, bool sense ) { CondSet set; CondKeySet vals; set.insert( condAction ); vals.append( sense ? 1 : 0 ); for ( StateList::Iter state = stateList; state.lte(); state++ ) embedCondition( this, state, set, vals ); } void FsmAp::leaveFsmCondition( Action *condAction, bool sense ) { for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) addOutCondition( *state, condAction, sense ); }