272 lines
6.3 KiB
C
272 lines
6.3 KiB
C
/*
|
|
* 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();
|
|
}
|