Added deserialization of FSMs
This commit is contained in:
		
							parent
							
								
									6d0ef8c6d2
								
							
						
					
					
						commit
						0741d9d62d
					
				| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
package nl.andrewlalis.grammar_tool;
 | 
			
		||||
 | 
			
		||||
import nl.andrewlalis.grammar_tool.grammar.ContextFreeGrammar;
 | 
			
		||||
import nl.andrewlalis.grammar_tool.machine.FiniteStateMachine;
 | 
			
		||||
 | 
			
		||||
public class GrammarTool {
 | 
			
		||||
	public static void main(String[] args) {
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +15,14 @@ public class GrammarTool {
 | 
			
		|||
				"C -> c,C | ε"
 | 
			
		||||
		);
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public boolean isEmpty() {
 | 
			
		||||
		return this.identifier.isEmpty();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static Symbol of(String identifier) {
 | 
			
		||||
		return new Symbol(identifier);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,8 +2,13 @@ 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.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
public class FiniteStateMachine {
 | 
			
		||||
	private final Set<Symbol> alphabet;
 | 
			
		||||
| 
						 | 
				
			
			@ -13,11 +18,11 @@ public class FiniteStateMachine {
 | 
			
		|||
	private final Set<Transition> transitions;
 | 
			
		||||
 | 
			
		||||
	public FiniteStateMachine(Set<Symbol> alphabet, Set<State> states, Set<State> finalStates, State startState, Set<Transition> transitions) {
 | 
			
		||||
		this.alphabet = alphabet;
 | 
			
		||||
		this.states = states;
 | 
			
		||||
		this.finalStates = finalStates;
 | 
			
		||||
		this.startState = startState;
 | 
			
		||||
		this.transitions = transitions;
 | 
			
		||||
		this.alphabet = Objects.requireNonNull(alphabet);
 | 
			
		||||
		this.states = Objects.requireNonNull(states);
 | 
			
		||||
		this.finalStates = Objects.requireNonNull(finalStates);
 | 
			
		||||
		this.startState = Objects.requireNonNull(startState);
 | 
			
		||||
		this.transitions = Objects.requireNonNull(transitions);
 | 
			
		||||
		this.ensureValidElements();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,4 +65,129 @@ public class FiniteStateMachine {
 | 
			
		|||
	public int hashCode() {
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
public class State {
 | 
			
		||||
public class State implements Comparable<State> {
 | 
			
		||||
	private final String name;
 | 
			
		||||
 | 
			
		||||
	public State(String name) {
 | 
			
		||||
| 
						 | 
				
			
			@ -26,4 +26,9 @@ public class State {
 | 
			
		|||
	public String toString() {
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
@Getter
 | 
			
		||||
public class Transition {
 | 
			
		||||
public class Transition implements Comparable<Transition> {
 | 
			
		||||
	private final State startState;
 | 
			
		||||
	private final Symbol acceptingSymbol;
 | 
			
		||||
	private final State endState;
 | 
			
		||||
| 
						 | 
				
			
			@ -36,4 +36,17 @@ public class Transition {
 | 
			
		|||
	public String toString() {
 | 
			
		||||
		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