/* * Copyright (c) Camden Dixie O'Brien * SPDX-License-Identifier: AGPL-3.0-only */ #include "convert.h" #include "testing.h" static bool is_deterministic(const fsa_t *fsa) { for (int i = 0; i < fsa->count; ++i) { bool seen[CHAR_COUNT] = { 0 }; fsa_state_t *state = &fsa->states[i]; for (int j = 0; j < state->count; ++j) { const int input = state->rules[j].input; if (EPSILON == input) return false; if (seen[input]) return false; seen[input] = true; } } return true; } static void test_trivial_case(void) { fsa_t nfa; fsa_init(&nfa); const int a = nfa.initial; const int b = fsa_add_state(&nfa); nfa.states[b].final = true; fsa_add_rule(&nfa, a, b, 'a'); fsa_t dfa; convert_to_dfa(&nfa, &dfa); ASSERT_TRUE(is_deterministic(&dfa)); ASSERT_ACCEPTS(&dfa, "a"); ASSERT_REJECTS(&dfa, "aa"); ASSERT_REJECTS(&dfa, "b"); fsa_free(&nfa); fsa_free(&dfa); } static void test_epsilon_move(void) { fsa_t nfa; fsa_init(&nfa); const int a = nfa.initial; const int b = fsa_add_state(&nfa); const int c = fsa_add_state(&nfa); nfa.states[c].final = true; fsa_add_rule(&nfa, a, b, EPSILON); fsa_add_rule(&nfa, a, c, 'a'); fsa_add_rule(&nfa, b, c, 'b'); fsa_t dfa; convert_to_dfa(&nfa, &dfa); ASSERT_TRUE(is_deterministic(&dfa)); ASSERT_ACCEPTS(&dfa, "a"); ASSERT_ACCEPTS(&dfa, "b"); ASSERT_REJECTS(&dfa, "aa"); ASSERT_REJECTS(&dfa, "bb"); ASSERT_REJECTS(&dfa, "ab"); ASSERT_REJECTS(&dfa, "ba"); ASSERT_REJECTS(&dfa, "c"); fsa_free(&nfa); fsa_free(&dfa); } static void test_branch(void) { fsa_t nfa; fsa_init(&nfa); const int a = nfa.initial; const int b = fsa_add_state(&nfa); const int c = fsa_add_state(&nfa); const int d = fsa_add_state(&nfa); nfa.states[d].final = true; fsa_add_rule(&nfa, a, b, 'a'); fsa_add_rule(&nfa, a, c, 'a'); fsa_add_rule(&nfa, b, d, 'b'); fsa_add_rule(&nfa, c, d, 'a'); fsa_t dfa; convert_to_dfa(&nfa, &dfa); ASSERT_TRUE(is_deterministic(&dfa)); ASSERT_ACCEPTS(&dfa, "aa"); ASSERT_ACCEPTS(&dfa, "ab"); ASSERT_REJECTS(&dfa, "a"); ASSERT_REJECTS(&dfa, "aaa"); ASSERT_REJECTS(&dfa, "abb"); ASSERT_REJECTS(&dfa, "c"); ASSERT_REJECTS(&dfa, "ac"); fsa_free(&nfa); fsa_free(&dfa); } static void test_nfa_a(void) { fsa_t nfa; fsa_init(&nfa); const int a = nfa.initial; const int b = fsa_add_state(&nfa); const int c = fsa_add_state(&nfa); const int d = fsa_add_state(&nfa); nfa.states[c].final = true; nfa.states[d].final = true; fsa_add_rule(&nfa, a, b, 'a'); fsa_add_rule(&nfa, a, c, EPSILON); fsa_add_rule(&nfa, b, b, 'b'); fsa_add_rule(&nfa, b, d, 'b'); fsa_add_rule(&nfa, c, b, EPSILON); fsa_add_rule(&nfa, c, d, 'a'); fsa_add_rule(&nfa, d, c, 'a'); fsa_t dfa; convert_to_dfa(&nfa, &dfa); ASSERT_TRUE(is_deterministic(&dfa)); ASSERT_ACCEPTS(&dfa, ""); ASSERT_ACCEPTS(&dfa, "a"); ASSERT_ACCEPTS(&dfa, "b"); ASSERT_ACCEPTS(&dfa, "ab"); ASSERT_ACCEPTS(&dfa, "ba"); ASSERT_ACCEPTS(&dfa, "aaaab"); ASSERT_REJECTS(&dfa, "aaab"); ASSERT_REJECTS(&dfa, "aaaba"); ASSERT_REJECTS(&dfa, "aaabb"); ASSERT_REJECTS(&dfa, "aaaaab"); ASSERT_REJECTS(&dfa, "aaaaaba"); ASSERT_REJECTS(&dfa, "aaaaabb"); fsa_free(&nfa); fsa_free(&dfa); } static void test_nfa_b(void) { fsa_t nfa; fsa_init(&nfa); const int a = nfa.initial; const int b = fsa_add_state(&nfa); const int c = fsa_add_state(&nfa); const int d = fsa_add_state(&nfa); nfa.states[c].final = true; fsa_add_rule(&nfa, a, b, 'a'); fsa_add_rule(&nfa, a, c, EPSILON); fsa_add_rule(&nfa, b, c, EPSILON); fsa_add_rule(&nfa, c, b, 'b'); fsa_add_rule(&nfa, c, d, 'a'); fsa_add_rule(&nfa, d, b, 'a'); fsa_t dfa; convert_to_dfa(&nfa, &dfa); ASSERT_TRUE(is_deterministic(&dfa)); ASSERT_ACCEPTS(&dfa, ""); ASSERT_ACCEPTS(&dfa, "a"); ASSERT_ACCEPTS(&dfa, "aaaaaa"); ASSERT_ACCEPTS(&dfa, "b"); ASSERT_ACCEPTS(&dfa, "bbbbb"); ASSERT_ACCEPTS(&dfa, "aaaaaa"); ASSERT_ACCEPTS(&dfa, "aaaaabaa"); ASSERT_ACCEPTS(&dfa, "aaaaabaab"); ASSERT_REJECTS(&dfa, "ba"); ASSERT_REJECTS(&dfa, "aba"); ASSERT_REJECTS(&dfa, "abab"); ASSERT_REJECTS(&dfa, "aaaaaba"); ASSERT_REJECTS(&dfa, "aaaaabaaa"); ASSERT_REJECTS(&dfa, "aaaaabbaabbaaa"); fsa_free(&nfa); fsa_free(&dfa); } static void test_nfa_c(void) { fsa_t nfa; fsa_init(&nfa); const int a = nfa.initial; const int b = fsa_add_state(&nfa); const int c = fsa_add_state(&nfa); const int d = fsa_add_state(&nfa); const int e = fsa_add_state(&nfa); nfa.states[e].final = true; fsa_add_rule(&nfa, a, b, 'a'); fsa_add_rule(&nfa, a, c, 'a'); fsa_add_rule(&nfa, a, d, 'b'); fsa_add_rule(&nfa, b, b, 'a'); fsa_add_rule(&nfa, b, d, 'b'); fsa_add_rule(&nfa, b, e, EPSILON); fsa_add_rule(&nfa, d, b, 'a'); fsa_add_rule(&nfa, d, c, 'b'); fsa_add_rule(&nfa, d, d, 'a'); fsa_add_rule(&nfa, e, a, 'b'); fsa_t dfa; convert_to_dfa(&nfa, &dfa); ASSERT_TRUE(is_deterministic(&dfa)); ASSERT_ACCEPTS(&dfa, "a"); ASSERT_ACCEPTS(&dfa, "aba"); ASSERT_ACCEPTS(&dfa, "aaba"); ASSERT_ACCEPTS(&dfa, "abaaba"); ASSERT_ACCEPTS(&dfa, "ba"); ASSERT_ACCEPTS(&dfa, "babba"); ASSERT_ACCEPTS(&dfa, "baaa"); ASSERT_ACCEPTS(&dfa, "baba"); ASSERT_ACCEPTS(&dfa, "babaa"); ASSERT_REJECTS(&dfa, ""); ASSERT_REJECTS(&dfa, "ab"); ASSERT_REJECTS(&dfa, "aab"); ASSERT_REJECTS(&dfa, "abbab"); ASSERT_REJECTS(&dfa, "b"); ASSERT_REJECTS(&dfa, "bb"); ASSERT_REJECTS(&dfa, "baaabab"); ASSERT_REJECTS(&dfa, "aabababab"); fsa_free(&nfa); fsa_free(&dfa); } int main(void) { TESTING_BEGIN(); // Base cases test_trivial_case(); test_epsilon_move(); test_branch(); // Compound cases test_nfa_a(); test_nfa_b(); test_nfa_c(); return TESTING_END(); }