2024-04-05 18:16:58 +01:00
|
|
|
#include "compiler.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2024-04-07 03:50:29 +01:00
|
|
|
size_t string_hash_djb2(const char* value)
|
2024-04-05 18:16:58 +01:00
|
|
|
{
|
|
|
|
size_t hash = 5381;
|
2024-04-07 03:50:29 +01:00
|
|
|
for (size_t i = 0; value[i] != '\0'; ++i)
|
|
|
|
hash = ((hash << 5) + hash) + (unsigned char)value[i];
|
2024-04-05 18:16:58 +01:00
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
int string_symbol_map_construct(StringSymbolMap* map)
|
|
|
|
{
|
|
|
|
const size_t start_capacity = 4;
|
|
|
|
*map = (StringSymbolMap) {
|
|
|
|
.data = malloc(sizeof(StringSymbolMap) * start_capacity),
|
|
|
|
.length = 0,
|
|
|
|
.capacity = start_capacity,
|
|
|
|
};
|
|
|
|
if (map->data == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void string_symbol_map_destroy(StringSymbolMap* map)
|
|
|
|
{
|
|
|
|
if (map->data != NULL) {
|
|
|
|
free(map->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Symbol* string_symbol_map_get(const StringSymbolMap* map, size_t key_hash)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < map->length; ++i) {
|
|
|
|
if (map->data[i].key_hash == key_hash) {
|
|
|
|
return &map->data[i].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int string_symbol_map_set(StringSymbolMap* map, size_t key_hash, Symbol value)
|
|
|
|
{
|
|
|
|
Symbol* existing = string_symbol_map_get(map, key_hash);
|
|
|
|
if (existing != NULL) {
|
|
|
|
*existing = value;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (map->length + 1 >= map->capacity) {
|
|
|
|
map->capacity *= 2;
|
|
|
|
StringSymbolMapEntry* data
|
|
|
|
= realloc(map->data, sizeof(StringSymbolMapEntry) * map->capacity);
|
|
|
|
if (data == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
map->data = data;
|
|
|
|
}
|
|
|
|
map->data[map->length] = (StringSymbolMapEntry) { key_hash, value };
|
|
|
|
map->length += 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-04-05 23:43:45 +01:00
|
|
|
int statements_symbols_vec_construct(StatementsSymbolsVec* vec)
|
|
|
|
{
|
2024-04-06 16:29:21 +01:00
|
|
|
const size_t start_capacity = 4;
|
|
|
|
StatementsSymbols* data
|
|
|
|
= malloc(start_capacity * sizeof(StatementsSymbols));
|
|
|
|
if (data == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*vec = (StatementsSymbolsVec) {
|
|
|
|
.data = data,
|
|
|
|
.length = 0,
|
|
|
|
.capacity = start_capacity,
|
|
|
|
};
|
|
|
|
return 0;
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void statements_symbols_vec_destroy(StatementsSymbolsVec* vec)
|
|
|
|
{
|
2024-04-06 16:29:21 +01:00
|
|
|
if (vec->data != NULL) {
|
|
|
|
free(vec->data);
|
|
|
|
}
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
2024-04-06 16:29:21 +01:00
|
|
|
int statements_symbols_vec_push(
|
|
|
|
StatementsSymbolsVec* vec, StatementsSymbols pair)
|
2024-04-05 23:43:45 +01:00
|
|
|
{
|
2024-04-06 16:29:21 +01:00
|
|
|
if (vec->length + 1 > vec->capacity) {
|
|
|
|
StatementsSymbols* new_data
|
|
|
|
= realloc(vec->data, vec->capacity * 2 * sizeof(StatementsSymbols));
|
|
|
|
if (new_data == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
vec->capacity *= 2;
|
|
|
|
vec->data = new_data;
|
|
|
|
}
|
|
|
|
vec->data[vec->length] = pair;
|
|
|
|
vec->length += 1;
|
|
|
|
return 0;
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void symbol_table_construct(SymbolTable* table, SymbolTable* parent)
|
|
|
|
{
|
2024-04-06 16:29:21 +01:00
|
|
|
*table = (SymbolTable) {
|
|
|
|
.parent = parent,
|
|
|
|
.map = { 0 },
|
|
|
|
};
|
|
|
|
string_symbol_map_construct(&table->map);
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void symbol_table_destroy(SymbolTable* table)
|
|
|
|
{
|
2024-04-06 16:29:21 +01:00
|
|
|
string_symbol_map_destroy(&table->map);
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
2024-04-07 03:50:29 +01:00
|
|
|
Symbol* symbol_table_resolve(SymbolTable* table, const char* id)
|
|
|
|
{
|
|
|
|
size_t hash = string_hash_djb2(id);
|
|
|
|
return symbol_table_resolve_hash(table, hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
Symbol* symbol_table_resolve_hash(SymbolTable* table, size_t hash)
|
|
|
|
{
|
|
|
|
Symbol* found = string_symbol_map_get(&table->map, hash);
|
|
|
|
if (found != NULL) {
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
if (table->parent != NULL) {
|
|
|
|
return symbol_table_resolve_hash(table->parent, hash);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Symbol* symbol_table_resolve_local(SymbolTable* table, const char* id)
|
|
|
|
{
|
|
|
|
return string_symbol_map_get(&table->map, string_hash_djb2(id));
|
|
|
|
}
|
|
|
|
|
|
|
|
void symbol_table_define(SymbolTable* table, const char* id, Symbol symbol)
|
|
|
|
{
|
|
|
|
size_t hash = string_hash_djb2(id);
|
|
|
|
string_symbol_map_set(&table->map, hash, symbol);
|
|
|
|
}
|
|
|
|
|
2024-04-05 18:16:58 +01:00
|
|
|
void checker_construct(Checker* checker)
|
|
|
|
{
|
|
|
|
*checker = (Checker) {
|
|
|
|
.failed = false,
|
2024-04-07 03:50:29 +01:00
|
|
|
.head_table = { 0 },
|
2024-04-06 16:29:21 +01:00
|
|
|
.statements_symbols = { 0 },
|
2024-04-05 18:16:58 +01:00
|
|
|
};
|
2024-04-07 03:50:29 +01:00
|
|
|
symbol_table_construct(&checker->head_table, NULL);
|
2024-04-06 16:29:21 +01:00
|
|
|
statements_symbols_vec_construct(&checker->statements_symbols);
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
2024-04-06 16:29:21 +01:00
|
|
|
void checker_destroy(Checker* checker) { (void)checker; }
|
2024-04-05 18:16:58 +01:00
|
|
|
|
|
|
|
bool checker_failed(const Checker* checker) { return checker->failed; }
|
|
|
|
|
2024-04-05 23:43:45 +01:00
|
|
|
CheckerResult checker_result(Checker* checker)
|
|
|
|
{
|
2024-04-06 16:29:21 +01:00
|
|
|
return (CheckerResult) {
|
|
|
|
.head_table = checker->head_table,
|
|
|
|
.statements_symbols = checker->statements_symbols,
|
|
|
|
};
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
2024-04-07 03:50:29 +01:00
|
|
|
void checker_check_statements(
|
|
|
|
Checker* checker, SymbolTable* table, ASTNode* node)
|
2024-04-05 18:16:58 +01:00
|
|
|
{
|
2024-04-07 03:50:29 +01:00
|
|
|
SymbolTable statements_table;
|
|
|
|
symbol_table_construct(&statements_table, table);
|
2024-04-05 23:43:45 +01:00
|
|
|
for (size_t i = 0; i < node->statements.length; ++i) {
|
2024-04-07 03:50:29 +01:00
|
|
|
checker_check_statement(
|
|
|
|
checker, &statements_table, node->statements.data[i]);
|
2024-04-06 16:29:21 +01:00
|
|
|
}
|
|
|
|
statements_symbols_vec_push(&checker->statements_symbols,
|
|
|
|
(StatementsSymbols) {
|
2024-04-07 03:50:29 +01:00
|
|
|
.table = statements_table,
|
2024-04-06 16:29:21 +01:00
|
|
|
.statements = node,
|
|
|
|
});
|
2024-04-05 23:43:45 +01:00
|
|
|
}
|
|
|
|
|
2024-04-06 16:29:21 +01:00
|
|
|
void checker_check_statement(
|
|
|
|
Checker* checker, SymbolTable* table, ASTNode* node)
|
2024-04-05 23:43:45 +01:00
|
|
|
{
|
2024-04-06 16:29:21 +01:00
|
|
|
switch (node->node_type) {
|
|
|
|
case ASTNodeType_Block:
|
2024-04-07 03:50:29 +01:00
|
|
|
checker_check_statements(checker, table, node);
|
|
|
|
break;
|
2024-04-06 16:29:21 +01:00
|
|
|
case ASTNodeType_If:
|
2024-04-07 03:50:29 +01:00
|
|
|
checker_check_expr(checker, table, node->if_node.condition);
|
|
|
|
checker_check_statements(checker, table, node->if_node.truthy);
|
|
|
|
if (node->if_node.falsy) {
|
|
|
|
checker_check_statements(checker, table, node->if_node.falsy);
|
|
|
|
}
|
|
|
|
break;
|
2024-04-06 16:29:21 +01:00
|
|
|
case ASTNodeType_Loop:
|
2024-04-07 03:50:29 +01:00
|
|
|
checker_check_statements(checker, table, node->loop_node.body);
|
|
|
|
break;
|
2024-04-06 16:29:21 +01:00
|
|
|
case ASTNodeType_Assign:
|
2024-04-07 03:50:29 +01:00
|
|
|
checker_check_assign(checker, table, node);
|
|
|
|
break;
|
2024-04-06 16:29:21 +01:00
|
|
|
case ASTNodeType_Let:
|
2024-04-07 03:50:29 +01:00
|
|
|
checker_check_let(checker, table, node);
|
|
|
|
break;
|
2024-04-06 16:29:21 +01:00
|
|
|
case ASTNodeType_Break:
|
2024-04-07 03:50:29 +01:00
|
|
|
break;
|
2024-04-06 16:29:21 +01:00
|
|
|
case ASTNodeType_Fn:
|
2024-04-07 03:50:29 +01:00
|
|
|
checker_check_fn(checker, table, node);
|
|
|
|
break;
|
2024-04-06 16:29:21 +01:00
|
|
|
case ASTNodeType_Return:
|
2024-04-07 03:50:29 +01:00
|
|
|
if (node->return_node.value != NULL) {
|
|
|
|
checker_check_expr(checker, table, node->return_node.value);
|
|
|
|
}
|
2024-04-06 16:29:21 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2024-04-05 18:16:58 +01:00
|
|
|
}
|
2024-04-07 03:50:29 +01:00
|
|
|
|
|
|
|
void checker_check_assign(Checker* checker, SymbolTable* table, ASTNode* node)
|
|
|
|
{
|
|
|
|
checker_check_expr(checker, table, node->assign_node.subject);
|
|
|
|
switch (node->assign_node.subject->node_type) {
|
|
|
|
case ASTNodeType_Id:
|
|
|
|
case ASTNodeType_Index:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
checker->failed = true;
|
|
|
|
print_error("checker: cannot assign to expression", node->pos);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
checker_check_expr(checker, table, node->assign_node.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void checker_check_let(Checker* checker, SymbolTable* table, ASTNode* node)
|
|
|
|
{
|
|
|
|
Symbol* found
|
|
|
|
= symbol_table_resolve_local(table, node->let_node.id->id_value);
|
|
|
|
if (found != NULL) {
|
|
|
|
checker->failed = true;
|
|
|
|
print_error("checker: redefinition", node->pos);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
checker_check_expr(checker, table, node->let_node.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void checker_check_fn(Checker* checker, SymbolTable* table, ASTNode* node) { }
|
|
|
|
|
|
|
|
void checker_check_expr(Checker* checker, SymbolTable* table, ASTNode* node)
|
|
|
|
{
|
|
|
|
switch (node->node_type) {
|
|
|
|
case ASTNodeType_Error:
|
|
|
|
checker->failed = true;
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Id:
|
|
|
|
checker_check_id(checker, table, node);
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Int:
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Group:
|
|
|
|
checker_check_expr(checker, table, node->group_value);
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Block:
|
|
|
|
checker_check_statements(checker, table, node->loop_node.body);
|
|
|
|
break;
|
|
|
|
case ASTNodeType_If:
|
|
|
|
checker_check_expr(checker, table, node->if_node.condition);
|
|
|
|
checker_check_statements(checker, table, node->if_node.truthy);
|
|
|
|
if (node->if_node.falsy != NULL) {
|
|
|
|
checker_check_statements(checker, table, node->if_node.falsy);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Call:
|
|
|
|
checker_check_expr(checker, table, node->call_node.subject);
|
|
|
|
for (size_t i = 0; i < node->call_node.args.length; ++i) {
|
|
|
|
checker_check_expr(
|
|
|
|
checker, table, node->call_node.args.data[i]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Index:
|
|
|
|
checker_check_expr(checker, table, node->index_node.subject);
|
|
|
|
checker_check_expr(checker, table, node->index_node.value);
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Unary:
|
|
|
|
checker_check_expr(checker, table, node->unary_node.subject);
|
|
|
|
break;
|
|
|
|
case ASTNodeType_Binary:
|
|
|
|
checker_check_expr(checker, table, node->binary_node.left);
|
|
|
|
checker_check_expr(checker, table, node->binary_node.right);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
checker->failed = true;
|
|
|
|
print_error("checker: unrecognzed expression", node->pos);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void checker_check_id(Checker* checker, SymbolTable* table, ASTNode* node)
|
|
|
|
{
|
|
|
|
Symbol* found = symbol_table_resolve(table, node->id_value);
|
|
|
|
if (found == NULL) {
|
|
|
|
checker->failed = true;
|
|
|
|
print_error("checker: undefined identifier", node->pos);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
node->id_symbol = found;
|
|
|
|
}
|