Implement star construction

This commit is contained in:
Camden Dixie O'Brien 2024-10-27 13:24:29 +00:00
parent d54080032c
commit bbecd12c91
2 changed files with 85 additions and 1 deletions

View File

@ -17,6 +17,53 @@ static void construct_literal(char literal, fsa_t *out)
out->initial = id; out->initial = id;
} }
static void star_fsa(fsa_t *fsa)
{
// If the initial state is already the final state then nothing
// needs to be done.
if (0 == fsa->initial)
return;
// Copy inital state's rules to final state.
fsa_state_t *final = &fsa->states[0];
const fsa_state_t *initial = &fsa->states[fsa->initial];
if (final->capacity < final->count + initial->count) {
do
final->capacity *= 2;
while (final->capacity < final->count + initial->count);
final->rules
= realloc(final->rules, final->capacity * sizeof(fsa_rule_t));
assert(final->rules);
}
const int copy_size = initial->count * sizeof(fsa_rule_t);
memcpy(&final->rules[final->count], initial->rules, copy_size);
final->count += initial->count;
// Move states that come after initial state if there are any.
if (fsa->count - 1 > fsa->initial) {
const int count = fsa->count - fsa->initial - 1;
fsa_state_t *start = &fsa->states[fsa->initial];
memmove(start, start + 1, count * sizeof(fsa_state_t));
}
// Retarget all states' rules.
for (int i = 0; i < fsa->count - 1; ++i) {
for (int j = 0; j < fsa->states[i].count; ++j) {
if (fsa->states[i].rules[j].next == fsa->initial)
fsa->states[i].rules[j].next = 0;
else if (fsa->states[i].rules[j].next > fsa->initial)
// All states after the initial state have been moved
// down by one position.
--fsa->states[i].rules[j].next;
}
}
--fsa->count;
fsa->initial = 0;
free(initial->rules);
}
static void construct_term(const regex_term_t *term, fsa_t *out) static void construct_term(const regex_term_t *term, fsa_t *out)
{ {
switch (term->type) { switch (term->type) {
@ -28,10 +75,22 @@ static void construct_term(const regex_term_t *term, fsa_t *out)
break; break;
case REGEX_TERM_SUBEXPR: case REGEX_TERM_SUBEXPR:
return; return;
case REGEX_TERM_WILDCARD: case REGEX_TERM_WILDCARD:
case REGEX_TERM_CLASS: case REGEX_TERM_CLASS:
assert(false); assert(false);
break;
}
switch (term->quantifier) {
case REGEX_QUANTIFIER_NONE:
break;
case REGEX_QUANTIFIER_STAR:
star_fsa(out);
break;
case REGEX_QUANTIFIER_PLUS:
case REGEX_QUANTIFIER_QMARK:
assert(false);
break;
} }
assert(out->states[0].final); assert(out->states[0].final);

View File

@ -132,6 +132,30 @@ static void test_union(void)
fsa_free(&fsa); fsa_free(&fsa);
} }
static void test_star(void)
{
regex_term_t *terms = malloc(1 * sizeof(regex_term_t));
terms[0].quantifier = REGEX_QUANTIFIER_STAR;
terms[0].type = REGEX_TERM_LITERAL;
terms[0].literal = 'a';
regex_sequence_t *alternatives = malloc(1 * sizeof(regex_sequence_t));
alternatives[0].count = alternatives[0].capacity = 1;
alternatives[0].contents = terms;
regex_t regex = { .count = 1, .capacity = 1, .contents = alternatives };
fsa_t fsa;
construct(&regex, &fsa);
const fsa_state_t *initial = &fsa.states[fsa.initial];
ASSERT_TRUE(initial->final);
ASSERT_EQ(1, initial->count);
ASSERT_EQ('a', initial->rules[0].input);
ASSERT_EQ(fsa.initial, initial->rules[0].next);
regex_free(&regex);
fsa_free(&fsa);
}
int main(void) int main(void)
{ {
TESTING_BEGIN(); TESTING_BEGIN();
@ -139,5 +163,6 @@ int main(void)
test_literal_expression(); test_literal_expression();
test_sequence(); test_sequence();
test_union(); test_union();
test_star();
return TESTING_END(); return TESTING_END();
} }