#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; }; union Intern_String{ // Basically just String String s; struct{ uint8_t *str; int64_t len; }; }; 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_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_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_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_TYPE_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 uint32_t Ast_Flag; enum{ AST_EXPR = 1ull << 1, AST_STMT = 1ull << 2, AST_STRICT = 1ull << 3, AST_AGGREGATE = 1ull << 4, AST_AGGREGATE_CHILD = 1ull << 5, AST_ATOM = 1ull << 7, AST_FOREIGN = 1ull << 8, AST_DECL = 1ull << 9, AST_GLOBAL = 1ull << 10, AST_FLAG = 1ull << 11, AST_VAR_IS_CONST = 1ull << 12, AST_OPERATOR_OVERLOAD = 1ull << 13, AST_IS_LVALUE = 1ull << 14, }; struct Ast{ uint64_t 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_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 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. // 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 expr; }; struct Ast_If_Node: Ast{ Ast_Expr *expr ; Ast_Scope *scope; Ast_Binary*init; }; struct Ast_If: Ast{ Array 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 args; Array 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; }; /* 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 implicit_imports; List decls; Array stmts; uint32_t visit_id; 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 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; 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*/ };