1447 lines
38 KiB
C
1447 lines
38 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;
|
|
|
|
static const double FLOAT_TEST_PRECISION = 0.01;
|
|
|
|
typedef struct {
|
|
TestResult result;
|
|
LexerInfo lexer_info;
|
|
} LexerTest;
|
|
|
|
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 = ceil(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 = ceil(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 =
|
|
ceil(log10(i + 1)) +
|
|
strnlen(TOKEN_TYPES_NAMES[should], TYPE_NAMES_SAFE_LENGTH) +
|
|
strnlen(TOKEN_TYPES_NAMES[found], TYPE_NAMES_SAFE_LENGTH) +
|
|
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 =
|
|
ceil(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 =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(should + 1)) +
|
|
ceil(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 =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(should + 1) + 3) +
|
|
ceil(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 = ceil(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 = ceil(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 =
|
|
ceil(log10(i + 1)) +
|
|
strnlen(TOKEN_TYPES_NAMES[type], TYPE_NAMES_SAFE_LENGTH) +
|
|
ceil(log10(should + 1)) +
|
|
ceil(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 value_length,
|
|
const char *should,
|
|
const char *found
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
strnlen(TOKEN_TYPES_NAMES[type], TYPE_NAMES_SAFE_LENGTH) +
|
|
strnlen(should, value_length) +
|
|
strnlen(found, value_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 = ceil(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;
|
|
}
|
|
|
|
static char *array_type_should_be(size_t i, ArrayType should, ArrayType found) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
strnlen(ARRAY_TYPES_NAMES[should], TYPE_NAMES_SAFE_LENGTH) +
|
|
strnlen(ARRAY_TYPES_NAMES[found], TYPE_NAMES_SAFE_LENGTH) +
|
|
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,
|
|
ARRAY_TYPES_NAMES[should],
|
|
ARRAY_TYPES_NAMES[found]
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *array_dimensions_should_be(size_t i, size_t should, size_t found) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(should + 1)) +
|
|
ceil(log10(found + 1)) +
|
|
48;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d array dimensions should be %s, but found %s",
|
|
i,
|
|
should,
|
|
found
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *array_element_shape_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
ArrayType type,
|
|
uint64_t should,
|
|
uint64_t found
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(j + 1)) +
|
|
strnlen(ARRAY_TYPES_NAMES[type], TYPE_NAMES_SAFE_LENGTH) +
|
|
ceil(log10(should + 1) + 3) +
|
|
ceil(log10(found + 1) + 3) +
|
|
63;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d dimension %d of array type %s should be shape %d, but found %d",
|
|
i,
|
|
j,
|
|
TOKEN_TYPES_NAMES[type],
|
|
should,
|
|
found
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *array_element_integer_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
ArrayType type,
|
|
uint64_t should,
|
|
uint64_t found
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(j + 1)) +
|
|
strnlen(ARRAY_TYPES_NAMES[type], TYPE_NAMES_SAFE_LENGTH) +
|
|
ceil(log10(should + 1) + 3) +
|
|
ceil(log10(found + 1) + 3) +
|
|
55;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of array type %s should be %d, but found %d",
|
|
i,
|
|
j,
|
|
TOKEN_TYPES_NAMES[type],
|
|
should,
|
|
found
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *array_element_float_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
ArrayType type,
|
|
double should,
|
|
double found
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(j + 1)) +
|
|
strnlen(ARRAY_TYPES_NAMES[type], TYPE_NAMES_SAFE_LENGTH) +
|
|
ceil(log10(should + 1)) +
|
|
ceil(log10(found + 1)) +
|
|
55;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of array type %s should be %.2f, but found %.2f",
|
|
i,
|
|
j,
|
|
TOKEN_TYPES_NAMES[type],
|
|
should,
|
|
found
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *array_element_string_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
ArrayType type,
|
|
size_t value_length,
|
|
const char *should,
|
|
const char *found
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(j + 1)) +
|
|
strnlen(ARRAY_TYPES_NAMES[type], TYPE_NAMES_SAFE_LENGTH) +
|
|
strnlen(should, value_length) +
|
|
strnlen(found, value_length) +
|
|
55;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of array type %s should be %s, but found %s",
|
|
i,
|
|
j,
|
|
TOKEN_TYPES_NAMES[type],
|
|
should,
|
|
found
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *array_element_boolean_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
ArrayType type,
|
|
Boolean value
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(j + 1)) +
|
|
strnlen(ARRAY_TYPES_NAMES[type], TYPE_NAMES_SAFE_LENGTH) +
|
|
64;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
if (value)
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of array type %s should be true, but found false",
|
|
i,
|
|
j,
|
|
TOKEN_TYPES_NAMES[type]
|
|
);
|
|
else
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of array type %s should be false, but found true",
|
|
i,
|
|
j,
|
|
TOKEN_TYPES_NAMES[type]
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *type_tuple_element_integer_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
uint64_t should,
|
|
uint64_t found
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(j + 1)) +
|
|
ceil(log10(should + 1) + 3) +
|
|
ceil(log10(found + 1) + 3) +
|
|
54;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of type tuple should be %d, but found %d",
|
|
i,
|
|
j,
|
|
should,
|
|
found
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *type_tuple_element_string_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
size_t value_length,
|
|
const char *should,
|
|
const char *found
|
|
) {
|
|
size_t length =
|
|
ceil(log10(i + 1)) +
|
|
ceil(log10(j + 1)) +
|
|
strnlen(should, value_length) +
|
|
strnlen(found, value_length) +
|
|
54;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of type tuple should be %s, but found %s",
|
|
i,
|
|
j,
|
|
should,
|
|
found
|
|
);
|
|
return string;
|
|
}
|
|
|
|
static char *type_tuple_element_boolean_should_be(
|
|
size_t i,
|
|
size_t j,
|
|
Boolean value
|
|
) {
|
|
size_t length = ceil(log10(i + 1)) + ceil(log10(j + 1)) + 63;
|
|
char *string = malloc(sizeof(char) * length);
|
|
if (string = 0) return string;
|
|
if (value)
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of type tuple should be true, but found false",
|
|
i,
|
|
j
|
|
);
|
|
else
|
|
snprintf(
|
|
string,
|
|
length,
|
|
"Token #%d element %d of type tuple should be false, but found true",
|
|
i,
|
|
j
|
|
);
|
|
return string;
|
|
}
|
|
|
|
// Test parts
|
|
|
|
static Boolean test_token_type(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
TokenType token_type
|
|
) {
|
|
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_type) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
token_should_be(i + 1, token_type, head->result.type)
|
|
);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_eof_value(LexerTest *test, LexerResult result, size_t i) {
|
|
static const TokenType token_type = TOKEN_EOF;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_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
|
|
) {
|
|
static const TokenType token_type = TOKEN_IDENTIFIER;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_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_type,
|
|
strnlen(name, length),
|
|
head->result.identifier.length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (strncmp(head->result.identifier.name, name, length) != 0) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
token_value_string_should_be(
|
|
i + 1,
|
|
token_type,
|
|
fmax(
|
|
strnlen(name, length),
|
|
strnlen(head->result.string_literal.value, 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
|
|
) {
|
|
static const TokenType token_type = TOKEN_INTEGER;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_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
|
|
) {
|
|
static const TokenType token_type = TOKEN_FLOAT;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_type)) {
|
|
return TRUE;
|
|
} if (fabsf(head->result.float_literal - value) >= FLOAT_TEST_PRECISION) {
|
|
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
|
|
) {
|
|
static const TokenType token_type = TOKEN_DOUBLE;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_type)) {
|
|
return TRUE;
|
|
} if (fabs(head->result.float_literal - value) >= FLOAT_TEST_PRECISION) {
|
|
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
|
|
) {
|
|
static const TokenType token_type = TOKEN_STRING;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_type)) {
|
|
return TRUE;
|
|
} if (head->result.string_literal.length == strnlen(value, length)) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
token_length_should_be(
|
|
i + 1,
|
|
token_type,
|
|
strnlen(value, length),
|
|
head->result.string_literal.length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (strncmp(head->result.string_literal.value, value, length) != 0) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
token_value_string_should_be(
|
|
i + 1,
|
|
token_type,
|
|
fmax(
|
|
strnlen(value, length),
|
|
strnlen(head->result.string_literal.value, length)
|
|
),
|
|
value,
|
|
head->result.string_literal.value
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_boolean_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
Boolean value
|
|
) {
|
|
static const TokenType token_type = TOKEN_BOOLEAN;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_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_type(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
ArrayType array_type,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, TOKEN_ARRAY)) {
|
|
return TRUE;
|
|
} if (head->result.array_literal.type != array_type) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_type_should_be(
|
|
i + 1,
|
|
array_type,
|
|
head->result.array_literal.type
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (head->result.array_literal.dimensions != dimensions) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_dimensions_should_be(
|
|
i + 1,
|
|
dimensions,
|
|
head->result.array_literal.dimensions
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
for (int j = 0; j < dimensions; j++) {
|
|
if (head->result.array_literal.shape[j] != shape[j]) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_shape_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
shape[j],
|
|
head->result.array_literal.shape[j]
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_array_identifier_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
ArrayStringValues *values,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
static const ArrayType array_type = ARRAY_IDENTIFIER;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_array_type(test, result, i, array_type, shape, dimensions)) {
|
|
return TRUE;
|
|
}
|
|
size_t length = 1;
|
|
for (int j = 0; j < dimensions; j++) length *= shape[j];
|
|
for (int j = 0; j < length; j++) {
|
|
if (
|
|
head->result.array_literal.identifiers[j].length ==
|
|
values[j].length
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_integer_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j].length,
|
|
head->result.array_literal.identifiers[j].length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (
|
|
strncmp(
|
|
head->result.array_literal.identifiers[j].name,
|
|
values[j].value,
|
|
values[j].length
|
|
)
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_string_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j].length,
|
|
values[j].value,
|
|
head->result.array_literal.identifiers[j].name
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (!head->result.array_literal.identifiers[j].is_literal) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_boolean_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
TRUE
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_array_integer_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
IntegerBuiltInType array_type,
|
|
uint64_t *values,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_array_type(test, result, i, array_type, shape, dimensions)) {
|
|
return TRUE;
|
|
}
|
|
size_t length = 1;
|
|
for (int j = 0; j < dimensions; j++) length *= shape[j];
|
|
for (int j = 0; j < length; j++) {
|
|
if (head->result.array_literal.integer_literals[j] == values[j]) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_integer_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j],
|
|
head->result.array_literal.integer_literals[j]
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_array_float_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
float *values,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
static const ArrayType array_type = ARRAY_FLOAT;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_array_type(test, result, i, array_type, shape, dimensions)) {
|
|
return TRUE;
|
|
}
|
|
size_t length = 1;
|
|
for (int j = 0; j < dimensions; j++) length *= shape[j];
|
|
for (int j = 0; j < length; j++) {
|
|
if (
|
|
fabsf(
|
|
head->result.array_literal.float_literals[j] - values[j]
|
|
) >= FLOAT_TEST_PRECISION
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_float_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j],
|
|
head->result.array_literal.float_literals[j]
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_array_double_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
double *values,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
static const ArrayType array_type = ARRAY_DOUBLE;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_array_type(test, result, i, array_type, shape, dimensions)) {
|
|
return TRUE;
|
|
}
|
|
size_t length = 1;
|
|
for (int j = 0; j < dimensions; j++) length *= shape[j];
|
|
for (int j = 0; j < length; j++) {
|
|
if (
|
|
fabs(
|
|
head->result.array_literal.float_literals[j] - values[j]
|
|
) >= FLOAT_TEST_PRECISION
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_float_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j],
|
|
head->result.array_literal.float_literals[j]
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_array_string_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
ArrayStringValues *values,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
static const ArrayType array_type = ARRAY_STRING;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_array_type(test, result, i, array_type, shape, dimensions)) {
|
|
return TRUE;
|
|
}
|
|
size_t length = 1;
|
|
for (int j = 0; j < dimensions; j++) length *= shape[j];
|
|
for (int j = 0; j < length; j++) {
|
|
if (
|
|
head->result.array_literal.string_literals[j].length ==
|
|
values[j].length
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_integer_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j].length,
|
|
head->result.array_literal.string_literals[j].length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (
|
|
strncmp(
|
|
head->result.array_literal.string_literals[j].value,
|
|
values[j].value,
|
|
values[j].length
|
|
)
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_string_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j].length,
|
|
values[j].value,
|
|
head->result.array_literal.string_literals[j].value
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_array_boolean_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
Boolean *values,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
static const ArrayType array_type = ARRAY_BOOLEAN;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_array_type(test, result, i, array_type, shape, dimensions)) {
|
|
return TRUE;
|
|
}
|
|
size_t length = 1;
|
|
for (int j = 0; j < dimensions; j++) length *= shape[j];
|
|
for (int j = 0; j < length; j++) {
|
|
if (head->result.array_literal.boolean_literals[j] == values[j]) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
array_element_boolean_should_be(
|
|
i + 1,
|
|
j,
|
|
array_type,
|
|
values[j]
|
|
)
|
|
);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static Boolean test_array_token_string_value(
|
|
LexerTest *test,
|
|
LexerResult result,
|
|
size_t i,
|
|
void *values,
|
|
size_t *shape,
|
|
size_t dimensions
|
|
) {
|
|
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 *shape,
|
|
size_t dimensions
|
|
) {
|
|
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 *shape,
|
|
size_t dimensions
|
|
) {
|
|
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 *shape,
|
|
size_t dimensions
|
|
) {
|
|
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
|
|
) {
|
|
static const TokenType token_type = TOKEN_TYPE_TUPLE;
|
|
LexerTokenResult *head = get_token(result.result, i);
|
|
if (test_token_type(test, result, i, token_type)) {
|
|
return TRUE;
|
|
} if (head->result.type_tuple.input_length != input_length) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
token_length_should_be(
|
|
i + 1,
|
|
token_type,
|
|
input_length,
|
|
head->result.type_tuple.input_length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (head->result.type_tuple.output_length != output_length) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
token_length_should_be(
|
|
i + 1,
|
|
token_type,
|
|
output_length,
|
|
head->result.type_tuple.output_length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} for (int j = 0; j < input_length; j++) {
|
|
if (
|
|
head->result.type_tuple.input_identifiers[j].length ==
|
|
input_values[j].length
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
type_tuple_element_integer_should_be(
|
|
i + 1,
|
|
j,
|
|
input_values[j].length,
|
|
head->result.type_tuple.input_identifiers[j].length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (
|
|
strncmp(
|
|
head->result.type_tuple.input_identifiers[j].name,
|
|
input_values[j].value,
|
|
input_values[j].length
|
|
)
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
type_tuple_element_string_should_be(
|
|
i + 1,
|
|
j,
|
|
input_values[j].length,
|
|
input_values[j].value,
|
|
head->result.type_tuple.input_identifiers[j].name
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (!head->result.type_tuple.input_identifiers[j].is_literal) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
type_tuple_element_boolean_should_be(i + 1, j, TRUE)
|
|
);
|
|
return TRUE;
|
|
}
|
|
} for (int j = 0; j < output_length; j++) {
|
|
if (
|
|
head->result.type_tuple.output_identifiers[j].length ==
|
|
output_values[j].length
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
type_tuple_element_integer_should_be(
|
|
i + 1,
|
|
j,
|
|
output_values[j].length,
|
|
head->result.type_tuple.output_identifiers[j].length
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (
|
|
strncmp(
|
|
head->result.type_tuple.output_identifiers[j].name,
|
|
output_values[j].value,
|
|
input_values[j].length
|
|
)
|
|
) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
type_tuple_element_string_should_be(
|
|
i + 1,
|
|
j,
|
|
output_values[j].length,
|
|
output_values[j].value,
|
|
head->result.type_tuple.output_identifiers[j].name
|
|
)
|
|
);
|
|
return TRUE;
|
|
} if (!head->result.type_tuple.output_identifiers[j].is_literal) {
|
|
logic_fail_test(
|
|
test,
|
|
result,
|
|
type_tuple_element_boolean_should_be(i + 1, j, TRUE)
|
|
);
|
|
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, 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;
|
|
}
|