Restructure

This commit is contained in:
Krzosa Karol
2022-05-31 18:29:35 +02:00
parent 20accf8293
commit 0360086bab
7 changed files with 516 additions and 479 deletions

View File

@@ -13,6 +13,10 @@ if_stmt :: (cond: int): type
return CONSTANT
for_stmt :: ()
for i := 0, i + 10, i
pass
add_10 :: (size: int): int
add_20 :: (new_size: int): int
return 20

View File

@@ -1,11 +1,3 @@
#include "base.cpp"
#include "base_unicode.cpp"
#include "new_lex.cpp"
#include "new_ast.cpp"
#include "new_parse.cpp"
#include "typechecking.cpp"
#include "ccodegen.cpp"
/*
-------------------------------------------------------------------------------
@@ -41,6 +33,7 @@ For now I don't thing it should be overloadable.
@todo
[ ] - For loop
[ ] - More operators
[ ] - Init statements
[ ] - Fixing access to constants, in C we cant have constants inside of structs / functions so we need to rewrite the tree
[ ] - Default values in structs??? Should compound stmts bring values from default values?? Maybe not? Whats the alternative
@@ -73,7 +66,14 @@ For now I don't thing it should be overloadable.
*/
#include "base.cpp"
#include "base_unicode.cpp"
#include "new_lex.cpp"
#include "new_ast.cpp"
#include "new_parse.cpp"
#include "typecheck.h"
#include "typecheck.cpp"
#include "ccodegen.cpp"
int main(){
test_os_memory();

View File

@@ -11,6 +11,7 @@ Intern_String keyword_return;
Intern_String keyword_if;
Intern_String keyword_else;
Intern_String keyword_for;
Intern_String keyword_pass;
Intern_String keyword_cast;
Intern_String keyword_enum;
@@ -59,6 +60,7 @@ struct Parse_Ctx:Lexer{
keyword_cast = intern("cast"_s);
keyword_return = intern("return"_s);
keyword_if = intern("if"_s);
keyword_pass = intern("pass"_s);
keyword_else = intern("else"_s);
keyword_for = intern("for"_s);
keyword_enum = intern_string(&interns, "enum"_s);
@@ -98,6 +100,7 @@ enum Ast_Kind: U32{
AST_IF_NODE,
AST_RETURN,
AST_BLOCK,
AST_PASS,
AST_LAMBDA,
AST_LAMBDA_ARG,
AST_ENUM,
@@ -410,6 +413,12 @@ ast_for(Token *pos, Ast_Expr *init, Ast_Expr *cond, Ast_Expr *iter, Ast_Block *b
return result;
}
function Ast_Pass *
ast_pass(Token *pos){
AST_NEW(Pass, PASS, pos, AST_STMT);
return result;
}
function Ast_Return *
ast_return(Token *pos, Ast_Expr *expr){
AST_NEW(Return, RETURN, pos, AST_STMT);

View File

@@ -207,8 +207,11 @@ parse_block(){
if(token_match_keyword(keyword_return)){
Ast_Expr *expr = 0;
if(!token_is_scope()) expr = parse_expr();
Ast_Return *return_stmt = ast_return(token, expr);
stmts.add(return_stmt);
stmts.add(ast_return(token, expr));
}
else if(token_match_keyword(keyword_pass)){
stmts.add(ast_pass(token));
}
else if(token_match_keyword(keyword_for)){
@@ -225,7 +228,8 @@ parse_block(){
}
}
stmts.add(ast_for(token, actual_init, cond, iter, parse_block()));
Ast_Block *for_block = parse_block();
stmts.add(ast_for(token, actual_init, cond, iter, for_block));
}
else if(token_match_keyword(keyword_if)){

View File

@@ -1,268 +1 @@
enum Ast_Resolved_Type_Kind{
TYPE_NONE,
TYPE_NULL,
TYPE_COMPLETING,
TYPE_INCOMPLETE,
TYPE_INT,
TYPE_BOOL,
TYPE_UNSIGNED,
TYPE_STRING,
TYPE_VOID,
TYPE_POINTER,
TYPE_ARRAY,
TYPE_LAMBDA,
TYPE_STRUCT,
TYPE_UNION,
TYPE_ENUM,
TYPE_TYPE,
};
const char *type_names[] = {
"[Invalid Ast_Resolved_Type]",
"[Null]",
"[Completing]",
"[Incomplete]",
"[Int]",
"[Bool]",
"[Unsigned]",
"[String]",
"[Void]",
"[Pointer]",
"[Array]",
"[Lambda]",
"[Struct]",
"[Union]",
"[Enum]",
"[Type]",
};
struct Ast_Resolved_Member{
Ast_Resolved_Type *type;
Intern_String name;
U64 offset;
};
struct Ast_Resolved_Type{
Ast_Resolved_Type_Kind kind;
SizeU size;
SizeU align;
Ast *ast;
union{
Ast_Resolved_Type *base;
struct{
Ast_Resolved_Type *base;
SizeU size;
}arr;
struct{
Array<Ast_Resolved_Member> members;
}agg;
struct{
Ast_Resolved_Type *ret;
Array<Ast_Resolved_Type *> args;
}func;
};
};
const SizeU pointer_size = sizeof(SizeU);
const SizeU pointer_align = __alignof(SizeU);
global Ast_Resolved_Type type__null = {TYPE_NULL};
global Ast_Resolved_Type type__void = {TYPE_VOID};
global Ast_Resolved_Type type__int = {TYPE_INT, sizeof(int), __alignof(int)};
global Ast_Resolved_Type type__unsigned = {TYPE_INT, sizeof(unsigned), __alignof(unsigned)};
global Ast_Resolved_Type type__string = {TYPE_STRING, sizeof(String), __alignof(String)};
global Ast_Resolved_Type type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)};
global Ast_Resolved_Type type__type = {TYPE_TYPE};
global Ast_Resolved_Type *type_type = &type__type;
global Ast_Resolved_Type *type_void = &type__void;
global Ast_Resolved_Type *type_int = &type__int;
global Ast_Resolved_Type *type_unsigned = &type__unsigned;
global Ast_Resolved_Type *type_string = &type__string;
global Ast_Resolved_Type *type_bool = &type__bool;
global Ast_Resolved_Type *type_null = &type__null;
force_inline B32 is_string(Ast_Resolved_Type *type){return type->kind == TYPE_STRING;}
force_inline B32 is_int(Ast_Resolved_Type *type){return type->kind == TYPE_INT;}
force_inline B32 is_struct(Ast_Resolved_Type *type){return type->kind == TYPE_STRUCT;}
force_inline B32 is_array(Ast_Resolved_Type *type){return type->kind == TYPE_ARRAY;}
force_inline B32 is_enum(Ast_Resolved_Type *type){return type->kind == TYPE_ENUM;}
force_inline B32 is_pointer(Ast_Resolved_Type *type){return type->kind == TYPE_POINTER;}
function Ast_Resolved_Type *
type_new(Allocator *allocator, Ast_Resolved_Type_Kind kind, SizeU size, SizeU align){
Ast_Resolved_Type *result = exp_alloc_type(allocator, Ast_Resolved_Type, AF_ZeroMemory);
result->kind = kind;
result->size = size;
result->align = align;
return result;
}
function Ast_Resolved_Type *
type_copy(Allocator *a, Ast_Resolved_Type *type){
Ast_Resolved_Type *result = exp_alloc_type(a, Ast_Resolved_Type);
memory_copy(result, type, sizeof(Ast_Resolved_Type));
return result;
}
function Ast_Resolved_Type *
type_pointer(Ast_Resolved_Type *base){
Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, (void *)base);
if(!result){
result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align);
result->base = base;
map_insert(&pctx->type_map, base, result);
}
assert(result->kind == TYPE_POINTER);
return result;
}
function Ast_Resolved_Type *
type_array(Ast_Resolved_Type *base, SizeU size){
U64 hash = hash_mix(hash_ptr(base), hash_u64(size));
Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash);
if(result){
assert(result->kind == TYPE_ARRAY);
assert(result->arr.size == size);
assert(result->arr.base == base);
return result;
}
result = type_new(pctx->perm, TYPE_ARRAY, pointer_size, pointer_align);
result->arr.base = base;
result->arr.size = size;
map_insert(&pctx->type_map, hash, result);
return result;
}
function Ast_Resolved_Type *
type_lambda(Ast *ast, Ast_Resolved_Type *ret, Array<Ast_Resolved_Type *> args){
U64 hash = hash_ptr(ret);
For(args) hash = hash_mix(hash, hash_ptr(it));
Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash);
if(result){
assert(result->kind == TYPE_LAMBDA);
assert(result->func.ret == ret);
assert(result->func.args.len == args.len);
return result;
}
result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align);
result->ast = ast;
result->func.ret = ret;
result->func.args = args.tight_copy(pctx->perm);
map_insert(&pctx->type_map, hash, result);
return result;
}
function Ast_Resolved_Type *
type_enum(Ast_Enum *ast){
Ast_Resolved_Type *type = resolve_typespec(ast->typespec, AST_CAN_BE_NULL);
if(!type) {
type = type_int;
}
Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align);
result->base = type;
result->ast = ast;
return result;
}
function Ast_Resolved_Type *
type_incomplete(Ast *ast){
Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_INCOMPLETE, 0, 0);
result->ast = ast;
return result;
}
function void
type_struct_complete(Ast_Resolved_Type *type, Ast_Struct *node){
// @todo: compute size, alignement, offset !!!
// @note: resolve all the struct members first
type->kind = TYPE_COMPLETING;
Scratch scratch;
Array<Ast_Resolved_Member> members = {scratch};
For(node->members){
Operand op = resolve_binding(it);
Intern_String name = ast_get_name(it);
sym_new_resolved(SYM_VAR, name, op.type, {}, it);
members.add({op.type, name});
}
type->agg.members = members.tight_copy(pctx->perm);
type->kind = TYPE_STRUCT;
// @note: resolve constant members after the struct got resolved
// this way we avoid a problem where we start resolving the function
// and this function has parameter of type parent struct
// which is being resolved right now, cyclic dependency happens.
// constants arent required to make struct work
For(node->const_members){
Operand op = resolve_binding(it);
Intern_String name = ast_get_name(it);
sym_new_resolved(SYM_CONST, name, op.type, op.value, it);
}
}
function Ast_Resolved_Type *
type_struct(Ast_Struct *agg){
Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_STRUCT, 0, 0);
result->ast = agg;
type_struct_complete(result, agg);
return result;
}
function void
type_complete(Ast_Resolved_Type *type){
if(type->kind == TYPE_COMPLETING){
parsing_error(type->ast->pos, "Cyclic type dependency");
}
else if(type->kind != TYPE_INCOMPLETE){
return;
}
Ast_Struct *node = (Ast_Struct *)type->ast;
type_struct_complete(type, node);
pctx->resolving_package->ordered.add((Ast_Named *)node->parent);
}
function void
test_types(){
Scratch scratch;
Parse_Ctx ctx = {};
ctx.init(scratch, scratch);
pctx = &ctx;
Ast_Resolved_Type *array_type1 = type_array(type_int, 32);
Ast_Resolved_Type *array_type2 = type_array(type_int, 32);
Ast_Resolved_Type *array_type3 = type_array(type_int, 48);
assert(array_type1 == array_type2);
assert(array_type2 != array_type3);
Ast_Resolved_Type *pointer_type1 = type_pointer(type_int);
Ast_Resolved_Type *pointer_type2 = type_pointer(type_int);
assert(pointer_type2 == pointer_type1);
Ast_Resolved_Type *pointer_type3 = type_pointer(pointer_type1);
Ast_Resolved_Type *pointer_type4 = type_pointer(pointer_type2);
assert(pointer_type3 != pointer_type1);
assert(pointer_type3 == pointer_type4);
Array<Ast_Resolved_Type*> types = {scratch};
types.add(type_array(type_int, 32));
Ast_Resolved_Type *func_type1 = type_lambda(0, types[0], types);
Ast_Resolved_Type *func_type2 = type_lambda(0, types[0], types);
assert(func_type1 == func_type2);
Array<Ast_Resolved_Type*> types2 = {scratch};
{
types2.add(type_array(type_int, 32));
types2.add(type_int);
}
types.add(type_int);
Ast_Resolved_Type *func_type3 = type_lambda(0, types[0], types);
Ast_Resolved_Type *func_type4 = type_lambda(0, types[0], types2);
assert(func_type1 != func_type3);
assert(func_type3 == func_type4);
}

