Added deserialization of FSMs
This commit is contained in:
		
							parent
							
								
									6d0ef8c6d2
								
							
						
					
					
						commit
						0741d9d62d
					
				| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
package nl.andrewlalis.grammar_tool;
 | 
					package nl.andrewlalis.grammar_tool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import nl.andrewlalis.grammar_tool.grammar.ContextFreeGrammar;
 | 
					import nl.andrewlalis.grammar_tool.grammar.ContextFreeGrammar;
 | 
				
			||||||
 | 
					import nl.andrewlalis.grammar_tool.machine.FiniteStateMachine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class GrammarTool {
 | 
					public class GrammarTool {
 | 
				
			||||||
	public static void main(String[] args) {
 | 
						public static void main(String[] args) {
 | 
				
			||||||
| 
						 | 
					@ -14,7 +15,14 @@ public class GrammarTool {
 | 
				
			||||||
				"C -> c,C | ε"
 | 
									"C -> c,C | ε"
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		System.out.println(g2);
 | 
							System.out.println(g2);
 | 
				
			||||||
		ContextFreeGrammar productive = g2.toProductiveForm();
 | 
					
 | 
				
			||||||
		System.out.println(productive);
 | 
							FiniteStateMachine f1 = FiniteStateMachine.fromString("""
 | 
				
			||||||
 | 
									-> q0 : "" -> q1, "b" -> q2
 | 
				
			||||||
 | 
									   q1 : "c" -> q1, "a" -> q2,
 | 
				
			||||||
 | 
									   q2 : "d" -> q2, "d" -> q3,
 | 
				
			||||||
 | 
									 * q3 : "c" -> q1
 | 
				
			||||||
 | 
									""");
 | 
				
			||||||
 | 
							System.out.println(f1);
 | 
				
			||||||
 | 
							System.out.println("Deterministic? " + f1.isDeterministic());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,10 @@ public class Symbol implements Comparable<Symbol> {
 | 
				
			||||||
		return this.identifier.compareTo(o.identifier);
 | 
							return this.identifier.compareTo(o.identifier);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public boolean isEmpty() {
 | 
				
			||||||
 | 
							return this.identifier.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static Symbol of(String identifier) {
 | 
						public static Symbol of(String identifier) {
 | 
				
			||||||
		return new Symbol(identifier);
 | 
							return new Symbol(identifier);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,8 +2,13 @@ package nl.andrewlalis.grammar_tool.machine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import nl.andrewlalis.grammar_tool.grammar.Symbol;
 | 
					import nl.andrewlalis.grammar_tool.grammar.Symbol;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Objects;
 | 
					import java.util.Objects;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					import java.util.regex.Matcher;
 | 
				
			||||||
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class FiniteStateMachine {
 | 
					public class FiniteStateMachine {
 | 
				
			||||||
	private final Set<Symbol> alphabet;
 | 
						private final Set<Symbol> alphabet;
 | 
				
			||||||
| 
						 | 
					@ -13,11 +18,11 @@ public class FiniteStateMachine {
 | 
				
			||||||
	private final Set<Transition> transitions;
 | 
						private final Set<Transition> transitions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public FiniteStateMachine(Set<Symbol> alphabet, Set<State> states, Set<State> finalStates, State startState, Set<Transition> transitions) {
 | 
						public FiniteStateMachine(Set<Symbol> alphabet, Set<State> states, Set<State> finalStates, State startState, Set<Transition> transitions) {
 | 
				
			||||||
		this.alphabet = alphabet;
 | 
							this.alphabet = Objects.requireNonNull(alphabet);
 | 
				
			||||||
		this.states = states;
 | 
							this.states = Objects.requireNonNull(states);
 | 
				
			||||||
		this.finalStates = finalStates;
 | 
							this.finalStates = Objects.requireNonNull(finalStates);
 | 
				
			||||||
		this.startState = startState;
 | 
							this.startState = Objects.requireNonNull(startState);
 | 
				
			||||||
		this.transitions = transitions;
 | 
							this.transitions = Objects.requireNonNull(transitions);
 | 
				
			||||||
		this.ensureValidElements();
 | 
							this.ensureValidElements();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,4 +65,129 @@ public class FiniteStateMachine {
 | 
				
			||||||
	public int hashCode() {
 | 
						public int hashCode() {
 | 
				
			||||||
		return Objects.hash(alphabet, states, finalStates, startState, transitions);
 | 
							return Objects.hash(alphabet, states, finalStates, startState, transitions);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public String toString() {
 | 
				
			||||||
 | 
							StringBuilder sb = new StringBuilder();
 | 
				
			||||||
 | 
							List<State> sortedStates = this.states.stream().sorted().collect(Collectors.toList());
 | 
				
			||||||
 | 
							for (State state : sortedStates) {
 | 
				
			||||||
 | 
								if (this.startState.equals(state)) {
 | 
				
			||||||
 | 
									sb.append("-> ");
 | 
				
			||||||
 | 
								} else if (this.finalStates.contains(state)) {
 | 
				
			||||||
 | 
									sb.append(" * ");
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									sb.append("   ");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								sb.append(state);
 | 
				
			||||||
 | 
								List<Transition> sortedTransitions = this.getTransitionsStartingAt(state).stream().sorted().collect(Collectors.toList());
 | 
				
			||||||
 | 
								if (!sortedTransitions.isEmpty()) {
 | 
				
			||||||
 | 
									sb.append(" : ");
 | 
				
			||||||
 | 
									for (int i = 0; i < sortedTransitions.size(); i++) {
 | 
				
			||||||
 | 
										Transition t = sortedTransitions.get(i);
 | 
				
			||||||
 | 
										sb.append('"').append(t.getAcceptingSymbol()).append('"').append(" -> ").append(t.getEndState());
 | 
				
			||||||
 | 
										if (i < sortedTransitions.size() - 1) sb.append(", ");
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								sb.append("\n");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return sb.toString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Set<Transition> getTransitionsStartingAt(State state) {
 | 
				
			||||||
 | 
							Set<Transition> stateTransitions = new HashSet<>();
 | 
				
			||||||
 | 
							for (Transition t : this.transitions) {
 | 
				
			||||||
 | 
								if (t.getStartState().equals(state)) {
 | 
				
			||||||
 | 
									stateTransitions.add(t);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return stateTransitions;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Set<State> getNextStates(State currentState, Symbol acceptingSymbol) {
 | 
				
			||||||
 | 
							Set<State> 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<Transition> transitions, Set<State> finalStates) {
 | 
				
			||||||
 | 
							Set<Symbol> alphabet = new HashSet<>();
 | 
				
			||||||
 | 
							Set<State> states = new HashSet<>();
 | 
				
			||||||
 | 
							for (Transition t : transitions) {
 | 
				
			||||||
 | 
								states.add(t.getStartState());
 | 
				
			||||||
 | 
								states.add(t.getEndState());
 | 
				
			||||||
 | 
								alphabet.add(t.getAcceptingSymbol());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return new FiniteStateMachine(alphabet, states,finalStates, startState, transitions);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs an FSM from a series of strings depicting states and their
 | 
				
			||||||
 | 
						 * possible transitions. The following format is used:
 | 
				
			||||||
 | 
						 * <pre><code>
 | 
				
			||||||
 | 
						 *     -> q0 : "a" -> q1, "" -> q2
 | 
				
			||||||
 | 
						 *        q1 : "b" -> q2, "c" -> q3
 | 
				
			||||||
 | 
						 *      * q2 : "c" -> q3
 | 
				
			||||||
 | 
						 *      * q3
 | 
				
			||||||
 | 
						 * </code></pre>
 | 
				
			||||||
 | 
						 * @param fsmString The string containing the FSM specification.
 | 
				
			||||||
 | 
						 * @return The finite state machine that was created.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static FiniteStateMachine fromString(String fsmString) {
 | 
				
			||||||
 | 
							String[] stateStrings = fsmString.split("\\n+");
 | 
				
			||||||
 | 
							State startState = null;
 | 
				
			||||||
 | 
							Set<State> finalStates = new HashSet<>();
 | 
				
			||||||
 | 
							Set<Transition> transitions = new HashSet<>();
 | 
				
			||||||
 | 
							for (String stateString : stateStrings) {
 | 
				
			||||||
 | 
								if (stateString.isBlank()) continue;
 | 
				
			||||||
 | 
								String[] parts = stateString.split("\\s*:\\s*");
 | 
				
			||||||
 | 
								String stateDefinition = parts[0].trim();
 | 
				
			||||||
 | 
								String[] definitionParts = stateDefinition.split("\\s+");
 | 
				
			||||||
 | 
								String modifier = null;
 | 
				
			||||||
 | 
								String stateName;
 | 
				
			||||||
 | 
								if (definitionParts.length > 1) {
 | 
				
			||||||
 | 
									modifier = definitionParts[0].trim();
 | 
				
			||||||
 | 
									stateName = definitionParts[1].trim();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									stateName = definitionParts[0].trim();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								State state = new State(stateName);
 | 
				
			||||||
 | 
								if (modifier != null) {
 | 
				
			||||||
 | 
									if (modifier.trim().equals("->")) {
 | 
				
			||||||
 | 
										startState = state;
 | 
				
			||||||
 | 
									} else if (modifier.trim().equals("*")) {
 | 
				
			||||||
 | 
										finalStates.add(state);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										throw new IllegalArgumentException("Invalid state modifier: " + modifier);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (parts.length == 1) continue;
 | 
				
			||||||
 | 
								String[] transitionDefinitions = parts[1].split("\\s*,\\s*");
 | 
				
			||||||
 | 
								for (String transitionDefinition : transitionDefinitions) {
 | 
				
			||||||
 | 
									String[] transitionParts = transitionDefinition.split("\\s*->\\s*");
 | 
				
			||||||
 | 
									if (transitionParts.length != 2) throw new IllegalArgumentException("Invalid transition format: " + transitionDefinition);
 | 
				
			||||||
 | 
									Pattern p = Pattern.compile("\"([^\"]*)\"");
 | 
				
			||||||
 | 
									Matcher m = p.matcher(transitionParts[0]);
 | 
				
			||||||
 | 
									if (!m.find()) throw new IllegalArgumentException("Invalid transition format: " + transitionDefinition);
 | 
				
			||||||
 | 
									Symbol acceptingSymbol = new Symbol(m.group(1));
 | 
				
			||||||
 | 
									State endState = new State(transitionParts[1].trim());
 | 
				
			||||||
 | 
									transitions.add(new Transition(state, acceptingSymbol, endState));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fromTransitions(startState, transitions, finalStates);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ package nl.andrewlalis.grammar_tool.machine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Objects;
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class State {
 | 
					public class State implements Comparable<State> {
 | 
				
			||||||
	private final String name;
 | 
						private final String name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public State(String name) {
 | 
						public State(String name) {
 | 
				
			||||||
| 
						 | 
					@ -26,4 +26,9 @@ public class State {
 | 
				
			||||||
	public String toString() {
 | 
						public String toString() {
 | 
				
			||||||
		return this.name;
 | 
							return this.name;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public int compareTo(State o) {
 | 
				
			||||||
 | 
							return this.name.compareTo(o.name);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import nl.andrewlalis.grammar_tool.grammar.Symbol;
 | 
				
			||||||
import java.util.Objects;
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Getter
 | 
					@Getter
 | 
				
			||||||
public class Transition {
 | 
					public class Transition implements Comparable<Transition> {
 | 
				
			||||||
	private final State startState;
 | 
						private final State startState;
 | 
				
			||||||
	private final Symbol acceptingSymbol;
 | 
						private final Symbol acceptingSymbol;
 | 
				
			||||||
	private final State endState;
 | 
						private final State endState;
 | 
				
			||||||
| 
						 | 
					@ -36,4 +36,17 @@ public class Transition {
 | 
				
			||||||
	public String toString() {
 | 
						public String toString() {
 | 
				
			||||||
		return String.format("%s (%s) -> %s", this.startState, this.acceptingSymbol, this.endState);
 | 
							return String.format("%s (%s) -> %s", this.startState, this.acceptingSymbol, this.endState);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public boolean isEpsilon() {
 | 
				
			||||||
 | 
							return this.acceptingSymbol.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public int compareTo(Transition o) {
 | 
				
			||||||
 | 
							int result = this.startState.compareTo(o.getStartState());
 | 
				
			||||||
 | 
							if (result != 0) return result;
 | 
				
			||||||
 | 
							int symbolResult = this.acceptingSymbol.compareTo(o.getAcceptingSymbol());
 | 
				
			||||||
 | 
							if (symbolResult != 0) return symbolResult;
 | 
				
			||||||
 | 
							return this.endState.compareTo(o.getEndState());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue