#pragma once #include 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 struct Array { void *allocator; T *data; int64_t cap; int64_t len; }; template struct List_Node { List_Node *next; List_Node *prev; int cap; int len; T data[]; }; template struct List { int block_size = 0; int allocation_multiplier = 0; List_Node *first = 0; List_Node *last = 0; List_Node *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 is_int start TYPE_S32, TYPE_S16, TYPE_S8, TYPE_INT, TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_LLONG, TYPE_ULLONG, TYPE_SHORT, TYPE_USHORT, TYPE_CHAR, TYPE_UCHAR, TYPE_U64, TYPE_U32, TYPE_U16, TYPE_U8, // is_int end 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_COMPLETING, TYPE_INCOMPLETE, TYPE_POLYMORPH, TYPE_VARGS, 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 members; } agg; struct { Ast_Type *ret; Array 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_VARGS_LAMBDA_PARAM, AST_DEFER, AST_LABEL, AST_GOTO, AST_SWITCH, AST_SWITCH_CASE, AST_VAR_UNPACK, AST_BREAK, AST_COMPILER_BREAKPOINT_STMT, AST_CONTINUE, 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_PARENT_POLYMORPH = 1 << 17, AST_POLYMORPH_INSTANCE = 1 << 18, AST_TYPESPEC = 1 << 19, AST_COMPILER_BREAKPOINT = 1 << 20, }; 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, }; struct Ast_Call_Item : Ast_Expr { Ast_Call_Item_Flag call_flags; int32_t resolved_index; // This is probably for compound array Ast_Expr *item; union { Ast_Atom *name; Ast_Expr *index; }; Intern_String resolved_name; // This is probably for compound struct }; struct Ast_Call : Ast_Expr { union { Ast_Expr *name; Ast_Expr *typespec; }; Array exprs; Ast_Decl *resolved_decl; }; struct Ast_Var_Unpack : Ast_Expr { Array 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. // Because it would have symbols from previous function we were in middle of resolving. // // 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. // // // This seems slow though, would be nice to have a simpler scheme that's more flat. // Would be nice to just reuse single map while resolving that would keep track of which // function we are resolving. // // Also would be nice to have a more flat module scheme. The Ion approach seemed // very straight forward when I looked at it. It used a single locals list with // an index that signified from where we should consider declarations. Not really // sure how the packages worked though. // // The idea that you have a flat list of packages, each package has a flat list of declarations. // Seems nice. // struct Ast_Return : Ast { Ast_Type *resolved_type; Ast_Expr *expr; }; struct Ast_If_Node : Ast { Ast_Expr *expr; Ast_Scope *scope; Ast_Binary *init; }; struct Ast_If : Ast { Array ifs; }; // @todo: Ast_Simple_Stmt #define Ast_Pass Ast #define Ast_Break Ast struct Ast_Goto : Ast { Intern_String label; }; struct Ast_Defer : Ast { Ast_Scope *scope; }; 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 args; 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 labels; Ast_Scope *scope; bool fallthrough; }; struct Ast_Switch : Ast { Ast_Expr *value; Array cases; Ast_Scope *default_scope; }; struct Ast_Scope : Ast { String debug_name; // Only for debug purposes, dont depend on it Intern_String first_namespace_name; List implicit_imports; List decls; Array stmts; uint32_t scope_id; Ast_Scope *file; // Self referential for file and module Ast_Module *module; union { Ast *parent_ast; // @language_todo: add parent decl Ast_Decl *parent_decl; }; }; 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 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 // @todo: Move this to Ast_Operator_Overload uint64_t operator_overload_arguments_hash; Ast_Operator_Info *overload_op_info; // @todo: move this to Ast_Poly, perhaps this change needs to be combined with // the change to lambda where they stop being expressions uint64_t polymorph_hash; Array polymorph_resolved_parameter_types; Array polymorph_parameters; Array 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*/ }; struct Ast_Label : Ast_Decl { bool enable_goto; }; 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; };