Files
corelang/core_ast.cpp
2022-09-29 14:16:44 +02:00

682 lines
16 KiB
C++

//-----------------------------------------------------------------------------
// AST
//-----------------------------------------------------------------------------
enum Ast_Kind: U32{
AST_NONE,
AST_FILE_NAMESPACE,
AST_MODULE_NAMESPACE,
AST_MODULE,
AST_FILE,
AST_SCOPE,
AST_VALUE,
AST_CAST,
AST_IDENT,
AST_INDEX,
AST_UNARY,
AST_BINARY,
AST_CALL_ITEM,
AST_CALL,
AST_CONSTANT_ASSERT,
AST_RUNTIME_ASSERT,
AST_SIZE_OF,
AST_LENGTH_OF,
AST_ALIGN_OF,
AST_SWITCH,
AST_SWITCH_CASE,
AST_VAR_UNPACK,
AST_BREAK,
AST_COMPOUND,
AST_TYPE,
AST_VAR,
AST_CONST,
AST_POINTER,
AST_ARRAY,
AST_FOR,
AST_IF,
AST_IF_NODE,
AST_RETURN,
AST_BLOCK,
AST_PASS,
AST_LAMBDA,
AST_LAMBDA_EXPR,
AST_LAMBDA_ARG,
AST_ENUM,
AST_ENUM_MEMBER,
AST_STRUCT,
};
typedef U32 Ast_Flag;
enum{
AST_EXPR = bit_flag(1),
AST_STMT = bit_flag(2),
AST_STRICT = bit_flag(3),
AST_AGGREGATE = bit_flag(4),
AST_AGGREGATE_CHILD = bit_flag(5),
AST_ANY_VARGS = bit_flag(6),
AST_ATOM = bit_flag(7),
AST_FOREIGN = bit_flag(8),
AST_DECL = bit_flag(9),
AST_GLOBAL = bit_flag(10),
AST_FLAG = bit_flag(11),
AST_VAR_IS_CONST = bit_flag(12),
AST_OPERATOR_OVERLOAD = bit_flag(13),
};
struct Ast{
U64 di; // Debug id, shouldn't ever be used in the program
Token *pos;
Ast_Kind kind;
Ast_Scope *parent_scope;
Ast_Flag flags;
};
struct Ast_Type;
struct Ast_Expr:Ast{
Ast_Type *resolved_type;
union{
Ast_Type *index_original_type;
Ast_Type *cast_after_type;
Ast_Type *dot_access_step_resolution;
};
};
struct Ast_Atom: Ast_Expr{
Ast_Decl *resolved_decl;
INLINE_VALUE_FIELDS;
};
typedef U32 Ast_Call_Item_Flag;
enum{
CALL_INDEX = bit_flag(1),
CALL_NAME = bit_flag(2),
CALL_DOT_ANY = bit_flag(3),
CALL_INCLUDED= bit_flag(4),
};
struct Ast_Call_Item: Ast_Expr{
Ast_Call_Item_Flag call_flags;
S32 resolved_index;
Ast_Expr *item;
union {
Ast_Atom *name;
Ast_Expr *index;
};
Intern_String resolved_name;
};
struct Ast_Lambda;
struct Ast_Call: Ast_Expr{
union{
Ast_Expr *name;
Ast_Expr *typespec;
};
Array<Ast_Call_Item *> exprs;
Ast_Decl *resolved_decl;
};
struct Ast_Var_Unpack: Ast_Expr{
Array<Ast_Decl *> vars;
Ast_Expr *expr;
};
struct Ast_Unary: Ast_Expr{
Token_Kind op;
Ast_Expr *expr;
U64 padding[2]; // For folding constants into atoms
};
struct Ast_Index: Ast_Expr{
Ast_Expr *expr;
Ast_Expr *index;
};
struct Ast_Binary: Ast_Expr{
Token_Kind op;
Ast_Expr *left;
Ast_Expr *right;
Ast_Type *before_type;
};
struct Ast_Builtin: Ast_Expr{
Ast_Expr *expr;
Intern_String assert_message;
U64 padding[1]; // For folding constants into atoms
};
// Problem: We are parsing out of order, in the middle of parsing a function
// we can jump down a different function, we cant therfore use global map.
// Each scope needs to have it's checked locals list. To lookup syms we need to
// look into global scope and to the locals list.
//
struct Ast_Return: Ast{
Ast_Type *resolved_type;
Array<Ast_Expr *> expr;
};
struct Ast_If_Node: Ast{
Ast_Expr *expr ;
Ast_Scope *scope;
Ast_Binary*init;
};
struct Ast_If: Ast{
Array<Ast_If_Node *> ifs;
};
struct Ast_Pass: Ast{};
struct Ast_Break: Ast{};
struct Ast_For: Ast{
Ast_Expr *init;
Ast_Expr *cond;
Ast_Expr *iter;
Ast_Scope *scope;
Ast_Decl *array_traversal_var;
bool is_array_traversal;
bool is_also_slice_traversal;
};
struct Ast_Lambda : Ast_Expr {
Array<Ast_Decl *> args;
Array<Ast_Expr *> ret;
Ast_Scope *scope;
};
struct Ast_Array: Ast_Expr{
Ast_Expr *base;
Ast_Expr *expr;
U64 padding[2];
};
struct Ast_Switch_Case: Ast{
Array<Ast_Expr *> labels;
Ast_Scope *scope;
B32 fallthrough;
};
struct Ast_Switch: Ast{
Ast_Expr *value;
Array<Ast_Switch_Case *> cases;
Ast_Scope *default_scope;
};
/*
How does current declaration order resolver works:
* First we put all the global declarations into the global scope (when parsing) all unresolved
* All the types are declared INCOMPLETE and RESOLVED
* We descent the tree by resolving each of the named declarations, we resolve by their name
When we start resolving we set RESOLVING flag and when we complete RESOLVED flag
and put into ordered list
* When we meet a symbol (named declaration) while descending the tree,
we resolve that symbol instead before resolving current declaration.
* When we meet a declaration that requires size of a type - field access, var assignment,
we need to call "complete_type", it sets COMPLETING flag.
This call resolves all the dependencies of that type,
sets size of type and marks it as COMPLETE and puts into ordered list.
If it detects COMPLETING while
resolving, we got a circular dependency. That might happen when we have
that struct without pointer inside itself.
*/
struct Ast_Scope: Ast{
String debug_name; // Dont use
Array<Ast_Scope *> implicit_imports;
Array<Ast_Decl *> decls;
Array<Ast *> stmts;
U32 visit_id;
U32 scope_id;
Ast_Scope *file; // Self referential for scope and module
Ast_Module *module;
};
enum Ast_Module_State{
MODULE_REGISTERED,
MODULE_PARSED,
MODULE_RESOLVED,
};
struct Ast_Module: Ast_Scope{
Ast_Module_State state;
String absolute_base_folder;
String absolute_file_path;
Array<Ast_File *> all_loaded_files;
};
struct Ast_File: Ast_Scope{
String absolute_file_path;
String filecontent;
};
enum Ast_Decl_State{
DECL_NOT_RESOLVED,
DECL_RESOLVED,
DECL_RESOLVED_TYPE,
DECL_RESOLVING,
};
struct Ast_Decl: Ast{
Ast_Decl_State state;
Intern_String name;
Intern_String unique_name; // For code generation, currently only present on lambdas
Ast_Scope *scope;
Ast_Expr *typespec;
union{
Ast_Expr *expr;
Ast_Lambda *lambda;
};
INLINE_VALUE_FIELDS;
};
//-----------------------------------------------------------------------------
// AST Constructors beginning with expressions
//-----------------------------------------------------------------------------
#define AST_NEW(T,ikind,ipos,iflags) \
Ast_##T *result = exp_alloc_type(pctx->perm, Ast_##T, AF_ZeroMemory);\
result->flags = iflags; \
result->kind = AST_##ikind; \
result->parent_scope = pctx->currently_parsed_scope; \
result->pos = ipos; \
result->di = ++pctx->unique_ids
#define ast_new(T,kind,pos,flags) (T *)_ast_new(sizeof(T), kind, pos, flags)
function Ast *
_ast_new(SizeU size, Ast_Kind kind, Token *pos, Ast_Flag flags = 0){
Ast *result = (Ast *)exp_alloc(pctx->perm, size, AF_ZeroMemory);
result->flags = flags;
result->kind = kind;
result->parent_scope = pctx->currently_parsed_scope;
result->pos = pos;
result->di = ++pctx->unique_ids;
return result;
}
function Ast_Atom *
ast_str(Token *pos, Intern_String string){
AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM);
result->type = untyped_string;
result->intern_val = string;
return result;
}
function Ast_Atom *
ast_ident(Token *pos, Intern_String string){
AST_NEW(Atom, IDENT, pos, AST_EXPR | AST_ATOM);
result->intern_val = string;
return result;
}
function Ast_Atom *
ast_bool(Token *pos, B32 bool_val){
AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM);
result->bool_val = bool_val;
result->type = untyped_bool;
return result;
}
function Ast_Atom *
ast_float(Token *pos, F64 value){
AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM);
result->type = untyped_float;
result->f64_val = value;
return result;
}
function Ast_Atom *
ast_int(Token *pos, BigInt val){
AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM);
result->type = untyped_int;
result->big_int_val = bigint_copy(pctx->perm, &val);
return result;
}
function Ast_Atom *
ast_int(Token *pos, U64 value){
return ast_int(pos, bigint_u64(value));
}
function Ast_Expr *
ast_expr_binary(Ast_Expr *left, Ast_Expr *right, Token *op){
AST_NEW(Binary, BINARY, op, AST_EXPR);
result->op = op->kind;
result->left = left;
result->right = right;
return result;
}
function Ast_Call *
ast_call(Token *pos, Ast_Expr *name, Array<Ast_Call_Item *> exprs){
// name here specifies also typespec for compound expressions !
AST_NEW(Call, CALL, pos, AST_EXPR);
result->name = name;
result->exprs = exprs.tight_copy(pctx->perm);
return result;
}
function Ast_Call_Item *
ast_call_item(Token *pos, Ast_Atom *name, Ast_Expr *index, Ast_Expr *item){
AST_NEW(Call_Item, CALL_ITEM, pos, AST_EXPR);
result->name = name;
result->item = item;
result->index = index;
return result;
}
function Ast_Expr *
ast_expr_unary(Token *pos, Token_Kind op, Ast_Expr *expr){
AST_NEW(Unary, UNARY, pos, AST_EXPR);
result->flags = AST_EXPR;
result->expr = expr;
result->op = op;
return result;
}
function Ast_Expr *
ast_expr_index(Token *pos, Ast_Expr *expr, Ast_Expr *index){
AST_NEW(Index, INDEX, pos, AST_EXPR);
result->flags = AST_EXPR;
result->expr = expr;
result->index = index;
return result;
}
function Ast_Lambda *
ast_lambda(Token *pos, Array<Ast_Decl *> params, Array<Ast_Expr *> ret, Ast_Scope *scope){
AST_NEW(Lambda, LAMBDA_EXPR, pos, AST_EXPR);
result->flags = AST_EXPR;
result->args = params.tight_copy(pctx->perm);
result->ret = ret.tight_copy(pctx->perm);
result->scope = scope;
return result;
}
function Ast_If *
ast_if(Token *pos, Array<Ast_If_Node *> ifs){
AST_NEW(If, IF, pos, AST_STMT);
result->ifs = ifs.tight_copy(pctx->perm);
return result;
}
function Ast_For *
ast_for(Token *pos, Ast_Expr *init, Ast_Expr *cond, Ast_Expr *iter, Ast_Scope *scope){
AST_NEW(For, FOR, pos, AST_STMT);
result->init = init;
result->cond = cond;
result->iter = iter;
result->scope = scope;
return result;
}
function Ast_Pass *
ast_pass(Token *pos){
AST_NEW(Pass, PASS, pos, AST_STMT);
return result;
}
function Ast_Break *
ast_break(Token *pos){
AST_NEW(Break, BREAK, pos, AST_STMT);
return result;
}
function Ast_Return *
ast_return(Token *pos, Array<Ast_Expr *> expr){
AST_NEW(Return, RETURN, pos, AST_STMT);
if(expr.len){
For(expr) assert(is_flag_set(it->flags, AST_EXPR));
result->expr = expr.tight_copy(pctx->perm);
}
return result;
}
function Ast_If_Node *
ast_if_node(Token *pos, Ast_Expr *init, Ast_Expr *expr, Ast_Scope *scope){
AST_NEW(If_Node, IF_NODE, pos, AST_STMT);
result->scope = scope;
result->expr = expr;
result->init = (Ast_Binary *)init;
if(result->init) {
assert(init->kind == AST_VAR);
}
return result;
}
function Ast_Array *
ast_array(Token *pos, Ast_Expr *expr){
AST_NEW(Array, ARRAY, pos, AST_EXPR);
result->expr = expr;
return result;
}
function Ast_Scope *
begin_decl_scope(Allocator *scratch, Token *pos){
AST_NEW(Scope, SCOPE, pos, AST_DECL);
result->decls = {scratch};
result->file = pctx->currently_parsed_file;
result->module = pctx->currently_parsed_file->module;
result->scope_id = pctx->scope_ids++;
result->debug_name = pos->string;
assert(result->file);
pctx->currently_parsed_scope = result;
return result;
}
function void
finalize_decl_scope(Ast_Scope *scope){
scope->decls = scope->decls.tight_copy(pctx->perm);
pctx->currently_parsed_scope = scope->parent_scope;
}
function Ast_Scope *
begin_stmt_scope(Allocator *scratch, Token *pos){
AST_NEW(Scope, SCOPE, pos, AST_STMT);
result->stmts = {scratch};
result->decls = {pctx->heap};
result->file = pctx->currently_parsed_file;
result->module = pctx->currently_parsed_file->module;
result->scope_id = pctx->scope_ids++;
result->debug_name = pos->string;
assert(result->file);
pctx->currently_parsed_scope = result;
return result;
}
function void
finalize_stmt_scope(Ast_Scope *scope){
scope->stmts = scope->stmts.tight_copy(pctx->perm);
pctx->currently_parsed_scope = scope->parent_scope;
}
function Ast_Decl *
ast_struct(Token *pos, Ast_Scope *scope){
AST_NEW(Decl, STRUCT, pos, AST_DECL | AST_AGGREGATE);
result->scope = scope;
return result;
}
function Ast_Decl *
ast_enum(Token *pos, Ast_Expr *typespec, Ast_Scope *scope){
AST_NEW(Decl, ENUM, pos, AST_DECL | AST_AGGREGATE);
result->scope = scope;
result->typespec = typespec;
return result;
}
function Ast_Decl *
ast_var(Token *pos, Ast_Expr *typespec, Intern_String name, Ast_Expr *expr){
AST_NEW(Decl, VAR, pos, AST_DECL);
result->name = name;
result->typespec = typespec;
result->expr = expr;
return result;
}
function Ast_Decl *
ast_const(Token *pos, Intern_String name, Value value){
AST_NEW(Decl, CONST, pos, AST_DECL);
result->value = value;
result->name = name;
return result;
}
function Ast_Decl *
ast_const(Token *pos, Intern_String name, Ast_Expr *expr){
AST_NEW(Decl, CONST, pos, AST_DECL);
result->expr = expr;
result->name = name;
return result;
}
function Ast_Decl *
ast_type(Token *pos, Intern_String name, Ast_Type *type){
AST_NEW(Decl, TYPE, pos, AST_DECL);
result->type = type_type;
result->type_val = type;
result->name = name;
return result;
}
function Ast_Scope *
ast_decl_scope(Token *pos, Allocator *allocator, Ast_File *file){
AST_NEW(Scope, SCOPE, pos, AST_DECL);
result->decls = {allocator};
result->file = file;
result->scope_id = pctx->scope_ids++;
assert(result->file);
return result;
}
function Ast_Decl *
ast_file_namespace(Token *pos, Ast_File *file, Intern_String name){
AST_NEW(Decl, FILE_NAMESPACE, pos, AST_DECL);
result->scope = file;
result->name = name;
return result;
}
function Ast_Decl *
ast_module_namespace(Token *pos, Ast_Module *module, Intern_String name){
AST_NEW(Decl, MODULE_NAMESPACE, pos, AST_DECL);
result->scope = module;
result->name = name;
return result;
}
function Ast_Builtin *
ast_runtime_assert(Token *pos, Ast_Expr *expr, Intern_String message){
AST_NEW(Builtin, RUNTIME_ASSERT, pos, AST_EXPR);
result->expr = expr;
result->assert_message = message;
return result;
}
function Ast_Builtin *
ast_constant_assert(Token *pos, Ast_Expr *expr, Intern_String message){
AST_NEW(Builtin, CONSTANT_ASSERT, pos, AST_EXPR);
result->expr = expr;
result->assert_message = message;
return result;
}
function Ast_Builtin *
ast_sizeof(Token *pos, Ast_Expr *expr){
AST_NEW(Builtin, SIZE_OF, pos, AST_EXPR);
result->expr = expr;
return result;
}
function Ast_Builtin *
ast_len(Token *pos, Ast_Expr *expr){
AST_NEW(Builtin, LENGTH_OF, pos, AST_EXPR);
result->expr = expr;
return result;
}
function Ast_Builtin *
ast_alignof(Token *pos, Ast_Expr *expr){
AST_NEW(Builtin, ALIGN_OF, pos, AST_EXPR);
result->expr = expr;
return result;
}
function Ast_Var_Unpack *
ast_var_unpack(Token *pos, Array<Ast_Decl *> vars, Ast_Expr *expr){
AST_NEW(Var_Unpack, VAR_UNPACK, pos, AST_STMT);
result->vars = vars.tight_copy(pctx->perm);
result->expr = expr;
return result;
}
//-----------------------------------------------------------------------------
// Value
//-----------------------------------------------------------------------------
function Value
value_bool(B32 v){
Value value;
value.bool_val = v;
value.type = untyped_bool;
return value;
}
function Value
value_int(BigInt b){
Value value;
value.big_int_val = b;
value.type = untyped_int;
return value;
}
function Value
value_int(S64 s64){
Value value;
value.type = untyped_int;
bigint_init_signed(&value.big_int_val, s64);
return value;
}
function Value
value_float(F64 b){
Value value;
value.f64_val = b;
value.type = untyped_float;
return value;
}
function Value
value_float(BigInt a){
Value value;
value.f64_val = bigint_as_float(&a);
value.type = untyped_float;
return value;
}
function B32
is_ident(Ast *ast){
B32 result = ast->kind == AST_IDENT;
return result;
}
function B32
is_binary(Ast *ast){
B32 result = ast->kind == AST_BINARY;
return result;
}
function B32
is_atom(Ast *ast){
B32 result = is_flag_set(ast->flags, AST_ATOM);
return result;
}