654 lines
13 KiB
C++
654 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,
|
|
|
|
AST_TYPESPEC = 1 << 17,
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
struct Ast_Lambda : Ast_Expr {
|
|
Array<Ast_Decl *> args;
|
|
// @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.
|
|
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;
|
|
};
|