diff --git a/lib/construct.c b/lib/construct.c new file mode 100644 index 0000000..e40956a --- /dev/null +++ b/lib/construct.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) Camden Dixie O'Brien + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "construct.h" + +#include +#include + +static void construct_literal(char literal, fsa_t *out) +{ + fsa_init(out); + const int id = fsa_add_state(out); + fsa_add_rule(out, id, out->initial, literal); + out->initial = id; +} + +static void construct_term(const regex_term_t *term, fsa_t *out) +{ + switch (term->type) { + case REGEX_TERM_EMPTY: + fsa_init(out); + break; + case REGEX_TERM_LITERAL: + construct_literal(term->literal, out); + break; + case REGEX_TERM_SUBEXPR: + return; + + case REGEX_TERM_WILDCARD: + case REGEX_TERM_CLASS: + assert(false); + } +} + +static void construct_sequence(const regex_sequence_t *seq, fsa_t *out) +{ + construct_term(&seq->contents[0], out); +} + +void construct(const regex_t *regex, fsa_t *out) +{ + construct_sequence(®ex->contents[0], out); +} diff --git a/lib/construct.h b/lib/construct.h new file mode 100644 index 0000000..0154c45 --- /dev/null +++ b/lib/construct.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Camden Dixie O'Brien + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#ifndef CONSTRUCT_H +#define CONSTRUCT_H + +#include "fsa.h" +#include "regex.h" + +void construct(const regex_t *regex, fsa_t *out); + +#endif diff --git a/scripts/build.sh b/scripts/build.sh index 773f7ab..254102f 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -13,7 +13,9 @@ clang $CFLAGS -Ilib -c lib/parse.c -o build/parse.o clang $CFLAGS -Ilib -c lib/desugar.c -o build/desugar.o clang $CFLAGS -Ilib -c lib/regex.c -o build/regex.o clang $CFLAGS -Ilib -c lib/fsa.c -o build/fsa.o -ar -crs build/lib.a build/parse.o build/desugar.o build/regex.o build/fsa.o +clang $CFLAGS -Ilib -c lib/construct.c -o build/construct.o +ar -crs build/lib.a build/parse.o build/desugar.o build/regex.o \ + build/fsa.o build/construct.o # Build tests clang $CFLAGS -Itests -c tests/testing.c -o build/testing.o @@ -23,3 +25,5 @@ clang $CFLAGS -Ilib -Itests -o build/desugar_tests \ tests/desugar_tests.c build/testing.o build/lib.a clang $CFLAGS -Ilib -Itests -o build/fsa_tests \ tests/fsa_tests.c build/testing.o build/lib.a +clang $CFLAGS -Ilib -Itests -o build/construct_tests \ + tests/construct_tests.c build/testing.o build/lib.a diff --git a/scripts/test.sh b/scripts/test.sh index 4a1ac84..4920bd9 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -7,5 +7,6 @@ fails=0 build/parse_tests || fails=`expr $fails + 1` build/desugar_tests || fails=`expr $fails + 1` build/fsa_tests || fails=`expr $fails + 1` +build/construct_tests || fails=`expr $fails + 1` if [ $fails -eq 0 ]; then echo Tests OK; fi diff --git a/tests/construct_tests.c b/tests/construct_tests.c new file mode 100644 index 0000000..461ce76 --- /dev/null +++ b/tests/construct_tests.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) Camden Dixie O'Brien + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "construct.h" +#include "testing.h" + +static void test_empty_expression(void) +{ + regex_term_t *terms = malloc(1 * sizeof(regex_term_t)); + terms[0].quantifier = REGEX_QUANTIFIER_NONE; + terms[0].type = REGEX_TERM_EMPTY; + regex_sequence_t *alternatives = malloc(1 * sizeof(regex_sequence_t)); + alternatives[0].count = alternatives[0].capacity = 1; + alternatives[0].contents = terms; + const regex_t regex + = { .count = 1, .capacity = 1, .contents = alternatives }; + + fsa_t fsa; + construct(®ex, &fsa); + + ASSERT_EQ(1, fsa.count); + ASSERT_TRUE(fsa.states[fsa.initial].final); + ASSERT_EQ(0, fsa.states[fsa.initial].count); + + regex_free(®ex); + fsa_free(&fsa); +} + +static void test_literal_expression(void) +{ + regex_term_t *terms = malloc(1 * sizeof(regex_term_t)); + terms[0].quantifier = REGEX_QUANTIFIER_NONE; + 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; + const regex_t regex + = { .count = 1, .capacity = 1, .contents = alternatives }; + + fsa_t fsa; + construct(®ex, &fsa); + + const fsa_state_t *initial = &fsa.states[fsa.initial]; + ASSERT_EQ(2, fsa.count); + ASSERT_EQ(1, initial->count); + ASSERT_EQ('a', initial->rules[0].input); + ASSERT_TRUE(fsa.states[initial->rules[0].next].final); + ASSERT_EQ(0, fsa.states[initial->rules[0].next].count); + + regex_free(®ex); + fsa_free(&fsa); +} + +int main(void) +{ + TESTING_BEGIN(); + test_empty_expression(); + test_literal_expression(); + return TESTING_END(); +}