diff --git a/src/main/java/nl/andrewlalis/grammar_tool/GrammarTool.java b/src/main/java/nl/andrewlalis/grammar_tool/GrammarTool.java index 2782e95..561a147 100644 --- a/src/main/java/nl/andrewlalis/grammar_tool/GrammarTool.java +++ b/src/main/java/nl/andrewlalis/grammar_tool/GrammarTool.java @@ -20,9 +20,13 @@ public class GrammarTool { -> q0 : "" -> q1, "b" -> q2 q1 : "c" -> q1, "a" -> q2, q2 : "d" -> q2, "d" -> q3, - * q3 : "c" -> q1 + * q3 : "c" -> q0 """); System.out.println(f1); System.out.println("Deterministic? " + f1.isDeterministic()); + + FiniteStateMachine d1 = f1.toDeterministic(); + System.out.println(d1); + System.out.println("Deterministic? " + d1.isDeterministic()); } } \ No newline at end of file diff --git a/src/main/java/nl/andrewlalis/grammar_tool/grammar/Symbol.java b/src/main/java/nl/andrewlalis/grammar_tool/grammar/Symbol.java index 20566ee..c87c5d4 100644 --- a/src/main/java/nl/andrewlalis/grammar_tool/grammar/Symbol.java +++ b/src/main/java/nl/andrewlalis/grammar_tool/grammar/Symbol.java @@ -7,6 +7,8 @@ import java.util.Objects; import java.util.Set; public class Symbol implements Comparable { + public static final Symbol EMPTY = Symbol.of(""); + @Getter private final String identifier; diff --git a/src/main/java/nl/andrewlalis/grammar_tool/machine/FiniteStateMachine.java b/src/main/java/nl/andrewlalis/grammar_tool/machine/FiniteStateMachine.java index 36220d8..7950667 100644 --- a/src/main/java/nl/andrewlalis/grammar_tool/machine/FiniteStateMachine.java +++ b/src/main/java/nl/andrewlalis/grammar_tool/machine/FiniteStateMachine.java @@ -2,10 +2,7 @@ package nl.andrewlalis.grammar_tool.machine; import nl.andrewlalis.grammar_tool.grammar.Symbol; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -49,6 +46,90 @@ public class FiniteStateMachine { } } + public Set getTransitionsStartingAt(State state) { + Set stateTransitions = new HashSet<>(); + for (Transition t : this.transitions) { + if (t.getStartState().equals(state)) { + stateTransitions.add(t); + } + } + return stateTransitions; + } + + public Set getNextStates(State currentState, Symbol acceptingSymbol) { + Set nextStates = new HashSet<>(); + for (Transition t : this.transitions) { + if (t.getStartState().equals(currentState) && t.getAcceptingSymbol().equals(acceptingSymbol)) { + nextStates.add(t.getEndState()); + } + } + return nextStates; + } + + public Set getNextStates(Set states, Symbol acceptingSymbol) { + Set nextStates = new HashSet<>(); + for (State state : states) { + nextStates.addAll(this.getNextStates(state, acceptingSymbol)); + } + return nextStates; + } + + public Set getEpsilonClosure(State currentState) { + Set nextStates = this.getNextStates(currentState, Symbol.EMPTY); + nextStates.add(currentState); + return nextStates; + } + + public Set getEpsilonClosure(Set states) { + Set nextStates = this.getNextStates(states, Symbol.EMPTY); + nextStates.addAll(states); + return nextStates; + } + + public boolean isDeterministic() { + for (State state : this.states) { + for (Symbol symbol : this.alphabet) { + if (this.getNextStates(state, symbol).size() > 1) { + return false; + } + } + } + return true; + } + + public FiniteStateMachine toDeterministic() { + Set finalStates = new HashSet<>(); + Set transitions = new HashSet<>(); + Set startClosure = this.getEpsilonClosure(this.startState); + State startState = State.of(startClosure); + Deque> stateQueue = new LinkedList<>(); + Set> visitedClosures = new HashSet<>(); + stateQueue.add(startClosure); + while (!stateQueue.isEmpty()) { + Set currentClosure = stateQueue.pop(); + visitedClosures.add(currentClosure); + State combinedState = State.of(currentClosure); + for (State state : currentClosure) { + if (this.finalStates.contains(state)) { + finalStates.add(combinedState); + } + } + for (Symbol symbol : this.alphabet) { + if (symbol.isEmpty()) continue; + Set nextStates = this.getNextStates(currentClosure, symbol); + Set nextStateClosure = this.getEpsilonClosure(nextStates); + if (nextStateClosure.isEmpty()) continue; + State combinedNextState = State.of(nextStateClosure); +// System.out.println(combinedState + " : " + "\"" + symbol + "\" -> " + combinedNextState); + transitions.add(new Transition(combinedState, symbol, combinedNextState)); + if (!visitedClosures.contains(nextStateClosure)) { + stateQueue.add(nextStateClosure); + } + } + } + return FiniteStateMachine.fromTransitions(startState, transitions, finalStates); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -93,37 +174,6 @@ public class FiniteStateMachine { return sb.toString(); } - public Set getTransitionsStartingAt(State state) { - Set stateTransitions = new HashSet<>(); - for (Transition t : this.transitions) { - if (t.getStartState().equals(state)) { - stateTransitions.add(t); - } - } - return stateTransitions; - } - - public Set getNextStates(State currentState, Symbol acceptingSymbol) { - Set nextStates = new HashSet<>(); - for (Transition t : this.transitions) { - if (t.getStartState().equals(currentState) && t.getAcceptingSymbol().equals(acceptingSymbol)) { - nextStates.add(t.getEndState()); - } - } - return nextStates; - } - - public boolean isDeterministic() { - for (State state : this.states) { - for (Symbol symbol : this.alphabet) { - if (this.getNextStates(state, symbol).size() > 1) { - return false; - } - } - } - return true; - } - public static FiniteStateMachine fromTransitions(State startState, Set transitions, Set finalStates) { Set alphabet = new HashSet<>(); Set states = new HashSet<>(); diff --git a/src/main/java/nl/andrewlalis/grammar_tool/machine/State.java b/src/main/java/nl/andrewlalis/grammar_tool/machine/State.java index b8695af..ba90576 100644 --- a/src/main/java/nl/andrewlalis/grammar_tool/machine/State.java +++ b/src/main/java/nl/andrewlalis/grammar_tool/machine/State.java @@ -1,6 +1,8 @@ package nl.andrewlalis.grammar_tool.machine; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; public class State implements Comparable { private final String name; @@ -31,4 +33,11 @@ public class State implements Comparable { public int compareTo(State o) { return this.name.compareTo(o.name); } + + public static State of(Set states) { + if (states.isEmpty()) throw new IllegalArgumentException("Cannot construct state of empty states."); + if (states.size() == 1) return states.stream().findAny().get(); + String name = states.stream().sorted().map(State::toString).collect(Collectors.joining(", ")); + return new State("{" + name + "}"); + } }