YREA-SLS/SLS_C/tests/lexer_tests.c

306 lines
12 KiB
C

// Kyler Olsen
// YREA SLS
// Lexer Tests
// October 2025
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#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",
};
// 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) {
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 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);
snprintf(string, length, "Unexpected end of token stream (%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);
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);
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)) + 21;
char *string = malloc(sizeof(char) * length);
snprintf(string, length, "Token #%d integer value should be %d, but found %d", 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);
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);
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);
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_name_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) + 45;
char *string = malloc(sizeof(char) * length);
snprintf(string, length, "Token #%d of type %s name should be %s, but found %s", i, TOKEN_TYPES_NAMES[type], should, found);
return string;
}
// Test parts
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_identifier_value(LexerTest *test, LexerResult result, size_t i, Boolean is_literal, 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)) {
logic_fail_test(test, result, token_length_should_be(i + 1, TOKEN_IDENTIFIER, strnlen(name), head->result.identifier.length));
return TRUE;
} if (strcmp(head->result.identifier.name, "+") != 0) {
logic_fail_test(test, result, token_length_should_be(i + 1, TOKEN_IDENTIFIER, strnlen(name), head->result.identifier.length));
return TRUE;
}
return FALSE;
}
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 (more tokens found)");
return TRUE;
}
return FALSE;
}
// 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, "+")) 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, "-")) 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, "*")) 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, "/")) 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, "+")) return test.result;
if (test_integer_value(&test, result, i++, INTEGER_I64, 4)) return test.result;
if (test_identifier_value(&test, result, i++, FALSE, "*")) 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, "dup")) return test.result;
if (test_identifier_value(&test, result, i++, FALSE, "*")) 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;
}