ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

sem.c (7119B)



#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "sem.h"
#include "parser.h"
#include "utils.h"

#define CALLOC_SZ 16
#define BASE_DEPTH 1

static int next_id = 100;

Scope
scope_init(Node* node)
{
	Scope s = (Scope) { .parent = NULL,
		.symbols = (Symbol**)calloc(CALLOC_SZ, sizeof(Symbol*)),
		.children = (Scope**)calloc(CALLOC_SZ, sizeof(Scope*)),
		.cap = CALLOC_SZ,
		.len = 0,
		.ch_cap = CALLOC_SZ,
		.ch_len = 0,
		.depth = BASE_DEPTH,
		.owner = node,
		.id = next_id++ };

	if (s.symbols == NULL) panic_at(node, "scope_init: could not alloc");
	if (s.children == NULL) panic_at(node, "scope_init: could not alloc");
	return s;
}

static Scope*
new_scope_from_scope(Scope* parent_scope, Node* node)
{
	// new scope
	Scope* scope = (Scope*)calloc(1, sizeof(Scope));
	if (scope == NULL) panic_at(node, "new_scope_from_scope: could not alloc");

	scope->id = next_id++;
	scope->owner = node;
	node->scope = scope;

	// init symbols list
	scope->symbols = (Symbol**)calloc(CALLOC_SZ, sizeof(Symbol*));
	if (scope->symbols == NULL) panic_at(node, "new_scope_from_scope: symbols: could not alloc");
	scope->cap = CALLOC_SZ;
	scope->len = 0;

	scope->children = (Scope**)calloc(CALLOC_SZ, sizeof(Scope*));
	if (scope->children == NULL) panic_at(node, "new_scope_from_scope: children: could not alloc");
	scope->ch_cap = CALLOC_SZ;
	scope->ch_len = 0;

	// init parent and depth
	if (parent_scope != NULL) {
		scope->parent = parent_scope;
		scope->depth = parent_scope->depth + 1;
		assert(parent_scope->children != NULL);
		if (parent_scope->ch_len == parent_scope->ch_cap) {
			parent_scope->ch_cap *= 2;
			parent_scope->children = (Scope**)realloc(parent_scope->children, parent_scope->ch_cap * sizeof(Scope*));
			assert(parent_scope->children != NULL && "realloc failed");
		}
		parent_scope->children[parent_scope->ch_len++] = scope;
	} else {
		scope->parent = NULL;
		scope->depth = BASE_DEPTH;
	}

	return scope;
}

static void
add_to_scope(Scope* scope, Symbol* sym)
{
	if (scope->len >= scope->cap) {
		scope->cap *= 2;
		scope->symbols = (Symbol**)realloc(scope->symbols, scope->cap * sizeof(Symbol*));
	}
	scope->symbols[scope->len++] = sym;
}

static void
scope_var(Scope* scope, Ast* ast, Node* node)
{
	const char* var_name = span_str(ast->src, node->data.var_decl.name, (char[IDENTSZ]) { 0 });
	const char* type_name = span_str(ast->src, node->data.var_decl.type->data.ident.name, (char[IDENTSZ]) { 0 });

	Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
	if (sym == NULL) panic_at(node, "scope_var: symbol: could not alloc");

	TypeInfo* type = (TypeInfo*)calloc(1, sizeof(TypeInfo));
	if (type == NULL) panic_at(node, "scope_var: type: could not alloc");

	if (strcmp(type_name, "float") == 0) {
		type->type = SYMTYPE_FLOAT;
	} else if (strcmp(type_name, "int") == 0) {
		type->type = SYMTYPE_INT;
	} else if (strcmp(type_name, "uint") == 0) {
		type->type = SYMTYPE_UINT;

	} else if (strcmp(type_name, "str") == 0) {
		type->type = SYMTYPE_STR;
	} else if (strcmp(type_name, "chr") == 0) {
		type->type = SYMTYPE_CHR;
	} else if (strcmp(type_name, "bool") == 0) {
		type->type = SYMTYPE_BOOL;

	} else if (strcmp(type_name, "struct") == 0) {
		type->type = SYMTYPE_STRUCT;

	} else if (strcmp(type_name, "i8") == 0) {
		type->type = SYMTYPE_I8;
	} else if (strcmp(type_name, "i16") == 0) {
		type->type = SYMTYPE_I16;
	} else if (strcmp(type_name, "i32") == 0) {
		type->type = SYMTYPE_I32;
	} else if (strcmp(type_name, "i64") == 0) {
		type->type = SYMTYPE_I64;
	} else if (strcmp(type_name, "i128") == 0) {
		type->type = SYMTYPE_I128;

	} else if (strcmp(type_name, "u8") == 0) {
		type->type = SYMTYPE_U8;
	} else if (strcmp(type_name, "u16") == 0) {
		type->type = SYMTYPE_U16;
	} else if (strcmp(type_name, "u32") == 0) {
		type->type = SYMTYPE_U32;
	} else if (strcmp(type_name, "u64") == 0) {
		type->type = SYMTYPE_U64;
	} else if (strcmp(type_name, "u128") == 0) {
		type->type = SYMTYPE_U128;

	} else if (strcmp(type_name, "f32") == 0) {
		type->type = SYMTYPE_F32;
	} else if (strcmp(type_name, "f64") == 0) {
		type->type = SYMTYPE_F64;
	} else if (strcmp(type_name, "f128") == 0) {
		type->type = SYMTYPE_F128;

	} else {
		if (type_name[0] >= 'A' && type_name[0] <= 'Z') {
			type->type = SYMTYPE_USER;
		} else {
			panic_at(node, "sem: not yet defined type '%s' for variable '%s'", type_name, var_name);
		}
	}

	sym->name = node->data.var_decl.name;
	sym->decl = node->data.var_decl.init;
	sym->type = type;
	printf("scope_var: adding to scope %s (%s)\n", var_name, type_name);
	add_to_scope(scope, sym);
}

static void
scope_func(Scope* parent_scope, Ast* ast, Node* node)
{
	Scope* scope = new_scope_from_scope(parent_scope, node);
	node->scope = scope;

	for (size_t i = 0; i < node->data.block.len; i++) {
		Node* stmt = node->data.block.stmts[i];
		stmt->scope = scope;
		switch (stmt->type) {
		case NODE_VAR_DECL: {
			scope_var(scope, ast, stmt);
			break;
		}
		default:
			continue;
		}
	}
}

void
scope_program(Scope* scope, Ast* ast)
{
	for (size_t i = 0; i < ast->node->data.program.len; i++) {
		Node* node = ast->node->data.program.decl[i];
		node->scope = scope;
		switch (node->type) {
		case NODE_VAR_DECL:
			scope_var(scope, ast, node);
			break;
		case NODE_FUNCTION_DECL:
			scope_func(/*parent_scope*/ scope, ast, node->data.function_decl.body);
			break;
		default:
			printf("unknown definition at TODO\n");
		}
	}
}

void
scope_print(Scope* scope, Ast* ast)
{
	if (scope == NULL || scope->symbols == NULL) return;

	for (size_t i = 0; i < scope->len; i++) {
		Symbol* sym = scope->symbols[i];
		const char* name = span_str(ast->src, sym->name, (char[IDENTSZ]) { 0 });
		int parent = -1;
		if (scope->parent != NULL) parent = scope->parent->id;
		bool has_owner_node = false;
		if (scope->owner != NULL) has_owner_node = true;
		printf("[depth %d] [id %d] Symbol name `%s` \t of type %s (parent %d, owner %s)\n",
			scope->depth,
			scope->id,
			name,
			type_kind_str(sym->type->type),
			parent,
			has_owner_node ? "yes" : "no");
	}

	if (scope->ch_len == 0) return;

	for (size_t j = 0; j < scope->ch_len; j++) {
		Scope* child_scope = scope->children[j];
		scope_print(child_scope, ast);
	}
}

const char*
type_kind_str(SymbolType t)
{
	static const char* type_strings[] = {
		[SYMTYPE_VOID] = "TYPE_VOID",
		[SYMTYPE_INT] = "TYPE_INT",

		[SYMTYPE_I8] = "TYPE_I8",
		[SYMTYPE_I16] = "TYPE_I16",
		[SYMTYPE_I32] = "TYPE_I32",
		[SYMTYPE_I64] = "TYPE_I64",
		[SYMTYPE_I128] = "TYPE_I128",
		[SYMTYPE_UINT] = "TYPE_UINT",

		[SYMTYPE_U8] = "TYPE_U8",
		[SYMTYPE_U16] = "TYPE_U16",
		[SYMTYPE_U32] = "TYPE_U32",
		[SYMTYPE_U64] = "TYPE_U64",
		[SYMTYPE_U128] = "TYPE_U128",

		[SYMTYPE_F32] = "TYPE_F32",
		[SYMTYPE_F64] = "TYPE_F64",
		[SYMTYPE_F128] = "TYPE_F128",
		[SYMTYPE_FLOAT] = "TYPE_FLOAT",

		[SYMTYPE_STR] = "TYPE_STR",
		[SYMTYPE_CHR] = "TYPE_CHR",
		[SYMTYPE_BOOL] = "TYPE_BOOL",
		[SYMTYPE_STRUCT] = "TYPE_STRUCT",
		[SYMTYPE_USER] = "TYPE_USER",
		[SYMTYPE_FUNC] = "TYPE_FUNC",
		[SYMTYPE_TODO] = "TYPE_TODO",
	};

	return (t >= SYMTYPE_VOID && t <= SYMTYPE_TODO) ? type_strings[t] : "UNKNOWN_TYPE_KIND";
}