regex-engine/tests/convert_tests.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();
}