diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ffb7a64..99b839e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -40,3 +40,8 @@ the agent to update the implementation. When creating a commit, make sure that both the user's and the agent's modifications are included in the commit. + +Only create a commit when specifically asked for that. Never assume implicitly that the +user wants you to create a commit. +Even if they asked you to create a commit in an earlier task, it does not mean that +you should also create a commit in a later task. diff --git a/v0/test.c b/v0/test.c index ce48420..63a2457 100644 --- a/v0/test.c +++ b/v0/test.c @@ -4,6 +4,7 @@ #include #include #include "util.h" +#include "parser.h" static jmp_buf s_testJmp; static const char* s_failMsg; @@ -16,22 +17,6 @@ void fail(const char* msg) { longjmp(s_testJmp, 1); } -void assert_not_null(void* ptr, const char* msg) { - if (ptr == NULL) { - fail(msg); - } -} - -void assert_str(const char* expected, const char* actual, const char* msg) { - if (expected == NULL || actual == NULL || strcmp(expected, actual) != 0) { - fail(msg); - } -} - -void assert_log(const char* expected, const char* msg) { - assert_str(expected, s_logOutput, msg); -} - char* read_file_content(const char* filepath) { FILE* f; long size; @@ -53,6 +38,56 @@ char* read_file_content(const char* filepath) { return content; } +void assert_not_null(void* ptr, const char* msg) { + if (ptr == NULL) { + fail(msg); + } +} + +void assert_string(const char* expected, String actual, const char* msg) { + if (expected == NULL || actual.data == NULL || strlen(expected) != actual.length || strncmp(expected, actual.data, actual.length) != 0) { + fail(msg); + } +} + +void assert_str(const char* expected, const char* actual, const char* msg) { + if (expected == NULL || actual == NULL || strcmp(expected, actual) != 0) { + fail(msg); + } +} + +TokenStream* tokenstream_get_test(void) { + char* filepath = NULL; + TokenStream* ts = NULL; + + filepath = format_string("v0/tests/%s.c2", s_currentTestName); + if (!filepath) { + fail("out of memory"); + return NULL; + } + + if (s_testSource) free(s_testSource); + s_testSource = read_file_content(filepath); + if (!s_testSource) { + fail("could not read test source file"); + free(filepath); + return NULL; + } + ts = tokenstream_open(filepath, s_testSource); + free(filepath); + return ts; +} + +Module* test_get_ast(void) { + TokenStream* ts = tokenstream_get_test(); + if (!ts) return NULL; + return parser_parse(ts); +} + +void assert_log(const char* expected, const char* msg) { + assert_str(expected, s_logOutput, msg); +} + void assert_log_file(const char* msg) { char* filepath = format_string("v0/tests/%s.log", s_currentTestName); const char* generate; @@ -109,28 +144,6 @@ void assert_false(bool condition, const char* msg) { } } -TokenStream* tokenstream_get_test(void) { - char* filepath = NULL; - TokenStream* ts = NULL; - - filepath = format_string("v0/tests/%s.c2", s_currentTestName); - if (!filepath) { - fail("out of memory"); - return NULL; - } - - if (s_testSource) free(s_testSource); - s_testSource = read_file_content(filepath); - if (!s_testSource) { - fail("could not read test source file"); - free(filepath); - return NULL; - } - ts = tokenstream_open(filepath, s_testSource); - free(filepath); - return ts; -} - static void log_append(const char* msg) { size_t oldLen = s_logOutput ? strlen(s_logOutput) : 0; size_t newLen = oldLen + strlen(msg) + 1; diff --git a/v0/test.h b/v0/test.h index ca74f1b..e6d15c2 100644 --- a/v0/test.h +++ b/v0/test.h @@ -5,6 +5,7 @@ #define TEST_H #include "token.h" +#include "ast.h" typedef void (*Test)(void); @@ -35,6 +36,17 @@ void assert_not_null(void* ptr, const char* msg); */ void assert_str(const char* expected, const char* actual, const char* msg); +/** + * Asserts that a string has the expected value. + * + * Calls `fail` if the assertion does not hold. + * + * @param expected The expected value. This is typically a string literal. + * @param actual The actual value. This is typically an expression. + * @param msg The message to print if these do not match. + */ +void assert_string(const char* expected, String actual, const char* msg); + /** * Asserts that the logged output matches the expected value. */ @@ -66,7 +78,17 @@ void assert_false(bool condition, const char* msg); /** * Get the token stream used for this test. * It reads from the `v0/tests/xyz.c2` file, where xyz is the test name. + * + * At the end of the test, the tokenstream will be freed automatically by the test harness. */ -TokenStream* tokenstream_get_test(void); +TokenStream* test_get_tokenstream(void); + +/** + * Gets a parsed module for the this test. + * It reads from the `v0/tests/xyz.c2` file, where xyz is the test name. + * + * At the end of the test, the AST will be freed automatically by the test harness. + */ +Module* test_get_ast(void); #endif diff --git a/v0/test_parser.c b/v0/test_parser.c index c9767e9..dbb2e97 100644 --- a/v0/test_parser.c +++ b/v0/test_parser.c @@ -6,16 +6,10 @@ static void test_parser_module_name(void) { TokenStream* ts = tokenstream_open("test.c", "module my_module;"); Module* m = parser_parse(ts); - char* buf; assert_not_null(m, "expected module to be parsed"); - buf = malloc(m->name.text.length + 1); - memcpy(buf, m->name.text.data, m->name.text.length); - buf[m->name.text.length] = '\0'; - assert_str("my_module", buf, "expected name 'my_module'"); - free(buf); - - parser_free(m); + assert_string("my_module", m->name.text, "expected name 'my_module'"); + parser_free(m); tokenstream_close(ts); } @@ -62,49 +56,29 @@ static void test_parser_bad_import_name(void) { static void test_parser_imports(void) { TokenStream* ts = tokenstream_open("test.c", "module my_module; import other_module;"); Module* m = parser_parse(ts); - char* buf; - + assert_not_null(m, "expected module to be parsed"); - buf = malloc(m->name.text.length + 1); - memcpy(buf, m->name.text.data, m->name.text.length); - buf[m->name.text.length] = '\0'; - assert_str("my_module", buf, "expected name 'my_module'"); - free(buf); + assert_string("my_module", m->name.text, "expected name 'my_module'"); assert_not_null(m->imports, "expected imports to be parsed"); assert_int(1, (int)m->import_count, "expected one import"); - buf = malloc(m->imports[0].module_name.text.length + 1); - memcpy(buf, m->imports[0].module_name.text.data, m->imports[0].module_name.text.length); - buf[m->imports[0].module_name.text.length] = '\0'; - assert_str("other_module", buf, "expected import name 'other_module'"); - free(buf); + assert_string("other_module", m->imports[0].module_name.text, "expected import name 'other_module'"); assert_false(m->imports[0].is_public, "expected import to not be public"); - - parser_free(m); + parser_free(m); tokenstream_close(ts); } static void test_parser_public_imports(void) { TokenStream* ts = tokenstream_open("test.c", "module my_module; import public other_module;"); Module* m = parser_parse(ts); - char* buf; - + assert_not_null(m, "expected module to be parsed"); - buf = malloc(m->name.text.length + 1); - memcpy(buf, m->name.text.data, m->name.text.length); - buf[m->name.text.length] = '\0'; - assert_str("my_module", buf, "expected name 'my_module'"); - free(buf); + assert_string("my_module", m->name.text, "expected name 'my_module'"); assert_not_null(m->imports, "expected imports to be parsed"); assert_int(1, (int)m->import_count, "expected one import"); - buf = malloc(m->imports[0].module_name.text.length + 1); - memcpy(buf, m->imports[0].module_name.text.data, m->imports[0].module_name.text.length); - buf[m->imports[0].module_name.text.length] = '\0'; - assert_str("other_module", buf, "expected import name 'other_module'"); - free(buf); + assert_string("other_module", m->imports[0].module_name.text, "expected import name 'other_module'"); assert_true(m->imports[0].is_public, "expected import to be public"); - - parser_free(m); + parser_free(m); tokenstream_close(ts); } diff --git a/v0/types.h b/v0/types.h new file mode 100644 index 0000000..f96466a --- /dev/null +++ b/v0/types.h @@ -0,0 +1,9 @@ +/** + * Contains runtime information about types. + */ +#ifndef TYPES_H +#define TYPES_H + + + +#endif