diff --git a/.idea/misc.xml b/.idea/misc.xml index 12f22af..86c6651 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/src/ChomskyChecker.java b/src/ChomskyChecker.java new file mode 100644 index 0000000..ddbd855 --- /dev/null +++ b/src/ChomskyChecker.java @@ -0,0 +1,40 @@ +import java.util.List; +import java.util.Map; + +public class ChomskyChecker { + public static String classifyGrammar(Map> grammar) { + for (Map.Entry> entry : grammar.entrySet()) { + for (String listEntry : entry.getValue()) { + String prod = entry.getKey() + " -> " + listEntry; + if (isType3(prod)) { + return "Regular"; + } else if (isType2(prod)) { + return "Context-free"; + } else if (isType1(prod)) { + return "Context-sensitive"; + } else if (isType0(prod)) { + return "Unrestricted"; + } + } + + } + return "Unknown"; + } + + private static boolean isType0(String production) { + return production.matches("^[^ ]+ -> .+$"); + } + + private static boolean isType1(String production) { + return production.matches("^.+ .+ .+$"); + } + + private static boolean isType2(String production) { + return production.matches("^[A-Z]+ -> [^A-Z]+$"); + } + + private static boolean isType3(String production) { + return production.matches("^[A-Z]+ -> [a-zA-Z]$") || production.matches("^[A-Z]+ -> [a-zA-Z][A-Z]$"); + } + +} diff --git a/src/FiniteAutomaton.java b/src/FiniteAutomaton.java index 73210af..cb56dda 100644 --- a/src/FiniteAutomaton.java +++ b/src/FiniteAutomaton.java @@ -1,36 +1,35 @@ +import java.util.HashMap; import java.util.Map; import java.util.Set; public class FiniteAutomaton { - private final Set ALPHABET; // Set of symbols in the alphabet - private final Map> P; // Transition function represented as a map of maps - private final String SS; // Starting state - - // Constructor to initialize the FiniteAutomaton with the alphabet, transition function, and starting state - public FiniteAutomaton(Set ALPHABET, Map> P, String SS) { - this.ALPHABET = ALPHABET; // Initialize the alphabet - this.P = P; // Initialize the transition function - this.SS = SS; // Initialize the starting state + private final String SS; + private final Set alphabet; + private final Map> T; + public FiniteAutomaton(String SS, Set VN, Set VT, Map> T) { + this.SS = SS; + this.alphabet = VT; + this.T = T; } - // Method to validate an input string against the finite automaton - public boolean isValid(String input) { - String currentState = SS; // Start from the initial state + public boolean isValid(String word) { + String currentState = SS; + for (char symbol : word.toCharArray()) { + String symbolStr = String.valueOf(symbol); - // Iterate over each symbol in the input string - for (char symbol : input.toCharArray()) { - String symbolStr = String.valueOf(symbol); // Convert the symbol to a string + if (!alphabet.contains(symbolStr)) return false; - // Check if the symbol is in the alphabet - if (!ALPHABET.contains(symbolStr)) return false; // If not, the input is invalid - - // Check if there is a transition defined for the current state and symbol - if (P.containsKey(currentState) && P.get(currentState).containsKey(symbolStr)) - currentState = P.get(currentState).get(symbolStr); // Move to the next state + if (T.containsKey(currentState) && T.get(currentState).containsKey(symbolStr)) + currentState = T.get(currentState).get(symbolStr); else - return false; // If no transition is defined, the input is invalid + return false; } - // Check if the final state is the "OK" state, indicating the input is valid return currentState.equals("OK"); } + + + public Map> getTransitions() { + return T; + } + } diff --git a/src/Grammar.java b/src/Grammar.java index 9ab205b..ef34d1f 100644 --- a/src/Grammar.java +++ b/src/Grammar.java @@ -1,91 +1,66 @@ -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; +import java.util.*; public class Grammar { - // Instance variables to hold the grammar components - private final String SS; // Starting symbol - private final Set VN; // Set of non-terminal symbols - private final Map> P; // Production rules - private final FiniteAutomaton FA; // Finite Automaton representation of the grammar + private final String SS; + private final Set VN; + private final Set VT; + private final Map> P; + private final FiniteAutomaton FA; - // Constructor to initialize the Grammar object with the provided grammar components. public Grammar(String SS, Set VN, Set VT, Map> P) { - this.SS = SS; // Initialize the starting symbol - this.VN = VN; // Initialize the set of non-terminal symbols - this.P = P; // Initialize the production rules - // Convert the grammar to a Finite Automaton for validation. - // The Finite Automaton will use the terminal symbols and transitions built from the production rules. - this.FA = new FiniteAutomaton(VT, buildTransitions(P), SS); + this.SS = SS; + this.VN = VN; + this.VT = VT; + this.P = P; + this.FA = new FiniteAutomaton(SS, VN, VT, generateTransitions(P)); } - // Method to generate and print a specified number of strings and their validity. - public void generateNeededStrings(int num) { - System.out.println("Strings + Validity:"); - // Loop to generate and check validity for each string - for (int i = 0; i < num; i++) { - // Generate a random string according to the grammar. - String generatedString = generateString(); - // Check if the generated string is valid and print its validity. - System.out.println((i + 1) + ": " + generatedString + " " + (isValid(generatedString) ? "is valid" : "is not valid")); - } - } - - // Method to check if a given word is valid according to the grammar. - public boolean isValid(String word) { - // Delegate the validation to the Finite Automaton representation of the grammar. - return FA.isValid(word); - } - - // Method to generate a random string according to the grammar. - public String generateString() { - // Start with the starting symbol. - String result = getRandomProduction(SS); - boolean containsNonTerminal = true; - - // Continue replacing non-terminals with their productions until no more non-terminals remain. - while (containsNonTerminal) { - containsNonTerminal = false; - // Iterate over each character in the current string. - for (char entry : result.toCharArray()) { - String entryString = String.valueOf(entry); - // If the character is a non-terminal, replace it with a random production. - if (VN.contains(entryString)) { - result = result.replaceFirst(entryString, getRandomProduction(entryString)); - containsNonTerminal = true; - } - } - } - return result; - } - - // Method to get a random production for a given non-terminal symbol. - private String getRandomProduction(String nonTerminal) { - // Get the list of productions for the given non-terminal and select one randomly. - return P.get(nonTerminal).get(ThreadLocalRandom.current().nextInt(P.get(nonTerminal).size())); - } - - // Method to build transitions for the Finite Automaton representation of the grammar. - private Map> buildTransitions(Map> productions) { - // Initialize a map to hold the transitions for each state (non-terminal symbol). + public Map> generateTransitions(Map> P) { Map> transitions = new HashMap<>(); - // Iterate over each production rule. - for (Map.Entry> entry : productions.entrySet()) { - String state = entry.getKey(); // Get the non-terminal symbol as the state. - List productionList = entry.getValue(); // Get the list of productions for the state. + for (Map.Entry> entryP : P.entrySet()) { + String state = entryP.getKey(); + List productionList = entryP.getValue(); Map stateTransitions = new HashMap<>(); - // Iterate over each production. for (String production : productionList) { - String symbol = production.substring(0, 1); // Get the first character as the symbol. - // Get the next state as the rest of the production, or "OK" if it's empty. + String symbol = production.substring(0, 1); String nextState = production.length() > 1 ? production.substring(1) : "OK"; - stateTransitions.put(symbol, nextState); // Add the transition to the state's transitions. + stateTransitions.put(symbol, nextState); } - transitions.put(state, stateTransitions); // Add the state and its transitions to the overall transitions map. + transitions.put(state, stateTransitions); } return transitions; } + public FiniteAutomaton getFA() { + return this.FA; + } + + public boolean isValid(String word) { + return this.FA.isValid(word); + } + + public void generateWords(int numWords) { + List generatedWords = new ArrayList<>(); + Random random = new Random(); + + for (int i = 0; i < numWords; i++) { + StringBuilder wordBuilder = new StringBuilder(); + String currentState = SS; + + while (!currentState.equals("OK")) { + Map transitions = FA.getTransitions().get(currentState); + List symbols = new ArrayList<>(transitions.keySet()); + String symbol = symbols.get(random.nextInt(symbols.size())); + wordBuilder.append(symbol); + currentState = transitions.get(symbol); + } + + generatedWords.add(wordBuilder.toString()); + } + + System.out.println("==================================="); + for (String word: generatedWords) { + System.out.println(word + " " + (isValid(word) ? "- Valid" : "- Not valid")); + } + } } diff --git a/src/Main.java b/src/Main.java index 137b42c..7204ef7 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,25 +1,48 @@ -import java.util.*; +import util.Pair; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; public class Main { - // Define the starting symbol, non-terminal symbols, terminal symbols, and production rules - public static final String SS = "S"; // Starting symbol - public static final Set VN = Set.of("S", "B", "C"); // Non-terminal symbols - public static final Set VT = Set.of("a", "b", "c"); // Terminal symbols - public static final Map> P = Map.of( // Production rules - "S", List.of("aB"), // Production rule for S - "B", List.of("aC", "bB"), // Production rule for B - "C", List.of("bB", "c", "aS")); // Production rule for C + + // LAB 1 + public static final String SS = "S"; + public static final Set VN = Set.of("S", "B", "C"); + public static final Set VT = Set.of("a", "b", "c"); + public static final Map> P = Map.of( + "S", List.of("aB"), + "B", List.of("aC", "bB"), + "C", List.of("aS", "c")); + + // LAB 2 + public static final List stateList = Arrays.asList("q0", "q1", "q2"); + public static final Set alphabet = Set.of("a", "b"); + public static final Set acceptingStates = Set.of("q2"); + public static final Map> transitions= Map.of( + "q0", List.of(new Pair("a", "q0"), new Pair("a", "q1")), + "q1", List.of(new Pair("b", "q2"), new Pair("a", "q0")), + "q2", List.of(new Pair("b", "q2")) + ); + public static void main(String[] args) { - String toCheck = "aac"; // Word to check - - // Create a Grammar object with the defined grammar + String toCheck = "aac"; Grammar grammar = new Grammar(SS, VN, VT, P); - - // Generate strings needed for the validation process - grammar.generateNeededStrings(5); - - // Check if the given word is valid according to the grammar + grammar.generateWords(5); + System.out.println("==================================="); System.out.println("Is " + toCheck + " valid? " + grammar.isValid(toCheck)); + System.out.println("==================================="); + System.out.println(ChomskyChecker.classifyGrammar(P)); + + ManualFiniteAutomaton MFA = new ManualFiniteAutomaton(stateList, alphabet, acceptingStates, transitions); + System.out.println("==================================="); + MFA.toGrammar(); + System.out.println("==================================="); + MFA.isDeterministic(); + System.out.println("==================================="); + MFA.toDFA(); + System.out.println("==================================="); } -} +} \ No newline at end of file diff --git a/src/ManualFiniteAutomaton.java b/src/ManualFiniteAutomaton.java new file mode 100644 index 0000000..8970c94 --- /dev/null +++ b/src/ManualFiniteAutomaton.java @@ -0,0 +1,116 @@ +import util.Pair; +import java.util.*; + +public class ManualFiniteAutomaton { + + private final List stateList; + private final Set alphabet; + private final Set acceptingStates; + private final Map> T; + + public ManualFiniteAutomaton(List stateList, Set alphabet, Set acceptingStates, Map> T) { + this.stateList = stateList; + this.alphabet = alphabet; + this.acceptingStates = acceptingStates; + this.T = T; + } + + // Method to map original states to equivalent single-character states + public Map mapStates() { + Map mappedStates = new HashMap<>(); // Creating a HashMap to store mappings + char mappedState = 'A'; // Starting mapping with character 'A' + for (String state : stateList) { // Iterating through each state in stateList + mappedStates.put(state, String.valueOf(mappedState)); // Mapping original state to a single character + mappedState++; // Moving to the next character for mapping + } + return mappedStates; // Returning the mapped states + } + + // Method to check if the automaton is deterministic + public void isDeterministic() { + for (Map.Entry> entry : T.entrySet()) { // Iterating through each transition in T + if (entry.getValue().size() > 1) { // If there are multiple transitions for a state and symbol + System.out.println("It is not deterministic!"); // Print that the automaton is not deterministic + return; // Exit the method + } + } + System.out.println("It is deterministic!"); // Print that the automaton is deterministic + } + + // Method to convert the NFA to DFA + public void toDFA() { + Map, Map>> dfaTransitions = new HashMap<>(); // Creating a map for DFA transitions + Queue> queue = new LinkedList<>(); // Queue to store sets of states + + Set initialState = new HashSet<>(stateList); // Creating initial set of states containing all states + queue.add(initialState); // Adding the initial state to the queue + + while (!queue.isEmpty()) { // While there are unprocessed states in the queue + Set currentState = queue.poll(); // Dequeue a state from the queue + + Map> transitions = new HashMap<>(); // Map to store transitions from current state + for (String symbol : alphabet) { // For each symbol in the alphabet + Set nextStates = new HashSet<>(); // Set to store next states for the symbol + for (String state : currentState) { // For each state in the current set of states + List transitionsForState = T.get(state); // Get transitions for the current state + if (transitionsForState != null) { // If there are transitions for the state + for (Pair p : transitionsForState) { // For each transition from the state + if (p.first().equals(symbol)) { // If the transition symbol matches the current symbol + nextStates.add(p.second()); // Add the destination state to nextStates + } + } + } + } + transitions.put(symbol, nextStates); // Add transitions for the current symbol + if (!nextStates.isEmpty() && !dfaTransitions.containsKey(nextStates)) { // If nextStates is not empty and not already processed + queue.add(nextStates); // Enqueue nextStates for further processing + } + } + + dfaTransitions.put(currentState, transitions); // Add transitions from current state to DFA transitions + } + + // Printing DFA transitions + System.out.println("DFA Transitions:"); + for (Map.Entry, Map>> entry : dfaTransitions.entrySet()) { // For each DFA state and its transitions + Set currentState = entry.getKey(); // Get the current DFA state + Map> transitions = entry.getValue(); // Get its transitions + System.out.println("State: " + currentState); // Print the current DFA state + for (Map.Entry> transition : transitions.entrySet()) { // For each transition from the state + String inputSymbol = transition.getKey(); // Get the input symbol + Set nextState = transition.getValue(); // Get the next state(s) + if (!nextState.isEmpty()) { // If there is a transition + System.out.println(" Input: " + inputSymbol + " --> " + nextState); // Print the transition + } + } + } + } + + // Method to convert the NFA to Grammar + public void toGrammar() { + Map mappedStates = mapStates(); // Map original states to single-character states + Map> grammar = new HashMap<>(); // Create a map to store the grammar rules + + for(Map.Entry> te : T.entrySet()) { // For each transition in the transition function + List maps = new ArrayList<>(); // Create a list to store grammar rules for the current state + for (Pair p : te.getValue()) { // For each transition from the current state + maps.add(p.first() + mappedStates.get(p.second())); // Add a grammar rule to the list + } + grammar.put(mappedStates.get(te.getKey()), maps); // Add the grammar rules to the grammar map + } + + // Printing the grammar rules + for (Map.Entry> grammarEntry : grammar.entrySet()) { // For each state and its grammar rules + System.out.print(grammarEntry.getKey() + " -> "); // Print the state + if (grammarEntry.getValue().size() > 1) { // If there are multiple rules + System.out.print(grammarEntry.getValue().get(0)); // Print the first rule + for (int i = 1; i < grammarEntry.getValue().size(); i++) { // For the remaining rules + System.out.print(" | " + grammarEntry.getValue().get(i)); // Print each rule + } + } else { // If there is only one rule + System.out.print(grammarEntry.getValue().get(0)); // Print the rule + } + System.out.println(); // Move to the next line + } + } +} diff --git a/src/reports/lab_two.pdf b/src/reports/lab_two.pdf new file mode 100644 index 0000000..fd275d6 Binary files /dev/null and b/src/reports/lab_two.pdf differ diff --git a/src/util/Pair.java b/src/util/Pair.java new file mode 100644 index 0000000..355e377 --- /dev/null +++ b/src/util/Pair.java @@ -0,0 +1,9 @@ +package util; + +public record Pair(String first, String second) { + + @Override + public String toString() { + return "(" + first + ", " + second + ")"; + } +}