Starting from scratch on smaller scale, typechecking global and constant variables, compound expressions for arrays
This commit is contained in:
309
new_resolve.cpp
Normal file
309
new_resolve.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
#define Ast_Begin(kind,type) case kind: { type *node = (type *)ast;
|
||||
#define Ast_End() } break
|
||||
|
||||
enum Sym_Kind{
|
||||
SYM_None,
|
||||
SYM_Type,
|
||||
SYM_Const,
|
||||
SYM_Var,
|
||||
};
|
||||
|
||||
struct Sym{
|
||||
Intern_String name;
|
||||
Sym_Kind kind;
|
||||
Ast_Decl *decl;
|
||||
Type *type;
|
||||
union{
|
||||
S64 int_val;
|
||||
Intern_String intern_val;
|
||||
};
|
||||
};
|
||||
|
||||
struct Operand{
|
||||
Type *type;
|
||||
bool is_const;
|
||||
union {
|
||||
S64 int_val;
|
||||
Intern_String intern_val;
|
||||
};
|
||||
};
|
||||
|
||||
function void
|
||||
sym_insert(Sym *sym){
|
||||
U64 hash = hash_string(sym->name.s);
|
||||
Sym *is_sym = (Sym *)map_get_u64(&pctx->global_syms, hash);
|
||||
if(is_sym){
|
||||
parsing_error(sym->decl->pos, "Symbol with name: [%s] defined multiple times", sym->name.s.str);
|
||||
}
|
||||
|
||||
map_insert_u64(&pctx->global_syms, hash, sym);
|
||||
}
|
||||
|
||||
function Sym *
|
||||
sym_get(Intern_String name){
|
||||
Sym *result = (Sym *)map_get_u64(&pctx->global_syms, hash_string(name.s));
|
||||
return result;
|
||||
}
|
||||
|
||||
function Sym *
|
||||
sym_new(Sym_Kind kind, Intern_String name, Type *type, Ast_Decl *decl){
|
||||
Sym *result = exp_alloc_type(pctx->perm, Sym);
|
||||
result->name = name;
|
||||
result->kind = kind;
|
||||
result->type = type;
|
||||
result->decl = decl;
|
||||
return result;
|
||||
}
|
||||
|
||||
global Ast_Decl empty_decl = {};
|
||||
function void
|
||||
sym_insert_builtin_type(String name, Type *type){
|
||||
Intern_String string = intern_string(&pctx->interns, name);
|
||||
Sym *sym = sym_new(SYM_Type, string, type, &empty_decl);
|
||||
sym_insert(sym);
|
||||
}
|
||||
|
||||
function void
|
||||
sym_insert_builtins(){
|
||||
sym_insert_builtin_type("void"_s, type_void);
|
||||
sym_insert_builtin_type("bool"_s, type_bool);
|
||||
sym_insert_builtin_type("int"_s, type_int);
|
||||
sym_insert_builtin_type("String"_s, type_string);
|
||||
|
||||
{
|
||||
Intern_String string = intern_string(&pctx->interns, "true"_s);
|
||||
Sym *sym = sym_new(SYM_Const, string, type_bool, &empty_decl);
|
||||
sym_insert(sym);
|
||||
}
|
||||
|
||||
{
|
||||
Intern_String string = intern_string(&pctx->interns, "false"_s);
|
||||
Sym *sym = sym_new(SYM_Const, string, type_bool, &empty_decl);
|
||||
sym_insert(sym);
|
||||
}
|
||||
|
||||
{
|
||||
Intern_String string = intern_string(&pctx->interns, "null"_s);
|
||||
Sym *sym = sym_new(SYM_Const, string, type_null, &empty_decl);
|
||||
sym_insert(sym);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function Operand eval_expr(Ast_Expr *ast, Type *compound_required_type = 0);
|
||||
function Type *
|
||||
eval_typespec(Ast_Typespec *ast){
|
||||
if(!ast) return 0;
|
||||
|
||||
switch(ast->kind){
|
||||
Ast_Begin(AK_Typespec_Ident, Ast_Typespec){
|
||||
Sym *type_sym = sym_get(node->name);
|
||||
if(!type_sym){
|
||||
parsing_error(node->pos, "This type is not defined");
|
||||
}
|
||||
if(type_sym->kind != SYM_Type){
|
||||
parsing_error(node->pos, "This identifier is not a type");
|
||||
}
|
||||
return type_sym->type;
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Typespec_Pointer, Ast_Typespec){
|
||||
Type *type = eval_typespec(node->base);
|
||||
Type *result = type_pointer(type);
|
||||
return result;
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Typespec_Array, Ast_Typespec){
|
||||
Type *type = eval_typespec(node->arr.base);
|
||||
Operand expr = eval_expr(node->arr.expr);
|
||||
if(!expr.is_const) parsing_error(node->pos, "Array size is not a constant");
|
||||
if(expr.type != type_int) parsing_error(node->pos, "Array size is expected to be of type int");
|
||||
Type *result = type_array(type, expr.int_val);
|
||||
return result;
|
||||
Ast_End();
|
||||
}
|
||||
invalid_default_case;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function Type *
|
||||
resolve_type_pair(Token *pos, Type *a, Type *b){
|
||||
Type *result = 0;
|
||||
if(!a && b) result = b;
|
||||
else if(a && !b) result = a;
|
||||
else if(!a && !b) parsing_error(pos, "Trying to resolve a type pair where both types are null");
|
||||
else{ // a && b
|
||||
if(b->kind == TYPE_Null) result = a;
|
||||
else if(a->kind == TYPE_Null) result = b;
|
||||
else if(a != b) parsing_error(pos, "Expression and type specification are differing");
|
||||
else result = a; // Types are the same
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function Operand
|
||||
eval_expr(Ast_Expr *ast, Type *exp_compound_type){
|
||||
switch(ast->kind){
|
||||
Ast_Begin(AK_Expr_Int, Ast_Expr){
|
||||
Operand result = {type_int, true, {.int_val=(S64)node->int_val}};
|
||||
return result;
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Expr_Str, Ast_Expr){
|
||||
Operand result = {type_string, true, {.intern_val = node->intern_val}};
|
||||
return result;
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Expr_Compound, Ast_Expr){
|
||||
Type *type = eval_typespec(node->compound.typespec);
|
||||
Type *variable_type = exp_compound_type;
|
||||
if(!type && variable_type) type = variable_type;
|
||||
else if(!variable_type && type);
|
||||
else if(variable_type != type) parsing_error(node->pos, "Variable type different from explicit compound type");
|
||||
|
||||
if(type->kind == TYPE_Array){
|
||||
if(node->compound.exprs.len > type->arr.size) parsing_error(node->pos, "compound statement has too many items for this type");
|
||||
Type *item_type = type->arr.base;
|
||||
|
||||
For(node->compound.exprs){
|
||||
assert(it[0]->kind == AK_Expr_CompoundItem);
|
||||
if(it[0]->compound_item.name) parsing_error(it[0]->pos, "Invalid array indexing in compound expression");
|
||||
if(it[0]->compound_item.index){
|
||||
Operand index_op = eval_expr(it[0]->compound_item.index);
|
||||
if(!index_op.is_const) parsing_error(it[0]->pos, "Index in a compound expression is not a constant");
|
||||
if(index_op.type != type_int) parsing_error(it[0]->pos, "Index should be of type int");
|
||||
if(index_op.int_val > (type->arr.size - 1)) parsing_error(it[0]->pos, "Invalid index in compound expression, larger then type can store");
|
||||
}
|
||||
Operand expr = eval_expr(it[0]->compound_item.item);
|
||||
if(expr.type != item_type) parsing_error(it[0]->pos, "Invalid type of item in compound expression");
|
||||
}
|
||||
}
|
||||
else parsing_error(node->pos, "Invalid compound expression type");
|
||||
|
||||
Operand result = {type, false};
|
||||
return result;
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Expr_Ident, Ast_Expr){
|
||||
Sym *sym = sym_get(node->intern_val);
|
||||
if(!sym){
|
||||
parsing_error(node->pos, "Identifier is undefined");
|
||||
}
|
||||
|
||||
Operand result = {sym->type, sym->kind == SYM_Const ? true : false, {.int_val = sym->int_val}};
|
||||
return result;
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Expr_Cast, Ast_Expr){
|
||||
Operand expr = eval_expr(node->cast.expr);
|
||||
Type *type = eval_typespec(node->cast.typespec);
|
||||
|
||||
if(type == expr.type) return expr;
|
||||
|
||||
else if(expr.type == type_int && type == type_bool){
|
||||
expr.type = type_bool;
|
||||
return expr;
|
||||
}
|
||||
|
||||
else if(expr.type == type_bool && type == type_int){
|
||||
expr.type = type_int;
|
||||
return expr;
|
||||
}
|
||||
|
||||
else if(expr.type == type_null){
|
||||
expr.type = type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
else parsing_error(node->pos, "Failed to cast, incompatible types");
|
||||
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Expr_Binary, Ast_Expr){
|
||||
Operand left = eval_expr(ast->binary.left);
|
||||
Operand right = eval_expr(ast->binary.right);
|
||||
Operand result = {};
|
||||
result.type = resolve_type_pair(node->pos, left.type, right.type);
|
||||
if(left.is_const && right.is_const){
|
||||
result.is_const = true;
|
||||
if(result.type == type_int){
|
||||
switch(node->binary.op){
|
||||
case TK_Add: result.int_val = left.int_val + right.int_val; break;
|
||||
case TK_Sub: result.int_val = left.int_val - right.int_val; break;
|
||||
case TK_Mul: result.int_val = left.int_val * right.int_val; break;
|
||||
case TK_Div: result.int_val = left.int_val / right.int_val; break;
|
||||
invalid_default_case;
|
||||
}
|
||||
}
|
||||
else parsing_error(node->pos, "Arithmetic on type [TODO] is not supported");
|
||||
}
|
||||
|
||||
return result;
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
invalid_default_case;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function void
|
||||
eval_decl(Ast *ast){
|
||||
switch(ast->kind){
|
||||
|
||||
Ast_Begin(AK_Package, Ast_Package){
|
||||
For(node->decls) eval_decl(*it);
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Decl_Var, Ast_Decl){
|
||||
Type *type = eval_typespec(node->var.typespec);
|
||||
Operand expr = eval_expr(node->var.expr, type);
|
||||
Type *resolved_type = resolve_type_pair(node->pos, type, expr.type);
|
||||
|
||||
Sym *sym = sym_new(SYM_Var, node->name, resolved_type, node);
|
||||
sym_insert(sym);
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
Ast_Begin(AK_Decl_Const, Ast_Decl){
|
||||
Type *type = eval_typespec(node->var.typespec);
|
||||
if(type && type->kind == TYPE_Pointer) parsing_error(node->pos, "Const cant be a pointer");
|
||||
Operand expr = eval_expr(node->var.expr);
|
||||
if(!expr.type) parsing_error(node->pos, "Constant value without expression");
|
||||
if(!expr.is_const) parsing_error(node->pos, "Value of constant variable is not a constant expression");
|
||||
Type *resolved_type = resolve_type_pair(node->pos, type, expr.type);
|
||||
|
||||
Sym *sym = sym_new(SYM_Const, node->name, resolved_type, node);
|
||||
if(resolved_type == type_int) sym->int_val = expr.int_val;
|
||||
else if(resolved_type == type_string) sym->intern_val = expr.intern_val;
|
||||
sym_insert(sym);
|
||||
Ast_End();
|
||||
}
|
||||
|
||||
invalid_default_case;
|
||||
}
|
||||
}
|
||||
|
||||
function void
|
||||
test_resolve(){
|
||||
TEST_PARSER();
|
||||
String filename = "test3.kl"_s;
|
||||
String file_content = os_read_file(scratch, filename);
|
||||
lex_restream(&ctx, file_content, filename);
|
||||
Ast_Package *result = parse_file();
|
||||
sym_insert_builtins();
|
||||
eval_decl(result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user