diff options
Diffstat (limited to 'src/redbuild.cc')
-rw-r--r-- | src/redbuild.cc | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/src/redbuild.cc b/src/redbuild.cc new file mode 100644 index 00000000..7e0396d7 --- /dev/null +++ b/src/redbuild.cc @@ -0,0 +1,562 @@ +/* + * Copyright 2006-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 "redbuild.h" + +#include <assert.h> +#include <string.h> +#include <stdbool.h> + +#include <iostream> + +#include "fsmcodegen.h" + +using namespace std; + +RedFsmBuild::RedFsmBuild( Compiler *pd, FsmGraph *fsm ) +: + pd(pd), + fsm(fsm), + nextActionTableId(0), + startState(-1), + errState(-1) +{ +} + +void RedFsmBuild::initActionList( unsigned long length ) +{ + redFsm->allActions = new GenAction[length]; + memset( redFsm->allActions, 0, sizeof(GenAction) * length ); + for ( unsigned long a = 0; a < length; a++ ) + redFsm->genActionList.append( redFsm->allActions+a ); +} + + +void RedFsmBuild::makeActionList() +{ + /* Determine which actions to write. */ + int nextActionId = 0; + for ( ActionList::Iter act = pd->actionList; act.lte(); act++ ) { + if ( act->numRefs() > 0 || act->numCondRefs > 0 ) + act->actionId = nextActionId++; + } + + initActionList( nextActionId ); + curAction = 0; + + for ( ActionList::Iter act = pd->actionList; act.lte(); act++ ) { + if ( act->actionId >= 0 ) + makeAction( act ); + } +} + +void RedFsmBuild::initActionTableList( unsigned long length ) +{ + redFsm->allActionTables = new RedAction[length]; +} + +void RedFsmBuild::initStateList( unsigned long length ) +{ + redFsm->allStates = new RedState[length]; + for ( unsigned long s = 0; s < length; s++ ) + redFsm->stateList.append( redFsm->allStates+s ); + + /* We get the start state as an offset, set the pointer now. */ + assert( startState >= 0 ); + redFsm->startState = redFsm->allStates + startState; + if ( errState >= 0 ) + redFsm->errState = redFsm->allStates + errState; + for ( EntryIdVect::Iter en = redFsm->entryPointIds; en.lte(); en++ ) + redFsm->entryPoints.insert( redFsm->allStates + *en ); + + /* The nextStateId is no longer used to assign state ids (they come in set + * from the frontend now), however generation code still depends on it. + * Should eventually remove this variable. */ + redFsm->nextStateId = redFsm->stateList.length(); +} + +void RedFsmBuild::addEntryPoint( int entryId, unsigned long entryState ) +{ + redFsm->entryPointIds.append( entryState ); + redFsm->redEntryMap.insert( entryId, entryState ); +} + +void RedFsmBuild::addRegionToEntry( int regionId, int entryId ) +{ + assert( regionId == redFsm->regionToEntry.length() ); + redFsm->regionToEntry.append( entryId ); +} + +void RedFsmBuild::initTransList( int snum, unsigned long length ) +{ + /* Could preallocate the out range to save time growing it. For now do + * nothing. */ +} + +void RedFsmBuild::newTrans( int snum, int tnum, Key lowKey, + Key highKey, long targ, long action ) +{ + /* Get the current state and range. */ + RedState *curState = redFsm->allStates + snum; + RedTransList &destRange = curState->outRange; + + if ( curState == redFsm->errState ) + return; + + /* Make the new transitions. */ + RedState *targState = targ >= 0 ? (redFsm->allStates + targ) : + redFsm->wantComplete ? redFsm->getErrorState() : 0; + RedAction *actionTable = action >= 0 ? (redFsm->allActionTables + action) : 0; + RedTrans *trans = redFsm->allocateTrans( targState, actionTable ); + RedTransEl transEl( lowKey, highKey, trans ); + + if ( redFsm->wantComplete ) { + /* If the machine is to be complete then we need to fill any gaps with + * the error transitions. */ + if ( destRange.length() == 0 ) { + /* Range is currently empty. */ + if ( keyOps->minKey < lowKey ) { + /* The first range doesn't start at the low end. */ + Key fillHighKey = lowKey; + fillHighKey.decrement(); + + /* Create the filler with the state's error transition. */ + RedTransEl newTel( keyOps->minKey, fillHighKey, redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + } + else { + /* The range list is not empty, get the the last range. */ + RedTransEl *last = &destRange[destRange.length()-1]; + Key nextKey = last->highKey; + nextKey.increment(); + if ( nextKey < lowKey ) { + /* There is a gap to fill. Make the high key. */ + Key fillHighKey = lowKey; + fillHighKey.decrement(); + + /* Create the filler with the state's error transtion. */ + RedTransEl newTel( nextKey, fillHighKey, redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + } + } + + /* Filler taken care of. Append the range. */ + destRange.append( RedTransEl( lowKey, highKey, trans ) ); +} + +void RedFsmBuild::finishTransList( int snum ) +{ + /* Get the current state and range. */ + RedState *curState = redFsm->allStates + snum; + RedTransList &destRange = curState->outRange; + + if ( curState == redFsm->errState ) + return; + + /* If building a complete machine we may need filler on the end. */ + if ( redFsm->wantComplete ) { + /* Check if there are any ranges already. */ + if ( destRange.length() == 0 ) { + /* Fill with the whole alphabet. */ + /* Add the range on the lower and upper bound. */ + RedTransEl newTel( keyOps->minKey, keyOps->maxKey, redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + else { + /* Get the last and check for a gap on the end. */ + RedTransEl *last = &destRange[destRange.length()-1]; + if ( last->highKey < keyOps->maxKey ) { + /* Make the high key. */ + Key fillLowKey = last->highKey; + fillLowKey.increment(); + + /* Create the new range with the error trans and append it. */ + RedTransEl newTel( fillLowKey, keyOps->maxKey, redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + } + } +} + +void RedFsmBuild::setId( int snum, int id ) +{ + RedState *curState = redFsm->allStates + snum; + curState->id = id; +} + +void RedFsmBuild::setEofTrans( int snum, int eofTarget, int actId ) +{ + RedState *curState = redFsm->allStates + snum; + RedState *targState = redFsm->allStates + eofTarget; + RedAction *eofAct = redFsm->allActionTables + actId; + curState->eofTrans = redFsm->allocateTrans( targState, eofAct ); +} + +void RedFsmBuild::setFinal( int snum ) +{ + RedState *curState = redFsm->allStates + snum; + curState->isFinal = true; +} + + +void RedFsmBuild::setStateActions( int snum, long toStateAction, + long fromStateAction, long eofAction ) +{ + RedState *curState = redFsm->allStates + snum; + if ( toStateAction >= 0 ) + curState->toStateAction = redFsm->allActionTables + toStateAction; + if ( fromStateAction >= 0 ) + curState->fromStateAction = redFsm->allActionTables + fromStateAction; + if ( eofAction >= 0 ) + curState->eofAction = redFsm->allActionTables + eofAction; +} + +void RedFsmBuild::closeMachine() +{ +} + + +void RedFsmBuild::initStateCondList( int snum, ulong length ) +{ + /* Could preallocate these, as we could with transitions. */ +} + +void RedFsmBuild::setForcedErrorState() +{ + redFsm->forcedErrorState = true; +} + +Key RedFsmBuild::findMaxKey() +{ + Key maxKey = keyOps->maxKey; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + assert( st->outSingle.length() == 0 ); + assert( st->defTrans == 0 ); + + long rangeLen = st->outRange.length(); + if ( rangeLen > 0 ) { + Key highKey = st->outRange[rangeLen-1].highKey; + if ( highKey > maxKey ) + maxKey = highKey; + } + } + return maxKey; +} + + +void RedFsmBuild::makeActionTableList() +{ + /* Must first order the action tables based on their id. */ + int numTables = nextActionTableId; + RedActionTable **tables = new RedActionTable*[numTables]; + for ( ActionTableMap::Iter at = actionTableMap; at.lte(); at++ ) + tables[at->id] = at; + + initActionTableList( numTables ); + curActionTable = 0; + + for ( int t = 0; t < numTables; t++ ) { + long length = tables[t]->key.length(); + + /* Collect the action table. */ + RedAction *redAct = redFsm->allActionTables + curActionTable; + redAct->actListId = curActionTable; + redAct->key.setAsNew( length ); + + int pos = 0; + for ( ActionTable::Iter atel = tables[t]->key; atel.lte(); atel++ ) { + int actionId = atel->value->actionId; + redAct->key[pos].key = 0; + redAct->key[pos].value = redFsm->allActions+actionId; + pos += 1; + } + + /* Insert into the action table map. */ + redFsm->actionMap.insert( redAct ); + + curActionTable += 1; + + } + + delete[] tables; +} + +void RedFsmBuild::reduceActionTables() +{ + /* Reduce the actions tables to a set. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + RedActionTable *actionTable = 0; + + /* Reduce To State Actions. */ + if ( st->toStateActionTable.length() > 0 ) { + if ( actionTableMap.insert( st->toStateActionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + + /* Reduce From State Actions. */ + if ( st->fromStateActionTable.length() > 0 ) { + if ( actionTableMap.insert( st->fromStateActionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + + /* Reduce EOF actions. */ + if ( st->eofActionTable.length() > 0 ) { + if ( actionTableMap.insert( st->eofActionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + + /* Loop the transitions and reduce their actions. */ + for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) { + if ( trans->actionTable.length() > 0 ) { + if ( actionTableMap.insert( trans->actionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + } + } +} + +void RedFsmBuild::appendTrans( TransListVect &outList, Key lowKey, + Key highKey, FsmTrans *trans ) +{ + if ( trans->toState != 0 || trans->actionTable.length() > 0 ) + outList.append( TransEl( lowKey, highKey, trans ) ); +} + +void RedFsmBuild::makeTrans( Key lowKey, Key highKey, FsmTrans *trans ) +{ + /* First reduce the action. */ + RedActionTable *actionTable = 0; + if ( trans->actionTable.length() > 0 ) + actionTable = actionTableMap.find( trans->actionTable ); + + long targ = trans->toState == 0 ? -1 : trans->toState->alg.stateNum; + long action = actionTable == 0 ? -1 : actionTable->id; + + newTrans( curState, curTrans++, lowKey, highKey, targ, action ); +} + +void RedFsmBuild::makeTransList( FsmState *state ) +{ + TransListVect outList; + + /* If there is only are no ranges the task is simple. */ + if ( state->outList.length() > 0 ) { + /* Loop each source range. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + /* Reduce the transition. If it reduced to anything then add it. */ + appendTrans( outList, trans->lowKey, trans->highKey, trans ); + } + } + + long length = outList.length(); + initTransList( curState, length ); + curTrans = 0; + + for ( TransListVect::Iter tvi = outList; tvi.lte(); tvi++ ) + makeTrans( tvi->lowKey, tvi->highKey, tvi->value ); + finishTransList( curState ); +} + +void RedFsmBuild::newAction( int anum, char *name, int line, int col, Action *action ) +{ + redFsm->allActions[anum].actionId = anum; + redFsm->allActions[anum].name = name; + redFsm->allActions[anum].loc.line = line; + redFsm->allActions[anum].loc.col = col; + redFsm->allActions[anum].inlineList = action->inlineList; + redFsm->allActions[anum].objField = action->objField; + redFsm->allActions[anum].markType = action->markType; + redFsm->allActions[anum].markId = action->markId + 1; +} + +void RedFsmBuild::makeAction( Action *action ) +{ + int line = action->loc.line; + int col = action->loc.col; + + char *name = 0; + if ( action->name != 0 ) + name = action->name; + + newAction( curAction++, name, line, col, action ); +} + +void xmlEscapeHost( std::ostream &out, char *data, int len ) +{ + char *end = data + len; + while ( data != end ) { + switch ( *data ) { + case '<': out << "<"; break; + case '>': out << ">"; break; + case '&': out << "&"; break; + default: out << *data; break; + } + data += 1; + } +} + +void RedFsmBuild::makeStateActions( FsmState *state ) +{ + RedActionTable *toStateActions = 0; + if ( state->toStateActionTable.length() > 0 ) + toStateActions = actionTableMap.find( state->toStateActionTable ); + + RedActionTable *fromStateActions = 0; + if ( state->fromStateActionTable.length() > 0 ) + fromStateActions = actionTableMap.find( state->fromStateActionTable ); + + RedActionTable *eofActions = 0; + if ( state->eofActionTable.length() > 0 ) + eofActions = actionTableMap.find( state->eofActionTable ); + + if ( toStateActions != 0 || fromStateActions != 0 || eofActions != 0 ) { + long toStateAction = -1; + long fromStateAction = -1; + long eofAction = -1; + + if ( toStateActions != 0 ) + toStateAction = toStateActions->id; + if ( fromStateActions != 0 ) + fromStateAction = fromStateActions->id; + if ( eofActions != 0 ) + eofAction = eofActions->id; + + setStateActions( curState, toStateAction, + fromStateAction, eofAction ); + } +} + +void RedFsmBuild::makeStateList() +{ + /* Write the list of states. */ + long length = fsm->stateList.length(); + initStateList( length ); + curState = 0; + + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + /* Both or neither should be set. */ + assert( !( (st->eofTarget != 0) xor (st->eofActionTable.length() > 0) ) ); + + makeStateActions( st ); + makeTransList( st ); + + setId( curState, st->alg.stateNum ); + if ( st->isFinState() ) + setFinal( curState ); + + /* If there is an eof target, make an eof transition. */ + if ( st->eofTarget != 0 ) { + /* Find the eof actions. */ + RedActionTable *eofActions = 0; + eofActions = actionTableMap.find( st->eofActionTable ); + setEofTrans( curState, st->eofTarget->alg.stateNum, eofActions->id ); + } + + curState += 1; + } +} + +void RedFsmBuild::makeEntryPoints() +{ + if ( fsm->lmRequiresErrorState ) + setForcedErrorState(); + + for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) { + /* Get the name instantiation from nameIndex. */ + FsmState *state = en->value; + long entry = state->alg.stateNum; + addEntryPoint( en->key, entry ); + } + + for ( RegionList::Iter reg = pd->regionList; reg.lte(); reg++ ) { + assert( reg->impl->regionNameInst != 0 ); + + TokenRegion *use = reg; + + if ( use->zeroLel != 0 ) + use = use->ignoreOnly; + + NameInst *regionName = use->impl->regionNameInst; + addRegionToEntry( reg->id, regionName->id ); + } +} + +void RedFsmBuild::makeMachine() +{ + /* Action tables. */ + reduceActionTables(); + + makeActionList(); + makeActionTableList(); + makeConditions(); + + /* Start state. */ + startState = fsm->startState->alg.stateNum; + + /* Error state. */ + if ( fsm->errState != 0 ) + errState = fsm->errState->alg.stateNum; + + makeEntryPoints(); + makeStateList(); +} + +void RedFsmBuild::makeConditions() +{ +} + +RedFsm *RedFsmBuild::reduceMachine() +{ + redFsm = new RedFsm(); + redFsm->wantComplete = true; + + /* Open the definition. */ + makeMachine(); + + /* Do this before distributing transitions out to singles and defaults + * makes life easier. */ + redFsm->maxKey = findMaxKey(); + + redFsm->assignActionLocs(); + + /* Find the first final state (The final state with the lowest id). */ + redFsm->findFirstFinState(); + + /* Choose default transitions and the single transition. */ + redFsm->chooseDefaultSpan(); + + /* Maybe do flat expand, otherwise choose single. */ + redFsm->chooseSingle(); + + /* Set up incoming transitions. */ + 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. */ + redFsm->analyzeMachine(); + + return redFsm; +} + |