/* * Copyright 2001-2007 Adrian Thurston */ /* This file is part of Ragel. * * Ragel 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. * * Ragel 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 Ragel; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ragel.h" #include "dotcodegen.h" #include "gendata.h" using std::istream; using std::ifstream; using std::ostream; using std::ios; using std::cin; using std::cout; using std::cerr; using std::endl; /* Override this so that write statement processing is ignored */ bool GraphvizDotGen::writeStatement( InputLoc &, int, char ** ) { return false; } std::ostream &GraphvizDotGen::KEY( Key key ) { if ( displayPrintables && key.isPrintable() ) { // Output values as characters, ensuring we escape the quote (") character char cVal = (char) key.getVal(); switch ( cVal ) { case '"': case '\\': out << "'\\" << cVal << "'"; break; case '\a': out << "'\\\\a'"; break; case '\b': out << "'\\\\b'"; break; case '\t': out << "'\\\\t'"; break; case '\n': out << "'\\\\n'"; break; case '\v': out << "'\\\\v'"; break; case '\f': out << "'\\\\f'"; break; case '\r': out << "'\\\\r'"; break; case ' ': out << "SP"; break; default: out << "'" << cVal << "'"; break; } } else { if ( keyOps->isSigned ) out << key.getVal(); else out << (unsigned long) key.getVal(); } return out; } std::ostream &GraphvizDotGen::TRANS_ACTION( RedStateAp *fromState, RedTransAp *trans ) { int n = 0; RedAction *actions[3]; if ( fromState->fromStateAction != 0 ) actions[n++] = fromState->fromStateAction; if ( trans->action != 0 ) actions[n++] = trans->action; if ( trans->targ != 0 && trans->targ->toStateAction != 0 ) actions[n++] = trans->targ->toStateAction; if ( n > 0 ) out << " / "; /* Loop the existing actions and write out what's there. */ for ( int a = 0; a < n; a++ ) { for ( GenActionTable::Iter actIt = actions[a]->key.first(); actIt.lte(); actIt++ ) { GenAction *action = actIt->value; out << action->nameOrLoc(); if ( a < n-1 || !actIt.last() ) out << ", "; } } return out; } std::ostream &GraphvizDotGen::ACTION( RedAction *action ) { /* The action. */ out << " / "; for ( GenActionTable::Iter actIt = action->key.first(); actIt.lte(); actIt++ ) { GenAction *action = actIt->value; if ( action->name != 0 ) out << action->name; else out << action->loc.line << ":" << action->loc.col; if ( !actIt.last() ) out << ", "; } return out; } std::ostream &GraphvizDotGen::ONCHAR( Key lowKey, Key highKey ) { GenCondSpace *condSpace; if ( lowKey > keyOps->maxKey && (condSpace=findCondSpace(lowKey, highKey) ) ) { Key values = ( lowKey - condSpace->baseKey ) / keyOps->alphSize(); lowKey = keyOps->minKey + (lowKey - condSpace->baseKey - keyOps->alphSize() * values.getVal()); highKey = keyOps->minKey + (highKey - condSpace->baseKey - keyOps->alphSize() * values.getVal()); KEY( lowKey ); if ( lowKey != highKey ) { out << ".."; KEY( highKey ); } out << "("; for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { bool set = values & (1 << csi.pos()); if ( !set ) out << "!"; out << (*csi)->nameOrLoc(); if ( !csi.last() ) out << ", "; } out << ")"; } else { /* Output the key. Possibly a range. */ KEY( lowKey ); if ( highKey != lowKey ) { out << ".."; KEY( highKey ); } } return out; } void GraphvizDotGen::writeTransList( RedStateAp *state ) { /* Build the set of unique transitions out of this state. */ RedTransSet stTransSet; for ( RedTransList::Iter tel = state->outRange; tel.lte(); tel++ ) { /* If we haven't seen the transitions before, the move forward * emitting all the transitions on the same character. */ if ( stTransSet.insert( tel->value ) ) { /* Write out the from and to states. */ out << "\t" << state->id << " -> "; if ( tel->value->targ == 0 ) out << "err_" << state->id; else out << tel->value->targ->id; /* Begin the label. */ out << " [ label = \""; ONCHAR( tel->lowKey, tel->highKey ); /* Walk the transition list, finding the same. */ for ( RedTransList::Iter mtel = tel.next(); mtel.lte(); mtel++ ) { if ( mtel->value == tel->value ) { out << ", "; ONCHAR( mtel->lowKey, mtel->highKey ); } } /* Write the action and close the transition. */ TRANS_ACTION( state, tel->value ); out << "\" ];\n"; } } /* Write the default transition. */ if ( state->defTrans != 0 ) { /* Write out the from and to states. */ out << "\t" << state->id << " -> "; if ( state->defTrans->targ == 0 ) out << "err_" << state->id; else out << state->defTrans->targ->id; /* Begin the label. */ out << " [ label = \"DEF"; /* Write the action and close the transition. */ TRANS_ACTION( state, state->defTrans ); out << "\" ];\n"; } } void GraphvizDotGen::writeDotFile( ) { out << "digraph " << fsmName << " {\n" " rankdir=LR;\n"; /* Define the psuedo states. Transitions will be done after the states * have been defined as either final or not final. */ out << " node [ shape = point ];\n"; if ( redFsm->startState != 0 ) out << " ENTRY;\n"; /* Psuedo states for entry points in the entry map. */ for ( EntryIdVect::Iter en = entryPointIds; en.lte(); en++ ) { RedStateAp *state = allStates + *en; out << " en_" << state->id << ";\n"; } /* Psuedo states for final states with eof actions. */ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { if ( st->eofTrans != 0 && st->eofTrans->action != 0 ) out << " eof_" << st->id << ";\n"; if ( st->eofAction != 0 ) out << " eof_" << st->id << ";\n"; } out << " node [ shape = circle, height = 0.2 ];\n"; /* Psuedo states for states whose default actions go to error. */ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { bool needsErr = false; if ( st->defTrans != 0 && st->defTrans->targ == 0 ) needsErr = true; else { for ( RedTransList::Iter tel = st->outRange; tel.lte(); tel++ ) { if ( tel->value->targ == 0 ) { needsErr = true; break; } } } if ( needsErr ) out << " err_" << st->id << " [ label=\"\"];\n"; } /* Attributes common to all nodes, plus double circle for final states. */ out << " node [ fixedsize = true, height = 0.65, shape = doublecircle ];\n"; /* List Final states. */ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { if ( st->isFinal ) out << " " << st->id << ";\n"; } /* List transitions. */ out << " node [ shape = circle ];\n"; /* Walk the states. */ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) writeTransList( st ); /* Transitions into the start state. */ if ( redFsm->startState != 0 ) out << " ENTRY -> " << redFsm->startState->id << " [ label = \"IN\" ];\n"; /* Transitions into the entry points. */ for ( EntryIdVect::Iter en = entryPointIds; en.lte(); en++ ) { RedStateAp *state = allStates + *en; char *name = entryPointNames[en.pos()]; out << " en_" << state->id << " -> " << state->id << " [ label = \"" << name << "\" ];\n"; } /* Out action transitions. */ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { if ( st->eofTrans != 0 && st->eofTrans->action != 0 ) { out << " " << st->id << " -> eof_" << st->id << " [ label = \"EOF"; ACTION( st->eofTrans->action ) << "\" ];\n"; } if ( st->eofAction != 0 ) { out << " " << st->id << " -> eof_" << st->id << " [ label = \"EOF"; ACTION( st->eofAction ) << "\" ];\n"; } } out << "}\n"; } void GraphvizDotGen::finishRagelDef() { /* For dot file generation we want to pick default transitions. */ redFsm->chooseDefaultSpan(); }