diff options
Diffstat (limited to 'src/fsmcodegen.cc')
-rw-r--r-- | src/fsmcodegen.cc | 931 |
1 files changed, 931 insertions, 0 deletions
diff --git a/src/fsmcodegen.cc b/src/fsmcodegen.cc new file mode 100644 index 0000000..61a48f7 --- /dev/null +++ b/src/fsmcodegen.cc @@ -0,0 +1,931 @@ +/* + * Copyright 2006-2012 Adrian Thurston <thurston@complang.org> + */ + +/* This file is part of Colm. + * + * Colm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Colm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Colm; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "parsedata.h" +#include "fsmcodegen.h" +#include "redfsm.h" +#include "bstmap.h" +#include <sstream> +#include <string> +#include <assert.h> + + +using std::ostream; +using std::ostringstream; +using std::string; +using std::cerr; +using std::endl; + + +/* Init code gen with in parameters. */ +FsmCodeGen::FsmCodeGen( ostream &out, + RedFsm *redFsm, FsmTables *fsmTables ) +: + out(out), + redFsm(redFsm), + fsmTables(fsmTables), + codeGenErrCount(0), + dataPrefix(true), + writeFirstFinal(true), + writeErr(true), + skipTokenLabelNeeded(false) +{ +} + +/* Write out the fsm name. */ +string FsmCodeGen::FSM_NAME() +{ + return "parser"; +} + +/* Emit the offset of the start state as a decimal integer. */ +string FsmCodeGen::START_STATE_ID() +{ + ostringstream ret; + ret << redFsm->startState->id; + return ret.str(); +}; + +/* Write out the array of actions. */ +std::ostream &FsmCodeGen::ACTIONS_ARRAY() +{ + out << "\t0, "; + int totalActions = 1; + for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { + /* Write out the length, which will never be the last character. */ + out << act->key.length() << ", "; + /* Put in a line break every 8 */ + if ( totalActions++ % 8 == 7 ) + out << "\n\t"; + + for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) { + out << item->value->actionId; + if ( ! (act.last() && item.last()) ) + out << ", "; + + /* Put in a line break every 8 */ + if ( totalActions++ % 8 == 7 ) + out << "\n\t"; + } + } + out << "\n"; + return out; +} + + +string FsmCodeGen::CS() +{ + ostringstream ret; + /* Expression for retrieving the key, use simple dereference. */ + ret << ACCESS() << "cs"; + return ret.str(); +} + +string FsmCodeGen::GET_WIDE_KEY() +{ + return GET_KEY(); +} + +string FsmCodeGen::GET_WIDE_KEY( RedState *state ) +{ + return GET_KEY(); +} + +string FsmCodeGen::GET_KEY() +{ + ostringstream ret; + /* Expression for retrieving the key, use simple dereference. */ + ret << "(*" << P() << ")"; + return ret.str(); +} + +/* Write out level number of tabs. Makes the nested binary search nice + * looking. */ +string FsmCodeGen::TABS( int level ) +{ + string result; + while ( level-- > 0 ) + result += "\t"; + return result; +} + +/* Write out a key from the fsm code gen. Depends on wether or not the key is + * signed. */ +string FsmCodeGen::KEY( Key key ) +{ + ostringstream ret; + ret << key.getVal(); + return ret.str(); +} + +void FsmCodeGen::SET_ACT( ostream &ret, InlineItem *item ) +{ + ret << ACT() << " = " << item->longestMatchPart->longestMatchId << ";"; +} + +void FsmCodeGen::SET_TOKEND( ostream &ret, InlineItem *item ) +{ + /* The tokend action sets tokend. */ + ret << "{ " << TOKEND() << " = " << TOKLEN() << " + ( " << P() << " - " << BLOCK_START() << " ) + 1; }"; +} +void FsmCodeGen::INIT_TOKSTART( ostream &ret, InlineItem *item ) +{ + ret << TOKSTART() << " = 0;"; +} + +void FsmCodeGen::INIT_ACT( ostream &ret, InlineItem *item ) +{ + ret << ACT() << " = 0;"; +} + +void FsmCodeGen::SET_TOKSTART( ostream &ret, InlineItem *item ) +{ + ret << TOKSTART() << " = " << P() << ";"; +} + +void FsmCodeGen::EMIT_TOKEN( ostream &ret, LangEl *token ) +{ + ret << " " << MATCHED_TOKEN() << " = " << token->id << ";\n"; +} + +void FsmCodeGen::LM_SWITCH( ostream &ret, InlineItem *item, + int targState, int inFinish ) +{ + ret << + " " << TOKLEN() << " = " << TOKEND() << ";\n" + " switch( " << ACT() << " ) {\n"; + + /* If the switch handles error then we also forced the error state. It + * will exist. */ + if ( item->tokenRegion->lmSwitchHandlesError ) { + ret << " case 0: " //<< P() << " = " << TOKSTART() << ";" << + "goto st" << redFsm->errState->id << ";\n"; + } + + for ( TokenInstanceListReg::Iter lmi = item->tokenRegion->tokenInstanceList; lmi.lte(); lmi++ ) { + if ( lmi->inLmSelect ) { + assert( lmi->tokenDef->tdLangEl != 0 ); + ret << " case " << lmi->longestMatchId << ":\n"; + EMIT_TOKEN( ret, lmi->tokenDef->tdLangEl ); + ret << " break;\n"; + } + } + + ret << + " }\n" + "\t" + " goto skip_toklen;\n"; + + skipTokenLabelNeeded = true; +} + +void FsmCodeGen::LM_ON_LAST( ostream &ret, InlineItem *item ) +{ + assert( item->longestMatchPart->tokenDef->tdLangEl != 0 ); + + ret << " " << P() << " += 1;\n"; + EMIT_TOKEN( ret, item->longestMatchPart->tokenDef->tdLangEl ); + ret << " goto out;\n"; +} + +void FsmCodeGen::LM_ON_NEXT( ostream &ret, InlineItem *item ) +{ + assert( item->longestMatchPart->tokenDef->tdLangEl != 0 ); + + EMIT_TOKEN( ret, item->longestMatchPart->tokenDef->tdLangEl ); + ret << " goto out;\n"; +} + +void FsmCodeGen::LM_ON_LAG_BEHIND( ostream &ret, InlineItem *item ) +{ + assert( item->longestMatchPart->tokenDef->tdLangEl != 0 ); + + ret << " " << TOKLEN() << " = " << TOKEND() << ";\n"; + EMIT_TOKEN( ret, item->longestMatchPart->tokenDef->tdLangEl ); + ret << " goto skip_toklen;\n"; + + skipTokenLabelNeeded = true; +} + + +/* Write out an inline tree structure. Walks the list and possibly calls out + * to virtual functions than handle language specific items in the tree. */ +void FsmCodeGen::INLINE_LIST( ostream &ret, InlineList *inlineList, + int targState, bool inFinish ) +{ + for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) { + switch ( item->type ) { + case InlineItem::Text: + assert( false ); + break; + case InlineItem::LmSetActId: + SET_ACT( ret, item ); + break; + case InlineItem::LmSetTokEnd: + SET_TOKEND( ret, item ); + break; + case InlineItem::LmInitTokStart: + assert( false ); + break; + case InlineItem::LmInitAct: + INIT_ACT( ret, item ); + break; + case InlineItem::LmSetTokStart: + SET_TOKSTART( ret, item ); + break; + case InlineItem::LmSwitch: + LM_SWITCH( ret, item, targState, inFinish ); + break; + case InlineItem::LmOnLast: + LM_ON_LAST( ret, item ); + break; + case InlineItem::LmOnNext: + LM_ON_NEXT( ret, item ); + break; + case InlineItem::LmOnLagBehind: + LM_ON_LAG_BEHIND( ret, item ); + break; + } + } +} + +/* Write out paths in line directives. Escapes any special characters. */ +string FsmCodeGen::LDIR_PATH( char *path ) +{ + ostringstream ret; + for ( char *pc = path; *pc != 0; pc++ ) { + if ( *pc == '\\' ) + ret << "\\\\"; + else + ret << *pc; + } + return ret.str(); +} + +void FsmCodeGen::ACTION( ostream &ret, GenAction *action, int targState, bool inFinish ) +{ + /* Write the block and close it off. */ + ret << "\t{"; + INLINE_LIST( ret, action->inlineList, targState, inFinish ); + + if ( action->markId > 0 ) + ret << "mark[" << action->markId-1 << "] = " << P() << ";\n"; + + ret << "}\n"; + +} + +void FsmCodeGen::CONDITION( ostream &ret, GenAction *condition ) +{ + ret << "\n"; + INLINE_LIST( ret, condition->inlineList, 0, false ); +} + +string FsmCodeGen::ERROR_STATE() +{ + ostringstream ret; + if ( redFsm->errState != 0 ) + ret << redFsm->errState->id; + else + ret << "-1"; + return ret.str(); +} + +string FsmCodeGen::FIRST_FINAL_STATE() +{ + ostringstream ret; + if ( redFsm->firstFinState != 0 ) + ret << redFsm->firstFinState->id; + else + ret << redFsm->nextStateId; + return ret.str(); +} + +string FsmCodeGen::DATA_PREFIX() +{ + if ( dataPrefix ) + return FSM_NAME() + "_"; + return ""; +} + +/* Emit the alphabet data type. */ +string FsmCodeGen::ALPH_TYPE() +{ + string ret = keyOps->alphType->data1; + if ( keyOps->alphType->data2 != 0 ) { + ret += " "; + ret += + keyOps->alphType->data2; + } + return ret; +} + +/* Emit the alphabet data type. */ +string FsmCodeGen::WIDE_ALPH_TYPE() +{ + string ret; + ret = ALPH_TYPE(); + return ret; +} + + +string FsmCodeGen::PTR_CONST() +{ + return "const "; +} + +std::ostream &FsmCodeGen::OPEN_ARRAY( string type, string name ) +{ + out << "static const " << type << " " << name << "[] = {\n"; + return out; +} + +std::ostream &FsmCodeGen::CLOSE_ARRAY() +{ + return out << "};\n"; +} + +std::ostream &FsmCodeGen::STATIC_VAR( string type, string name ) +{ + out << "static const " << type << " " << name; + return out; +} + +string FsmCodeGen::UINT( ) +{ + return "unsigned int"; +} + +string FsmCodeGen::ARR_OFF( string ptr, string offset ) +{ + return ptr + " + " + offset; +} + +string FsmCodeGen::CAST( string type ) +{ + return "(" + type + ")"; +} + +std::ostream &FsmCodeGen::TO_STATE_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = redFsm->genActionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numToStateRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\tcase " << act->actionId << ":\n"; + ACTION( out, act, 0, false ); + out << "\tbreak;\n"; + } + } + + return out; +} + +std::ostream &FsmCodeGen::FROM_STATE_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = redFsm->genActionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numFromStateRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\tcase " << act->actionId << ":\n"; + ACTION( out, act, 0, false ); + out << "\tbreak;\n"; + } + } + + return out; +} + +std::ostream &FsmCodeGen::ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = redFsm->genActionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numTransRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\tcase " << act->actionId << ":\n"; + ACTION( out, act, 0, false ); + out << "\tbreak;\n"; + } + } + + return out; +} + +void FsmCodeGen::emitSingleSwitch( RedState *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 << "\tif ( " << GET_WIDE_KEY(state) << " == " << + KEY(data[0].lowKey) << " )\n\t\t"; + + /* Virtual function for writing the target of the transition. */ + TRANS_GOTO(data[0].value, 0) << "\n"; + } + else if ( numSingles > 1 ) { + /* Write out single keys in a switch if there is more than one. */ + out << "\tswitch( " << GET_WIDE_KEY(state) << " ) {\n"; + + /* Write out the single indicies. */ + for ( int j = 0; j < numSingles; j++ ) { + out << "\t\tcase " << KEY(data[j].lowKey) << ": "; + TRANS_GOTO(data[j].value, 0) << "\n"; + } + + /* Close off the transition switch. */ + out << "\t}\n"; + } +} + +void FsmCodeGen::emitRangeBSearch( RedState *state, int level, 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 = data[mid].lowKey == keyOps->minKey; + bool limitHigh = data[mid].highKey == keyOps->maxKey; + + if ( anyLower && anyHigher ) { + /* Can go lower and higher than mid. */ + out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " < " << + KEY(data[mid].lowKey) << " ) {\n"; + emitRangeBSearch( state, level+1, low, mid-1 ); + out << TABS(level) << "} else if ( " << GET_WIDE_KEY(state) << " > " << + KEY(data[mid].highKey) << " ) {\n"; + emitRangeBSearch( state, level+1, mid+1, high ); + out << TABS(level) << "} else\n"; + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + else if ( anyLower && !anyHigher ) { + /* Can go lower than mid but not higher. */ + out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " < " << + KEY(data[mid].lowKey) << " ) {\n"; + emitRangeBSearch( state, level+1, low, mid-1 ); + + /* if the higher is the highest in the alphabet then there is no + * sense testing it. */ + if ( limitHigh ) { + out << TABS(level) << "} else\n"; + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + else { + out << TABS(level) << "} else if ( " << GET_WIDE_KEY(state) << " <= " << + KEY(data[mid].highKey) << " )\n"; + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + } + else if ( !anyLower && anyHigher ) { + /* Can go higher than mid but not lower. */ + out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " > " << + KEY(data[mid].highKey) << " ) {\n"; + emitRangeBSearch( state, level+1, mid+1, high ); + + /* If the lower end is the lowest in the alphabet then there is no + * sense testing it. */ + if ( limitLow ) { + out << TABS(level) << "} else\n"; + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + else { + out << TABS(level) << "} else if ( " << GET_WIDE_KEY(state) << " >= " << + KEY(data[mid].lowKey) << " )\n"; + TRANS_GOTO(data[mid].value, level+1) << "\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 << TABS(level) << "if ( " << KEY(data[mid].lowKey) << " <= " << + GET_WIDE_KEY(state) << " && " << GET_WIDE_KEY(state) << " <= " << + KEY(data[mid].highKey) << " )\n"; + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + else if ( limitLow && !limitHigh ) { + out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " <= " << + KEY(data[mid].highKey) << " )\n"; + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + else if ( !limitLow && limitHigh ) { + out << TABS(level) << "if ( " << KEY(data[mid].lowKey) << " <= " << + GET_WIDE_KEY(state) << " )\n"; + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + else { + /* Both high and low are at the limit. No tests to do. */ + TRANS_GOTO(data[mid].value, level+1) << "\n"; + } + } +} + +std::ostream &FsmCodeGen::STATE_GOTOS() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st == redFsm->errState ) + STATE_GOTO_ERROR(); + else { + /* Writing code above state gotos. */ + GOTO_HEADER( st ); + + /* Try singles. */ + if ( st->outSingle.length() > 0 ) + emitSingleSwitch( st ); + + /* Default case is to binary search for the ranges, if that fails then */ + if ( st->outRange.length() > 0 ) + emitRangeBSearch( st, 1, 0, st->outRange.length() - 1 ); + + /* Write the default transition. */ + TRANS_GOTO( st->defTrans, 1 ) << "\n"; + } + } + return out; +} + +unsigned int FsmCodeGen::TO_STATE_ACTION( RedState *state ) +{ + int act = 0; + if ( state->toStateAction != 0 ) + act = state->toStateAction->location+1; + return act; +} + +unsigned int FsmCodeGen::FROM_STATE_ACTION( RedState *state ) +{ + int act = 0; + if ( state->fromStateAction != 0 ) + act = state->fromStateAction->location+1; + return act; +} + +std::ostream &FsmCodeGen::TO_STATE_ACTIONS() +{ + /* 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); + + out << "\t"; + for ( int st = 0; st < redFsm->nextStateId; st++ ) { + /* Write any eof action. */ + out << vals[st]; + if ( st < numStates-1 ) { + out << ", "; + if ( (st+1) % IALL == 0 ) + out << "\n\t"; + } + } + out << "\n"; + delete[] vals; + return out; +} + +std::ostream &FsmCodeGen::FROM_STATE_ACTIONS() +{ + /* 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); + + out << "\t"; + for ( int st = 0; st < redFsm->nextStateId; st++ ) { + /* Write any eof action. */ + out << vals[st]; + if ( st < numStates-1 ) { + out << ", "; + if ( (st+1) % IALL == 0 ) + out << "\n\t"; + } + } + out << "\n"; + delete[] vals; + return out; +} + +bool FsmCodeGen::IN_TRANS_ACTIONS( RedState *state ) +{ + /* Emit any transitions that have actions and that go to this state. */ + for ( int it = 0; it < state->numInTrans; it++ ) { + RedTrans *trans = state->inTrans[it]; + if ( trans->action != 0 && trans->labelNeeded ) { + /* Write the label for the transition so it can be jumped to. */ + out << "tr" << 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 << " " << CS() << " = " << trans->targ->id << ";\n"; + + /* Write each action in the list. */ + for ( GenActionTable::Iter item = trans->action->key; item.lte(); item++ ) + ACTION( out, item->value, trans->targ->id, false ); + + out << "\tgoto st" << trans->targ->id << ";\n"; + } + } + + return 0; +} + +/* Called from FsmCodeGen::STATE_GOTOS just before writing the gotos for each + * state. */ +void FsmCodeGen::GOTO_HEADER( RedState *state ) +{ + IN_TRANS_ACTIONS( state ); + + if ( state->labelNeeded ) + out << "st" << state->id << ":\n"; + + if ( state->toStateAction != 0 ) { + /* Remember that we wrote an action. Write every action in the list. */ + for ( GenActionTable::Iter item = state->toStateAction->key; item.lte(); item++ ) + ACTION( out, item->value, state->id, false ); + } + + /* Give the state a switch case. */ + out << "case " << state->id << ":\n"; + + /* Advance and test buffer pos. */ + out << + " if ( ++" << P() << " == " << PE() << " )\n" + " goto out" << state->id << ";\n"; + + if ( state->fromStateAction != 0 ) { + /* Remember that we wrote an action. Write every action in the list. */ + for ( GenActionTable::Iter item = state->fromStateAction->key; item.lte(); item++ ) + ACTION( out, item->value, state->id, false ); + } + + /* Record the prev state if necessary. */ + if ( state->anyRegCurStateRef() ) + out << " _ps = " << state->id << ";\n"; +} + +void FsmCodeGen::STATE_GOTO_ERROR() +{ + /* In the error state we need to emit some stuff that usually goes into + * the header. */ + RedState *state = redFsm->errState; + IN_TRANS_ACTIONS( state ); + + if ( state->labelNeeded ) + out << "st" << state->id << ":\n"; + + /* We do not need a case label here because the the error state is checked + * at the head of the loop. */ + + /* Break out here. */ + out << " goto out" << state->id << ";\n"; +} + + +/* Emit the goto to take for a given transition. */ +std::ostream &FsmCodeGen::TRANS_GOTO( RedTrans *trans, int level ) +{ + if ( trans->action != 0 ) { + /* Go to the transition which will go to the state. */ + out << TABS(level) << "goto tr" << trans->id << ";"; + } + else { + /* Go directly to the target state. */ + out << TABS(level) << "goto st" << trans->targ->id << ";"; + } + return out; +} + +std::ostream &FsmCodeGen::EXIT_STATES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + out << " case " << st->id << ": out" << st->id << ": "; + if ( st->eofTrans != 0 ) { + out << "if ( " << DATA_EOF() << " ) {"; + TRANS_GOTO( st->eofTrans, 0 ); + out << "\n"; + out << "}"; + } + + /* Exit. */ + out << CS() << " = " << st->id << "; goto out; \n"; + } + return out; +} + +/* Set up labelNeeded flag for each state. */ +void FsmCodeGen::setLabelsNeeded() +{ + /* 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; + + if ( redFsm->errState != 0 && redFsm->anyLmSwitchError() ) + redFsm->errState->labelNeeded = true; + + /* Walk all transitions and set only those that have targs. */ + for ( RedTransSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + /* If there is no action with a next statement, then the label will be + * needed. */ + if ( trans->action == 0 || !trans->action->anyNextStmt() ) + trans->targ->labelNeeded = true; + } + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->outNeeded = st->labelNeeded; +} + +void FsmCodeGen::writeData() +{ + out << "#define " << START() << " " << START_STATE_ID() << "\n"; + out << "#define " << FIRST_FINAL() << " " << FIRST_FINAL_STATE() << "\n"; + out << "#define " << ERROR() << " " << ERROR_STATE() << "\n"; + out << "#define false 0\n"; + out << "#define true 1\n"; + out << "\n"; + + out << "static long " << ENTRY_BY_REGION() << "[] = {\n\t"; + for ( int i = 0; i < fsmTables->numRegions; i++ ) { + out << fsmTables->entryByRegion[i]; + + if ( i < fsmTables->numRegions-1 ) { + out << ", "; + if ( (i+1) % 8 == 0 ) + out << "\n\t"; + } + } + out << "\n};\n\n"; + + out << + "static FsmTables fsmTables_start =\n" + "{\n" + " 0, " /* actions */ + " 0, " /* keyOffsets */ + " 0, " /* transKeys */ + " 0, " /* singleLengths */ + " 0, " /* rangeLengths */ + " 0, " /* indexOffsets */ + " 0, " /* transTargsWI */ + " 0, " /* transActionsWI */ + " 0, " /* toStateActions */ + " 0, " /* fromStateActions */ + " 0, " /* eofActions */ + " 0,\n" /* eofTargs */ + " " << ENTRY_BY_REGION() << ",\n" + + "\n" + " 0, " /* numStates */ + " 0, " /* numActions */ + " 0, " /* numTransKeys */ + " 0, " /* numSingleLengths */ + " 0, " /* numRangeLengths */ + " 0, " /* numIndexOffsets */ + " 0, " /* numTransTargsWI */ + " 0,\n" /* numTransActionsWI */ + " " << redFsm->regionToEntry.length() << ",\n" + "\n" + " " << START() << ",\n" + " " << FIRST_FINAL() << ",\n" + " " << ERROR() << ",\n" + "\n" + " 0,\n" /* actionSwitch */ + " 0\n" /* numActionSwitch */ + "};\n" + "\n"; +} + +void FsmCodeGen::writeInit() +{ + out << + " " << CS() << " = " << START() << ";\n"; + + /* If there are any calls, then the stack top needs initialization. */ + if ( redFsm->anyActionCalls() || redFsm->anyActionRets() ) + out << "\t" << TOP() << " = 0;\n"; + + out << + " " << TOKSTART() << " = 0;\n" + " " << TOKEND() << " = 0;\n" + " " << ACT() << " = 0;\n"; + + out << "\n"; +} + +void FsmCodeGen::writeExec() +{ + setLabelsNeeded(); + + out << + "static void fsmExecute( FsmRun *fsmRun, StreamImpl *inputStream )\n" + "{\n" + " " << BLOCK_START() << " = fsmRun->p;\n" + "/*_resume:*/\n"; + + if ( redFsm->errState != 0 ) { + out << + " if ( " << CS() << " == " << redFsm->errState->id << " )\n" + " goto out;\n"; + } + + out << + " if ( " << P() << " == " << PE() << " )\n" + " goto out_switch;\n" + " --" << P() << ";\n" + "\n" + " switch ( " << CS() << " )\n {\n"; + STATE_GOTOS() << + " }\n"; + + out << + "out_switch:\n" + " switch ( " << CS() << " )\n {\n"; + EXIT_STATES() << + " }\n"; + + out << + "out:\n" + " if ( " << P() << " != 0 )\n" + " " << TOKLEN() << " += " << P() << " - " << BLOCK_START() << ";\n"; + + if ( skipTokenLabelNeeded ) { + out << + "skip_toklen:\n" + " {}\n"; + } + + out << + "}\n" + "\n"; +} + +void FsmCodeGen::writeIncludes() +{ + out << + "#include <colm/pdarun.h>\n" + "#include <colm/debug.h>\n" + "#include <colm/bytecode.h>\n" + "#include <stdio.h>\n" + "#include <stdlib.h>\n" + "#include <string.h>\n" + "#include <assert.h>\n" + "#include <colm/config.h>\n" + "#include <colm/defs.h>\n" + "#include <colm/input.h>\n" + "#include <colm/tree.h>\n" + "#include <colm/program.h>\n" + "#include <colm/colm.h>\n" + "\n" + "\n"; +} + +void FsmCodeGen::writeCode() +{ + redFsm->depthFirstOrdering(); + + writeData(); + writeExec(); + + /* Referenced in the runtime lib, but used only in the compiler. Probably + * should use the preprocessor to make these go away. */ + out << + "static void sendNamedLangEl( Program *prg, Tree **tree, PdaRun *pdaRun,\n" + " FsmRun *fsmRun, StreamImpl *inputStream ) { }\n" + "static void initBindings( PdaRun *pdaRun ) {}\n" + "static void popBinding( PdaRun *pdaRun, ParseTree *tree ) {}\n" + "\n" + "\n"; +} + + |