struct Ast; struct Scope; struct Type; struct Entity; struct DeclInfo; struct AstFile; struct AstPackage; enum AddressingMode : u8 { Addressing_Invalid = 0, // invalid addressing mode Addressing_NoValue = 1, // no value (void in C) Addressing_Value = 2, // computed value (rvalue) Addressing_Context = 3, // context value Addressing_Variable = 4, // addressable variable (lvalue) Addressing_Constant = 5, // constant Addressing_Type = 6, // type Addressing_Builtin = 7, // built-in procedure Addressing_ProcGroup = 8, // procedure group (overloaded procedure) Addressing_MapIndex = 9, // map index expression - // lhs: acts like a Variable // rhs: acts like OptionalOk Addressing_OptionalOk = 10, // rhs: acts like a value with an optional boolean part (for existence check) Addressing_OptionalOkPtr = 11, // rhs: same as OptionalOk but the value is a pointer Addressing_SoaVariable = 12, // Struct-Of-Arrays indexed variable Addressing_SwizzleValue = 13, // Swizzle indexed value Addressing_SwizzleVariable = 14, // Swizzle indexed variable }; gb_global String const addressing_mode_strings[] = { str_lit("Invalid"), str_lit("NoValue"), str_lit("Value"), str_lit("Context"), str_lit("Variable"), str_lit("Constant"), str_lit("Type"), str_lit("Builtin"), str_lit("ProcGroup"), str_lit("MapIndex"), str_lit("OptionalOk"), str_lit("OptionalOkPtr"), str_lit("SoaVariable"), str_lit("SwizzleValue"), str_lit("SwizzleVariable"), }; struct TypeAndValue { Type * type; AddressingMode mode; bool is_lhs; // Debug info ExactValue value; }; enum ParseFileError { ParseFile_None, ParseFile_WrongExtension, ParseFile_InvalidFile, ParseFile_EmptyFile, ParseFile_Permission, ParseFile_NotFound, ParseFile_InvalidToken, ParseFile_GeneralError, ParseFile_FileTooLarge, ParseFile_DirectoryAlreadyExists, ParseFile_Count, }; struct CommentGroup { Slice list; // Token_Comment }; enum PackageKind { Package_Normal, Package_Runtime, Package_Init, Package_Builtin, }; struct ImportedFile { AstPackage *pkg; FileInfo fi; TokenPos pos; // import isize index; }; enum AstFileFlag : u32 { AstFile_IsPrivatePkg = 1<<0, AstFile_IsPrivateFile = 1<<1, AstFile_IsLazy = 1<<4, AstFile_NoInstrumentation = 1<<5, }; enum AstDelayQueueKind { AstDelayQueue_Import, AstDelayQueue_Expr, AstDelayQueue_ForeignBlock, AstDelayQueue_COUNT, }; struct AstFile { i32 id; u32 flags; AstPackage * pkg; Scope * scope; Ast * pkg_decl; String fullpath; String filename; String directory; Tokenizer tokenizer; Array tokens; isize curr_token_index; isize prev_token_index; Token curr_token; Token prev_token; // previous non-comment Token package_token; String package_name; u64 vet_flags; u64 feature_flags; bool vet_flags_set; bool feature_flags_set; // >= 0: In Expression // < 0: In Control Clause // NOTE(bill): Used to prevent type literals in control clauses isize expr_level; bool allow_newline; // Only valid for expr_level == 0 bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases bool allow_in_expr; // NOTE(bill): in expression are only allowed in certain cases bool in_foreign_block; bool allow_type; bool in_when_statement; isize total_file_decl_count; isize delayed_decl_count; Slice decls; Array imports; // 'import' isize directive_count; Ast * curr_proc; isize error_count; ParseFileError last_error; f64 time_to_tokenize; // seconds f64 time_to_parse; // seconds CommentGroup *lead_comment; // Comment (block) before the decl CommentGroup *line_comment; // Comment after the semicolon CommentGroup *docs; // current docs Array comments; // All the comments! // This is effectively a queue but does not require any multi-threading capabilities Array delayed_decls_queues[AstDelayQueue_COUNT]; std::atomic seen_load_directive_count; #define PARSER_MAX_FIX_COUNT 6 isize fix_count; TokenPos fix_prev_pos; struct LLVMOpaqueMetadata *llvm_metadata; struct LLVMOpaqueMetadata *llvm_metadata_scope; }; enum AstForeignFileKind { AstForeignFile_Invalid, AstForeignFile_S, // Source, AstForeignFile_COUNT }; struct AstForeignFile { AstForeignFileKind kind; String source; }; struct AstPackageExportedEntity { Ast *identifier; Entity *entity; }; struct AstPackage { PackageKind kind; isize id; String name; String fullpath; Array files; Array foreign_files; bool is_single_file; isize order; BlockingMutex files_mutex; BlockingMutex foreign_files_mutex; BlockingMutex type_and_value_mutex; BlockingMutex name_mutex; // NOTE(bill): This must be a MPMCQueue MPMCQueue exported_entity_queue; // NOTE(bill): Created/set in checker Scope * scope; DeclInfo *decl_info; bool is_extra; }; struct ParseFileErrorNode { ParseFileErrorNode *next, *prev; ParseFileError err; }; struct Parser { String init_fullpath; StringSet imported_files; // fullpath BlockingMutex imported_files_mutex; Array packages; BlockingMutex packages_mutex; std::atomic file_to_process_count; std::atomic total_token_count; std::atomic total_line_count; std::atomic total_seen_load_directive_count; // TODO(bill): What should this mutex be per? // * Parser // * Package // * File BlockingMutex file_decl_mutex; BlockingMutex file_error_mutex; ParseFileErrorNode * file_error_head; ParseFileErrorNode * file_error_tail; }; struct ParserWorkerData { Parser *parser; ImportedFile imported_file; }; struct ForeignFileWorkerData { Parser *parser; ImportedFile imported_file; AstForeignFileKind foreign_kind; }; enum ProcInlining { ProcInlining_none = 0, ProcInlining_inline = 1, ProcInlining_no_inline = 2, }; enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, ProcTag_type_assert = 1<<2, ProcTag_no_type_assert = 1<<3, ProcTag_require_results = 1<<4, ProcTag_optional_ok = 1<<5, ProcTag_optional_allocator_error = 1<<6, }; enum ProcCallingConvention : i32 { ProcCC_Invalid = 0, ProcCC_Odin = 1, ProcCC_Contextless = 2, ProcCC_CDecl = 3, ProcCC_StdCall = 4, ProcCC_FastCall = 5, ProcCC_None = 6, ProcCC_Naked = 7, ProcCC_InlineAsm = 8, ProcCC_Win64 = 9, ProcCC_SysV = 10, ProcCC_MAX, ProcCC_ForeignBlockDefault = -1, }; gb_global char const *proc_calling_convention_strings[ProcCC_MAX] = { "", "odin", "contextless", "cdecl", "stdcall", "fastcall", "none", "naked", "inlineasm", "win64", "sysv", }; gb_internal ProcCallingConvention default_calling_convention(void) { return ProcCC_Odin; } enum StateFlag : u8 { StateFlag_bounds_check = 1<<0, StateFlag_no_bounds_check = 1<<1, StateFlag_type_assert = 1<<2, StateFlag_no_type_assert = 1<<3, StateFlag_SelectorCallExpr = 1<<5, StateFlag_DirectiveWasFalse = 1<<6, StateFlag_BeenHandled = 1<<7, }; enum ViralStateFlag : u8 { ViralStateFlag_ContainsDeferredProcedure = 1<<0, ViralStateFlag_ContainsOrBreak = 1<<1, ViralStateFlag_ContainsOrReturn = 1<<2, }; enum FieldFlag : u32 { FieldFlag_NONE = 0, FieldFlag_ellipsis = 1<<0, FieldFlag_using = 1<<1, FieldFlag_no_alias = 1<<2, FieldFlag_c_vararg = 1<<3, FieldFlag_const = 1<<5, FieldFlag_any_int = 1<<6, FieldFlag_subtype = 1<<7, FieldFlag_by_ptr = 1<<8, FieldFlag_no_broadcast = 1<<9, // disallow array programming FieldFlag_no_capture = 1<<11, // Internal use by the parser only FieldFlag_Tags = 1<<15, FieldFlag_Results = 1<<16, FieldFlag_Unknown = 1u<<30, FieldFlag_Invalid = 1u<<31, // Parameter List Restrictions FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg| FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast| FieldFlag_no_capture, FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; enum StmtAllowFlag { StmtAllowFlag_None = 0, StmtAllowFlag_In = 1<<0, StmtAllowFlag_Label = 1<<1, }; enum InlineAsmDialectKind : u8 { InlineAsmDialect_Default, // ATT is default InlineAsmDialect_ATT, InlineAsmDialect_Intel, InlineAsmDialect_COUNT, }; gb_global char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { "", "att", "intel", }; enum UnionTypeKind : u8 { UnionType_Normal = 0, UnionType_no_nil = 2, UnionType_shared_nil = 3, UnionType_COUNT }; gb_global char const *union_type_kind_strings[UnionType_COUNT] = { "(normal)", "#maybe", "#no_nil", "#shared_nil", }; struct AstSplitArgs { Slice positional; Slice named; }; #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ Token token; \ Entity *entity; \ u32 hash; \ }) \ AST_KIND(Implicit, "implicit", Token) \ AST_KIND(Uninit, "uninitialized value", Token) \ AST_KIND(BasicLit, "basic literal", struct { \ Token token; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ Token token; \ Token name; \ }) \ AST_KIND(Ellipsis, "ellipsis", struct { \ Token token; \ Ast *expr; \ }) \ AST_KIND(ProcGroup, "procedure group", struct { \ Token token; \ Token open; \ Token close; \ Slice args; \ }) \ AST_KIND(ProcLit, "procedure literal", struct { \ Ast *type; \ Ast *body; \ u64 tags; \ ProcInlining inlining; \ Token where_token; \ Slice where_clauses; \ DeclInfo *decl; \ }) \ AST_KIND(CompoundLit, "compound literal", struct { \ Ast *type; \ Slice elems; \ Token open, close; \ i64 max_count; \ Ast *tag; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ AST_KIND(TagExpr, "tag expression", struct { Token token, name; Ast *expr; }) \ AST_KIND(UnaryExpr, "unary expression", struct { Token op; Ast *expr; }) \ AST_KIND(BinaryExpr, "binary expression", struct { Token op; Ast *left, *right; } ) \ AST_KIND(ParenExpr, "parentheses expression", struct { Ast *expr; Token open, close; }) \ AST_KIND(SelectorExpr, "selector expression", struct { \ Token token; \ Ast *expr, *selector; \ u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \ u8 swizzle_indices; /*2 bits per component*/ \ bool is_bit_field; \ }) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { \ Token token; \ Ast *expr, *call; \ bool modified_call; \ }) \ AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \ AST_KIND(DerefExpr, "dereference expression", struct { Ast *expr; Token op; }) \ AST_KIND(SliceExpr, "slice expression", struct { \ Ast *expr; \ Token open, close; \ Token interval; \ Ast *low, *high; \ }) \ AST_KIND(CallExpr, "call expression", struct { \ Ast * proc; \ Slice args; \ Token open; \ Token close; \ Token ellipsis; \ ProcInlining inlining; \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ Entity *entity_procedure_of; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ Ast *name; \ Ast *value; \ CommentGroup *docs; \ CommentGroup *comment; \ }) \ AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(OrElseExpr, "or_else expression", struct { Ast *x; Token token; Ast *y; }) \ AST_KIND(OrReturnExpr, "or_return expression", struct { Ast *expr; Token token; }) \ AST_KIND(OrBranchExpr, "or branch expression", struct { Ast *expr; Token token; Ast *label; }) \ AST_KIND(TypeAssertion, "type assertion", struct { \ Ast *expr; \ Token dot; \ Ast *type; \ Type *type_hint; \ bool ignores[2]; \ }) \ AST_KIND(TypeCast, "type cast", struct { Token token; Ast *type, *expr; }) \ AST_KIND(AutoCast, "auto_cast", struct { Token token; Ast *expr; }) \ AST_KIND(InlineAsmExpr, "inline asm expression", struct { \ Token token; \ Token open, close; \ Slice param_types; \ Ast *return_type; \ Ast *asm_string; \ Ast *constraints_string; \ bool has_side_effects; \ bool is_align_stack; \ InlineAsmDialectKind dialect; \ }) \ AST_KIND(MatrixIndexExpr, "matrix index expression", struct { Ast *expr, *row_index, *column_index; Token open, close; }) \ AST_KIND(_ExprEnd, "", bool) \ AST_KIND(_StmtBegin, "", bool) \ AST_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \ AST_KIND(EmptyStmt, "empty statement", struct { Token token; }) \ AST_KIND(ExprStmt, "expression statement", struct { Ast *expr; } ) \ AST_KIND(AssignStmt, "assign statement", struct { \ Token op; \ Slice lhs, rhs; \ }) \ AST_KIND(_ComplexStmtBegin, "", bool) \ AST_KIND(BlockStmt, "block statement", struct { \ Scope *scope; \ Slice stmts; \ Ast *label; \ Token open, close; \ }) \ AST_KIND(IfStmt, "if statement", struct { \ Scope *scope; \ Token token; \ Ast *label; \ Ast * init; \ Ast * cond; \ Ast * body; \ Ast * else_stmt; \ }) \ AST_KIND(WhenStmt, "when statement", struct { \ Token token; \ Ast *cond; \ Ast *body; \ Ast *else_stmt; \ bool is_cond_determined; \ bool determined_cond; \ }) \ AST_KIND(ReturnStmt, "return statement", struct { \ Token token; \ Slice results; \ }) \ AST_KIND(ForStmt, "for statement", struct { \ Scope *scope; \ Token token; \ Ast *label; \ Ast *init; \ Ast *cond; \ Ast *post; \ Ast *body; \ }) \ AST_KIND(RangeStmt, "range statement", struct { \ Scope *scope; \ Token token; \ Ast *label; \ Slice vals; \ Token in_token; \ Ast *expr; \ Ast *body; \ bool reverse; \ }) \ AST_KIND(UnrollRangeStmt, "#unroll range statement", struct { \ Scope *scope; \ Token unroll_token; \ Slice args; \ Token for_token; \ Ast *val0; \ Ast *val1; \ Token in_token; \ Ast *expr; \ Ast *body; \ }) \ AST_KIND(CaseClause, "case clause", struct { \ Scope *scope; \ Token token; \ Slice list; \ Slice stmts; \ Entity *implicit_entity; \ }) \ AST_KIND(SwitchStmt, "switch statement", struct { \ Scope *scope; \ Token token; \ Ast *label; \ Ast *init; \ Ast *tag; \ Ast *body; \ bool partial; \ }) \ AST_KIND(TypeSwitchStmt, "type switch statement", struct { \ Scope *scope; \ Token token; \ Ast *label; \ Ast *tag; \ Ast *body; \ bool partial; \ }) \ AST_KIND(DeferStmt, "defer statement", struct { Token token; Ast *stmt; }) \ AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \ AST_KIND(UsingStmt, "using statement", struct { \ Token token; \ Slice list; \ }) \ AST_KIND(_ComplexStmtEnd, "", bool) \ AST_KIND(_StmtEnd, "", bool) \ AST_KIND(_DeclBegin, "", bool) \ AST_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \ AST_KIND(ForeignBlockDecl, "foreign block declaration", struct { \ Token token; \ Ast *foreign_library; \ Ast *body; \ Array attributes; \ CommentGroup *docs; \ }) \ AST_KIND(Label, "label", struct { \ Token token; \ Ast *name; \ }) \ AST_KIND(ValueDecl, "value declaration", struct { \ Slice names; \ Ast * type; \ Slice values; \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ bool is_using; \ bool is_mutable; \ }) \ AST_KIND(PackageDecl, "package declaration", struct { \ Token token; \ Token name; \ CommentGroup *docs; \ CommentGroup *comment; \ }) \ AST_KIND(ImportDecl, "import declaration", struct { \ AstPackage *package; \ Token token; \ Token relpath; \ String fullpath; \ Token import_name; \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ Slice filepaths; \ bool multiple_filepaths; \ Token library_name; \ String collection_name; \ Slice fullpaths; \ Array attributes; \ CommentGroup *docs; \ CommentGroup *comment; \ }) \ AST_KIND(_DeclEnd, "", bool) \ AST_KIND(Attribute, "attribute", struct { \ Token token; \ Slice elems; \ Token open, close; \ }) \ AST_KIND(Field, "field", struct { \ Slice names; \ Ast * type; \ Ast * default_value; \ Token tag; \ u32 flags; \ CommentGroup * docs; \ CommentGroup * comment; \ }) \ AST_KIND(BitFieldField, "bit field field", struct { \ Ast * name; \ Ast * type; \ Ast * bit_size; \ Token tag; \ CommentGroup *docs; \ CommentGroup *comment; \ }) \ AST_KIND(FieldList, "field list", struct { \ Token token; \ Slice list; \ }) \ AST_KIND(_TypeBegin, "", bool) \ AST_KIND(TypeidType, "typeid", struct { \ Token token; \ Ast *specialization; \ }) \ AST_KIND(HelperType, "helper type", struct { \ Token token; \ Ast *type; \ }) \ AST_KIND(DistinctType, "distinct type", struct { \ Token token; \ Ast *type; \ }) \ AST_KIND(PolyType, "polymorphic type", struct { \ Token token; \ Ast * type; \ Ast * specialization; \ }) \ AST_KIND(ProcType, "procedure type", struct { \ Scope *scope; \ Token token; \ Ast *params; \ Ast *results; \ u64 tags; \ ProcCallingConvention calling_convention; \ bool generic; \ bool diverging; \ }) \ AST_KIND(PointerType, "pointer type", struct { \ Token token; \ Ast *type; \ Ast *tag; \ }) \ AST_KIND(RelativeType, "relative type", struct { \ Ast *tag; \ Ast *type; \ }) \ AST_KIND(MultiPointerType, "multi pointer type", struct { \ Token token; \ Ast *type; \ }) \ AST_KIND(ArrayType, "array type", struct { \ Token token; \ Ast *count; \ Ast *elem; \ Ast *tag; \ }) \ AST_KIND(DynamicArrayType, "dynamic array type", struct { \ Token token; \ Ast *elem; \ Ast *tag; \ }) \ AST_KIND(StructType, "struct type", struct { \ Scope *scope; \ Token token; \ Slice fields; \ isize field_count; \ Ast *polymorphic_params; \ Ast *align; \ Ast *min_field_align; \ Ast *max_field_align; \ Token where_token; \ Slice where_clauses; \ bool is_packed; \ bool is_raw_union; \ bool is_no_copy; \ }) \ AST_KIND(UnionType, "union type", struct { \ Scope *scope; \ Token token; \ Slice variants; \ Ast *polymorphic_params; \ Ast * align; \ UnionTypeKind kind; \ Token where_token; \ Slice where_clauses; \ }) \ AST_KIND(EnumType, "enum type", struct { \ Scope *scope; \ Token token; \ Ast * base_type; \ Slice fields; /* FieldValue */ \ bool is_using; \ }) \ AST_KIND(BitSetType, "bit set type", struct { \ Token token; \ Ast * elem; \ Ast * underlying; \ }) \ AST_KIND(BitFieldType, "bit field type", struct { \ Scope *scope; \ Token token; \ Ast * backing_type; \ Token open; \ Slice fields; /* BitFieldField */ \ Token close; \ }) \ AST_KIND(MapType, "map type", struct { \ Token token; \ Ast *count; \ Ast *key; \ Ast *value; \ }) \ AST_KIND(MatrixType, "matrix type", struct { \ Token token; \ Ast *row_count; \ Ast *column_count; \ Ast *elem; \ bool is_row_major; \ }) \ AST_KIND(_TypeEnd, "", bool) enum AstKind : u16 { Ast_Invalid, #define AST_KIND(_kind_name_, ...) GB_JOIN2(Ast_, _kind_name_), AST_KINDS #undef AST_KIND Ast_COUNT, }; gb_global String const ast_strings[] = { {cast(u8 *)"invalid node", gb_size_of("invalid node")}, #define AST_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1}, AST_KINDS #undef AST_KIND }; #define AST_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(Ast, _kind_name_); AST_KINDS #undef AST_KIND gb_global isize const ast_variant_sizes[] = { 0, #define AST_KIND(_kind_name_, name, ...) gb_size_of(GB_JOIN2(Ast, _kind_name_)), AST_KINDS #undef AST_KIND }; struct AstCommonStuff { AstKind kind; // u16 u8 state_flags; u8 viral_state_flags; i32 file_id; TypeAndValue tav; // NOTE(bill): Making this a pointer is slower }; struct Ast { AstKind kind; // u16 u8 state_flags; u8 viral_state_flags; i32 file_id; TypeAndValue tav; // NOTE(bill): Making this a pointer is slower // IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant union { #define AST_KIND(_kind_name_, name, ...) GB_JOIN2(Ast, _kind_name_) _kind_name_; AST_KINDS #undef AST_KIND }; // NOTE(bill): I know I dislike methods but this is hopefully a temporary thing // for refactoring purposes gb_inline AstFile *file() const { // NOTE(bill): This doesn't need to call get_ast_file_from_id which return global_files[this->file_id]; } gb_inline AstFile *thread_safe_file() const { return thread_safe_get_ast_file_from_id(this->file_id); } }; #define ast_node(n_, Kind_, node_) GB_JOIN2(Ast, Kind_) *n_ = &(node_)->Kind_; gb_unused(n_); GB_ASSERT_MSG((node_)->kind == GB_JOIN2(Ast_, Kind_), \ "expected '%.*s' got '%.*s'", \ LIT(ast_strings[GB_JOIN2(Ast_, Kind_)]), LIT(ast_strings[(node_)->kind])) #define case_ast_node(n_, Kind_, node_) case GB_JOIN2(Ast_, Kind_): { ast_node(n_, Kind_, node_); #ifndef case_end #define case_end } break; #endif gb_internal gb_inline bool is_ast_expr(Ast *node) { return gb_is_between(node->kind, Ast__ExprBegin+1, Ast__ExprEnd-1); } gb_internal gb_inline bool is_ast_stmt(Ast *node) { return gb_is_between(node->kind, Ast__StmtBegin+1, Ast__StmtEnd-1); } gb_internal gb_inline bool is_ast_complex_stmt(Ast *node) { return gb_is_between(node->kind, Ast__ComplexStmtBegin+1, Ast__ComplexStmtEnd-1); } gb_internal gb_inline bool is_ast_decl(Ast *node) { return gb_is_between(node->kind, Ast__DeclBegin+1, Ast__DeclEnd-1); } gb_internal gb_inline bool is_ast_type(Ast *node) { return gb_is_between(node->kind, Ast__TypeBegin+1, Ast__TypeEnd-1); } gb_internal gb_inline bool is_ast_when_stmt(Ast *node) { return node->kind == Ast_WhenStmt; } gb_internal gb_inline gbAllocator ast_allocator(AstFile *f) { return permanent_allocator(); } gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind); gb_internal gbString expr_to_string(Ast *expression); gb_internal bool allow_field_separator(AstFile *f); gb_internal void parse_enforce_tabs(AstFile *f);