/* * 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 bool accepts(const fsa_t *dfa, const char *input) { int current = dfa->initial; while ('\0' != *input) { bool found = false; const fsa_rule_t *rules = dfa->states[current].rules; for (int i = 0; i < dfa->states[current].count; ++i) { if (rules[i].input == *input) { current = rules[i].next; found = true; break; } } if (!found) return false; ++input; } return dfa->states[current].final; } 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_TRUE(accepts(&dfa, "a")); ASSERT_FALSE(accepts(&dfa, "aa")); ASSERT_FALSE(accepts(&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_TRUE(accepts(&dfa, "a")); ASSERT_TRUE(accepts(&dfa, "b")); ASSERT_FALSE(accepts(&dfa, "aa")); ASSERT_FALSE(accepts(&dfa, "bb")); ASSERT_FALSE(accepts(&dfa, "ab")); ASSERT_FALSE(accepts(&dfa, "ba")); ASSERT_FALSE(accepts(&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_TRUE(accepts(&dfa, "aa")); ASSERT_TRUE(accepts(&dfa, "ab")); ASSERT_FALSE(accepts(&dfa, "a")); ASSERT_FALSE(accepts(&dfa, "aaa")); ASSERT_FALSE(accepts(&dfa, "abb")); ASSERT_FALSE(accepts(&dfa, "c")); ASSERT_FALSE(accepts(&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_TRUE(accepts(&dfa, "")); ASSERT_TRUE(accepts(&dfa, "a")); ASSERT_TRUE(accepts(&dfa, "b")); ASSERT_TRUE(accepts(&dfa, "ab")); ASSERT_TRUE(accepts(&dfa, "ba")); ASSERT_TRUE(accepts(&dfa, "aaaab")); ASSERT_FALSE(accepts(&dfa, "aaab")); ASSERT_FALSE(accepts(&dfa, "aaaba")); ASSERT_FALSE(accepts(&dfa, "aaabb")); ASSERT_FALSE(accepts(&dfa, "aaaaab")); ASSERT_FALSE(accepts(&dfa, "aaaaaba")); ASSERT_FALSE(accepts(&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_TRUE(accepts(&dfa, "")); ASSERT_TRUE(accepts(&dfa, "a")); ASSERT_TRUE(accepts(&dfa, "aaaaaa")); ASSERT_TRUE(accepts(&dfa, "b")); ASSERT_TRUE(accepts(&dfa, "bbbbb")); ASSERT_TRUE(accepts(&dfa, "aaaaaa")); ASSERT_TRUE(accepts(&dfa, "aaaaabaa")); ASSERT_TRUE(accepts(&dfa, "aaaaabaab")); ASSERT_FALSE(accepts(&dfa, "ba")); ASSERT_FALSE(accepts(&dfa, "aba")); ASSERT_FALSE(accepts(&dfa, "abab")); ASSERT_FALSE(accepts(&dfa, "aaaaaba")); ASSERT_FALSE(accepts(&dfa, "aaaaabaaa")); ASSERT_FALSE(accepts(&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_TRUE(accepts(&dfa, "a")); ASSERT_TRUE(accepts(&dfa, "aba")); ASSERT_TRUE(accepts(&dfa, "aaba")); ASSERT_TRUE(accepts(&dfa, "abaaba")); ASSERT_TRUE(accepts(&dfa, "ba")); ASSERT_TRUE(accepts(&dfa, "babba")); ASSERT_TRUE(accepts(&dfa, "baaa")); ASSERT_TRUE(accepts(&dfa, "baba")); ASSERT_TRUE(accepts(&dfa, "babaa")); ASSERT_FALSE(accepts(&dfa, "")); ASSERT_FALSE(accepts(&dfa, "ab")); ASSERT_FALSE(accepts(&dfa, "aab")); ASSERT_FALSE(accepts(&dfa, "abbab")); ASSERT_FALSE(accepts(&dfa, "b")); ASSERT_FALSE(accepts(&dfa, "bb")); ASSERT_FALSE(accepts(&dfa, "baaabab")); ASSERT_FALSE(accepts(&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(); }