package project.phase2.ll1parsergenerator.dfastuff; import java.util.*; /** * A finite automata is any system that utilizes a finite number of states and associated transitions in order to represent a behavioral model. * * T represents the transition values. */ public abstract class TableDrivenFiniteAutomaton<T> implements IFiniteAutomaton<Integer, T> { // // DATA // /** * The empty transition. */ protected final T EMPTY_TRANSITION = null; /** * A representation of the automaton implemented as a table of states and their associated transitions. */ private List<Map<T, Transition<T, Integer>>> mAutomaton; /** * The starting state of this automaton. */ private int mStartState; /** * The final states of this automaton. */ private Set<Integer> mGoalStates; /** * The labels of the goal states. */ private Map<Integer, String> mGoalLabels; // // CTOR // public TableDrivenFiniteAutomaton() { mAutomaton = new ArrayList<Map<T, Transition<T, Integer>>>(); mGoalStates = new HashSet<Integer>(); mGoalLabels = new HashMap<Integer, String>(); } // // PUBLIC METHODS // /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomata#getStates() */ public List<Integer> getStates() { ArrayList<Integer> ret = new ArrayList<Integer>(mAutomaton.size()); for(int i = 0; i < mAutomaton.size(); i++) ret.add(i); return ret; } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomata#getStartState() */ public Integer getStartState() { return mStartState; } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomata#getGoalStates() */ public Set<Integer> getGoalStates() { Set<Integer> ret = new HashSet<Integer>(); ret.addAll(mGoalStates); return ret; } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#getGoalLabel(java.lang.Object) */ @Override public String getGoalLabel(Integer goalState) { if(mGoalLabels.containsKey(goalState)) return mGoalLabels.get(goalState); return null; } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomata#getTransitions(java.lang.Object) */ public List<Transition<T, Integer>> getTransitions(Integer state) { if(state >= mAutomaton.size() || state < 0) return null; ArrayList<Transition<T, Integer>> ret = new ArrayList<Transition<T, Integer>>(); ret.addAll(mAutomaton.get(state).values()); return ret; } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#getTransition(java.lang.Object, java.lang.Object) */ public Transition<T, Integer> getTransition(T value, Integer state) { if(state >= mAutomaton.size() || state < 0) return null; return mAutomaton.get(state).get(value); } public List<T> getTransitionValues(Integer state) { if(state >= mAutomaton.size() || state < 0) return null; ArrayList<T> ret = new ArrayList<T>(); ret.addAll(mAutomaton.get(state).keySet()); return ret; } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#createState() */ public Integer createState() { int ret = mAutomaton.size(); mAutomaton.add(new HashMap<T, Transition<T, Integer>>()); return ret; } /* (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#setStartState(java.lang.Object) */ @Override public void setStartState(Integer state) { if(state >= mAutomaton.size() || state < 0) return; mStartState = state; } /* (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#setGoalState(java.lang.Object) */ @Override public void setGoalState(Integer state) { setGoalState(state, null); } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#setGoalState(java.lang.Object, java.lang.String) */ @Override public void setGoalState(Integer state, String label) { if(state >= mAutomaton.size() || state < 0) return; mGoalStates.add(state); mGoalLabels.put(state, label); } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#setGoalLabels(java.lang.String) */ @Override public void setGoalLabels(String label) { for(Integer goal : mGoalStates) mGoalLabels.put(goal, label); } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#addTransition(dfabuilder.Transition) */ public boolean addTransition(Transition<T, Integer> trans) { // Make sure we have the start state. if(trans.getStart() >= mAutomaton.size() || trans.getStart() < 0) return false; // Make sure that we have the end states. for(int i : trans.getDestinations()) { if(i >= mAutomaton.size() || i < 0) { return false; } } // If we already have a transition for the given state/value, reject. Map<T, Transition<T, Integer>> state = mAutomaton.get(trans.getStart()); if(state.containsKey(trans.getValue())) { Set<Integer> dests = new HashSet<Integer>(); for(int org : getTransition(trans.getValue(), trans.getStart()).getDestinations()) { dests.add(org); } for(int newD : trans.getDestinations()) { dests.add(newD); } state.put(trans.getValue(), new Transition<T, Integer>(trans.getValue(), trans.getStart(), dests.toArray(new Integer[0]))); } else state.put(trans.getValue(), trans); return true; } /* * (non-Javadoc) * @see dfabuilder.IFiniteAutomaton#testInput(T[]) */ public AcceptLabel testInput(T[] input) { Transition<T, Integer> t; Set<Integer> newStates, currentStates = new HashSet<Integer>(); currentStates.add(mStartState); currentStates = epsilonClosure(currentStates); // Walk through transitions until we have reached the end of input. for(int transition = 0; transition < input.length; transition++) { newStates = new HashSet<Integer>(); for(Integer state : currentStates) { t = this.getTransition(input[transition], state); if(t != null) newStates.addAll(Arrays.asList(t.getDestinations())); } // If the transition was not valid for any of our current states return false. if(newStates.isEmpty()) return new AcceptLabel(); currentStates = epsilonClosure(newStates); } // If any of our current states are a goal, we accept. for(Integer state : currentStates) { if(mGoalStates.contains(state)) { return new AcceptLabel(true, getGoalLabel(state)); } } // We stayed within the FA but did not reach a goal. return new AcceptLabel(); } /** * Generates a string representation of the automaton in the form: * * This string is of the form: ({StateNumber: [ Transitions ]} * {StateNumber: [ Transitions ]} * ... * {StateNumber: [ Transitions ]}) * * The starting state number will be preceded by a ==>, and goal state numbers will be encapsulated in parentheses () along with the label if the goal has one. * * @return a string representation of the automaton. */ public String toString() { String ret = "("; for(int i = 0; i < mAutomaton.size(); i++) { ret += "{"; if((Integer.valueOf(mStartState)).equals(i)) ret += "==>"; if(mGoalStates.contains(i)) { ret += "(" + i + ((getGoalLabel(i) != null)?("-\"" + getGoalLabel(i) + "\")"):(")")); } else ret += i; ret += ": "; ret += Arrays.toString(getTransitions(i).toArray()); ret += "}\n"; } ret = ret.trim(); ret += ")"; return ret; } // // PRIVATE METHODS // private Set<Integer> epsilonClosure(Set<Integer> states) { Set<Integer> interSet, newSet = new HashSet<Integer>(), retSet = new HashSet<Integer>(); Transition<T, Integer> t; retSet.addAll(states); newSet.addAll(states); int oldSize = 0, newSize = retSet.size(); while((oldSize != newSize) && !newSet.isEmpty()) { interSet = newSet; newSet = new HashSet<Integer>(); for(Integer state : interSet) { t = this.getTransition(EMPTY_TRANSITION, state); if(t != null) newSet.addAll(Arrays.asList(t.getDestinations())); } // This rather than add all to prevent duplications and same states being expaned over and over. interSet = new HashSet<Integer>(); for(Integer newS : newSet) { if(!retSet.contains(newS)) { interSet.add(newS); retSet.add(newS); } } newSet = interSet; oldSize = newSize; newSize = retSet.size(); } return retSet; } }