573 lines
13 KiB
C++
573 lines
13 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_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_ITEM_INCLUDED = 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),
|
|
};
|
|
|
|
struct Ast{
|
|
U64 id;
|
|
Token *pos;
|
|
|
|
Ast_Kind kind;
|
|
Ast_Scope *parent_scope;
|
|
Ast_Flag flags;
|
|
};
|
|
|
|
struct Ast_Type;
|
|
struct Ast_Expr:Ast{
|
|
};
|
|
|
|
#define VALUE_FIELDS \
|
|
Ast_Type *type; \
|
|
union{ \
|
|
bool bool_val; \
|
|
F64 f64_val; \
|
|
Intern_String intern_val; \
|
|
BigInt big_int_val;\
|
|
Ast_Type *type_val; \
|
|
};
|
|
#define INLINE_VALUE_FIELDS union{Value value; struct{VALUE_FIELDS};}
|
|
struct Value{VALUE_FIELDS};
|
|
// BigInt big_int_val;
|
|
|
|
struct Ast_Atom: Ast_Expr{
|
|
Ast_Decl *resolved_decl;
|
|
INLINE_VALUE_FIELDS;
|
|
};
|
|
|
|
struct Ast_Call_Item: Ast_Expr{
|
|
Ast_Atom *name; // for calls only name, for compounds name | index
|
|
Ast_Expr *item;
|
|
Ast_Expr *index;
|
|
};
|
|
|
|
struct Ast_Call: Ast_Expr{
|
|
union{
|
|
Ast_Expr *name;
|
|
Ast_Expr *typespec;
|
|
};
|
|
Array<Ast_Call_Item *> exprs;
|
|
Ast_Type *type;
|
|
};
|
|
|
|
struct Ast_Unary: Ast_Expr{
|
|
Token_Kind op;
|
|
Ast_Expr *expr;
|
|
Ast_Type *type;
|
|
U64 padding[2]; // For folding constants into atoms
|
|
};
|
|
|
|
struct Ast_Index: Ast_Expr{
|
|
Ast_Expr *expr;
|
|
Ast_Expr *index;
|
|
Ast_Type *original_type;
|
|
};
|
|
|
|
struct Ast_Binary: Ast_Expr{
|
|
Token_Kind op;
|
|
Ast_Expr *left;
|
|
Ast_Expr *right;
|
|
|
|
Ast_Type *type;
|
|
Ast_Type *before_type;
|
|
};
|
|
|
|
// 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_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_For: Ast{
|
|
Ast_Expr *init;
|
|
Ast_Expr *cond;
|
|
Ast_Expr *iter;
|
|
Ast_Scope *scope;
|
|
};
|
|
|
|
struct Ast_Lambda : Ast_Expr {
|
|
Array<Ast_Decl *> args;
|
|
Ast_Expr *ret;
|
|
Ast_Scope *scope;
|
|
Ast_Type *type;
|
|
};
|
|
|
|
struct Ast_Array: Ast_Expr{
|
|
Ast_Expr *base;
|
|
Ast_Expr *expr;
|
|
Ast_Type *type;
|
|
};
|
|
|
|
/*
|
|
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.
|
|
|
|
*/
|
|
|
|
enum Ast_Decl_State{
|
|
DECL_NOT_RESOLVED,
|
|
DECL_RESOLVED,
|
|
DECL_RESOLVING,
|
|
};
|
|
|
|
struct Ast_Scope: Ast{
|
|
Intern_String name; // for debugging
|
|
Array<Ast_Scope *> implicit_imports;
|
|
Array<Ast_Decl *> decls;
|
|
Array<Ast *> stmts;
|
|
|
|
Ast_Scope *file; // Self referential for scope and module
|
|
Ast_Module *module;
|
|
};
|
|
|
|
|
|
struct Ast_Module: Ast_Scope{
|
|
Array<Ast_File *> all_loaded_files;
|
|
};
|
|
|
|
struct Ast_File: Ast_Scope{
|
|
Intern_String filename;
|
|
String filecontent;
|
|
};
|
|
|
|
struct Ast_Decl: Ast{
|
|
Ast_Decl_State state;
|
|
Intern_String name;
|
|
|
|
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->id = ++pctx->unique_ids
|
|
|
|
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){
|
|
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, 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->scope = scope;
|
|
result->ret = ret;
|
|
if(!ret) result->ret = ast_ident(result->pos, intern_void);
|
|
|
|
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_Return *
|
|
ast_return(Token *pos, Ast_Expr *expr){
|
|
AST_NEW(Return, RETURN, pos, AST_STMT);
|
|
if(expr){
|
|
assert(is_flag_set(expr->flags, AST_EXPR));
|
|
result->expr = expr;
|
|
}
|
|
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;
|
|
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;
|
|
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;
|
|
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_Module *
|
|
ast_module(Intern_String filename){
|
|
AST_NEW(Module, MODULE, 0, 0);
|
|
result->parent_scope = 0;
|
|
result->kind = AST_MODULE;
|
|
result->name = filename;
|
|
result->module = result;
|
|
result->file = result;
|
|
result->all_loaded_files = {pctx->heap};
|
|
result->implicit_imports = {pctx->heap};
|
|
result->decls = {pctx->heap};
|
|
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;
|
|
}
|