// Kyler Olsen // YREA SLS // Lexer Tests // October 2025 #include #include #include #include #include "sls/sls_errors.h" #include "sls/lexer.h" #include "tests/tests.h" static const size_t NUM_OF_TESTS = 6; typedef struct { TestResult result; LexerInfo lexer_info; } LexerTest; static const char *TOKEN_TYPES_NAMES[] = { "End of File", "Identifier", "Integer", "Float", "Double", "String", "Boolean", "Array", "Token String", "Type Tuple", }; static const char *INTEGER_TYPES_NAMES[] = { "i64", "i32", "i16", "i8", "u64", "u32", "u16", "u8", }; typedef struct { const char *value; size_t length; } ArrayStringValues; // Test start and end helpers static LexerTest start_up_test(const char *test_name, const char *test_code) { LexerTest test = (LexerTest) { .result = (TestResult) { .name = test_name, .status = TEST_NOT_IMPLEMENTED } }; lexer_init(&test.lexer_info, TEST_FILE_NAME, test_code); return test; } static void clean_up_test(LexerResult result) { if (result.type == SLS_RESULT) clean_token_result(result.result); } static TestResult logic_fail_test(LexerTest *test, LexerResult result, const char *message) { if (message == 0) return error_test(test, result, (SlsError) { .message = "Out of Memory Error!", .code = 3458, }); clean_up_test(result); test->result.status = TEST_LOGIC_FAIL; test->result.message = message; return test->result; } static TestResult error_fail_test(LexerTest *test, LexerResult result, SlsError error) { clean_up_test(result); test->result.status = TEST_ERROR_FAIL; test->result.error = error; return test->result; } static TestResult error_test(LexerTest *test, LexerResult result, SlsError error) { clean_up_test(result); test->result.status = TEST_ERROR; test->result.error = error; return test->result; } static TestResult skip_test(LexerTest *test, LexerResult result) { clean_up_test(result); test->result.status = TEST_NOT_IMPLEMENTED; return test->result; } static TestResult pass_test(LexerTest *test, LexerResult result) { clean_up_test(result); test->result.status = TEST_PASS; return test->result; } // Test messages static char *unexpected_end_of_token_stream(size_t i) { size_t length = floor(log10(i)) + 47; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Unexpected end of token stream (%d tokens found)", i-1); return string; } static char *expected_end_of_token_stream(size_t i) { size_t length = floor(log10(i)) + 47; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Expected end of token stream (more than %d tokens found)", i-1); return string; } static char *token_should_be(size_t i, TokenType should, TokenType found) { size_t length = floor(log10(i + 1)) + strnlen(TOKEN_TYPES_NAMES[should], 13) + strnlen(TOKEN_TYPES_NAMES[found], 13) + 35; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d should be a %s, but found a %s", i, TOKEN_TYPES_NAMES[should], TOKEN_TYPES_NAMES[found]); return string; } static char *integer_type_should_be(size_t i, TokenType should, TokenType found) { size_t length = floor(log10(i + 1)) + strnlen(INTEGER_TYPES_NAMES[should], 5) + strnlen(INTEGER_TYPES_NAMES[found], 5) + 48; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d integer type should be a %s, but found a %s", i, TOKEN_TYPES_NAMES[should], TOKEN_TYPES_NAMES[found]); return string; } static char *integer_value_should_be(size_t i, uint64_t should, uint64_t found) { size_t length = floor(log10(i + 1)) + floor(log10(should + 1)) + floor(log10(found + 1)) + 45; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d integer value should be %d, but found %d", i, should, found); return string; } static char *float_value_should_be(size_t i, double should, double found) { size_t length = floor(log10(i + 1)) + floor(log10(should + 1) + 3) + floor(log10(found + 1) + 3) + 43; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d float value should be %.2f, but found %.2f", i, should, found); return string; } static char *identifier_should_be_literal(size_t i) { size_t length = floor(log10(i + 1)) + 51; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d identifier should be an identifier literal", i); return string; } static char *identifier_should_not_be_literal(size_t i) { size_t length = floor(log10(i + 1)) + 55; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d identifier should not be an identifier literal", i); return string; } static char *token_length_should_be(size_t i, TokenType type, uint64_t should, uint64_t found) { size_t length = floor(log10(i + 1)) + strnlen(TOKEN_TYPES_NAMES[type], 13) + floor(log10(should + 1)) + floor(log10(found + 1)) + 47; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d of type %s length should be %d, but found %d", i, TOKEN_TYPES_NAMES[type], should, found); return string; } static char *token_value_string_should_be(size_t i, TokenType type, size_t length, const char *should, const char *found) { size_t length = floor(log10(i + 1)) + strnlen(TOKEN_TYPES_NAMES[type], 13) + strnlen(should, length) + strnlen(found, length) + 53; char *string = malloc(sizeof(char) * length); if (string = 0) return string; snprintf(string, length, "Token #%d of type %s string value should be %s, but found %s", i, TOKEN_TYPES_NAMES[type], should, found); return string; } static char *boolean_should_be(size_t i, Boolean value) { size_t length = floor(log10(i + 1)) + 45; char *string = malloc(sizeof(char) * length); if (string = 0) return string; if (value) snprintf(string, length, "Token #%d boolean should be true, but is false", i); else snprintf(string, length, "Token #%d boolean should be false, but is true", i); return string; } // Test parts static Boolean test_eof_value(LexerTest *test, LexerResult result, size_t i) { LexerTokenResult *head = get_token(result.result, i); if (head == 0) { logic_fail_test(test, result, unexpected_end_of_token_stream(i + 1)); return TRUE; } if (head->type == SLS_ERROR) { error_fail_test(test, result, result.error); return TRUE; } if (head->result.type != TOKEN_EOF) { logic_fail_test(test, result, token_should_be(i + 1, TOKEN_EOF, head->result.type)); return TRUE; } if (head->next != 0) { logic_fail_test(test, result, expected_end_of_token_stream(i + 1)); return TRUE; } return FALSE; } static Boolean test_identifier_value(LexerTest *test, LexerResult result, size_t i, Boolean is_literal, size_t length, const char *name) { LexerTokenResult *head = get_token(result.result, i); if (head == 0) { logic_fail_test(test, result, unexpected_end_of_token_stream(i + 1)); return TRUE; } if (head->type == SLS_ERROR) { error_fail_test(test, result, result.error); return TRUE; } if (head->result.type != TOKEN_IDENTIFIER) { logic_fail_test(test, result, token_should_be(i + 1, TOKEN_IDENTIFIER, head->result.type)); return TRUE; } if (head->result.identifier.is_literal != is_literal) { logic_fail_test(test, result, is_literal ? identifier_should_be_literal(i + 1) : identifier_should_not_be_literal(i + 1)); return TRUE; } if (head->result.identifier.length == strnlen(name, length)) { logic_fail_test(test, result, token_length_should_be(i + 1, TOKEN_IDENTIFIER, strnlen(name, length), head->result.identifier.length)); return TRUE; } if (strcmp(head->result.identifier.name, name) != 0) { logic_fail_test(test, result, token_value_string_should_be(i + 1, TOKEN_IDENTIFIER, strnlen(name, length), head->result.identifier.name, name)); return TRUE; } return FALSE; } static Boolean test_integer_value(LexerTest *test, LexerResult result, size_t i, IntegerBuiltInType type, uint64_t value) { LexerTokenResult *head = get_token(result.result, i); if (head == 0) { logic_fail_test(test, result, unexpected_end_of_token_stream(i + 1)); return TRUE; } if (head->type == SLS_ERROR) { error_fail_test(test, result, result.error); return TRUE; } if (head->result.type != TOKEN_INTEGER) { logic_fail_test(test, result, token_should_be(i + 1, TOKEN_INTEGER, head->result.type)); return TRUE; } if (head->result.integer_literal.type != type) { logic_fail_test(test, result, integer_type_should_be(i + 1, type, head->result.integer_literal.type)); return TRUE; } if (head->result.integer_literal.value != value) { logic_fail_test(test, result, integer_value_should_be(i + 1, value, head->result.integer_literal.value)); return TRUE; } return FALSE; } static Boolean test_float_value(LexerTest *test, LexerResult result, size_t i, float value) { LexerTokenResult *head = get_token(result.result, i); if (head == 0) { logic_fail_test(test, result, unexpected_end_of_token_stream(i + 1)); return TRUE; } if (head->type == SLS_ERROR) { error_fail_test(test, result, result.error); return TRUE; } if (head->result.type != TOKEN_FLOAT) { logic_fail_test(test, result, token_should_be(i + 1, TOKEN_FLOAT, head->result.type)); return TRUE; } if (head->result.float_literal - value) { logic_fail_test(test, result, float_value_should_be(i + 1, value, head->result.float_literal)); return TRUE; } return FALSE; } static Boolean test_double_value(LexerTest *test, LexerResult result, size_t i, double value) { LexerTokenResult *head = get_token(result.result, i); if (head == 0) { logic_fail_test(test, result, unexpected_end_of_token_stream(i + 1)); return TRUE; } if (head->type == SLS_ERROR) { error_fail_test(test, result, result.error); return TRUE; } if (head->result.type != TOKEN_DOUBLE) { logic_fail_test(test, result, token_should_be(i + 1, TOKEN_DOUBLE, head->result.type)); return TRUE; } if (head->result.float_literal - value) { logic_fail_test(test, result, float_value_should_be(i + 1, value, head->result.float_literal)); return TRUE; } return FALSE; } static Boolean test_string_value(LexerTest *test, LexerResult result, size_t i, size_t length, const char *value) { LexerTokenResult *head = get_token(result.result, i); if (head == 0) { logic_fail_test(test, result, unexpected_end_of_token_stream(i + 1)); return TRUE; } if (head->type == SLS_ERROR) { error_fail_test(test, result, result.error); return TRUE; } if (head->result.type != TOKEN_STRING) { logic_fail_test(test, result, token_should_be(i + 1, TOKEN_STRING, head->result.type)); return TRUE; } if (head->result.string_literal.length == strnlen(value, length)) { logic_fail_test(test, result, token_length_should_be(i + 1, TOKEN_STRING, strnlen(value, length), head->result.string_literal.length)); return TRUE; } if (strcmp(head->result.string_literal.value, value) != 0) { logic_fail_test(test, result, token_value_string_should_be(i + 1, TOKEN_STRING, strnlen(value, length), head->result.string_literal.value, value)); return TRUE; } return FALSE; } static Boolean test_boolean_value(LexerTest *test, LexerResult result, size_t i, Boolean value) { LexerTokenResult *head = get_token(result.result, i); if (head == 0) { logic_fail_test(test, result, unexpected_end_of_token_stream(i + 1)); return TRUE; } if (head->type == SLS_ERROR) { error_fail_test(test, result, result.error); return TRUE; } if (head->result.type != TOKEN_BOOLEAN) { logic_fail_test(test, result, token_should_be(i + 1, TOKEN_BOOLEAN, head->result.type)); return TRUE; } if (head->result.boolean_literal != value) { logic_fail_test(test, result, boolean_should_be(i + 1, value)); return TRUE; } return FALSE; } static Boolean test_array_identifier_value(LexerTest *test, LexerResult result, size_t i, ArrayStringValues *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_integer_value(LexerTest *test, LexerResult result, size_t i, IntegerBuiltInType type, uint64_t *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_float_value(LexerTest *test, LexerResult result, size_t i, float *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_double_value(LexerTest *test, LexerResult result, size_t i, double *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_string_value(LexerTest *test, LexerResult result, size_t i, ArrayStringValues *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_boolean_value(LexerTest *test, LexerResult result, size_t i, Boolean *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_array_value(LexerTest *test, LexerResult result, size_t i, void *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_token_string_value(LexerTest *test, LexerResult result, size_t i, void *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_type_tuple_value(LexerTest *test, LexerResult result, size_t i, void *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_array_struct_inline_value(LexerTest *test, LexerResult result, size_t i, void *values, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_token_string_value(LexerTest *test, LexerResult result, size_t i, void *value, size_t length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } static Boolean test_type_tuple_value(LexerTest *test, LexerResult result, size_t i, ArrayStringValues *input_values, size_t input_length, ArrayStringValues *output_values, size_t output_length) { error_test(test, result, (SlsError) { .message = "Test case not implemented!", .code = 984, }); // TODO return TRUE; } // Test cases static TestResult test_add_statement() { LexerTest test = start_up_test("test_add_statement", "3 4 +"); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; if (test_integer_value(&test, result, i++, INTEGER_I64, 3)) return test.result; if (test_integer_value(&test, result, i++, INTEGER_I64, 4)) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 1, "+")) return test.result; if (test_eof_value(&test, result, i++)) return test.result; return pass_test(&test, result); } static TestResult test_sub_statement() { LexerTest test = start_up_test("test_sub_statement", "10 3 -"); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; if (test_integer_value(&test, result, i++, INTEGER_I64, 10)) return test.result; if (test_integer_value(&test, result, i++, INTEGER_I64, 3)) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 1, "-")) return test.result; if (test_eof_value(&test, result, i++)) return test.result; return pass_test(&test, result); } static TestResult test_mult_statement() { LexerTest test = start_up_test("test_mult_statement", "5 6 *"); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; if (test_integer_value(&test, result, i++, INTEGER_I64, 5)) return test.result; if (test_integer_value(&test, result, i++, INTEGER_I64, 6)) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 1, "*")) return test.result; if (test_eof_value(&test, result, i++)) return test.result; return pass_test(&test, result); } static TestResult test_div_statement() { LexerTest test = start_up_test("test_div_statement", "20 4 /"); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; if (test_integer_value(&test, result, i++, INTEGER_I64, 20)) return test.result; if (test_integer_value(&test, result, i++, INTEGER_I64, 4)) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 1, "/")) return test.result; if (test_eof_value(&test, result, i++)) return test.result; return pass_test(&test, result); } static TestResult test_add_and_mult_statement() { LexerTest test = start_up_test("test_add_and_mult_statement", "2 3 + 4 *"); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; if (test_integer_value(&test, result, i++, INTEGER_I64, 2)) return test.result; if (test_integer_value(&test, result, i++, INTEGER_I64, 3)) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 1, "+")) return test.result; if (test_integer_value(&test, result, i++, INTEGER_I64, 4)) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 1, "*")) return test.result; if (test_eof_value(&test, result, i++)) return test.result; return pass_test(&test, result); } static TestResult test_dup_and_mult_statement() { LexerTest test = start_up_test("test_dup_and_mult_statement", "10 dup *"); LexerResult result = lexical_analysis(&test.lexer_info); if (result.type == SLS_ERROR) return error_fail_test(&test, result, result.error); size_t i = 0; if (test_integer_value(&test, result, i++, INTEGER_I64, 10)) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 3, "dup")) return test.result; if (test_identifier_value(&test, result, i++, FALSE, 1, "*")) return test.result; if (test_eof_value(&test, result, i++)) return test.result; return pass_test(&test, result); } // Lexer Tests Runner TestsReport run_lexer_tests() { TestsReport test_report = (TestsReport) { .section = "lexer_tests", .count = NUM_OF_TESTS, .tests = malloc(sizeof(TestResult) * NUM_OF_TESTS), }; size_t i = 0; test_report.tests[i++] = test_add_statement(); test_report.tests[i++] = test_sub_statement(); test_report.tests[i++] = test_mult_statement(); test_report.tests[i++] = test_div_statement(); test_report.tests[i++] = test_add_and_mult_statement(); test_report.tests[i++] = test_dup_and_mult_statement(); return test_report; }