254 lines
5.9 KiB
C
254 lines
5.9 KiB
C
/*
|
|
* Copyright (c) Camden Dixie O'Brien
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
#include "convert.h"
|
|
#include "testing.h"
|
|
|
|
#define ACCEPTS(dfa, s) fsa_accepts(dfa, s, strlen(s))
|
|
|
|
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_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();
|
|
}
|