Use the new import feature of Ragel for bringing in defines from the parser
[ragel.git] / ragel / fsmbase.cpp
blobf1d7141c09954295993246025f08968c07b6008a
1 /*
2 * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
3 */
5 /* This file is part of Ragel.
7 * Ragel is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * Ragel is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Ragel; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <string.h>
23 #include <assert.h>
24 #include "fsmgraph.h"
26 /* Simple singly linked list append routine for the fill list. The new state
27 * goes to the end of the list. */
28 void MergeData::fillListAppend( StateAp *state )
30 state->alg.next = 0;
32 if ( stfillHead == 0 ) {
33 /* List is empty, state becomes head and tail. */
34 stfillHead = state;
35 stfillTail = state;
37 else {
38 /* List is not empty, state goes after last element. */
39 stfillTail->alg.next = state;
40 stfillTail = state;
44 /* Graph constructor. */
45 FsmAp::FsmAp()
47 /* No start state. */
48 startState(0),
49 errState(0),
51 /* Misfit accounting is a switch, turned on only at specific times. It
52 * controls what happens when states have no way in from the outside
53 * world.. */
54 misfitAccounting(false)
58 /* Copy all graph data including transitions. */
59 FsmAp::FsmAp( const FsmAp &graph )
61 /* Lists start empty. Will be filled by copy. */
62 stateList(),
63 misfitList(),
65 /* Copy in the entry points,
66 * pointers will be resolved later. */
67 entryPoints(graph.entryPoints),
68 startState(graph.startState),
69 errState(0),
71 /* Will be filled by copy. */
72 finStateSet(),
74 /* Misfit accounting is only on during merging. */
75 misfitAccounting(false)
77 /* Create the states and record their map in the original state. */
78 StateList::Iter origState = graph.stateList;
79 for ( ; origState.lte(); origState++ ) {
80 /* Make the new state. */
81 StateAp *newState = new StateAp( *origState );
83 /* Add the state to the list. */
84 stateList.append( newState );
86 /* Set the mapsTo item of the old state. */
87 origState->alg.stateMap = newState;
90 /* Derefernce all the state maps. */
91 for ( StateList::Iter state = stateList; state.lte(); state++ ) {
92 for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
93 /* The points to the original in the src machine. The taget's duplicate
94 * is in the statemap. */
95 StateAp *toState = trans->toState != 0 ? trans->toState->alg.stateMap : 0;
97 /* Attach The transition to the duplicate. */
98 trans->toState = 0;
99 attachTrans( state, toState, trans );
103 /* Fix the state pointers in the entry points array. */
104 EntryMapEl *eel = entryPoints.data;
105 for ( int e = 0; e < entryPoints.length(); e++, eel++ ) {
106 /* Get the duplicate of the state. */
107 eel->value = eel->value->alg.stateMap;
109 /* Foreign in transitions must be built up when duping machines so
110 * increment it here. */
111 eel->value->foreignInTrans += 1;
114 /* Fix the start state pointer and the new start state's count of in
115 * transiions. */
116 startState = startState->alg.stateMap;
117 startState->foreignInTrans += 1;
119 /* Build the final state set. */
120 StateSet::Iter st = graph.finStateSet;
121 for ( ; st.lte(); st++ )
122 finStateSet.insert((*st)->alg.stateMap);
125 /* Deletes all transition data then deletes each state. */
126 FsmAp::~FsmAp()
128 /* Delete all the transitions. */
129 for ( StateList::Iter state = stateList; state.lte(); state++ ) {
130 /* Iterate the out transitions, deleting them. */
131 state->outList.empty();
134 /* Delete all the states. */
135 stateList.empty();
138 /* Set a state final. The state has its isFinState set to true and the state
139 * is added to the finStateSet. */
140 void FsmAp::setFinState( StateAp *state )
142 /* Is it already a fin state. */
143 if ( state->stateBits & SB_ISFINAL )
144 return;
146 state->stateBits |= SB_ISFINAL;
147 finStateSet.insert( state );
150 /* Set a state non-final. The has its isFinState flag set false and the state
151 * is removed from the final state set. */
152 void FsmAp::unsetFinState( StateAp *state )
154 /* Is it already a non-final state? */
155 if ( ! (state->stateBits & SB_ISFINAL) )
156 return;
158 /* When a state looses its final state status it must relinquish all the
159 * properties that are allowed only for final states. */
160 clearOutData( state );
162 state->stateBits &= ~ SB_ISFINAL;
163 finStateSet.remove( state );
166 /* Set and unset a state as the start state. */
167 void FsmAp::setStartState( StateAp *state )
169 /* Sould change from unset to set. */
170 assert( startState == 0 );
171 startState = state;
173 if ( misfitAccounting ) {
174 /* If the number of foreign in transitions is about to go up to 1 then
175 * take it off the misfit list and put it on the head list. */
176 if ( state->foreignInTrans == 0 )
177 stateList.append( misfitList.detach( state ) );
180 /* Up the foreign in transitions to the state. */
181 state->foreignInTrans += 1;
184 void FsmAp::unsetStartState()
186 /* Should change from set to unset. */
187 assert( startState != 0 );
189 /* Decrement the entry's count of foreign entries. */
190 startState->foreignInTrans -= 1;
192 if ( misfitAccounting ) {
193 /* If the number of foreign in transitions just went down to 0 then take
194 * it off the main list and put it on the misfit list. */
195 if ( startState->foreignInTrans == 0 )
196 misfitList.append( stateList.detach( startState ) );
199 startState = 0;
202 /* Associate an id with a state. Makes the state a named entry point. Has no
203 * effect if the entry point is already mapped to the state. */
204 void FsmAp::setEntry( int id, StateAp *state )
206 /* Insert the id into the state. If the state is already labelled with id,
207 * nothing to do. */
208 if ( state->entryIds.insert( id ) ) {
209 /* Insert the entry and assert that it succeeds. */
210 entryPoints.insertMulti( id, state );
212 if ( misfitAccounting ) {
213 /* If the number of foreign in transitions is about to go up to 1 then
214 * take it off the misfit list and put it on the head list. */
215 if ( state->foreignInTrans == 0 )
216 stateList.append( misfitList.detach( state ) );
219 /* Up the foreign in transitions to the state. */
220 state->foreignInTrans += 1;
224 /* Remove the association of an id with a state. The state looses it's entry
225 * point status. Assumes that the id is indeed mapped to state. */
226 void FsmAp::unsetEntry( int id, StateAp *state )
228 /* Find the entry point in on id. */
229 EntryMapEl *enLow = 0, *enHigh = 0;
230 entryPoints.findMulti( id, enLow, enHigh );
231 while ( enLow->value != state )
232 enLow += 1;
234 /* Remove the record from the map. */
235 entryPoints.remove( enLow );
237 /* Remove the state's sense of the link. */
238 state->entryIds.remove( id );
239 state->foreignInTrans -= 1;
240 if ( misfitAccounting ) {
241 /* If the number of foreign in transitions just went down to 0 then take
242 * it off the main list and put it on the misfit list. */
243 if ( state->foreignInTrans == 0 )
244 misfitList.append( stateList.detach( state ) );
248 /* Remove all association of an id with states. Assumes that the id is indeed
249 * mapped to a state. */
250 void FsmAp::unsetEntry( int id )
252 /* Find the entry point in on id. */
253 EntryMapEl *enLow = 0, *enHigh = 0;
254 entryPoints.findMulti( id, enLow, enHigh );
255 for ( EntryMapEl *mel = enLow; mel <= enHigh; mel++ ) {
256 /* Remove the state's sense of the link. */
257 mel->value->entryIds.remove( id );
258 mel->value->foreignInTrans -= 1;
259 if ( misfitAccounting ) {
260 /* If the number of foreign in transitions just went down to 0
261 * then take it off the main list and put it on the misfit list. */
262 if ( mel->value->foreignInTrans == 0 )
263 misfitList.append( stateList.detach( mel->value ) );
267 /* Remove the records from the entry points map. */
268 entryPoints.removeMulti( enLow, enHigh );
272 void FsmAp::changeEntry( int id, StateAp *to, StateAp *from )
274 /* Find the entry in the entry map. */
275 EntryMapEl *enLow = 0, *enHigh = 0;
276 entryPoints.findMulti( id, enLow, enHigh );
277 while ( enLow->value != from )
278 enLow += 1;
280 /* Change it to the new target. */
281 enLow->value = to;
283 /* Remove from's sense of the link. */
284 from->entryIds.remove( id );
285 from->foreignInTrans -= 1;
286 if ( misfitAccounting ) {
287 /* If the number of foreign in transitions just went down to 0 then take
288 * it off the main list and put it on the misfit list. */
289 if ( from->foreignInTrans == 0 )
290 misfitList.append( stateList.detach( from ) );
293 /* Add to's sense of the link. */
294 if ( to->entryIds.insert( id ) != 0 ) {
295 if ( misfitAccounting ) {
296 /* If the number of foreign in transitions is about to go up to 1 then
297 * take it off the misfit list and put it on the head list. */
298 if ( to->foreignInTrans == 0 )
299 stateList.append( misfitList.detach( to ) );
302 /* Up the foreign in transitions to the state. */
303 to->foreignInTrans += 1;
308 /* Clear all entry points from a machine. */
309 void FsmAp::unsetAllEntryPoints()
311 for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) {
312 /* Kill all the state's entry points at once. */
313 if ( en->value->entryIds.length() > 0 ) {
314 en->value->foreignInTrans -= en->value->entryIds.length();
316 if ( misfitAccounting ) {
317 /* If the number of foreign in transitions just went down to 0
318 * then take it off the main list and put it on the misfit
319 * list. */
320 if ( en->value->foreignInTrans == 0 )
321 misfitList.append( stateList.detach( en->value ) );
324 /* Clear the set of ids out all at once. */
325 en->value->entryIds.empty();
329 /* Now clear out the entry map all at once. */
330 entryPoints.empty();
333 /* Assigning an epsilon transition into final states. */
334 void FsmAp::epsilonTrans( int id )
336 for ( StateSet::Iter fs = finStateSet; fs.lte(); fs++ )
337 (*fs)->epsilonTrans.append( id );
340 /* Mark all states reachable from state. Traverses transitions forward. Used
341 * for removing states that have no path into them. */
342 void FsmAp::markReachableFromHere( StateAp *state )
344 /* Base case: return; */
345 if ( state->stateBits & SB_ISMARKED )
346 return;
348 /* Set this state as processed. We are going to visit all states that this
349 * state has a transition to. */
350 state->stateBits |= SB_ISMARKED;
352 /* Recurse on all out transitions. */
353 for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
354 if ( trans->toState != 0 )
355 markReachableFromHere( trans->toState );
359 void FsmAp::markReachableFromHereStopFinal( StateAp *state )
361 /* Base case: return; */
362 if ( state->stateBits & SB_ISMARKED )
363 return;
365 /* Set this state as processed. We are going to visit all states that this
366 * state has a transition to. */
367 state->stateBits |= SB_ISMARKED;
369 /* Recurse on all out transitions. */
370 for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
371 StateAp *toState = trans->toState;
372 if ( toState != 0 && !toState->isFinState() )
373 markReachableFromHereStopFinal( toState );
377 /* Mark all states reachable from state. Traverse transitions backwards. Used
378 * for removing dead end paths in graphs. */
379 void FsmAp::markReachableFromHereReverse( StateAp *state )
381 /* Base case: return; */
382 if ( state->stateBits & SB_ISMARKED )
383 return;
385 /* Set this state as processed. We are going to visit all states with
386 * transitions into this state. */
387 state->stateBits |= SB_ISMARKED;
389 /* Recurse on all items in transitions. */
390 for ( TransInList::Iter trans = state->inList; trans.lte(); trans++ )
391 markReachableFromHereReverse( trans->fromState );
394 /* Determine if there are any entry points into a start state other than the
395 * start state. Setting starting transitions requires that the start state be
396 * isolated. In most cases a start state will already be isolated. */
397 bool FsmAp::isStartStateIsolated()
399 /* If there are any in transitions then the state is not isolated. */
400 if ( startState->inList.head != 0 )
401 return false;
403 /* If there are any entry points then isolated. */
404 if ( startState->entryIds.length() > 0 )
405 return false;
407 return true;
410 /* Bring in other's entry points. Assumes others states are going to be
411 * copied into this machine. */
412 void FsmAp::copyInEntryPoints( FsmAp *other )
414 /* Use insert multi because names are not unique. */
415 for ( EntryMap::Iter en = other->entryPoints; en.lte(); en++ )
416 entryPoints.insertMulti( en->key, en->value );
420 void FsmAp::unsetAllFinStates()
422 for ( StateSet::Iter st = finStateSet; st.lte(); st++ )
423 (*st)->stateBits &= ~ SB_ISFINAL;
424 finStateSet.empty();
427 void FsmAp::setFinBits( int finStateBits )
429 for ( int s = 0; s < finStateSet.length(); s++ )
430 finStateSet.data[s]->stateBits |= finStateBits;
434 /* Tests the integrity of the transition lists and the fromStates. */
435 void FsmAp::verifyIntegrity()
437 for ( StateList::Iter state = stateList; state.lte(); state++ ) {
438 /* Walk the out transitions and assert fromState is correct. */
439 for ( TransList::Iter trans = state->outList; trans.lte(); trans++ )
440 assert( trans->fromState == state );
442 /* Walk the inlist and assert toState is correct. */
443 for ( TransInList::Iter trans = state->inList; trans.lte(); trans++ )
444 assert( trans->toState == state );
448 void FsmAp::verifyReachability()
450 /* Mark all the states that can be reached
451 * through the set of entry points. */
452 markReachableFromHere( startState );
453 for ( EntryMap::Iter en = entryPoints; en.lte(); en++ )
454 markReachableFromHere( en->value );
456 /* Check that everything got marked. */
457 for ( StateList::Iter st = stateList; st.lte(); st++ ) {
458 /* Assert it got marked and then clear the mark. */
459 assert( st->stateBits & SB_ISMARKED );
460 st->stateBits &= ~ SB_ISMARKED;
464 void FsmAp::verifyNoDeadEndStates()
466 /* Mark all states that have paths to the final states. */
467 for ( StateSet::Iter pst = finStateSet; pst.lte(); pst++ )
468 markReachableFromHereReverse( *pst );
470 /* Start state gets honorary marking. Must be done AFTER recursive call. */
471 startState->stateBits |= SB_ISMARKED;
473 /* Make sure everything got marked. */
474 for ( StateList::Iter st = stateList; st.lte(); st++ ) {
475 /* Assert the state got marked and unmark it. */
476 assert( st->stateBits & SB_ISMARKED );
477 st->stateBits &= ~ SB_ISMARKED;
481 void FsmAp::depthFirstOrdering( StateAp *state )
483 /* Nothing to do if the state is already on the list. */
484 if ( state->stateBits & SB_ONLIST )
485 return;
487 /* Doing depth first, put state on the list. */
488 state->stateBits |= SB_ONLIST;
489 stateList.append( state );
491 /* Recurse on everything ranges. */
492 for ( TransList::Iter tel = state->outList; tel.lte(); tel++ ) {
493 if ( tel->toState != 0 )
494 depthFirstOrdering( tel->toState );
498 /* Ordering states by transition connections. */
499 void FsmAp::depthFirstOrdering()
501 /* Init on state list flags. */
502 for ( StateList::Iter st = stateList; st.lte(); st++ )
503 st->stateBits &= ~SB_ONLIST;
505 /* Clear out the state list, we will rebuild it. */
506 int stateListLen = stateList.length();
507 stateList.abandon();
509 /* Add back to the state list from the start state and all other entry
510 * points. */
511 if ( errState != 0 )
512 depthFirstOrdering( errState );
513 depthFirstOrdering( startState );
514 for ( EntryMap::Iter en = entryPoints; en.lte(); en++ )
515 depthFirstOrdering( en->value );
517 /* Make sure we put everything back on. */
518 assert( stateListLen == stateList.length() );
521 /* Stable sort the states by final state status. */
522 void FsmAp::sortStatesByFinal()
524 /* Move forward through the list and throw final states onto the end. */
525 StateAp *state = 0;
526 StateAp *next = stateList.head;
527 StateAp *last = stateList.tail;
528 while ( state != last ) {
529 /* Move forward and load up the next. */
530 state = next;
531 next = state->next;
533 /* Throw to the end? */
534 if ( state->isFinState() ) {
535 stateList.detach( state );
536 stateList.append( state );
541 void FsmAp::setStateNumbers( int base )
543 for ( StateList::Iter state = stateList; state.lte(); state++ )
544 state->alg.stateNum = base++;
548 bool FsmAp::checkErrTrans( StateAp *state, TransAp *trans )
550 /* Might go directly to error state. */
551 if ( trans->toState == 0 )
552 return true;
554 if ( trans->prev == 0 ) {
555 /* If this is the first transition. */
556 if ( keyOps->minKey < trans->lowKey )
557 return true;
559 else {
560 /* Not the first transition. Compare against the prev. */
561 TransAp *prev = trans->prev;
562 Key nextKey = prev->highKey;
563 nextKey.increment();
564 if ( nextKey < trans->lowKey )
565 return true;
567 return false;
570 bool FsmAp::checkErrTransFinish( StateAp *state )
572 /* Check if there are any ranges already. */
573 if ( state->outList.length() == 0 )
574 return true;
575 else {
576 /* Get the last and check for a gap on the end. */
577 TransAp *last = state->outList.tail;
578 if ( last->highKey < keyOps->maxKey )
579 return true;
581 return 0;
584 bool FsmAp::hasErrorTrans()
586 bool result;
587 for ( StateList::Iter st = stateList; st.lte(); st++ ) {
588 for ( TransList::Iter tr = st->outList; tr.lte(); tr++ ) {
589 result = checkErrTrans( st, tr );
590 if ( result )
591 return true;
593 result = checkErrTransFinish( st );
594 if ( result )
595 return true;
597 return false;