Files
corelang/core_compiler_interface.hpp
2023-03-31 08:56:25 +02:00

652 lines
13 KiB
C++

#pragma once
#include <stdint.h>
struct Ast;
struct Ast_Scope;
struct Ast_Decl;
struct Ast_File_Namespace;
struct Ast_File;
struct Ast_Module;
struct Ast_Lambda;
struct Ast_Type;
struct Ast_Expr;
#ifndef CORE_BASE
#define CORE_BASE
struct Allocator {
typedef void *Allocate(Allocator *, size_t);
typedef void Deallocate(Allocator *, void *p);
Allocate *allocate;
Deallocate *deallocate;
};
struct String {
uint8_t *str;
int64_t len;
};
typedef String Intern_String;
template <class T>
struct Array {
void *allocator;
T *data;
int64_t cap;
int64_t len;
};
template <class T>
struct List_Node {
List_Node<T> *next;
List_Node<T> *prev;
int cap;
int len;
T data[];
};
template <class T>
struct List {
int block_size = 0;
int allocation_multiplier = 0;
List_Node<T> *first = 0;
List_Node<T> *last = 0;
List_Node<T> *first_free = 0;
};
#endif
enum Token_Kind {
TK_End,
/*#
import meta
for i in meta.token_kinds:
print(" TK_" + i[0] + ",")
*/
TK_Mul,
TK_Div,
TK_Mod,
TK_LeftShift,
TK_RightShift,
TK_FirstMul = TK_Mul,
TK_LastMul = TK_RightShift,
TK_Add,
TK_Sub,
TK_FirstAdd = TK_Add,
TK_LastAdd = TK_Sub,
TK_Equals,
TK_LesserThenOrEqual,
TK_GreaterThenOrEqual,
TK_LesserThen,
TK_GreaterThen,
TK_NotEquals,
TK_FirstCompare = TK_Equals,
TK_LastCompare = TK_NotEquals,
TK_BitAnd,
TK_BitOr,
TK_BitXor,
TK_And,
TK_Or,
TK_FirstLogical = TK_BitAnd,
TK_LastLogical = TK_Or,
TK_Neg,
TK_Not,
TK_Decrement,
TK_Increment,
TK_PostDecrement,
TK_PostIncrement,
TK_Assign,
TK_ColonAssign,
TK_DivAssign,
TK_MulAssign,
TK_ModAssign,
TK_SubAssign,
TK_AddAssign,
TK_AndAssign,
TK_OrAssign,
TK_XorAssign,
TK_LeftShiftAssign,
TK_RightShiftAssign,
TK_FirstAssign = TK_Assign,
TK_LastAssign = TK_RightShiftAssign,
TK_OpenParen,
TK_CloseParen,
TK_OpenBrace,
TK_CloseBrace,
TK_OpenBracket,
TK_CloseBracket,
TK_Comma,
TK_Pound,
TK_Question,
TK_ThreeDots,
TK_Semicolon,
TK_Dot,
TK_TwoDots,
TK_NewLine,
TK_Colon,
TK_DoubleColon,
TK_At,
TK_Arrow,
TK_Polymorph,
TK_ExprSizeof,
TK_DocComment,
TK_Comment,
TK_Identifier,
TK_UnicodeLit,
TK_StringLit,
TK_Error,
TK_Float,
TK_Integer,
TK_Keyword,
/*END*/
TK_Pointer = TK_Mul,
TK_Dereference = TK_BitAnd,
OPEN_SCOPE = 128,
CLOSE_SCOPE,
SAME_SCOPE,
};
struct BigInt {
unsigned digit_count;
bool is_negative;
union {
uint64_t digit;
uint64_t *digits;
};
};
struct Token {
Token_Kind kind;
uint32_t di; // debug_id
union {
String string;
struct {
uint8_t *str;
int64_t len;
};
};
union {
uint32_t unicode;
BigInt int_val;
double f64_val;
String error_val;
Intern_String intern_val;
int64_t indent;
};
Intern_String file;
int32_t line;
uint8_t *line_begin;
};
enum Ast_Type_Kind {
TYPE_NONE,
TYPE_S64, // FIRST_NUMERIC
TYPE_S32,
TYPE_S16,
TYPE_S8,
TYPE_INT,
TYPE_CHAR,
TYPE_U64,
TYPE_U32,
TYPE_U16,
TYPE_U8,
TYPE_F32,
TYPE_F64,
TYPE_POINTER,
TYPE_BOOL, // LAST_NUMERIC
TYPE_STRING,
TYPE_VOID,
TYPE_ARRAY,
TYPE_LAMBDA,
TYPE_STRUCT,
TYPE_UNION,
TYPE_ENUM,
TYPE_TYPE,
TYPE_SLICE,
TYPE_TUPLE,
TYPE_COMPLETING,
TYPE_INCOMPLETE,
TYPE_POLYMORPH,
TYPE_UNTYPED_BOOL, // FIRST_TYPED_NUMERIC, FIRST_NUMERIC
TYPE_UNTYPED_INT,
TYPE_UNTYPED_FLOAT, // LAST_TYPED_NUMERIC
TYPE_UNTYPED_STRING,
TYPE_UNTYPED_FIRST = TYPE_UNTYPED_BOOL,
TYPE_UNTYPED_LAST = TYPE_UNTYPED_STRING,
TYPE_UNTYPED_FIRST_NUMERIC = TYPE_UNTYPED_BOOL,
TYPE_UNTYPED_LAST_NUMERIC = TYPE_UNTYPED_FLOAT,
TYPE_FIRST_NUMERIC = TYPE_S64,
TYPE_LAST_NUMERIC = TYPE_BOOL,
};
struct Value {
/*#import meta
print(meta.value_struct_content)
*/
Ast_Type *type;
Ast_Decl *resolved_decl;
union {
bool bool_val;
double f64_val;
Intern_String intern_val;
BigInt big_int_val;
Ast_Type *type_val;
};
/*END*/
};
struct Ast_Resolved_Member {
Intern_String name;
int32_t offset;
bool visited;
/*#import meta
meta.inline_value_fields()
*/
union {
Value value;
struct {
Ast_Type *type;
Ast_Decl *resolved_decl;
union {
bool bool_val;
double f64_val;
Intern_String intern_val;
BigInt big_int_val;
Ast_Type *type_val;
};
};
};
/*END*/
};
struct Ast_Type {
Ast_Type_Kind kind;
int32_t size;
int32_t align;
int32_t is_unsigned;
int32_t type_id;
int32_t padding;
Ast *ast;
union {
Ast_Type *base;
struct {
Ast_Type *base;
int32_t size;
// @note: if you have array with size "[32]"
// you still want to pass that array into
// a function that expects an array of size "[]"
// so we want also should check this
uint64_t slice_hash;
} arr;
struct {
Array<Ast_Resolved_Member> members;
} agg;
struct {
Ast_Type *ret;
Array<Ast_Type *> args;
uint64_t hash_without_ret;
} func;
};
};
enum Ast_Kind : uint32_t {
AST_NONE,
AST_NAMESPACE,
AST_MODULE,
AST_FILE,
AST_SCOPE,
AST_VALUE,
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_TYPE_OF,
AST_SWITCH,
AST_SWITCH_CASE,
AST_VAR_UNPACK,
AST_BREAK,
AST_COMPOUND,
AST_TYPE,
AST_VAR,
AST_CONST,
AST_ARRAY,
AST_FOR,
AST_IF,
AST_IF_NODE,
AST_RETURN,
AST_PASS,
AST_LAMBDA,
AST_LAMBDA_EXPR,
AST_ENUM,
AST_STRUCT,
AST_UNION,
};
typedef uint32_t Ast_Flag;
enum {
AST_EXPR = 1 << 1,
AST_STMT = 1 << 2,
AST_STRICT = 1 << 3,
AST_AGGREGATE = 1 << 4,
AST_AGGREGATE_CHILD = 1 << 5,
AST_ATOM = 1 << 7,
AST_FOREIGN = 1 << 8,
AST_DECL = 1 << 9,
AST_GLOBAL = 1 << 10,
AST_FLAG = 1 << 11,
AST_VAR_IS_CONST = 1 << 12,
AST_OPERATOR_OVERLOAD = 1 << 13,
AST_IS_LVALUE = 1 << 14,
AST_IDENT_POLYMORPH = 1 << 15,
AST_TYPE_POLYMORPH = 1 << 16,
AST_POLYMORPH = AST_IDENT_POLYMORPH | AST_TYPE_POLYMORPH,
};
struct Ast {
uint64_t di; // Debug id, shouldn't ever be used in the program
Token *pos;
Ast_Kind kind;
uint32_t visit_id;
Ast_Scope *parent_scope;
Ast_Flag flags;
};
struct Ast_Expr : Ast {
Ast_Type *resolved_type;
Ast_Decl *resolved_operator_overload;
union {
Ast_Type *index_original_type;
Ast_Type *cast_after_type;
Ast_Type *dot_access_step_resolution;
};
};
struct Ast_Atom : Ast_Expr {
// We have a field type here
// it has a different purpose from the
// resolved_type of Ast_Expr, it describes
// the inherent type of a value
//
// resolved_type is a solid type that
// can be use during code generation
// it cannot be untyped. (or at least thats the hope :)
/*#import meta
meta.inline_value_fields()
*/
union {
Value value;
struct {
Ast_Type *type;
Ast_Decl *resolved_decl;
union {
bool bool_val;
double f64_val;
Intern_String intern_val;
BigInt big_int_val;
Ast_Type *type_val;
};
};
};
/*END*/
};
typedef uint32_t Ast_Call_Item_Flag;
enum {
CALL_INDEX = 1ull << 1,
CALL_NAME = 1ull << 2,
CALL_INCLUDED = 1ull << 4,
};
struct Ast_Call_Item : Ast_Expr {
Ast_Call_Item_Flag call_flags;
int32_t resolved_index;
Ast_Expr *item;
union {
Ast_Atom *name;
Ast_Expr *index;
};
Intern_String resolved_name;
};
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;
uint64_t 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;
uint64_t 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.
// On top of that in the future we want a way to inject scopes, for convenience.
// 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;
};
#define Ast_Pass Ast
#define 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;
};
// @cleanup @refactor: return value shouldn't be a array of expressions.
// It should be a single expression. So probably need a special type
// for that.
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;
uint64_t padding[2];
};
struct Ast_Switch_Case : Ast {
Array<Ast_Expr *> labels;
Ast_Scope *scope;
bool 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; // Only for debug purposes, dont depend on it
List<Ast_Scope *> implicit_imports;
List<Ast_Decl *> decls;
Array<Ast *> stmts;
uint32_t scope_id;
Ast_Scope *file; // Self referential for file 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;
List<Ast_File *> all_loaded_files;
};
struct Ast_File : Ast_Scope {
String absolute_base_folder;
String absolute_file_path;
String filecontent;
};
enum Ast_Decl_State {
DECL_NOT_RESOLVED,
DECL_RESOLVED,
DECL_RESOLVED_TYPE,
DECL_RESOLVING,
};
struct Ast_Operator_Info {
Intern_String op;
String name;
Token_Kind op_kind;
bool valid_binary_expr;
bool valid_unary_expr;
};
struct Ast_Decl : Ast {
Ast_Decl_State state;
Intern_String name;
Intern_String unique_name; // For code generation, currently only present on lambdas
uint64_t operator_overload_arguments_hash;
Ast_Operator_Info *overload_op_info;
Array<Ast_Decl *> polymorph_parameters;
Array<Ast_Decl *> polymorphs; // instantiated polymorphs
Ast_Scope *scope;
Ast_Expr *typespec;
union {
Ast_Expr *expr;
Ast_Lambda *lambda;
};
/*#import meta
meta.inline_value_fields()
*/
union {
Value value;
struct {
Ast_Type *type;
Ast_Decl *resolved_decl;
union {
bool bool_val;
double f64_val;
Intern_String intern_val;
BigInt big_int_val;
Ast_Type *type_val;
};
};
};
/*END*/
};
enum Core_Message_Kind {
CORE_ERROR,
CORE_WARNING,
CORE_TRACE,
};
struct Core_Message {
Core_Message *next;
Core_Message_Kind kind;
String string;
Token *tokens[2];
int trace_line;
char *trace_file;
};