2 * Copyright 2001 Adrian Thurston <thurston@cs.queensu.ca>
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
29 /* Insert a transition into an inlist. The head must be supplied. */
30 void FsmAp::attachToInList( StateAp
*from
, StateAp
*to
,
31 TransAp
*&head
, TransAp
*trans
)
36 /* If in trans list is not empty, set the head->prev to trans. */
40 /* Now insert ourselves at the front of the list. */
43 /* Keep track of foreign transitions for from and to. */
45 if ( misfitAccounting
) {
46 /* If the number of foreign in transitions is about to go up to 1 then
47 * move it from the misfit list to the main list. */
48 if ( to
->foreignInTrans
== 0 )
49 stateList
.append( misfitList
.detach( to
) );
52 to
->foreignInTrans
+= 1;
56 /* Detach a transition from an inlist. The head of the inlist must be supplied. */
57 void FsmAp::detachFromInList( StateAp
*from
, StateAp
*to
,
58 TransAp
*&head
, TransAp
*trans
)
60 /* Detach in the inTransList. */
61 if ( trans
->ilprev
== 0 )
64 trans
->ilprev
->ilnext
= trans
->ilnext
;
66 if ( trans
->ilnext
!= 0 )
67 trans
->ilnext
->ilprev
= trans
->ilprev
;
69 /* Keep track of foreign transitions for from and to. */
71 to
->foreignInTrans
-= 1;
73 if ( misfitAccounting
) {
74 /* If the number of foreign in transitions goes down to 0 then move it
75 * from the main list to the misfit list. */
76 if ( to
->foreignInTrans
== 0 )
77 misfitList
.append( stateList
.detach( to
) );
82 /* Attach states on the default transition, range list or on out/in list key.
83 * First makes a new transition. If there is already a transition out from
84 * fromState on the default, then will assertion fail. */
85 TransAp
*FsmAp::attachNewTrans( StateAp
*from
, StateAp
*to
, Key lowKey
, Key highKey
)
87 /* Make the new transition. */
88 TransAp
*retVal
= new TransAp();
90 /* The transition is now attached. Remember the parties involved. */
91 retVal
->fromState
= from
;
94 /* Make the entry in the out list for the transitions. */
95 from
->outList
.append( retVal
);
97 /* Set the the keys of the new trans. */
98 retVal
->lowKey
= lowKey
;
99 retVal
->highKey
= highKey
;
101 /* Attach using inList as the head pointer. */
103 attachToInList( from
, to
, to
->inList
.head
, retVal
);
108 /* Attach for range lists or for the default transition. This attach should
109 * be used when a transition already is allocated and must be attached to a
110 * target state. Does not handle adding the transition into the out list. */
111 void FsmAp::attachTrans( StateAp
*from
, StateAp
*to
, TransAp
*trans
)
113 assert( trans
->fromState
== 0 && trans
->toState
== 0 );
114 trans
->fromState
= from
;
118 /* Attach using the inList pointer as the head pointer. */
119 attachToInList( from
, to
, to
->inList
.head
, trans
);
123 /* Redirect a transition away from error and towards some state. This is just
124 * like attachTrans except it requires fromState to be set and does not touch
126 void FsmAp::redirectErrorTrans( StateAp
*from
, StateAp
*to
, TransAp
*trans
)
128 assert( trans
->fromState
!= 0 && trans
->toState
== 0 );
132 /* Attach using the inList pointer as the head pointer. */
133 attachToInList( from
, to
, to
->inList
.head
, trans
);
137 /* Detach for out/in lists or for default transition. */
138 void FsmAp::detachTrans( StateAp
*from
, StateAp
*to
, TransAp
*trans
)
140 assert( trans
->fromState
== from
&& trans
->toState
== to
);
141 trans
->fromState
= 0;
145 /* Detach using to's inList pointer as the head. */
146 detachFromInList( from
, to
, to
->inList
.head
, trans
);
151 /* Detach a state from the graph. Detaches and deletes transitions in and out
152 * of the state. Empties inList and outList. Removes the state from the final
153 * state set. A detached state becomes useless and should be deleted. */
154 void FsmAp::detachState( StateAp
*state
)
156 /* Detach the in transitions from the inList list of transitions. */
157 while ( state
->inList
.head
!= 0 ) {
158 /* Get pointers to the trans and the state. */
159 TransAp
*trans
= state
->inList
.head
;
160 StateAp
*fromState
= trans
->fromState
;
162 /* Detach the transitions from the source state. */
163 detachTrans( fromState
, state
, trans
);
165 /* Ok to delete the transition. */
166 fromState
->outList
.detach( trans
);
170 /* Remove the entry points in on the machine. */
171 while ( state
->entryIds
.length() > 0 )
172 unsetEntry( state
->entryIds
[0], state
);
174 /* Detach out range transitions. */
175 for ( TransList::Iter trans
= state
->outList
; trans
.lte(); ) {
176 TransList::Iter next
= trans
.next();
177 detachTrans( state
, trans
->toState
, trans
);
182 /* Delete all of the out range pointers. */
183 state
->outList
.abandon();
185 /* Unset final stateness before detaching from graph. */
186 if ( state
->stateBits
& STB_ISFINAL
)
187 finStateSet
.remove( state
);
191 /* Duplicate a transition. Makes a new transition that is attached to the same
192 * dest as srcTrans. The new transition has functions and priority taken from
193 * srcTrans. Used for merging a transition in to a free spot. The trans can
194 * just be dropped in. It does not conflict with an existing trans and need
195 * not be crossed. Returns the new transition. */
196 TransAp
*FsmAp::dupTrans( StateAp
*from
, TransAp
*srcTrans
)
198 /* Make a new transition. */
199 TransAp
*newTrans
= new TransAp();
201 /* We can attach the transition, one does not exist. */
202 attachTrans( from
, srcTrans
->toState
, newTrans
);
204 /* Call the user callback to add in the original source transition. */
205 addInTrans( newTrans
, srcTrans
);
210 /* In crossing, src trans and dest trans both go to existing states. Make one
211 * state from the sets of states that src and dest trans go to. */
212 TransAp
*FsmAp::fsmAttachStates( MergeData
&md
, StateAp
*from
,
213 TransAp
*destTrans
, TransAp
*srcTrans
)
215 /* The priorities are equal. We must merge the transitions. Does the
216 * existing trans go to the state we are to attach to? ie, are we to
217 * simply double up the transition? */
218 StateAp
*toState
= srcTrans
->toState
;
219 StateAp
*existingState
= destTrans
->toState
;
221 if ( existingState
== toState
) {
222 /* The transition is a double up to the same state. Copy the src
223 * trans into itself. We don't need to merge in the from out trans
224 * data, that was done already. */
225 addInTrans( destTrans
, srcTrans
);
228 /* The trans is not a double up. Dest trans cannot be the same as src
229 * trans. Set up the state set. */
232 /* We go to all the states the existing trans goes to, plus... */
233 if ( existingState
->stateDictEl
== 0 )
234 stateSet
.insert( existingState
);
236 stateSet
.insert( existingState
->stateDictEl
->stateSet
);
238 /* ... all the states that we have been told to go to. */
239 if ( toState
->stateDictEl
== 0 )
240 stateSet
.insert( toState
);
242 stateSet
.insert( toState
->stateDictEl
->stateSet
);
244 /* Look for the state. If it is not there already, make it. */
245 StateDictEl
*lastFound
;
246 if ( md
.stateDict
.insert( stateSet
, &lastFound
) ) {
247 /* Make a new state representing the combination of states in
248 * stateSet. It gets added to the fill list. This means that we
249 * need to fill in it's transitions sometime in the future. We
250 * don't do that now (ie, do not recurse). */
251 StateAp
*combinState
= addState();
253 /* Link up the dict element and the state. */
254 lastFound
->targState
= combinState
;
255 combinState
->stateDictEl
= lastFound
;
257 /* Add to the fill list. */
258 md
.fillListAppend( combinState
);
261 /* Get the state insertted/deleted. */
262 StateAp
*targ
= lastFound
->targState
;
264 /* Detach the state from existing state. */
265 detachTrans( from
, existingState
, destTrans
);
267 /* Re-attach to the new target. */
268 attachTrans( from
, targ
, destTrans
);
270 /* Add in src trans to the existing transition that we redirected to
271 * the new state. We don't need to merge in the from out trans data,
272 * that was done already. */
273 addInTrans( destTrans
, srcTrans
);
279 /* Two transitions are to be crossed, handle the possibility of either going
280 * to the error state. */
281 TransAp
*FsmAp::mergeTrans( MergeData
&md
, StateAp
*from
,
282 TransAp
*destTrans
, TransAp
*srcTrans
)
284 TransAp
*retTrans
= 0;
285 if ( destTrans
->toState
== 0 && srcTrans
->toState
== 0 ) {
286 /* Error added into error. */
287 addInTrans( destTrans
, srcTrans
);
288 retTrans
= destTrans
;
290 else if ( destTrans
->toState
== 0 && srcTrans
->toState
!= 0 ) {
291 /* Non error added into error we need to detach and reattach, */
292 detachTrans( from
, destTrans
->toState
, destTrans
);
293 attachTrans( from
, srcTrans
->toState
, destTrans
);
294 addInTrans( destTrans
, srcTrans
);
295 retTrans
= destTrans
;
297 else if ( srcTrans
->toState
== 0 ) {
298 /* Dest goes somewhere but src doesn't, just add it it in. */
299 addInTrans( destTrans
, srcTrans
);
300 retTrans
= destTrans
;
303 /* Both go somewhere, run the actual cross. */
304 retTrans
= fsmAttachStates( md
, from
, destTrans
, srcTrans
);
310 /* Find the trans with the higher priority. If src is lower priority then dest then
311 * src is ignored. If src is higher priority than dest, then src overwrites dest. If
312 * the priorities are equal, then they are merged. */
313 TransAp
*FsmAp::crossTransitions( MergeData
&md
, StateAp
*from
,
314 TransAp
*destTrans
, TransAp
*srcTrans
)
318 /* Compare the priority of the dest and src transitions. */
319 int compareRes
= comparePrior( destTrans
->priorTable
, srcTrans
->priorTable
);
320 if ( compareRes
< 0 ) {
321 /* Src trans has a higher priority than dest, src overwrites dest.
322 * Detach dest and return a copy of src. */
323 detachTrans( from
, destTrans
->toState
, destTrans
);
324 retTrans
= dupTrans( from
, srcTrans
);
326 else if ( compareRes
> 0 ) {
327 /* The dest trans has a higher priority, use dest. */
328 retTrans
= destTrans
;
331 /* Src trans and dest trans have the same priority, they must be merged. */
332 retTrans
= mergeTrans( md
, from
, destTrans
, srcTrans
);
335 /* Return the transition that resulted from the cross. */
339 /* Copy the transitions in srcList to the outlist of dest. The srcList should
340 * not be the outList of dest, otherwise you would be copying the contents of
341 * srcList into itself as it's iterated: bad news. */
342 void FsmAp::outTransCopy( MergeData
&md
, StateAp
*dest
, TransAp
*srcList
)
344 /* The destination list. */
347 /* Set up an iterator to stop at breaks. */
348 PairIter
<TransAp
> outPair( dest
->outList
.head
, srcList
);
349 for ( ; !outPair
.end(); outPair
++ ) {
350 switch ( outPair
.userState
) {
352 /* The pair iter is the authority on the keys. It may have needed
353 * to break the dest range. */
354 TransAp
*destTrans
= outPair
.s1Tel
.trans
;
355 destTrans
->lowKey
= outPair
.s1Tel
.lowKey
;
356 destTrans
->highKey
= outPair
.s1Tel
.highKey
;
357 destList
.append( destTrans
);
361 /* Src range may get crossed with dest's default transition. */
362 TransAp
*newTrans
= dupTrans( dest
, outPair
.s2Tel
.trans
);
364 /* Set up the transition's keys and append to the dest list. */
365 newTrans
->lowKey
= outPair
.s2Tel
.lowKey
;
366 newTrans
->highKey
= outPair
.s2Tel
.highKey
;
367 destList
.append( newTrans
);
371 /* Exact overlap, cross them. */
372 TransAp
*newTrans
= crossTransitions( md
, dest
,
373 outPair
.s1Tel
.trans
, outPair
.s2Tel
.trans
);
375 /* Set up the transition's keys and append to the dest list. */
376 newTrans
->lowKey
= outPair
.s1Tel
.lowKey
;
377 newTrans
->highKey
= outPair
.s1Tel
.highKey
;
378 destList
.append( newTrans
);
382 /* Since we are always writing to the dest trans, the dest needs
383 * to be copied when it is broken. The copy goes into the first
384 * half of the break to "break it off". */
385 outPair
.s1Tel
.trans
= dupTrans( dest
, outPair
.s1Tel
.trans
);
393 /* Abandon the old outList and transfer destList into it. */
394 dest
->outList
.transfer( destList
);
398 /* Move all the transitions that go into src so that they go into dest. */
399 void FsmAp::inTransMove( StateAp
*dest
, StateAp
*src
)
401 /* Do not try to move in trans to and from the same state. */
402 assert( dest
!= src
);
404 /* If src is the start state, dest becomes the start state. */
405 if ( src
== startState
) {
407 setStartState( dest
);
410 /* For each entry point into, create an entry point into dest, when the
411 * state is detached, the entry points to src will be removed. */
412 for ( EntryIdSet::Iter enId
= src
->entryIds
; enId
.lte(); enId
++ )
413 changeEntry( *enId
, dest
, src
);
415 /* Move the transitions in inList. */
416 while ( src
->inList
.head
!= 0 ) {
417 /* Get trans and from state. */
418 TransAp
*trans
= src
->inList
.head
;
419 StateAp
*fromState
= trans
->fromState
;
421 /* Detach from src, reattach to dest. */
422 detachTrans( fromState
, src
, trans
);
423 attachTrans( fromState
, dest
, trans
);