KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sablecc > sablecc > automaton > MinimalDfa


1 /* This file is part of SableCC ( http://sablecc.org ).
2  *
3  * Copyright 2007 Etienne M. Gagnon <egagnon@j-meg.com>
4  * Copyright 2007 Patrick Pelletier <pp.pelletier@gmail.com>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package org.sablecc.sablecc.automaton;
20
21 import java.util.Collections JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.LinkedHashSet JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Set JavaDoc;
26 import java.util.SortedMap JavaDoc;
27 import java.util.SortedSet JavaDoc;
28 import java.util.TreeMap JavaDoc;
29 import java.util.TreeSet JavaDoc;
30
31 import org.sablecc.sablecc.alphabet.Alphabet;
32 import org.sablecc.sablecc.alphabet.Symbol;
33 import org.sablecc.sablecc.exception.InternalException;
34 import org.sablecc.sablecc.util.WorkSet;
35
36 /**
37  * A minimal deterministic finite automaton (or MinimalDfa) is a state machine
38  * which is minimalist and equivalent to a corresponding <code>Dfa</code>.
39  */

40 public class MinimalDfa<T extends Comparable JavaDoc<? super T>> {
41
42     /** Only used for line separation in method toString. */
43     private static final String JavaDoc lineSeparator = System
44             .getProperty("line.separator");
45
46     /** The alphabet for this <code>MinimalDfa</code>. */
47     private Alphabet<T> alphabet;
48
49     /** The states of this <code>MinimalDfa</code>. */
50     private SortedSet JavaDoc<MinimalDfaState<T>> states;
51
52     /** The starting state of this <code>MinimalDfa</code>. */
53     private MinimalDfaState<T> startState;
54
55     /** The dead end state of this <code>MinimalDfa</code>. */
56     private MinimalDfaState<T> deadEndState;
57
58     /** The acceptation states of this <code>MinimalDfa</code>. */
59     private SortedSet JavaDoc<MinimalDfaState<T>> acceptStates;
60
61     /** A stability status for this <code>MinimalDfa</code>. */
62     private boolean isStable;
63
64     /**
65      * Cached string representation. Is <code>null</code> when not yet
66      * computed.
67      */

68     private String JavaDoc toString;
69
70     /**
71      * Constructs a <code>MinimalDfa</code> equivalent to the provided
72      * <code>Dfa</code>.
73      *
74      * @param dfa
75      * the <code>Dfa</code>.
76      * @throws InternalException
77      * if the <code>Dfa</code> is <code>null</code>.
78      */

79     public MinimalDfa(
80             Dfa<T> dfa) {
81
82         if (dfa == null) {
83             throw new InternalException("dfa may not be null");
84         }
85
86         // partition states groups
87
Partition<T> partition = new Partition<T>(dfa);
88
89         // find dead-end and start groups
90
Group<T> deadEndGroup = partition.getElement(dfa.getDeadEndState())
91                 .getGroup();
92         Group<T> startGroup = partition.getElement(dfa.getStartState())
93                 .getGroup();
94
95         // create minimal alphabet
96

97         // identify, for each symbol, all group pairs that are joined by a
98
// transition on this symbol (ignoring transitions from or to the
99
// dead-end group)
100

101         SortedMap JavaDoc<Symbol<T>, Set JavaDoc<GroupPair<T>>> symbolToPairSetMap = new TreeMap JavaDoc<Symbol<T>, Set JavaDoc<GroupPair<T>>>();
102
103         for (Group<T> sourceGroup : partition.getGroups()) {
104
105             if (sourceGroup == deadEndGroup) {
106                 continue;
107             }
108
109             for (Element<T> element : sourceGroup.getElements()) {
110
111                 for (Map.Entry JavaDoc<Symbol<T>, DfaState<T>> entry : element
112                         .getState().getTransitions().entrySet()) {
113
114                     Symbol<T> symbol = entry.getKey();
115                     DfaState<T> dfaTarget = entry.getValue();
116
117                     Group<T> targetGroup = partition.getElement(dfaTarget)
118                             .getGroup();
119
120                     if (targetGroup == deadEndGroup) {
121                         continue;
122                     }
123
124                     GroupPair<T> pair = new GroupPair<T>(sourceGroup,
125                             targetGroup);
126
127                     Set JavaDoc<GroupPair<T>> pairSet = symbolToPairSetMap.get(symbol);
128
129                     if (pairSet == null) {
130                         pairSet = new LinkedHashSet JavaDoc<GroupPair<T>>();
131                         symbolToPairSetMap.put(symbol, pairSet);
132                     }
133
134                     pairSet.add(pair);
135                 }
136             }
137         }
138
139         // for each pair set, identify symbols that map to it
140
Map JavaDoc<Set JavaDoc<GroupPair<T>>, SortedSet JavaDoc<Symbol<T>>> pairSetToSymbolSetMap = new HashMap JavaDoc<Set JavaDoc<GroupPair<T>>, SortedSet JavaDoc<Symbol<T>>>();
141         // build the set of "pair set" (i.e. the key set of
142
// pairSetToSymbolSetMap)
143
Set JavaDoc<Set JavaDoc<GroupPair<T>>> setOfPairSet = new LinkedHashSet JavaDoc<Set JavaDoc<GroupPair<T>>>();
144
145         for (Map.Entry JavaDoc<Symbol<T>, Set JavaDoc<GroupPair<T>>> entry : symbolToPairSetMap
146                 .entrySet()) {
147
148             Symbol<T> symbol = entry.getKey();
149             Set JavaDoc<GroupPair<T>> pairSet = entry.getValue();
150
151             SortedSet JavaDoc<Symbol<T>> symbolSet = pairSetToSymbolSetMap.get(pairSet);
152
153             if (symbolSet == null) {
154
155                 symbolSet = new TreeSet JavaDoc<Symbol<T>>();
156                 pairSetToSymbolSetMap.put(pairSet, symbolSet);
157             }
158
159             symbolSet.add(symbol);
160             setOfPairSet.add(pairSet);
161         }
162
163         // merge symbols and create the new alphabet
164
SortedSet JavaDoc<Symbol<T>> newSymbols = new TreeSet JavaDoc<Symbol<T>>();
165
166         for (Set JavaDoc<GroupPair<T>> pairSet : setOfPairSet) {
167
168             SortedSet JavaDoc<Symbol<T>> symbolSet = pairSetToSymbolSetMap.get(pairSet);
169
170             Symbol<T> newSymbol = Symbol.merge(symbolSet);
171
172             newSymbols.add(newSymbol);
173
174             for (GroupPair<T> pair : pairSet) {
175                 pair.getGroup1().addTransition(newSymbol, pair.getGroup2());
176             }
177         }
178
179         this.alphabet = new Alphabet<T>(newSymbols);
180
181         // initialize fields
182
this.states = new TreeSet JavaDoc<MinimalDfaState<T>>();
183         this.acceptStates = new TreeSet JavaDoc<MinimalDfaState<T>>();
184         this.isStable = false;
185
186         // create states and transitions in canonical order
187

188         deadEndGroup.setState(new MinimalDfaState<T>(this));
189
190         // sometimes, the start and dead-end groups are the same group
191
if (startGroup.getState() == null) {
192             startGroup.setState(new MinimalDfaState<T>(this));
193         }
194
195         WorkSet<Group<T>> workSet = new WorkSet<Group<T>>();
196         workSet.add(startGroup);
197
198         while (workSet.hasNext()) {
199
200             Group<T> sourceGroup = workSet.next();
201
202             for (Map.Entry JavaDoc<Symbol<T>, Group<T>> entry : sourceGroup
203                     .getTransitions().entrySet()) {
204
205                 Symbol<T> symbol = entry.getKey();
206                 Group<T> destinationGroup = entry.getValue();
207
208                 if (destinationGroup.getState() == null) {
209                     destinationGroup.setState(new MinimalDfaState<T>(this));
210                 }
211
212                 sourceGroup.getState().addTransition(symbol,
213                         destinationGroup.getState());
214                 workSet.add(destinationGroup);
215             }
216         }
217
218         // set start, dead-end, and accept states
219
this.startState = startGroup.getState();
220         this.deadEndState = deadEndGroup.getState();
221
222         for (DfaState<T> dfaState : dfa.getAcceptStates()) {
223             this.acceptStates.add(partition.getElement(dfaState).getGroup()
224                     .getState());
225         }
226
227         stabilize();
228     }
229
230     /**
231      * Stabilizes this <code>MinimalDfa</code> by stabilizing each of its
232      * states.
233      */

234     private void stabilize() {
235
236         for (MinimalDfaState<T> state : this.states) {
237             state.stabilize();
238         }
239
240         this.states = Collections.unmodifiableSortedSet(this.states);
241         this.acceptStates = Collections
242                 .unmodifiableSortedSet(this.acceptStates);
243         this.isStable = true;
244     }
245
246     /**
247      * Returns the alphabet of this <code>MinimalDfa</code>.
248      *
249      * @return the alphabet.
250      */

251     public Alphabet<T> getAlphabet() {
252
253         return this.alphabet;
254     }
255
256     /**
257      * Returns the states of this <code>MinimalDfa</code>.
258      *
259      * @return the set of states.
260      * @throws InternalException
261      * if this instance is not stable.
262      */

263     public SortedSet JavaDoc<MinimalDfaState<T>> getStates() {
264
265         if (!this.isStable) {
266             throw new InternalException("this MinimalDFA is not stable yet");
267         }
268
269         return this.states;
270     }
271
272     /**
273      * Returns the starting state of this <code>MinimalDfa</code>.
274      *
275      * @return the starting state.
276      * @throws InternalException
277      * if this instance is not stable.
278      */

279     public MinimalDfaState<T> getStartState() {
280
281         if (!this.isStable) {
282             throw new InternalException("this MinimalDFA is not stable yet");
283         }
284
285         return this.startState;
286     }
287
288     /**
289      * Returns the dead end state of this <code>MinimalDfa</code>.
290      *
291      * @return the dead end state.
292      * @throws InternalException
293      * if this instance is not stable.
294      */

295     public MinimalDfaState<T> getDeadEndState() {
296
297         if (!this.isStable) {
298             throw new InternalException("this MinimalDFA is not stable yet");
299         }
300
301         return this.deadEndState;
302     }
303
304     /**
305      * Returns the dead end state of this <code>MinimalDfa</code>if it is
306      * unstable.
307      *
308      * @return the dead end state.
309      */

310     MinimalDfaState<T> getUnstableDeadEndState() {
311
312         return this.deadEndState;
313     }
314
315     /**
316      * Returns the acceptation state of this <code>MinimalDfa</code>.
317      *
318      * @return the acceptation state.
319      * @throws InternalException
320      * if this instance is not stable.
321      */

322     public SortedSet JavaDoc<MinimalDfaState<T>> getAcceptStates() {
323
324         if (!this.isStable) {
325             throw new InternalException("this MinimalDFA is not stable yet");
326         }
327
328         return this.acceptStates;
329     }
330
331     /**
332      * Returns the string representation of this <code>MinimalDfa</code>.
333      *
334      * @return the string representation.
335      * @throws InternalException
336      * if this instance is not stable.
337      */

338     @Override JavaDoc
339     public String JavaDoc toString() {
340
341         if (this.toString == null) {
342
343             if (!this.isStable) {
344                 throw new InternalException("this MinimalDFA is not stable yet");
345             }
346
347             StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
348
349             sb.append("MinimalDFA:{");
350
351             for (MinimalDfaState<T> state : this.states) {
352                 sb.append(lineSeparator);
353                 sb.append(" ");
354                 sb.append(state);
355
356                 if (state == this.startState) {
357                     sb.append("(start)");
358                 }
359
360                 if (state == this.deadEndState) {
361                     sb.append("(dead-end)");
362                 }
363
364                 if (this.acceptStates.contains(state)) {
365                     sb.append("(accept)");
366                 }
367
368                 sb.append(":");
369                 for (Map.Entry JavaDoc<Symbol<T>, MinimalDfaState<T>> entry : state
370                         .getTransitions().entrySet()) {
371                     Symbol<T> symbol = entry.getKey();
372                     MinimalDfaState<T> target = entry.getValue();
373
374                     sb.append(lineSeparator);
375                     sb.append(" ");
376                     sb.append(symbol);
377                     sb.append(" -> ");
378                     sb.append(target);
379                 }
380             }
381
382             sb.append(lineSeparator);
383             sb.append("}");
384
385             this.toString = sb.toString();
386         }
387
388         return this.toString;
389     }
390
391     /**
392      * Returns the ID for the following state.
393      *
394      * @return the ID of the next state.
395      * @throws InternalException
396      * if this <code>MinimalDfa</code> instance is stable.
397      */

398     int getNextStateId() {
399
400         if (this.isStable) {
401             throw new InternalException(
402                     "a stable MinimalDFA may not be modified");
403         }
404
405         return this.states.size();
406     }
407
408     /**
409      * Adds a state to this <code>MinimalDfa</code>.
410      *
411      * @param state
412      * the state to add.
413      * @throws InternalException
414      * if this <code>MinimalDfa</code> is stable or or if the
415      * state is already in the state set.
416      */

417     void addState(
418             MinimalDfaState<T> state) {
419
420         if (this.isStable) {
421             throw new InternalException(
422                     "a stable MinimalDFA may not be modified");
423         }
424
425         if (!this.states.add(state)) {
426             throw new InternalException("state is already in state set");
427         }
428     }
429 }
430
Popular Tags