View File

@@ -1,187 +1,6 @@
#define CASE(kind,type) case AST_##kind: { Ast_##type *node = (Ast_##type *)ast;
#define BREAK() } break
enum Sym_Kind{
SYM_NONE,
SYM_CONST,
SYM_VAR,
};
enum Sym_State{
SYM_NOT_RESOLVED,
SYM_RESOLVING,
SYM_RESOLVED,
};
#define VALUE_FIELDS \
S64 int_val; \
Intern_String intern_val; \
Ast_Resolved_Type *type_val;
#define INLINE_VALUE_FIELDS union{Value value; union{VALUE_FIELDS};}
union Value{VALUE_FIELDS};
struct Sym{
Intern_String name;
Sym_Kind kind;
Sym_State state;
Ast *ast;
Ast_Resolved_Type *type;
INLINE_VALUE_FIELDS;
};
struct Operand{
Ast_Resolved_Type *type;
bool is_const;
INLINE_VALUE_FIELDS;
};
struct Typecheck_Ctx{ // @todo
Ast_Resolved_Type *required_type;
Sym *const_sym;
B32 expr_can_be_null;
};
enum{AST_CANT_BE_NULL = 0, AST_CAN_BE_NULL = 1};
function Ast_Resolved_Type *resolve_typespec(Ast_Expr *ast, B32 ast_can_be_null = AST_CANT_BE_NULL);
function Sym *resolve_name(Token *pos, Intern_String name);
function Operand resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *compound_required_type = 0, Sym *const_sym = 0);
function Operand resolve_binding(Ast *ast, Sym *sym = 0);
global Ast_Named empty_decl = {};
function void
sym_insert(Sym *sym){
U64 hash = hash_string(sym->name.s);
Sym *is_sym = (Sym *)map_get(&pctx->syms, hash);
if(is_sym){
parsing_error(sym->ast->pos, "Symbol with name: [%s] defined multiple times", sym->name.s.str);
}
if(pctx->scope > 0){
pctx->local_syms.add(sym);
}
map_insert(&pctx->syms, hash, sym);
}
function Sym *
sym_get(Intern_String name){
Sym *result = (Sym *)map_get(&pctx->syms, hash_string(name.s));
return result;
}
function S64
scope_open(){
S64 local_sym_count = pctx->local_syms.len;
pctx->scope++;
return local_sym_count;
}
function void
scope_close(S64 local_sym_count){
pctx->scope--;
assert(pctx->scope >= 0);
for(S64 i = local_sym_count; i < pctx->local_syms.len; i++){
Sym *it = pctx->local_syms.data[i];
void *removed = map_remove(&pctx->syms, hash_string(it->name.s));
assert(removed);
}
pctx->local_syms.len = local_sym_count;
}
function void
sym_associate(Ast *ast, Sym *sym){
assert(ast);
assert(sym);
map_insert(&pctx->resolved, ast, sym);
}
function Sym *
sym_new(Sym_Kind kind, Intern_String name, Ast *ast, B32 associate = true){
Sym *result = exp_alloc_type(pctx->perm, Sym, AF_ZeroMemory);
result->name = name;
result->kind = kind;
result->ast = ast;
if(associate) sym_associate(ast, result);
return result;
}
function Sym *
sym_new_resolved(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast, B32 associate = true){
Sym *result = sym_new(kind, name, ast, associate);
result->type = type;
result->state = SYM_RESOLVED;
result->value = value;
return result;
}
function Sym *
resolved_get(Ast *ast){
Sym *result = (Sym *)map_get(&pctx->resolved, ast);
assert(result);
return result;
}
#include "new_type.cpp"
function Ast_Resolved_Type *
resolved_type_get(Ast_Expr *ast){
Sym *result = resolved_get(ast);
assert(result->type == type_type);
assert(result->type);
return result->type_val;
}
function Sym *
sym_type(Ast *ast, Ast_Resolved_Type *type, Intern_String name = {}, B32 associate = true){
Value value; value.type_val = type;
Sym *result = sym_new_resolved(SYM_CONST, name, type_type, value, ast, associate);
return result;
}
function Sym *
sym_insert(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast){
Sym *sym = sym_new_resolved(kind, name, type, value, ast);
sym_insert(sym);
return sym;
}
function void
sym_insert_builtin_type(String name, Ast_Resolved_Type *type){
Intern_String string = intern_string(&pctx->interns, name);
Sym *sym = sym_type(&empty_decl, type, string, false);
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);
Value val; val.int_val = 1;
Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false);
sym_insert(sym);
}
{
Intern_String string = intern_string(&pctx->interns, "false"_s);
Value val; val.int_val = 0;
Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false);
sym_insert(sym);
}
{
Intern_String string = intern_string(&pctx->interns, "null"_s);
Value val; val.int_val = 0;
Sym *sym = sym_new_resolved(SYM_CONST, string, type_null, val, &empty_decl, false);
sym_insert(sym);
}
}
function Ast_Resolved_Type *
resolve_typespec(Ast_Expr *ast, B32 ast_can_be_null){
if(ast_can_be_null && ast == 0) return 0;
@@ -236,9 +55,8 @@ resolve_stmt(Ast *ast, Ast_Resolved_Type *ret){
CASE(BINARY, Binary){
switch(node->op){
case TK_Comma:{
Operand left = resolve_expr(node->left); // needs to be lvalue
unused(left);
case TK_Colon:{
// Operand left = resolve_expr(node->left); // needs to be lvalue
Operand right = resolve_expr(node->right);
assert(node->left->kind == AST_IDENT);
Ast_Atom *atom = (Ast_Atom *)node->left; // @todo use left operand
@@ -265,22 +83,6 @@ resolve_stmt(Ast *ast, Ast_Resolved_Type *ret){
}
}
function Operand
operand(Sym *sym){
Operand result = {};
result.type = sym->type;
result.is_const = sym->kind == SYM_CONST ? true : false;
result.value = sym->value;
return result;
}
function Operand
operand_type(Ast_Resolved_Type *type){
Operand result = {type_type, true};
result.type_val = type;
return result;
}
function Operand
require_const_int(Ast_Expr *expr, B32 ast_can_be_null){
Operand op = resolve_expr(expr);
@@ -839,3 +641,40 @@ parse_file(){
return result;
}
function void
test_types(){
Scratch scratch;
Parse_Ctx ctx = {};
ctx.init(scratch, scratch);
pctx = &ctx;
Ast_Resolved_Type *array_type1 = type_array(type_int, 32);
Ast_Resolved_Type *array_type2 = type_array(type_int, 32);
Ast_Resolved_Type *array_type3 = type_array(type_int, 48);
assert(array_type1 == array_type2);
assert(array_type2 != array_type3);
Ast_Resolved_Type *pointer_type1 = type_pointer(type_int);
Ast_Resolved_Type *pointer_type2 = type_pointer(type_int);
assert(pointer_type2 == pointer_type1);
Ast_Resolved_Type *pointer_type3 = type_pointer(pointer_type1);
Ast_Resolved_Type *pointer_type4 = type_pointer(pointer_type2);
assert(pointer_type3 != pointer_type1);
assert(pointer_type3 == pointer_type4);
Array<Ast_Resolved_Type*> types = {scratch};
types.add(type_array(type_int, 32));
Ast_Resolved_Type *func_type1 = type_lambda(0, types[0], types);
Ast_Resolved_Type *func_type2 = type_lambda(0, types[0], types);
assert(func_type1 == func_type2);
Array<Ast_Resolved_Type*> types2 = {scratch};
{
types2.add(type_array(type_int, 32));
types2.add(type_int);
}
types.add(type_int);
Ast_Resolved_Type *func_type3 = type_lambda(0, types[0], types);
Ast_Resolved_Type *func_type4 = type_lambda(0, types[0], types2);
assert(func_type1 != func_type3);
assert(func_type3 == func_type4);
}

448
typecheck.h Normal file
View File

@@ -0,0 +1,448 @@
//-----------------------------------------------------------------------------
// Resolved Types
//-----------------------------------------------------------------------------
enum Ast_Resolved_Type_Kind{
TYPE_NONE,
TYPE_NULL,
TYPE_COMPLETING,
TYPE_INCOMPLETE,
TYPE_INT,
TYPE_BOOL,
TYPE_UNSIGNED,
TYPE_STRING,
TYPE_VOID,
TYPE_POINTER,
TYPE_ARRAY,
TYPE_LAMBDA,
TYPE_STRUCT,
TYPE_UNION,
TYPE_ENUM,
TYPE_TYPE,
};
const char *type_names[] = {
"[Invalid Ast_Resolved_Type]",
"[Null]",
"[Completing]",
"[Incomplete]",
"[Int]",
"[Bool]",
"[Unsigned]",
"[String]",
"[Void]",
"[Pointer]",
"[Array]",
"[Lambda]",
"[Struct]",
"[Union]",
"[Enum]",
"[Type]",
};
struct Ast_Resolved_Member{
Ast_Resolved_Type *type;
Intern_String name;
U64 offset;
};
struct Ast_Resolved_Type{
Ast_Resolved_Type_Kind kind;
SizeU size;
SizeU align;
Ast *ast;
union{
Ast_Resolved_Type *base;
struct{
Ast_Resolved_Type *base;
SizeU size;
}arr;
struct{
Array<Ast_Resolved_Member> members;
}agg;
struct{
Ast_Resolved_Type *ret;
Array<Ast_Resolved_Type *> args;
}func;
};
};
//-----------------------------------------------------------------------------
// Type globals
//-----------------------------------------------------------------------------
const SizeU pointer_size = sizeof(SizeU);
const SizeU pointer_align = __alignof(SizeU);
global Ast_Resolved_Type type__null = {TYPE_NULL};
global Ast_Resolved_Type type__void = {TYPE_VOID};
global Ast_Resolved_Type type__int = {TYPE_INT, sizeof(int), __alignof(int)};
global Ast_Resolved_Type type__unsigned = {TYPE_INT, sizeof(unsigned), __alignof(unsigned)};
global Ast_Resolved_Type type__string = {TYPE_STRING, sizeof(String), __alignof(String)};
global Ast_Resolved_Type type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)};
global Ast_Resolved_Type type__type = {TYPE_TYPE};
global Ast_Resolved_Type *type_type = &type__type;
global Ast_Resolved_Type *type_void = &type__void;
global Ast_Resolved_Type *type_int = &type__int;
global Ast_Resolved_Type *type_unsigned = &type__unsigned;
global Ast_Resolved_Type *type_string = &type__string;
global Ast_Resolved_Type *type_bool = &type__bool;
global Ast_Resolved_Type *type_null = &type__null;
//-----------------------------------------------------------------------------
// Symbols
//-----------------------------------------------------------------------------
enum Sym_Kind{
SYM_NONE,
SYM_CONST,
SYM_VAR,
};
enum Sym_State{
SYM_NOT_RESOLVED,
SYM_RESOLVING,
SYM_RESOLVED,
};
#define VALUE_FIELDS \
S64 int_val; \
Intern_String intern_val; \
Ast_Resolved_Type *type_val;
#define INLINE_VALUE_FIELDS union{Value value; union{VALUE_FIELDS};}
union Value{VALUE_FIELDS};
struct Sym{
Intern_String name;
Sym_Kind kind;
Sym_State state;
Ast *ast;
Ast_Resolved_Type *type;
INLINE_VALUE_FIELDS;
};
struct Operand{
Ast_Resolved_Type *type;
bool is_const;
INLINE_VALUE_FIELDS;
};
struct Typecheck_Ctx{ // @todo
Ast_Resolved_Type *required_type;
Sym *const_sym;
B32 expr_can_be_null;
};
enum{AST_CANT_BE_NULL = 0, AST_CAN_BE_NULL = 1};
function Ast_Resolved_Type *resolve_typespec(Ast_Expr *ast, B32 ast_can_be_null = AST_CANT_BE_NULL);
function Sym *resolve_name(Token *pos, Intern_String name);
function Operand resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *compound_required_type = 0, Sym *const_sym = 0);
function Operand resolve_binding(Ast *ast, Sym *sym = 0);
global Ast_Named empty_decl = {};
//-----------------------------------------------------------------------------
// Symbol constructors and utils
//-----------------------------------------------------------------------------
function void
sym_insert(Sym *sym){
U64 hash = hash_string(sym->name.s);
Sym *is_sym = (Sym *)map_get(&pctx->syms, hash);
if(is_sym) parsing_error(sym->ast->pos, "Symbol with name: [%s] defined multiple times", sym->name.s.str);
if(pctx->scope > 0) pctx->local_syms.add(sym);
map_insert(&pctx->syms, hash, sym);
}
function Sym *
sym_get(Intern_String name){
Sym *result = (Sym *)map_get(&pctx->syms, hash_string(name.s));
return result;
}
function S64
scope_open(){
S64 local_sym_count = pctx->local_syms.len;
pctx->scope++;
return local_sym_count;
}
function void
scope_close(S64 local_sym_count){
pctx->scope--;
assert(pctx->scope >= 0);
for(S64 i = local_sym_count; i < pctx->local_syms.len; i++){
Sym *it = pctx->local_syms.data[i];
void *removed = map_remove(&pctx->syms, hash_string(it->name.s));
assert(removed);
}
pctx->local_syms.len = local_sym_count;
}
function void
sym_associate(Ast *ast, Sym *sym){
assert(ast);
assert(sym);
map_insert(&pctx->resolved, ast, sym);
}
function Sym *
sym_new(Sym_Kind kind, Intern_String name, Ast *ast, B32 associate = true){
Sym *result = exp_alloc_type(pctx->perm, Sym, AF_ZeroMemory);
result->name = name;
result->kind = kind;
result->ast = ast;
if(associate) sym_associate(ast, result);
return result;
}
function Sym *
sym_new_resolved(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast, B32 associate = true){
Sym *result = sym_new(kind, name, ast, associate);
result->type = type;
result->state = SYM_RESOLVED;
result->value = value;
return result;
}
function Sym *
resolved_get(Ast *ast){
Sym *result = (Sym *)map_get(&pctx->resolved, ast);
assert(result);
return result;
}
function Ast_Resolved_Type *
resolved_type_get(Ast_Expr *ast){
Sym *result = resolved_get(ast);
assert(result->type == type_type);
assert(result->type);
return result->type_val;
}
function Sym *
sym_type(Ast *ast, Ast_Resolved_Type *type, Intern_String name = {}, B32 associate = true){
Value value; value.type_val = type;
Sym *result = sym_new_resolved(SYM_CONST, name, type_type, value, ast, associate);
return result;
}
function Sym *
sym_insert(Sym_Kind kind, Intern_String name, Ast_Resolved_Type *type, Value value, Ast *ast){
Sym *sym = sym_new_resolved(kind, name, type, value, ast);
sym_insert(sym);
return sym;
}
function void
sym_insert_builtin_type(String name, Ast_Resolved_Type *type){
Intern_String string = intern_string(&pctx->interns, name);
Sym *sym = sym_type(&empty_decl, type, string, false);
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);
Value val; val.int_val = 1;
Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false);
sym_insert(sym);
}
{
Intern_String string = intern_string(&pctx->interns, "false"_s);
Value val; val.int_val = 0;
Sym *sym = sym_new_resolved(SYM_CONST, string, type_bool, val, &empty_decl, false);
sym_insert(sym);
}
{
Intern_String string = intern_string(&pctx->interns, "null"_s);
Value val; val.int_val = 0;
Sym *sym = sym_new_resolved(SYM_CONST, string, type_null, val, &empty_decl, false);
sym_insert(sym);
}
}
//-----------------------------------------------------------------------------
// Operands
//-----------------------------------------------------------------------------
function Operand
operand(Sym *sym){
Operand result = {};
result.type = sym->type;
result.is_const = sym->kind == SYM_CONST ? true : false;
result.value = sym->value;
return result;
}
function Operand
operand_type(Ast_Resolved_Type *type){
Operand result = {type_type, true};
result.type_val = type;
return result;
}
//-----------------------------------------------------------------------------
// Type constructors and utillities
//-----------------------------------------------------------------------------
force_inline B32 is_string(Ast_Resolved_Type *type){return type->kind == TYPE_STRING;}
force_inline B32 is_int(Ast_Resolved_Type *type){return type->kind == TYPE_INT;}
force_inline B32 is_struct(Ast_Resolved_Type *type){return type->kind == TYPE_STRUCT;}
force_inline B32 is_array(Ast_Resolved_Type *type){return type->kind == TYPE_ARRAY;}
force_inline B32 is_enum(Ast_Resolved_Type *type){return type->kind == TYPE_ENUM;}
force_inline B32 is_pointer(Ast_Resolved_Type *type){return type->kind == TYPE_POINTER;}
function Ast_Resolved_Type *
type_new(Allocator *allocator, Ast_Resolved_Type_Kind kind, SizeU size, SizeU align){
Ast_Resolved_Type *result = exp_alloc_type(allocator, Ast_Resolved_Type, AF_ZeroMemory);
result->kind = kind;
result->size = size;
result->align = align;
return result;
}
function Ast_Resolved_Type *
type_copy(Allocator *a, Ast_Resolved_Type *type){
Ast_Resolved_Type *result = exp_alloc_type(a, Ast_Resolved_Type);
memory_copy(result, type, sizeof(Ast_Resolved_Type));
return result;
}
function Ast_Resolved_Type *
type_pointer(Ast_Resolved_Type *base){
Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, (void *)base);
if(!result){
result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align);
result->base = base;
map_insert(&pctx->type_map, base, result);
}
assert(result->kind == TYPE_POINTER);
return result;
}
function Ast_Resolved_Type *
type_array(Ast_Resolved_Type *base, SizeU size){
U64 hash = hash_mix(hash_ptr(base), hash_u64(size));
Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash);
if(result){
assert(result->kind == TYPE_ARRAY);
assert(result->arr.size == size);
assert(result->arr.base == base);
return result;
}
result = type_new(pctx->perm, TYPE_ARRAY, pointer_size, pointer_align);
result->arr.base = base;
result->arr.size = size;
map_insert(&pctx->type_map, hash, result);
return result;
}
function Ast_Resolved_Type *
type_lambda(Ast *ast, Ast_Resolved_Type *ret, Array<Ast_Resolved_Type *> args){
U64 hash = hash_ptr(ret);
For(args) hash = hash_mix(hash, hash_ptr(it));
Ast_Resolved_Type *result = (Ast_Resolved_Type *)map_get(&pctx->type_map, hash);
if(result){
assert(result->kind == TYPE_LAMBDA);
assert(result->func.ret == ret);
assert(result->func.args.len == args.len);
return result;
}
result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align);
result->ast = ast;
result->func.ret = ret;
result->func.args = args.tight_copy(pctx->perm);
map_insert(&pctx->type_map, hash, result);
return result;
}
function Ast_Resolved_Type *
type_enum(Ast_Enum *ast){
Ast_Resolved_Type *type = resolve_typespec(ast->typespec, AST_CAN_BE_NULL);
if(!type) {
type = type_int;
}
Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align);
result->base = type;
result->ast = ast;
return result;
}
/*
2022.05.31 - Global scope structs vs nested structs
Structs exist in 2 variants, the global scope structs are a bit different
then scoped structs. They startout incomplete and when some operation
requires the actual struct size, alignment, field access etc. then it
should call complete_type. It resolves all the children, calculates the
size and makes sure there are no cyclic dependencies. This is require for
correct behaviour of order independent structs. If someone just wants a pointer
to that struct we don't need to complete the type, we know how large a pointer is.
This allows us to have cyclic dependency that is a pointer. Cause we know how large pointer is.
*/
function Ast_Resolved_Type *
type_incomplete(Ast *ast){
Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_INCOMPLETE, 0, 0);
result->ast = ast;
return result;
}
function void
type_struct_complete(Ast_Resolved_Type *type, Ast_Struct *node){
// @todo: compute size, alignement, offset !!!
// @note: resolve all the struct members first
type->kind = TYPE_COMPLETING;
Scratch scratch;
Array<Ast_Resolved_Member> members = {scratch};
For(node->members){
Operand op = resolve_binding(it);
Intern_String name = ast_get_name(it);
sym_new_resolved(SYM_VAR, name, op.type, {}, it);
members.add({op.type, name});
}
type->agg.members = members.tight_copy(pctx->perm);
type->kind = TYPE_STRUCT;
/*
@note: resolve constant members after the struct got resolved
this way we avoid a problem where we start resolving the function
and this function has parameter of type parent struct
which is being resolved right now, cyclic dependency happens.
constants arent required to make struct work
*/
For(node->const_members){
Operand op = resolve_binding(it);
Intern_String name = ast_get_name(it);
sym_new_resolved(SYM_CONST, name, op.type, op.value, it);
}
}
function Ast_Resolved_Type *
type_struct(Ast_Struct *agg){
Ast_Resolved_Type *result = type_new(pctx->perm, TYPE_STRUCT, 0, 0);
result->ast = agg;
type_struct_complete(result, agg);
return result;
}
function void
type_complete(Ast_Resolved_Type *type){
if(type->kind == TYPE_COMPLETING){
parsing_error(type->ast->pos, "Cyclic type dependency");
}
else if(type->kind != TYPE_INCOMPLETE){
return;
}
Ast_Struct *node = (Ast_Struct *)type->ast;
type_struct_complete(type, node);
pctx->resolving_package->ordered.add((Ast_Named *)node->parent);
}