From e39267c83a8af189a624891813ebd6014543b01a Mon Sep 17 00:00:00 2001 From: c Date: Sun, 24 Dec 2023 10:28:00 -0500 Subject: Proper tests. --- .gitignore | 2 +- Makefile | 16 +++++++++--- src/include/main.h | 14 +++++++++++ src/include/stack.h | 23 +++++++++++++++++ src/include/test.h | 27 -------------------- src/include/tree.h | 24 +++++++++++------- src/lexer.c | 4 --- src/main.c | 24 +++++++----------- src/parser.c | 2 -- src/stack.c | 38 ++++++++++++++++++++++++++++ src/tree.c | 43 ++++++++++++++++++++++++++++++++ test/include/test.h | 22 ++++++++++++++++ test/parser.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ test/tree.c | 54 ++++++++++++++++++++++++++++++++++++++++ 14 files changed, 303 insertions(+), 62 deletions(-) create mode 100644 src/include/main.h create mode 100644 src/include/stack.h delete mode 100644 src/include/test.h create mode 100644 src/stack.c create mode 100644 test/include/test.h create mode 100644 test/parser.c create mode 100644 test/tree.c diff --git a/.gitignore b/.gitignore index 248b074..fb8b336 100755 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ tags *.out *.tmp.* -*_notes.md +*.notes.md [._]sw[a-p] [._]*.sw[a-p] [._]ss[a-gi-z] diff --git a/Makefile b/Makefile index a50dbbf..e4a0a22 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,10 @@ CFLAGS := $(REG_CFLAGS) SRCS := $(wildcard src/*.c) SRCS := $(filter-out %doer.c,$(SRCS)) # Filter out incomplete doer for now. OBJS := $(SRCS:.c=.o) +TEST_SRCS := $(wildcard test/*.c) +TEST_OUTS := $(TEST_SRCS:.c=.out) -.PHONY: all reg_options dbg_options halk dbg install uninstall clean me a sandwich +.PHONY: all reg_options dbg_options halk dbg install uninstall clean test me a sandwich all: halk @@ -30,12 +32,18 @@ halk: reg_options $(OBJS) $(CC) $(OBJS) $(REG_CFLAGS) -o $(BIN).out dbg: CFLAGS := $(DBG_CFLAGS) -dbg: TEST := -D TEST dbg: dbg_options $(OBJS) $(CC) $(OBJS) $(DBG_CFLAGS) -o $(BIN).out +test: dbg $(TEST_OUTS) + set -e + for f in $(TEST_OUTS); do ./$$f; done + %.o: %.c - $(CC) $(TEST) -c $< -o $@ + $(CC) -c $< -o $@ + +%.out: %.c + $(CC) $< $(filter-out %main.o,$(OBJS)) -o $@ install: all mkdir -p $(PREFIX) @@ -46,7 +54,7 @@ uninstall: rm -f $(PREFIX)/$(BIN) clean: - rm -f $(BIN).out src/*.o + rm -f $(BIN).out src/*.o test/*.out me a: @exit diff --git a/src/include/main.h b/src/include/main.h new file mode 100644 index 0000000..63dd15c --- /dev/null +++ b/src/include/main.h @@ -0,0 +1,14 @@ +#ifndef MAIN_H +#define MAIN_H + +#include +#include + +#include "util.h" +#include "source.h" +#include "token.h" +#include "pp.h" +#include "lexer.h" +#include "parser.h" + +#endif diff --git a/src/include/stack.h b/src/include/stack.h new file mode 100644 index 0000000..7a16366 --- /dev/null +++ b/src/include/stack.h @@ -0,0 +1,23 @@ +#ifndef STACK_H +#define STACK_H + +#include +#include "util.h" + +#define STACK_MAXLEN 256 + +typedef struct STACK { + int sp; /* Index of first unused element of val. */ + void* val[STACK_MAXLEN]; +} stack_t; + +stack_t* stack_init(); +void stack_destroy(stack_t*); + +void stack_push(stack_t* stack, void* val); +void* stack_pop(stack_t* stack); +size_t stack_len(stack_t* stack); + +void stack_print(stack_t* stack); + +#endif diff --git a/src/include/test.h b/src/include/test.h deleted file mode 100644 index e862467..0000000 --- a/src/include/test.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEST_H -#define TEST_H - -#include "util.h" - -#ifdef TEST - -unsigned int TESTS_RUN = 0; -unsigned int TESTS_PASSED = 0; - -#define ASSERT(EXPR) \ - TESTS_RUN++; \ - (EXPR && ++TESTS_PASSED) ? \ - log_yay("Assertion passed!") : \ - log_err("%s:%d: Assertion failed:\n\t%s", __FILE__, __LINE__, #EXPR); - -#define TEST_REPORT \ - (TESTS_RUN == TESTS_PASSED) ? \ - log_yay("All %d tests passed!", TESTS_RUN) : \ - log_err("%d/%d tests failed.", TESTS_RUN - TESTS_PASSED, TESTS_RUN); - -#else -#define ASSERT(EXPR) NULL; -#define TEST_REPORT NULL; -#endif - -#endif diff --git a/src/include/tree.h b/src/include/tree.h index 20d97be..8b8e14a 100644 --- a/src/include/tree.h +++ b/src/include/tree.h @@ -4,15 +4,15 @@ #include "util.h" typedef enum TREE_TYPE { - TREE_TYPE_BLOCK, - TREE_TYPE_EXPR, - TREE_TYPE_LINT, - TREE_TYPE_LSTR, - TREE_TYPE_TAG, - TREE_TYPE_DARG, - TREE_TYPE_CARG, - TREE_TYPE_DEF, - TREE_TYPE_CALL, + TREE_TYPE_BLOCK, + TREE_TYPE_EXPR, + TREE_TYPE_LINT, + TREE_TYPE_LSTR, + TREE_TYPE_TAG, + TREE_TYPE_DARG, + TREE_TYPE_CARG, + TREE_TYPE_DEF, + TREE_TYPE_CALL, } tree_type_t; /* The Abstract Syntax Tree (AST) structure. */ @@ -86,6 +86,12 @@ tree_t* tree_init(int type); /* Destroy the AST. */ void tree_destroy(tree_t* tree); +/* + Compare two trees. For testing. + Returns 1 if the same, otherwise 0. +*/ +int tree_cmp(tree_t* tree_0, tree_t* tree_1); + /* Print a tree. */ void tree_print(tree_t* tree, int nest); diff --git a/src/lexer.c b/src/lexer.c index 13892a5..74bea85 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -36,7 +36,6 @@ void lexer_add_token(lexer_t* lexer, token_t* token) { lexer->tokenl_last = token; } - lexer->tokenc ++; } @@ -171,7 +170,4 @@ void lexer_run(lexer_t* lexer) { else if (lexer->state == LEXER_STATE_KWD) { lexer_do_kwd(lexer); } lexer->src ++; } - - /* print tokens *AFTER* they've been discovered */ - token_print(lexer->tokenl); } diff --git a/src/main.c b/src/main.c index d40da66..d8905b5 100644 --- a/src/main.c +++ b/src/main.c @@ -1,17 +1,12 @@ -#include -#include +#include "include/main.h" -#include "include/test.h" - -#include "include/util.h" -#include "include/source.h" -#include "include/token.h" -#include "include/pp.h" -#include "include/lexer.h" -#include "include/parser.h" +#ifdef TEST +unsigned int TESTS_RUN; +unsigned int TESTS_PASSED; +#endif int main(int argc, char* argv[]) { - char* src; /* the source "code" */ + char* src; pp_t* pp; lexer_t* lexer; parser_t* parser; @@ -21,8 +16,6 @@ int main(int argc, char* argv[]) { log_dbg("source gotten"); log_inf("source: %s", src); - ASSERT(src); - /* create pre-processor */ pp = pp_init(src); log_dbg("preprocessor created"); @@ -44,6 +37,9 @@ int main(int argc, char* argv[]) { lexer_run(lexer); log_dbg("lexer ran"); + /* Print the lexer's tokens. */ + token_print(lexer->tokenl); + /* Create the parser from the lexer's tokens. */ parser = parser_init(lexer->tokenl); parser_run(parser); @@ -56,7 +52,5 @@ int main(int argc, char* argv[]) { parser_destroy(parser); free(src); - TEST_REPORT; - return 0; } diff --git a/src/parser.c b/src/parser.c index 09ce7bc..0359c80 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1,6 +1,4 @@ #include "include/parser.h" -#include "include/token.h" -#include "include/tree.h" parser_t* parser_init(token_t* token) { parser_t* parser; diff --git a/src/stack.c b/src/stack.c new file mode 100644 index 0000000..5c4d0ca --- /dev/null +++ b/src/stack.c @@ -0,0 +1,38 @@ +#include "include/stack.h" + +stack_t* stack_init() { + stack_t* stack = emalloc(sizeof(stack_t)); + + stack->sp = 0; + for (int i = 0; i < STACK_MAXLEN; stack->val[i++] = 0); + + return stack; +} + +void stack_destroy(stack_t* stack) { + free(stack); +} + +void stack_push(stack_t* stack, void* val) { + stack->val[stack->sp++] = val; +} + +void* stack_pop(stack_t* stack) { + void* tmpval; + + tmpval = stack->val[--stack->sp]; + stack->val[stack->sp] = NULL; + + return tmpval; +} + +size_t stack_len(stack_t* stack) { + return stack->sp - 1; +} + +void stack_print(stack_t* stack) { + log_inf("stack_print(): %p", stack); + for (int i = stack_len(stack) - 1; i >= 0; --i) { + log_inf("%d: %p", i, stack[i]); + } +} diff --git a/src/tree.c b/src/tree.c index 8876e47..4729200 100644 --- a/src/tree.c +++ b/src/tree.c @@ -90,6 +90,49 @@ void tree_destroy(tree_t* tree) { free(tree); } +int tree_cmp(tree_t* tree_0, tree_t* tree_1) { + if ((tree_0 && !tree_1) || (!tree_0 && tree_1)) { return 0; } /* Only 1 is defined (failure). */ + if (!tree_0 && !tree_1) { return 1; } /* Both are undefined (success). */ + if (tree_0->type != tree_1->type) { return 0; } /* Types do not match (failure). */ + + switch (tree_0->type) { + case TREE_TYPE_BLOCK: + return tree_cmp(tree_0->data.block.val, tree_1->data.block.val) && + tree_cmp(tree_0->data.block.val, tree_1->data.block.val); + break; + case TREE_TYPE_EXPR: + return tree_cmp(tree_0->data.expr.val, tree_1->data.expr.val); + break; + case TREE_TYPE_LINT: + return tree_0->data.lint.val == tree_1->data.lint.val; + break; + case TREE_TYPE_LSTR: + return strcmp(tree_0->data.lstr.val, tree_1->data.lstr.val) == 0; + break; + case TREE_TYPE_TAG: + return (strcmp(tree_0->data.tag.val, tree_1->data.tag.val) == 0) && + tree_cmp(tree_0->data.tag.nxt, tree_1->data.tag.nxt); + break; + case TREE_TYPE_DARG: + return tree_cmp(tree_0->data.darg.tag, tree_1->data.darg.tag) && + tree_cmp(tree_0->data.darg.nxt, tree_1->data.darg.nxt); + break; + case TREE_TYPE_CARG: + return tree_cmp(tree_0->data.carg.val, tree_1->data.carg.val) && + tree_cmp(tree_0->data.carg.nxt, tree_1->data.carg.nxt); + break; + case TREE_TYPE_DEF: + return tree_cmp(tree_0->data.def.tag, tree_1->data.def.tag) && + tree_cmp(tree_0->data.def.arg, tree_1->data.def.arg) && + tree_cmp(tree_0->data.def.val, tree_1->data.def.val); + break; + case TREE_TYPE_CALL: + return (strcmp(tree_0->data.call.target, tree_1->data.call.target) == 0) && + tree_cmp(tree_0->data.call.arg, tree_1->data.call.arg); + break; + } +} + /* Every time I think there's a problem with the parser, it turns out it's just this stupid tree print function. diff --git a/test/include/test.h b/test/include/test.h new file mode 100644 index 0000000..4638a1e --- /dev/null +++ b/test/include/test.h @@ -0,0 +1,22 @@ +#ifndef TEST_H +#define TEST_H + +#include "../../src/include/util.h" + +extern unsigned int TESTS_RUN; +extern unsigned int TESTS_PASSED; + +#define HIDE(THE) do { THE } while ( 0 ); + +#define ASSERT(EXPR) \ + TESTS_RUN++; \ + (EXPR && ++TESTS_PASSED) ? \ + log_inf("%s:%d: Assertion passed!", __FILE__, __LINE__) : \ + log_err("%s:%d: Assertion failed:\n\t%s", __FILE__, __LINE__, #EXPR); + +#define TEST_REPORT \ + (TESTS_RUN == TESTS_PASSED) ? \ + log_yay("%s: All %d tests passed!", __FILE__, TESTS_RUN) : \ + log_err("%d/%d tests failed.", TESTS_RUN - TESTS_PASSED, TESTS_RUN); + +#endif /* ifndef TEST_H */ diff --git a/test/parser.c b/test/parser.c new file mode 100644 index 0000000..fb1c0b2 --- /dev/null +++ b/test/parser.c @@ -0,0 +1,72 @@ +#include "include/test.h" +#include "../src/include/pp.h" +#include "../src/include/lexer.h" +#include "../src/include/tree.h" +#include "../src/include/parser.h" + +unsigned int TESTS_RUN = 0, TESTS_PASSED = 0; + +int main(int argc, char** argv) { + tree_t* tree_0; + pp_t* pp; + lexer_t* lexer; + parser_t* parser; + + tree_0 = tree_init(TREE_TYPE_BLOCK); + tree_t* treep_00 = tree_0->data.block.val = tree_init(TREE_TYPE_EXPR); + tree_t* treep_01 = treep_00->data.expr.val = tree_init(TREE_TYPE_DEF); + tree_t* treep_02 = treep_01->data.def.tag = tree_init(TREE_TYPE_TAG); + treep_02->data.tag.val = "int"; + tree_t* treep_03 = treep_02->data.tag.nxt = tree_init(TREE_TYPE_TAG); + treep_03->data.tag.val = "f"; + treep_03->data.tag.nxt = NULL; + treep_01->data.def.arg = NULL; + tree_t* treep_04 = treep_01->data.def.val = tree_init(TREE_TYPE_EXPR); + tree_t* treep_05 = treep_04->data.expr.val = tree_init(TREE_TYPE_BLOCK); + tree_t* treep_06 = treep_05->data.block.val = tree_init(TREE_TYPE_EXPR); + tree_t* treep_07 = treep_06->data.expr.val = tree_init(TREE_TYPE_CALL); + treep_07->data.call.target = "a"; + tree_t* treep_08 = treep_07->data.call.arg = tree_init(TREE_TYPE_CARG); + tree_t* treep_09 = treep_08->data.carg.val = tree_init(TREE_TYPE_EXPR); + tree_t* treep_10 = treep_09->data.expr.val = tree_init(TREE_TYPE_CALL); + treep_10->data.call.target = "b"; + treep_10->data.call.arg = NULL; + treep_08->data.carg.nxt = NULL; + tree_t* treep_11 = treep_05->data.block.nxt = tree_init(TREE_TYPE_BLOCK); + tree_t* treep_12 = treep_11->data.block.val = tree_init(TREE_TYPE_EXPR); + tree_t* treep_13 = treep_12->data.expr.val = tree_init(TREE_TYPE_CALL); + treep_13->data.call.target = "c"; + tree_t* treep_14 = treep_13->data.call.arg = tree_init(TREE_TYPE_CARG); + tree_t* treep_15 = treep_14->data.carg.val = tree_init(TREE_TYPE_EXPR); + tree_t* treep_16 = treep_15->data.expr.val = tree_init(TREE_TYPE_CALL); + treep_16->data.call.target = "d"; + treep_16->data.call.arg = NULL; + treep_14->data.carg.nxt = NULL; + treep_11->data.block.nxt = NULL; + tree_0->data.block.nxt = NULL; + + char src_0[] = "" \ + ":int:f = {" \ + " a.b;" \ + " c.d;" \ + "}"; + + pp = pp_init(src_0); + pp_run(pp); + + lexer = lexer_init(pp->psrc); + lexer_run(lexer); + + parser = parser_init(lexer->tokenl); + parser_run(parser); + + ASSERT(tree_cmp(tree_0, parser->tree)); + + pp_destroy(pp); + parser_destroy(parser); + lexer_destroy(lexer); + + TEST_REPORT; + + return 0; +} diff --git a/test/tree.c b/test/tree.c new file mode 100644 index 0000000..4c32746 --- /dev/null +++ b/test/tree.c @@ -0,0 +1,54 @@ +#include "include/test.h" +#include "../src/include/pp.h" +#include "../src/include/lexer.h" +#include "../src/include/tree.h" + +unsigned int TESTS_RUN = 0, TESTS_PASSED = 0; + +int main(int argc, char** argv) { + tree_t* tree_0; + tree_t* tree_1; + + tree_t* tree_lint_0; + tree_t* tree_lint_1; + tree_t* tree_lstr_0; + tree_t* tree_lstr_1; + tree_t* tree_tag_0; + tree_t* tree_tag_1; + tree_t* tree_def_0; + tree_t* tree_def_1; + tree_t* tree_call_0; + tree_t* tree_call_1; + + tree_0 = tree_init(TREE_TYPE_EXPR); + tree_1 = tree_init(TREE_TYPE_BLOCK); + + tree_lint_0 = tree_init(TREE_TYPE_LINT); + tree_lint_1 = tree_init(TREE_TYPE_LINT); + tree_lstr_0 = tree_init(TREE_TYPE_LSTR); + tree_lstr_1 = tree_init(TREE_TYPE_LSTR); + tree_tag_0 = tree_init(TREE_TYPE_TAG); + tree_tag_1 = tree_init(TREE_TYPE_TAG); + tree_def_0 = tree_init(TREE_TYPE_DEF); + tree_def_1 = tree_init(TREE_TYPE_DEF); + tree_call_0 = tree_init(TREE_TYPE_CALL); + tree_call_1 = tree_init(TREE_TYPE_CALL); + + /* Simple cases with nonexistent trees. */ + ASSERT(tree_cmp(NULL, tree_0) == 0); + ASSERT(tree_cmp(tree_0, NULL) == 0); + ASSERT(tree_cmp(NULL, NULL) == 1); + + /* Test tree types. */ + ASSERT(tree_cmp(tree_0, tree_0) == 1); + ASSERT(tree_cmp(tree_0, tree_1) == 0); + + /* Test lints. */ + tree_lint_0->data.lint.val = 0; + tree_lint_1->data.lint.val = 1; + ASSERT(tree_cmp(tree_lint_0, tree_lint_0) == 1); + ASSERT(tree_cmp(tree_lint_0, tree_lint_1) == 0); + + TEST_REPORT; + return 0; +} -- cgit v1.2.3