diff --git a/core/fmt.odin b/core/fmt.odin index 2e6ab523c..2c78019ab 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -309,26 +309,26 @@ write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) { } -_parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool) { +_parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: bool) { is_digit :: inline proc(r: rune) -> bool{ return '0' <= r && r <= '9'; } - result := 0; - i := 0; - for i < len(s[offset..]) { - c := rune(s[offset+i]); + new_offset = offset; + n := len(s[new_offset..]); + for new_offset < n { + c := rune(s[new_offset]); if !is_digit(c) do break; - i += 1; + new_offset += 1; result *= 10; result += int(c)-'0'; } - - return result, offset+i, i != 0; + ok = new_offset > offset; + return; } -_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_count: int) -> (index, offset: int, ok: bool) { +_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) { parse_arg_number :: proc(format: string) -> (int, int, bool) { if len(format) < 3 do return 0, 1, false; @@ -350,7 +350,9 @@ _arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_c return arg_index, offset, false; } fi.reordered = true; - index, width, ok := parse_arg_number(format[offset..]); + + width: int; + index, width, ok = parse_arg_number(format[offset..]); if ok && 0 <= index && index < arg_count { return index, offset+width, true; } diff --git a/core/os.odin b/core/os.odin index 5927d8012..ef7ad7ac2 100644 --- a/core/os.odin +++ b/core/os.odin @@ -25,7 +25,7 @@ read_entire_file :: proc(name: string) -> (data: []byte, success: bool) { return nil, true; } - data := make([]byte, int(length)); + data = make([]byte, int(length)); if data == nil { return nil, false; } diff --git a/core/strconv.odin b/core/strconv.odin index e0c1cce3b..bb44348f2 100644 --- a/core/strconv.odin +++ b/core/strconv.odin @@ -14,7 +14,7 @@ parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) { case "0", "f", "F", "false", "FALSE", "False": return false, true; } - return ok = false; + return; } _digit_value :: proc(r: rune) -> int { @@ -428,7 +428,6 @@ digits := "0123456789abcdefghijklmnopqrstuvwxyz"; is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) { - neg := false; if is_signed { switch bit_size { case 8: diff --git a/examples/demo.odin b/examples/demo.odin index 3a915e354..6461ba5da 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -648,15 +648,35 @@ using_in :: proc() { println(f); } +named_proc_parameters :: proc() { + foo0 :: proc() -> int { + return 123; + } + foo1 :: proc() -> (a: int) { + a = 123; + return; + } + foo2 :: proc() -> (a, b: int) { + // Named return values act like variables within the scope + a = 321; + b = 567; + return b, a; + } + fmt.println("foo0 =", foo0()); + fmt.println("foo1 =", foo1()); + fmt.println("foo2 =", foo2()); +} + main :: proc() { - general_stuff(); when false { + general_stuff(); default_struct_values(); union_type(); parametric_polymorphism(); threading_example(); array_programming(); using_in(); + named_proc_parameters(); } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1b44bdf55..c839ad2af 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1108,6 +1108,12 @@ bool check_binary_op(Checker *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with numeric or pointer expressions", LIT(op.string)); return false; } +#if defined(NO_POINTER_ARITHMETIC) + if (is_type_pointer(type)) { + error(o->expr, "Pointer arithmetic is not supported"); + return false; + } +#else if (is_type_pointer(type)) { o->type = t_int; } @@ -1118,6 +1124,7 @@ bool check_binary_op(Checker *c, Operand *o, Token op) { return false; } break; +#endif case Token_Mul: case Token_Quo: @@ -1644,6 +1651,12 @@ Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offs return operand; } +#if defined(NO_POINTER_ARITHMETIC) + operand.mode = Addressing_Invalid; + error(operand.expr, "Pointer arithmetic is not supported"); + return operand; +#else + Type *base_ptr = base_type(ptr->type); GB_ASSERT(base_ptr->kind == Type_Pointer); Type *elem = base_ptr->Pointer.elem; i64 elem_size = type_size_of(c->allocator, elem); @@ -1670,6 +1683,7 @@ Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offs } return operand; +#endif } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a19ce32fa..0fb82f7d3 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1165,135 +1165,76 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { break; } - bool first_is_field_value = false; - if (rs->results.count > 0) { - bool fail = false; - first_is_field_value = (rs->results[0]->kind == AstNode_FieldValue); - for_array(i, rs->results) { - AstNode *arg = rs->results[i]; - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != AstNode_FieldValue; - } else { - mix = arg->kind == AstNode_FieldValue; - } - if (mix) { - error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed"); - fail = true; - } - } + // bool first_is_field_value = false; + // if (rs->results.count > 0) { + // bool fail = false; + // first_is_field_value = (rs->results[0]->kind == AstNode_FieldValue); + // for_array(i, rs->results) { + // AstNode *arg = rs->results[i]; + // bool mix = false; + // if (first_is_field_value) { + // mix = arg->kind != AstNode_FieldValue; + // } else { + // mix = arg->kind == AstNode_FieldValue; + // } + // if (mix) { + // error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed"); + // fail = true; + // } + // } - if (fail) { - return; - } - } + // if (fail) { + // return; + // } + // } Type *proc_type = c->proc_stack[c->proc_stack.count-1]; TypeProc *pt = &proc_type->Proc; isize result_count = 0; + bool has_named_results = pt->has_named_results; if (pt->results) { result_count = proc_type->Proc.results->Tuple.variables.count; } - isize result_count_excluding_defaults = result_count; - for (isize i = result_count-1; i >= 0; i--) { - Entity *e = pt->results->Tuple.variables[i]; - if (e->kind == Entity_TypeName) { - break; - } + // isize result_count_excluding_defaults = result_count; + // for (isize i = result_count-1; i >= 0; i--) { + // Entity *e = pt->results->Tuple.variables[i]; + // if (e->kind == Entity_TypeName) { + // break; + // } - GB_ASSERT(e->kind == Entity_Variable); - if (e->Variable.default_value.kind != ExactValue_Invalid || - e->Variable.default_is_nil) { - result_count_excluding_defaults--; - continue; - } - break; - } + // GB_ASSERT(e->kind == Entity_Variable); + // if (e->Variable.default_value.kind != ExactValue_Invalid || + // e->Variable.default_is_nil) { + // result_count_excluding_defaults--; + // continue; + // } + // break; + // } Array operands = {}; defer (array_free(&operands)); - if (first_is_field_value) { - array_init_count(&operands, heap_allocator(), rs->results.count); - for_array(i, rs->results) { - AstNode *arg = rs->results[i]; - ast_node(fv, FieldValue, arg); - check_expr(c, &operands[i], fv->value); - } - } else { + // if (first_is_field_value) { + // array_init_count(&operands, heap_allocator(), rs->results.count); + // for_array(i, rs->results) { + // AstNode *arg = rs->results[i]; + // ast_node(fv, FieldValue, arg); + // check_expr(c, &operands[i], fv->value); + // } + // } else { array_init(&operands, heap_allocator(), 2*rs->results.count); check_unpack_arguments(c, nullptr, -1, &operands, rs->results, false); - } + // } - - if (first_is_field_value) { - bool *visited = gb_alloc_array(c->allocator, bool, result_count); - - for_array(i, rs->results) { - AstNode *arg = rs->results[i]; - ast_node(fv, FieldValue, arg); - if (fv->field->kind != AstNode_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(arg, "Invalid parameter name '%s' in return statement", expr_str); - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; - isize index = lookup_procedure_result(pt, name); - if (index < 0) { - error(arg, "No result named '%.*s' for this procedure type", LIT(name)); - continue; - } - if (visited[index]) { - error(arg, "Duplicate result '%.*s' in return statement", LIT(name)); - continue; - } - - visited[index] = true; - Operand *o = &operands[i]; - Entity *e = pt->results->Tuple.variables[index]; - check_assignment(c, &operands[i], e->type, str_lit("return statement")); - } - - for (isize i = 0; i < result_count; i++) { - if (!visited[i]) { - Entity *e = pt->results->Tuple.variables[i]; - if (is_blank_ident(e->token)) { - continue; - } - GB_ASSERT(e->kind == Entity_Variable); - if (e->Variable.default_value.kind != ExactValue_Invalid) { - continue; - } - - if (e->Variable.default_is_nil) { - continue; - } - - gbString str = type_to_string(e->type); - error(node, "Return value '%.*s' of type '%s' is missing in return statement", - LIT(e->token.string), str); - gb_string_free(str); - } - } - - } else if (result_count == 0 && rs->results.count > 0) { + if (result_count == 0 && rs->results.count > 0) { error(rs->results[0], "No return values expected"); - } else if (operands.count > result_count) { - if (result_count_excluding_defaults < result_count) { - error(node, "Expected a maximum of %td return values, got %td", result_count, operands.count); - } else { - error(node, "Expected %td return values, got %td", result_count, operands.count); - } - } else if (operands.count < result_count_excluding_defaults) { - if (result_count_excluding_defaults < result_count) { - error(node, "Expected a minimum of %td return values, got %td", result_count_excluding_defaults, operands.count); - } else { - error(node, "Expected %td return values, got %td", result_count_excluding_defaults, operands.count); - } + } else if (has_named_results && operands.count == 0) { + // Okay + } else if (operands.count != result_count) { + error(node, "Expected %td return values, got %td", result_count, operands.count); } else { isize max_count = rs->results.count; for (isize i = 0; i < max_count; i++) { diff --git a/src/check_type.cpp b/src/check_type.cpp index f1a706b6d..e41670bd3 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1423,10 +1423,16 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *_results) { token = name->Ident.token; } + if (is_blank_ident(token)) { + error(name, "Result value cannot be a blank identifer `_`"); + } + Entity *param = make_entity_param(c->allocator, scope, token, type, false, false); + param->flags |= EntityFlag_Result; param->Variable.default_value = value; param->Variable.default_is_nil = default_is_nil; array_add(&variables, param); + add_entity(c, scope, name, param); } } } @@ -1673,6 +1679,12 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array } } + if (result_count > 0) { + Entity *first = results->Tuple.variables[0]; + type->Proc.has_named_results = first->token.string != ""; + } + + ProcCallingConvention cc = pt->calling_convention; if (cc == ProcCC_ForeignBlockDefault) { cc = ProcCC_CDecl; diff --git a/src/checker.cpp b/src/checker.cpp index 06b65c1ec..55df36fb4 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1,150 +1,6 @@ #include "entity.cpp" - -enum ExprKind { - Expr_Expr, - Expr_Stmt, -}; - -// Statements and Declarations -enum StmtFlag { - Stmt_BreakAllowed = 1<<0, - Stmt_ContinueAllowed = 1<<1, - Stmt_FallthroughAllowed = 1<<2, - - Stmt_CheckScopeDecls = 1<<5, -}; - -struct BuiltinProc { - String name; - isize arg_count; - bool variadic; - ExprKind kind; -}; -enum BuiltinProcId { - BuiltinProc_Invalid, - - BuiltinProc_len, - BuiltinProc_cap, - - // BuiltinProc_new, - BuiltinProc_make, - // BuiltinProc_free, - - // BuiltinProc_reserve, - // BuiltinProc_clear, - // BuiltinProc_append, - // BuiltinProc_delete, - - BuiltinProc_size_of, - BuiltinProc_align_of, - BuiltinProc_offset_of, - BuiltinProc_type_of, - BuiltinProc_type_info_of, - - BuiltinProc_compile_assert, - - BuiltinProc_swizzle, - - BuiltinProc_complex, - BuiltinProc_real, - BuiltinProc_imag, - BuiltinProc_conj, - - // BuiltinProc_slice_ptr, - // BuiltinProc_slice_to_bytes, - - BuiltinProc_expand_to_tuple, - - BuiltinProc_min, - BuiltinProc_max, - BuiltinProc_abs, - BuiltinProc_clamp, - - BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures - - BuiltinProc_COUNT, -}; -gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { - {STR_LIT(""), 0, false, Expr_Stmt}, - - {STR_LIT("len"), 1, false, Expr_Expr}, - {STR_LIT("cap"), 1, false, Expr_Expr}, - - // {STR_LIT("new"), 1, false, Expr_Expr}, - {STR_LIT("make"), 1, true, Expr_Expr}, - // {STR_LIT("free"), 1, false, Expr_Stmt}, - - // {STR_LIT("reserve"), 2, false, Expr_Stmt}, - // {STR_LIT("clear"), 1, false, Expr_Stmt}, - // {STR_LIT("append"), 1, true, Expr_Expr}, - // {STR_LIT("delete"), 2, false, Expr_Stmt}, - - {STR_LIT("size_of"), 1, false, Expr_Expr}, - {STR_LIT("align_of"), 1, false, Expr_Expr}, - {STR_LIT("offset_of"), 2, false, Expr_Expr}, - {STR_LIT("type_of"), 1, false, Expr_Expr}, - {STR_LIT("type_info_of"), 1, false, Expr_Expr}, - - {STR_LIT("compile_assert"), 1, false, Expr_Expr}, - - {STR_LIT("swizzle"), 1, true, Expr_Expr}, - - {STR_LIT("complex"), 2, false, Expr_Expr}, - {STR_LIT("real"), 1, false, Expr_Expr}, - {STR_LIT("imag"), 1, false, Expr_Expr}, - {STR_LIT("conj"), 1, false, Expr_Expr}, - - // {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, - // {STR_LIT("slice_to_bytes"), 1, false, Expr_Expr}, - - {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr}, - - {STR_LIT("min"), 2, false, Expr_Expr}, - {STR_LIT("max"), 2, false, Expr_Expr}, - {STR_LIT("abs"), 1, false, Expr_Expr}, - {STR_LIT("clamp"), 3, false, Expr_Expr}, - - {STR_LIT(""), 0, true, Expr_Expr}, // DIRECTIVE -}; - - #include "types.cpp" -enum AddressingMode { - Addressing_Invalid, // invalid addressing mode - Addressing_NoValue, // no value (void in C) - Addressing_Value, // computed value (rvalue) - Addressing_Immutable, // immutable computed value (const rvalue) - Addressing_Variable, // addressable variable (lvalue) - Addressing_Constant, // constant - Addressing_Type, // type - Addressing_Builtin, // built-in procedure - Addressing_ProcGroup, // procedure group (overloaded procedure) - Addressing_MapIndex, // map index expression - - // lhs: acts like a Variable - // rhs: acts like OptionalOk - Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check) -}; - -// Operand is used as an intermediate value whilst checking -// Operands store an addressing mode, the expression being evaluated, -// its type and node, and other specific information for certain -// addressing modes -// Its zero-value is a valid "invalid operand" -struct Operand { - AddressingMode mode; - Type * type; - ExactValue value; - AstNode * expr; - BuiltinProcId builtin_id; - Entity * proc_group; -}; - -struct TypeAndValue { - AddressingMode mode; - Type * type; - ExactValue value; -}; bool is_operand_value(Operand o) { switch (o.mode) { @@ -166,79 +22,6 @@ bool is_operand_undef(Operand o) { -struct BlockLabel { - String name; - AstNode *label; // AstNode_Label; -}; - -// DeclInfo is used to store information of certain declarations to allow for "any order" usage -struct DeclInfo { - DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment - Scope * scope; - - Entity ** entities; - isize entity_count; - - AstNode * type_expr; - AstNode * init_expr; - Array init_expr_list; - Array attributes; - AstNode * proc_lit; // AstNode_ProcLit - Type * gen_proc_type; // Precalculated - - PtrSet deps; - Array labels; -}; - -// ProcedureInfo stores the information needed for checking a procedure - - -struct ProcedureInfo { - AstFile * file; - Token token; - DeclInfo * decl; - Type * type; // Type_Procedure - AstNode * body; // AstNode_BlockStmt - u64 tags; - bool generated_from_polymorphic; -}; - -// ExprInfo stores information used for "untyped" expressions -struct ExprInfo { - bool is_lhs; // Debug info - AddressingMode mode; - Type * type; // Type_Basic - ExactValue value; -}; - -ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue value) { - ExprInfo ei = {is_lhs, mode, type, value}; - return ei; -} - - - -struct Scope { - AstNode * node; - Scope * parent; - Scope * prev, *next; - Scope * first_child; - Scope * last_child; - Map elements; // Key: String - PtrSet implicit; - - Array shared; - Array delayed_file_decls; - PtrSet imported; - PtrSet exported; // NOTE(bhall): Contains 'using import' too - bool is_proc; - bool is_global; - bool is_file; - bool is_init; - bool is_struct; - bool has_been_imported; // This is only applicable to file scopes - AstFile * file; -}; gb_global Scope *universal_scope = nullptr; void scope_reset(Scope *scope) { @@ -257,7 +40,7 @@ i32 is_scope_an_ancestor(Scope *parent, Scope *child) { isize i = 0; while (child != nullptr) { if (parent == child) { - return true; + return i; } child = child->parent; i++; @@ -265,18 +48,6 @@ i32 is_scope_an_ancestor(Scope *parent, Scope *child) { return -1; } - -struct EntityGraphNode; -typedef PtrSet EntityGraphNodeSet; - -struct EntityGraphNode { - Entity * entity; // Procedure, Variable, Constant - EntityGraphNodeSet pred; - EntityGraphNodeSet succ; - isize index; // Index in array/queue - isize dep_count; -}; - void entity_graph_node_set_destroy(EntityGraphNodeSet *s) { if (s->hashes.data != nullptr) { ptr_set_destroy(s); @@ -326,9 +97,6 @@ void entity_graph_node_swap(EntityGraphNode **data, isize i, isize j) { -struct ImportGraphNode; -typedef PtrSet ImportGraphNodeSet; - void import_graph_node_set_destroy(ImportGraphNodeSet *s) { if (s->hashes.data != nullptr) { ptr_set_destroy(s); @@ -350,16 +118,6 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) { ptr_set_remove(s, n); } -struct ImportGraphNode { - Scope * scope; - String path; - isize file_id; - ImportGraphNodeSet pred; - ImportGraphNodeSet succ; - isize index; // Index in array/queue - isize dep_count; -}; - ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) { ImportGraphNode *n = gb_alloc_item(a, ImportGraphNode); n->scope = scope; @@ -368,7 +126,6 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) { return n; } - void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) { import_graph_node_set_destroy(&n->pred); import_graph_node_set_destroy(&n->succ); @@ -409,117 +166,9 @@ GB_COMPARE_PROC(ast_node_cmp) { return token_pos_cmp(i.pos, j.pos); } -struct ForeignContext { - AstNode * curr_library; - ProcCallingConvention default_cc; - String link_prefix; - bool in_export; -}; - -struct CheckerContext { - Scope * file_scope; - Scope * scope; - DeclInfo * decl; - u32 stmt_state_flags; - bool in_defer; // TODO(bill): Actually handle correctly - String proc_name; - Type * type_hint; - DeclInfo * curr_proc_decl; - ForeignContext foreign_context; - - bool collect_delayed_decls; - bool allow_polymorphic_types; - bool no_polymorphic_errors; - Scope * polymorphic_scope; -}; - - -// CheckerInfo stores all the symbol information for a type-checked program -struct CheckerInfo { - Map types; // Key: AstNode * | Expression -> Type (and value) - Map untyped; // Key: AstNode * | Expression -> ExprInfo - Map files; // Key: String (full path) - Map foreigns; // Key: String - Array definitions; - Array entities; - Array variable_init_order; - - Map > gen_procs; // Key: AstNode * | Identifier -> Entity - Map > gen_types; // Key: Type * - - Map type_info_map; // Key: Type * - isize type_info_count; - - Scope * init_scope; - Entity * entry_point; - PtrSet minimum_dependency_set; -}; - -struct Checker { - Parser * parser; - CheckerInfo info; - gbMutex mutex; - - AstFile * curr_ast_file; - Scope * global_scope; - // NOTE(bill): Procedures to check - Array procs; - Map file_scopes; // Key: String (fullpath) - Array file_order; - - gbAllocator allocator; - gbArena arena; - gbArena tmp_arena; - gbAllocator tmp_allocator; - - CheckerContext context; - - Array proc_stack; - bool done_preload; - - PtrSet checked_files; - -}; -HashKey hash_node (AstNode *node) { return hash_pointer(node); } -HashKey hash_ast_file (AstFile *file) { return hash_pointer(file); } -HashKey hash_entity (Entity *e) { return hash_pointer(e); } -HashKey hash_type (Type *t) { return hash_pointer(t); } -HashKey hash_decl_info(DeclInfo *decl) { return hash_pointer(decl); } - -// CheckerInfo API -TypeAndValue type_and_value_of_expr (CheckerInfo *i, AstNode *expr); -Type * type_of_expr (CheckerInfo *i, AstNode *expr); -Entity * entity_of_ident (CheckerInfo *i, AstNode *identifier); -Entity * implicit_entity_of_node(CheckerInfo *i, AstNode *clause); -Scope * scope_of_node (CheckerInfo *i, AstNode *node); -DeclInfo * decl_info_of_ident (CheckerInfo *i, AstNode *ident); -DeclInfo * decl_info_of_entity (CheckerInfo *i, Entity * e); -AstFile * ast_file_of_filename (CheckerInfo *i, String filename); -// IMPORTANT: Only to use once checking is done -isize type_info_index (CheckerInfo *i, Type * type, bool error_on_failure = true); - - -Entity *current_scope_lookup_entity(Scope *s, String name); -Entity *scope_lookup_entity (Scope *s, String name); -void scope_lookup_parent_entity (Scope *s, String name, Scope **scope_, Entity **entity_); -Entity *scope_insert_entity (Scope *s, Entity *entity); - - -ExprInfo *check_get_expr_info (CheckerInfo *i, AstNode *expr); -void check_set_expr_info (CheckerInfo *i, AstNode *expr, ExprInfo info); -void check_remove_expr_info (CheckerInfo *i, AstNode *expr); -void add_untyped (CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value); -void add_type_and_value (CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value); -void add_entity_use (Checker *c, AstNode *identifier, Entity *entity); -void add_implicit_entity (Checker *c, AstNode *node, Entity *e); -void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d); - -void check_add_import_decl(Checker *c, AstNodeImportDecl *id); -void check_add_export_decl(Checker *c, AstNodeExportDecl *ed); -void check_add_foreign_import_decl(Checker *c, AstNode *decl); void init_declaration_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { @@ -771,6 +420,9 @@ Entity *scope_lookup_entity(Scope *s, String name) { Entity *scope_insert_entity(Scope *s, Entity *entity) { String name = entity->token.string; + if (name == "") { + return nullptr; + } HashKey key = hash_string(name); Entity **found = map_get(&s->elements, key); @@ -1742,30 +1394,6 @@ void init_preload(Checker *c) { -bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global = false); -void check_collect_entities(Checker *c, Array nodes); -void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws); -void check_delayed_file_import_entity(Checker *c, AstNode *decl); - -struct AttributeContext { - String link_name; - String link_prefix; - isize init_expr_list_count; - String thread_local_model; -}; - -AttributeContext make_attribute_context(String link_prefix) { - AttributeContext ac = {}; - ac.link_prefix = link_prefix; - return ac; -} - -#define DECL_ATTRIBUTE_PROC(_name) bool _name(Checker *c, AstNode *elem, String name, ExactValue value, AttributeContext *ac) -typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc); - - -void check_decl_attributes(Checker *c, Array attributes, DeclAttributeProc *proc, AttributeContext *ac); - DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { if (name == "default_calling_convention") { if (value.kind == ExactValue_String) { diff --git a/src/checker.hpp b/src/checker.hpp new file mode 100644 index 000000000..f3507d2d5 --- /dev/null +++ b/src/checker.hpp @@ -0,0 +1,400 @@ +// checker.hpp + +struct Type; +struct Entity; +struct Scope; +struct DeclInfo; +struct AstFile; + +enum AddressingMode { + Addressing_Invalid, // invalid addressing mode + Addressing_NoValue, // no value (void in C) + Addressing_Value, // computed value (rvalue) + Addressing_Immutable, // immutable computed value (const rvalue) + Addressing_Variable, // addressable variable (lvalue) + Addressing_Constant, // constant + Addressing_Type, // type + Addressing_Builtin, // built-in procedure + Addressing_ProcGroup, // procedure group (overloaded procedure) + Addressing_MapIndex, // map index expression - + // lhs: acts like a Variable + // rhs: acts like OptionalOk + Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check) +}; + +struct TypeAndValue { + AddressingMode mode; + Type * type; + ExactValue value; +}; + + +// ExprInfo stores information used for "untyped" expressions +struct ExprInfo { + bool is_lhs; // Debug info + AddressingMode mode; + Type * type; // Type_Basic + ExactValue value; +}; + +gb_inline ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue value) { + ExprInfo ei = {is_lhs, mode, type, value}; + return ei; +} + + + + +enum ExprKind { + Expr_Expr, + Expr_Stmt, +}; + +// Statements and Declarations +enum StmtFlag { + Stmt_BreakAllowed = 1<<0, + Stmt_ContinueAllowed = 1<<1, + Stmt_FallthroughAllowed = 1<<2, + + Stmt_CheckScopeDecls = 1<<5, +}; + +struct BuiltinProc { + String name; + isize arg_count; + bool variadic; + ExprKind kind; +}; +enum BuiltinProcId { + BuiltinProc_Invalid, + + BuiltinProc_len, + BuiltinProc_cap, + + // BuiltinProc_new, + BuiltinProc_make, + // BuiltinProc_free, + + // BuiltinProc_reserve, + // BuiltinProc_clear, + // BuiltinProc_append, + // BuiltinProc_delete, + + BuiltinProc_size_of, + BuiltinProc_align_of, + BuiltinProc_offset_of, + BuiltinProc_type_of, + BuiltinProc_type_info_of, + + BuiltinProc_compile_assert, + + BuiltinProc_swizzle, + + BuiltinProc_complex, + BuiltinProc_real, + BuiltinProc_imag, + BuiltinProc_conj, + + // BuiltinProc_slice_ptr, + // BuiltinProc_slice_to_bytes, + + BuiltinProc_expand_to_tuple, + + BuiltinProc_min, + BuiltinProc_max, + BuiltinProc_abs, + BuiltinProc_clamp, + + BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures + + BuiltinProc_COUNT, +}; +gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { + {STR_LIT(""), 0, false, Expr_Stmt}, + + {STR_LIT("len"), 1, false, Expr_Expr}, + {STR_LIT("cap"), 1, false, Expr_Expr}, + + // {STR_LIT("new"), 1, false, Expr_Expr}, + {STR_LIT("make"), 1, true, Expr_Expr}, + // {STR_LIT("free"), 1, false, Expr_Stmt}, + + // {STR_LIT("reserve"), 2, false, Expr_Stmt}, + // {STR_LIT("clear"), 1, false, Expr_Stmt}, + // {STR_LIT("append"), 1, true, Expr_Expr}, + // {STR_LIT("delete"), 2, false, Expr_Stmt}, + + {STR_LIT("size_of"), 1, false, Expr_Expr}, + {STR_LIT("align_of"), 1, false, Expr_Expr}, + {STR_LIT("offset_of"), 2, false, Expr_Expr}, + {STR_LIT("type_of"), 1, false, Expr_Expr}, + {STR_LIT("type_info_of"), 1, false, Expr_Expr}, + + {STR_LIT("compile_assert"), 1, false, Expr_Expr}, + + {STR_LIT("swizzle"), 1, true, Expr_Expr}, + + {STR_LIT("complex"), 2, false, Expr_Expr}, + {STR_LIT("real"), 1, false, Expr_Expr}, + {STR_LIT("imag"), 1, false, Expr_Expr}, + {STR_LIT("conj"), 1, false, Expr_Expr}, + + // {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, + // {STR_LIT("slice_to_bytes"), 1, false, Expr_Expr}, + + {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr}, + + {STR_LIT("min"), 2, false, Expr_Expr}, + {STR_LIT("max"), 2, false, Expr_Expr}, + {STR_LIT("abs"), 1, false, Expr_Expr}, + {STR_LIT("clamp"), 3, false, Expr_Expr}, + + {STR_LIT(""), 0, true, Expr_Expr}, // DIRECTIVE +}; + + +// Operand is used as an intermediate value whilst checking +// Operands store an addressing mode, the expression being evaluated, +// its type and node, and other specific information for certain +// addressing modes +// Its zero-value is a valid "invalid operand" +struct Operand { + AddressingMode mode; + Type * type; + ExactValue value; + AstNode * expr; + BuiltinProcId builtin_id; + Entity * proc_group; +}; + + +struct BlockLabel { + String name; + AstNode *label; // AstNode_Label; +}; + +// DeclInfo is used to store information of certain declarations to allow for "any order" usage +struct DeclInfo { + DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment + Scope * scope; + + Entity ** entities; + isize entity_count; + + AstNode * type_expr; + AstNode * init_expr; + Array init_expr_list; + Array attributes; + AstNode * proc_lit; // AstNode_ProcLit + Type * gen_proc_type; // Precalculated + + PtrSet deps; + Array labels; +}; + +// ProcedureInfo stores the information needed for checking a procedure + + +struct ProcedureInfo { + AstFile * file; + Token token; + DeclInfo * decl; + Type * type; // Type_Procedure + AstNode * body; // AstNode_BlockStmt + u64 tags; + bool generated_from_polymorphic; +}; + + +struct Scope { + AstNode * node; + Scope * parent; + Scope * prev, *next; + Scope * first_child; + Scope * last_child; + Map elements; // Key: String + PtrSet implicit; + + Array shared; + Array delayed_file_decls; + PtrSet imported; + PtrSet exported; // NOTE(bhall): Contains 'using import' too + bool is_proc; + bool is_global; + bool is_file; + bool is_init; + bool is_struct; + bool has_been_imported; // This is only applicable to file scopes + AstFile * file; +}; + + + + +struct EntityGraphNode; +typedef PtrSet EntityGraphNodeSet; + +struct EntityGraphNode { + Entity * entity; // Procedure, Variable, Constant + EntityGraphNodeSet pred; + EntityGraphNodeSet succ; + isize index; // Index in array/queue + isize dep_count; +}; + + + +struct ImportGraphNode; +typedef PtrSet ImportGraphNodeSet; + + +struct ImportGraphNode { + Scope * scope; + String path; + isize file_id; + ImportGraphNodeSet pred; + ImportGraphNodeSet succ; + isize index; // Index in array/queue + isize dep_count; +}; + + +struct ForeignContext { + AstNode * curr_library; + ProcCallingConvention default_cc; + String link_prefix; + bool in_export; +}; + +struct CheckerContext { + Scope * file_scope; + Scope * scope; + DeclInfo * decl; + u32 stmt_state_flags; + bool in_defer; // TODO(bill): Actually handle correctly + String proc_name; + Type * type_hint; + DeclInfo * curr_proc_decl; + ForeignContext foreign_context; + + bool collect_delayed_decls; + bool allow_polymorphic_types; + bool no_polymorphic_errors; + Scope * polymorphic_scope; +}; + + +// CheckerInfo stores all the symbol information for a type-checked program +struct CheckerInfo { + Map types; // Key: AstNode * | Expression -> Type (and value) + Map untyped; // Key: AstNode * | Expression -> ExprInfo + Map files; // Key: String (full path) + Map foreigns; // Key: String + Array definitions; + Array entities; + Array variable_init_order; + + Map > gen_procs; // Key: AstNode * | Identifier -> Entity + Map > gen_types; // Key: Type * + + Map type_info_map; // Key: Type * + isize type_info_count; + + Scope * init_scope; + Entity * entry_point; + PtrSet minimum_dependency_set; +}; + +struct Checker { + Parser * parser; + CheckerInfo info; + gbMutex mutex; + + AstFile * curr_ast_file; + Scope * global_scope; + // NOTE(bill): Procedures to check + Array procs; + Map file_scopes; // Key: String (fullpath) + Array file_order; + + gbAllocator allocator; + gbArena arena; + gbArena tmp_arena; + gbAllocator tmp_allocator; + + CheckerContext context; + + Array proc_stack; + bool done_preload; + + PtrSet checked_files; + +}; + + + +HashKey hash_node (AstNode *node) { return hash_pointer(node); } +HashKey hash_ast_file (AstFile *file) { return hash_pointer(file); } +HashKey hash_entity (Entity *e) { return hash_pointer(e); } +HashKey hash_type (Type *t) { return hash_pointer(t); } +HashKey hash_decl_info(DeclInfo *decl) { return hash_pointer(decl); } + + + +// CheckerInfo API +TypeAndValue type_and_value_of_expr (CheckerInfo *i, AstNode *expr); +Type * type_of_expr (CheckerInfo *i, AstNode *expr); +Entity * entity_of_ident (CheckerInfo *i, AstNode *identifier); +Entity * implicit_entity_of_node(CheckerInfo *i, AstNode *clause); +Scope * scope_of_node (CheckerInfo *i, AstNode *node); +DeclInfo * decl_info_of_ident (CheckerInfo *i, AstNode *ident); +DeclInfo * decl_info_of_entity (CheckerInfo *i, Entity * e); +AstFile * ast_file_of_filename (CheckerInfo *i, String filename); +// IMPORTANT: Only to use once checking is done +isize type_info_index (CheckerInfo *i, Type * type, bool error_on_failure = true); + + +Entity *current_scope_lookup_entity(Scope *s, String name); +Entity *scope_lookup_entity (Scope *s, String name); +void scope_lookup_parent_entity (Scope *s, String name, Scope **scope_, Entity **entity_); +Entity *scope_insert_entity (Scope *s, Entity *entity); + + +ExprInfo *check_get_expr_info (CheckerInfo *i, AstNode *expr); +void check_set_expr_info (CheckerInfo *i, AstNode *expr, ExprInfo info); +void check_remove_expr_info (CheckerInfo *i, AstNode *expr); +void add_untyped (CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value); +void add_type_and_value (CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value); +void add_entity_use (Checker *c, AstNode *identifier, Entity *entity); +void add_implicit_entity (Checker *c, AstNode *node, Entity *e); +void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d); + +void check_add_import_decl(Checker *c, AstNodeImportDecl *id); +void check_add_export_decl(Checker *c, AstNodeExportDecl *ed); +void check_add_foreign_import_decl(Checker *c, AstNode *decl); + + + +bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global = false); +void check_collect_entities(Checker *c, Array nodes); +void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws); +void check_delayed_file_import_entity(Checker *c, AstNode *decl); + + +struct AttributeContext { + String link_name; + String link_prefix; + isize init_expr_list_count; + String thread_local_model; +}; + +AttributeContext make_attribute_context(String link_prefix) { + AttributeContext ac = {}; + ac.link_prefix = link_prefix; + return ac; +} + +#define DECL_ATTRIBUTE_PROC(_name) bool _name(Checker *c, AstNode *elem, String name, ExactValue value, AttributeContext *ac) +typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc); + +void check_decl_attributes(Checker *c, Array attributes, DeclAttributeProc *proc, AttributeContext *ac); diff --git a/src/entity.cpp b/src/entity.cpp index 857c5adee..ff149351f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -37,14 +37,15 @@ enum EntityFlag { EntityFlag_Using = 1<<2, EntityFlag_Field = 1<<3, EntityFlag_Param = 1<<4, - EntityFlag_ArrayElem = 1<<5, - EntityFlag_Ellipsis = 1<<6, - EntityFlag_NoAlias = 1<<7, - EntityFlag_TypeField = 1<<8, - EntityFlag_Value = 1<<9, - EntityFlag_Sret = 1<<10, - EntityFlag_BitFieldValue = 1<<11, - EntityFlag_PolyConst = 1<<12, + EntityFlag_Result = 1<<5, + EntityFlag_ArrayElem = 1<<6, + EntityFlag_Ellipsis = 1<<7, + EntityFlag_NoAlias = 1<<8, + EntityFlag_TypeField = 1<<9, + EntityFlag_Value = 1<<10, + EntityFlag_Sret = 1<<11, + EntityFlag_BitFieldValue = 1<<12, + EntityFlag_PolyConst = 1<<13, EntityFlag_CVarArg = 1<<20, }; diff --git a/src/ir.cpp b/src/ir.cpp index 36e4836d2..c151a52cf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6657,63 +6657,14 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { isize return_count = proc->type->Proc.result_count; isize res_count = rs->results.count; - if (res_count > 0 && - rs->results[0]->kind == AstNode_FieldValue) { - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); - defer (gb_temp_arena_memory_end(tmp)); - - Array results; - array_init_count(&results, proc->module->tmp_allocator, return_count); - - for_array(arg_index, rs->results) { - AstNode *arg = rs->results[arg_index]; - ast_node(fv, FieldValue, arg); - GB_ASSERT(fv->field->kind == AstNode_Ident); - String name = fv->field->Ident.token.string; - isize index = lookup_procedure_result(&proc->type->Proc, name); - GB_ASSERT(index >= 0); - irValue *expr = ir_build_expr(proc, fv->value); - results[index] = expr; - } - for (isize i = 0; i < return_count; i++) { - Entity *e = tuple->variables[i]; - GB_ASSERT(e->kind == Entity_Variable); - if (results[i] == nullptr) { - if (e->Variable.default_value.kind != ExactValue_Invalid) { - results[i] = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value); - } else { - results[i] = ir_value_nil(proc->module->allocator, e->type); - } - } else { - results[i] = ir_emit_conv(proc, results[i], e->type); - } - } - - if (results.count == 1) { - v = results[0]; - } else { - GB_ASSERT(results.count == return_count); - - Type *ret_type = proc->type->Proc.results; - v = ir_add_local_generated(proc, ret_type); - for_array(i, results) { - irValue *field = ir_emit_struct_ep(proc, v, cast(i32)i); - irValue *res = results[i]; - ir_emit_store(proc, field, res); - } - - v = ir_emit_load(proc, v); - } - } else if (return_count == 0) { + if (return_count == 0) { // No return values } else if (return_count == 1) { Entity *e = tuple->variables[0]; if (res_count == 0) { - if (e->Variable.default_value.kind != ExactValue_Invalid) { - v = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value); - } else { - v = ir_value_nil(proc->module->allocator, e->type); - } + irValue **found = map_get(&proc->module->values, hash_entity(e)); + GB_ASSERT(found); + v = ir_emit_load(proc, *found); } else { v = ir_build_expr(proc, rs->results[0]); v = ir_emit_conv(proc, v, e->type); @@ -6725,34 +6676,29 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { Array results; array_init(&results, proc->module->tmp_allocator, return_count); - isize total_index = 0; - isize res_index = 0; - for (; res_index < res_count; res_index++) { - irValue *res = ir_build_expr(proc, rs->results[res_index]); - Type *t = ir_type(res); - if (t->kind == Type_Tuple) { - for_array(i, t->Tuple.variables) { - Entity *e = t->Tuple.variables[i]; - irValue *v = ir_emit_struct_ev(proc, res, cast(i32)i); - array_add(&results, v); - total_index++; + if (res_count != 0) { + for (isize res_index = 0; res_index < res_count; res_index++) { + irValue *res = ir_build_expr(proc, rs->results[res_index]); + Type *t = ir_type(res); + if (t->kind == Type_Tuple) { + for_array(i, t->Tuple.variables) { + Entity *e = t->Tuple.variables[i]; + irValue *v = ir_emit_struct_ev(proc, res, cast(i32)i); + array_add(&results, v); + } + } else { + array_add(&results, res); } - } else { + } + } else { + for (isize res_index = 0; res_index < return_count; res_index++) { + Entity *e = tuple->variables[res_index]; + irValue **found = map_get(&proc->module->values, hash_entity(e)); + GB_ASSERT(found); + irValue *res = ir_emit_load(proc, *found); array_add(&results, res); - total_index++; } } - while (total_index < return_count) { - Entity *e = tuple->variables[total_index]; - irValue *res = nullptr; - if (e->Variable.default_value.kind != ExactValue_Invalid) { - res = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value); - } else { - res = ir_value_nil(proc->module->allocator, e->type); - } - array_add(&results, res); - total_index++; - } GB_ASSERT(results.count == return_count); @@ -6766,7 +6712,6 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { } v = ir_emit_load(proc, v); - } ir_emit_return(proc, v); @@ -7441,6 +7386,26 @@ void ir_begin_procedure_body(irProcedure *proc) { } } + if (proc->type->Proc.has_named_results) { + GB_ASSERT(proc->type->Proc.result_count > 0); + TypeTuple *results = &proc->type->Proc.results->Tuple; + for_array(i, results->variables) { + Entity *e = results->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + + if (e->token.string != "") { + GB_ASSERT(!is_blank_ident(e->token)); + irValue *res = ir_add_local(proc, e, e->identifier, true); + if (e->Variable.default_value.kind != ExactValue_Invalid) { + irValue *c = ir_value_constant(a, e->type, e->Variable.default_value); + ir_emit_store(proc, res, c); + } + } + } + } + if (proc->type->Proc.calling_convention == ProcCC_Odin) { Entity *e = make_entity_param(a, nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); diff --git a/src/main.cpp b/src/main.cpp index c0d349b3f..a31821e9b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,15 @@ // #define NO_ARRAY_BOUNDS_CHECK +// #define NO_POINTER_ARITHMETIC #include "common.cpp" #include "timings.cpp" #include "build_settings.cpp" #include "tokenizer.cpp" #include "exact_value.cpp" + +#include "parser.hpp" +#include "checker.hpp" + #include "parser.cpp" #include "docs.cpp" #include "checker.cpp" @@ -37,6 +42,7 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) { // gb_printf_err("%.*s\n", cast(int)cmd_len, cmd_line); tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + defer (gb_temp_arena_memory_end(tmp)); cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1)); @@ -54,7 +60,6 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) { exit_code = -1; } - gb_temp_arena_memory_end(tmp); return exit_code; } #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) @@ -386,12 +391,10 @@ bool parse_build_flags(Array args) { case BuildFlag_CrossCompile: { GB_ASSERT(value.kind == ExactValue_String); cross_compile_target = value.value_string; -#ifdef GB_SYSTEM_UNIX -#ifdef GB_ARCH_64_BIT +#if defined(GB_SYSTEM_UNIX) && defined(GB_ARCH_64_BIT) if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) { } else -#endif #endif { gb_printf_err("Unsupported cross compilation target '%.*s'\n", LIT(cross_compile_target)); diff --git a/src/parser.cpp b/src/parser.cpp index 0c47bf360..e0c919667 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,543 +1,3 @@ -struct AstNode; -struct Scope; -struct Entity; -struct DeclInfo; - -enum ParseFileError { - ParseFile_None, - - ParseFile_WrongExtension, - ParseFile_InvalidFile, - ParseFile_EmptyFile, - ParseFile_Permission, - ParseFile_NotFound, - ParseFile_InvalidToken, - - ParseFile_Count, -}; - -struct CommentGroup { - Array list; // Token_Comment -}; - - -enum ImportedFileKind { - ImportedFile_Normal, - ImportedFile_Shared, - ImportedFile_Init, -}; - -struct ImportedFile { - ImportedFileKind kind; - String path; - String rel_path; - TokenPos pos; // import - isize index; -}; - - -struct AstFile { - isize id; - String fullpath; - gbArena arena; - Tokenizer tokenizer; - Array tokens; - isize curr_token_index; - Token curr_token; - Token prev_token; // previous non-comment - - // >= 0: In Expression - // < 0: In Control Clause - // NOTE(bill): Used to prevent type literals in control clauses - isize expr_level; - bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases - bool in_foreign_block; - bool allow_type; - isize when_level; - - Array decls; - ImportedFileKind file_kind; - bool is_global_scope; - Array imports_and_exports; // 'import' 'using import' 'export' - - - AstNode * curr_proc; - isize scope_level; - Scope * scope; // NOTE(bill): Created in checker - DeclInfo * decl_info; // NOTE(bill): Created in checker - - - CommentGroup lead_comment; // Comment (block) before the decl - CommentGroup line_comment; // Comment after the semicolon - CommentGroup docs; // current docs - Array comments; // All the comments! - - -#define PARSER_MAX_FIX_COUNT 6 - isize fix_count; - TokenPos fix_prev_pos; -}; - - -struct Parser { - String init_fullpath; - Array files; - Array imports; - isize total_token_count; - isize total_line_count; - gbMutex file_add_mutex; - gbMutex file_decl_mutex; -}; - -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_require_results = 1<<4, -}; - -enum ProcCallingConvention { - ProcCC_Invalid = 0, - ProcCC_Odin, - ProcCC_Contextless, - ProcCC_CDecl, - ProcCC_StdCall, - ProcCC_FastCall, - - // TODO(bill): Add extra calling conventions - // ProcCC_VectorCall, - // ProcCC_ClrCall, - - ProcCC_ForeignBlockDefault = -1, -}; - -enum StmtStateFlag { - StmtStateFlag_bounds_check = 1<<0, - StmtStateFlag_no_bounds_check = 1<<1, -}; - -enum FieldFlag { - FieldFlag_NONE = 0, - FieldFlag_ellipsis = 1<<0, - FieldFlag_using = 1<<1, - FieldFlag_no_alias = 1<<2, - FieldFlag_c_vararg = 1<<3, - FieldFlag_in = 1<<4, - - // FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_in, - FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg, - FieldFlag_Struct = FieldFlag_using, -}; - -enum StmtAllowFlag { - StmtAllowFlag_None = 0, - StmtAllowFlag_In = 1<<0, - StmtAllowFlag_Label = 1<<1, -}; - - - -Array make_ast_node_array(AstFile *f, isize init_capacity = 8) { - Array a; - array_init(&a, heap_allocator(), init_capacity); - return a; -} - - -// NOTE(bill): This massive define is so it is possible to create a discriminated union (and extra debug info) -// for the AstNode. I personally prefer discriminated unions over subtype polymorphism as I can preallocate -// all the nodes and even memcpy in a different kind of node -#define AST_NODE_KINDS \ - AST_NODE_KIND(Ident, "identifier", struct { \ - Token token; \ - Entity *entity; \ - }) \ - AST_NODE_KIND(Implicit, "implicit", Token) \ - AST_NODE_KIND(Undef, "undef", Token) \ - AST_NODE_KIND(BasicLit, "basic literal", struct { \ - Token token; \ - }) \ - AST_NODE_KIND(BasicDirective, "basic directive", struct { \ - Token token; \ - String name; \ - }) \ - AST_NODE_KIND(Ellipsis, "ellipsis", struct { \ - Token token; \ - AstNode *expr; \ - }) \ - AST_NODE_KIND(ProcGroup, "procedure group", struct { \ - Token token; \ - Token open; \ - Token close; \ - Array args; \ - }) \ - AST_NODE_KIND(ProcLit, "procedure literal", struct { \ - AstNode *type; \ - AstNode *body; \ - u64 tags; \ - ProcInlining inlining; \ - }) \ - AST_NODE_KIND(CompoundLit, "compound literal", struct { \ - AstNode *type; \ - Array elems; \ - Token open, close; \ - }) \ -AST_NODE_KIND(_ExprBegin, "", i32) \ - AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ - AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \ - AST_NODE_KIND(RunExpr, "run expression", struct { Token token, name; AstNode *expr; }) \ - AST_NODE_KIND(UnaryExpr, "unary expression", struct { Token op; AstNode *expr; }) \ - AST_NODE_KIND(BinaryExpr, "binary expression", struct { Token op; AstNode *left, *right; } ) \ - AST_NODE_KIND(ParenExpr, "parentheses expression", struct { AstNode *expr; Token open, close; }) \ - AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \ - AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \ - AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \ - AST_NODE_KIND(SliceExpr, "slice expression", struct { \ - AstNode *expr; \ - Token open, close; \ - Token interval; \ - AstNode *low, *high; \ - }) \ - AST_NODE_KIND(CallExpr, "call expression", struct { \ - AstNode * proc; \ - Array args; \ - Token open; \ - Token close; \ - Token ellipsis; \ - }) \ - AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \ - AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \ - AST_NODE_KIND(TypeAssertion, "type assertion", struct { AstNode *expr; Token dot; AstNode *type; }) \ - AST_NODE_KIND(TypeCast, "type cast", struct { Token token; AstNode *type, *expr; }) \ -AST_NODE_KIND(_ExprEnd, "", i32) \ -AST_NODE_KIND(_StmtBegin, "", i32) \ - AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \ - AST_NODE_KIND(EmptyStmt, "empty statement", struct { Token token; }) \ - AST_NODE_KIND(ExprStmt, "expression statement", struct { AstNode *expr; } ) \ - AST_NODE_KIND(TagStmt, "tag statement", struct { \ - Token token; \ - Token name; \ - AstNode *stmt; \ - }) \ - AST_NODE_KIND(AssignStmt, "assign statement", struct { \ - Token op; \ - Array lhs, rhs; \ - }) \ - AST_NODE_KIND(IncDecStmt, "increment decrement statement", struct { \ - Token op; \ - AstNode *expr; \ - }) \ -AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ - AST_NODE_KIND(BlockStmt, "block statement", struct { \ - Array stmts; \ - Token open, close; \ - }) \ - AST_NODE_KIND(IfStmt, "if statement", struct { \ - Token token; \ - AstNode *init; \ - AstNode *cond; \ - AstNode *body; \ - AstNode *else_stmt; \ - }) \ - AST_NODE_KIND(WhenStmt, "when statement", struct { \ - Token token; \ - AstNode *cond; \ - AstNode *body; \ - AstNode *else_stmt; \ - bool is_cond_determined; \ - bool determined_cond; \ - }) \ - AST_NODE_KIND(ReturnStmt, "return statement", struct { \ - Token token; \ - Array results; \ - }) \ - AST_NODE_KIND(ForStmt, "for statement", struct { \ - Token token; \ - AstNode *label; \ - AstNode *init; \ - AstNode *cond; \ - AstNode *post; \ - AstNode *body; \ - }) \ - AST_NODE_KIND(RangeStmt, "range statement", struct { \ - Token token; \ - AstNode *label; \ - AstNode *val0; \ - AstNode *val1; \ - Token in_token; \ - AstNode *expr; \ - AstNode *body; \ - }) \ - AST_NODE_KIND(CaseClause, "case clause", struct { \ - Token token; \ - Array list; \ - Array stmts; \ - Entity *implicit_entity; \ - }) \ - AST_NODE_KIND(SwitchStmt, "switch statement", struct { \ - Token token; \ - AstNode *label; \ - AstNode *init; \ - AstNode *tag; \ - AstNode *body; \ - }) \ - AST_NODE_KIND(TypeSwitchStmt, "type switch statement", struct { \ - Token token; \ - AstNode *label; \ - AstNode *tag; \ - AstNode *body; \ - }) \ - AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ - AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \ - AST_NODE_KIND(UsingStmt, "using statement", struct { \ - Token token; \ - Array list; \ - }) \ - AST_NODE_KIND(UsingInStmt, "using in statement", struct { \ - Token using_token; \ - Array list; \ - Token in_token; \ - AstNode *expr; \ - }) \ - AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ - Token string; \ - AstNode *operand; \ - }) \ - AST_NODE_KIND(AsmStmt, "assembly statement", struct { \ - Token token; \ - bool is_volatile; \ - Token open, close; \ - Token code_string; \ - AstNode *output_list; \ - AstNode *input_list; \ - AstNode *clobber_list; \ - isize output_count, input_count, clobber_count; \ - }) \ - AST_NODE_KIND(PushContext, "context <- statement", struct { \ - Token token; \ - AstNode *expr; \ - AstNode *body; \ - }) \ -AST_NODE_KIND(_ComplexStmtEnd, "", i32) \ -AST_NODE_KIND(_StmtEnd, "", i32) \ -AST_NODE_KIND(_DeclBegin, "", i32) \ - AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \ - AST_NODE_KIND(ForeignBlockDecl, "foreign block declaration", struct { \ - Token token; \ - AstNode * foreign_library; \ - Token open, close; \ - Array decls; \ - Array attributes; \ - bool been_handled; \ - CommentGroup docs; \ - }) \ - AST_NODE_KIND(Label, "label", struct { \ - Token token; \ - AstNode *name; \ - }) \ - AST_NODE_KIND(ValueDecl, "value declaration", struct { \ - Array names; \ - AstNode * type; \ - Array values; \ - bool is_using; \ - bool is_mutable; \ - bool been_handled; \ - Array attributes; \ - CommentGroup docs; \ - CommentGroup comment; \ - }) \ - AST_NODE_KIND(ImportDecl, "import declaration", struct { \ - AstFile *file; \ - Token token; \ - Token relpath; \ - String fullpath; \ - Token import_name; \ - bool is_using; \ - bool been_handled; \ - Array using_in_list; \ - CommentGroup docs; \ - CommentGroup comment; \ - }) \ - AST_NODE_KIND(ExportDecl, "export declaration", struct { \ - AstFile *file; \ - Token token; \ - Token relpath; \ - String fullpath; \ - bool been_handled; \ - Array using_in_list; \ - CommentGroup docs; \ - CommentGroup comment; \ - }) \ - AST_NODE_KIND(ForeignImportDecl, "foreign import declaration", struct { \ - Token token; \ - Token filepath; \ - Token library_name; \ - String base_dir; \ - String collection_name; \ - String fullpath; \ - bool been_handled; \ - CommentGroup docs; \ - CommentGroup comment; \ - }) \ -AST_NODE_KIND(_DeclEnd, "", i32) \ - AST_NODE_KIND(Attribute, "attribute", struct { \ - Token token; \ - AstNode *type; \ - Array elems; \ - Token open, close; \ - }) \ - AST_NODE_KIND(Field, "field", struct { \ - Array names; \ - AstNode * type; \ - AstNode * default_value; \ - u32 flags; \ - CommentGroup docs; \ - CommentGroup comment; \ - }) \ - AST_NODE_KIND(FieldList, "field list", struct { \ - Token token; \ - Array list; \ - }) \ - AST_NODE_KIND(UnionField, "union field", struct { \ - AstNode *name; \ - AstNode *list; \ - }) \ -AST_NODE_KIND(_TypeBegin, "", i32) \ - AST_NODE_KIND(TypeType, "type", struct { \ - Token token; \ - AstNode *specialization; \ - }) \ - AST_NODE_KIND(HelperType, "helper type", struct { \ - Token token; \ - AstNode *type; \ - }) \ - AST_NODE_KIND(AliasType, "alias type", struct { \ - Token token; \ - AstNode *type; \ - }) \ - AST_NODE_KIND(PolyType, "polymorphic type", struct { \ - Token token; \ - AstNode *type; \ - AstNode *specialization; \ - }) \ - AST_NODE_KIND(ProcType, "procedure type", struct { \ - Token token; \ - AstNode *params; \ - AstNode *results; \ - u64 tags; \ - ProcCallingConvention calling_convention; \ - bool generic; \ - }) \ - AST_NODE_KIND(PointerType, "pointer type", struct { \ - Token token; \ - AstNode *type; \ - }) \ - AST_NODE_KIND(ArrayType, "array type", struct { \ - Token token; \ - AstNode *count; \ - AstNode *elem; \ - }) \ - AST_NODE_KIND(DynamicArrayType, "dynamic array type", struct { \ - Token token; \ - AstNode *elem; \ - }) \ - AST_NODE_KIND(StructType, "struct type", struct { \ - Token token; \ - Array fields; \ - isize field_count; \ - AstNode * polymorphic_params; \ - bool is_packed; \ - bool is_raw_union; \ - AstNode * align; \ - }) \ - AST_NODE_KIND(UnionType, "union type", struct { \ - Token token; \ - Array variants; \ - AstNode * align; \ - }) \ - AST_NODE_KIND(EnumType, "enum type", struct { \ - Token token; \ - AstNode * base_type; \ - Array fields; /* FieldValue */ \ - }) \ - AST_NODE_KIND(BitFieldType, "bit field type", struct { \ - Token token; \ - Array fields; /* FieldValue with : */ \ - AstNode * align; \ - }) \ - AST_NODE_KIND(MapType, "map type", struct { \ - Token token; \ - AstNode *count; \ - AstNode *key; \ - AstNode *value; \ - }) \ -AST_NODE_KIND(_TypeEnd, "", i32) - -enum AstNodeKind { - AstNode_Invalid, -#define AST_NODE_KIND(_kind_name_, ...) GB_JOIN2(AstNode_, _kind_name_), - AST_NODE_KINDS -#undef AST_NODE_KIND - AstNode_Count, -}; - -String const ast_node_strings[] = { - {cast(u8 *)"invalid node", gb_size_of("invalid node")}, -#define AST_NODE_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1}, - AST_NODE_KINDS -#undef AST_NODE_KIND -}; - -#define AST_NODE_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(AstNode, _kind_name_); - AST_NODE_KINDS -#undef AST_NODE_KIND - -struct AstNode { - AstNodeKind kind; - u32 stmt_state_flags; - AstFile * file; - Scope * scope; - - union { -#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_; - AST_NODE_KINDS -#undef AST_NODE_KIND - }; -}; - - -#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_)) -#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_); -#ifndef case_end -#define case_end } break; -#endif - - -gb_inline bool is_ast_node_expr(AstNode *node) { - return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1); -} -gb_inline bool is_ast_node_stmt(AstNode *node) { - return gb_is_between(node->kind, AstNode__StmtBegin+1, AstNode__StmtEnd-1); -} -gb_inline bool is_ast_node_complex_stmt(AstNode *node) { - return gb_is_between(node->kind, AstNode__ComplexStmtBegin+1, AstNode__ComplexStmtEnd-1); -} -gb_inline bool is_ast_node_decl(AstNode *node) { - return gb_is_between(node->kind, AstNode__DeclBegin+1, AstNode__DeclEnd-1); -} -gb_inline bool is_ast_node_type(AstNode *node) { - return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1); -} -gb_inline bool is_ast_node_when_stmt(AstNode *node) { - return node->kind == AstNode_WhenStmt; -} - - Token ast_node_token(AstNode *node) { switch (node->kind) { case AstNode_Ident: return node->Ident.token; @@ -3135,7 +2595,7 @@ AstNode *parse_results(AstFile *f) { AstNode *list = nullptr; expect_token(f, Token_OpenParen); - list = parse_field_list(f, nullptr, 0, Token_CloseParen, true, false); + list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false); expect_token_after(f, Token_CloseParen, "parameter list"); return list; } @@ -3547,6 +3007,10 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok Array names = {}; AstNode *type = list[i].node; Token token = blank_token; + if (allowed_flags&FieldFlag_Results) { + // NOTE(bill): Make this nothing and not `_` + token.string = str_lit(""); + } array_init_count(&names, heap_allocator(), 1); token.pos = ast_node_token(type).pos; @@ -3725,11 +3189,11 @@ AstNode *parse_return_stmt(AstFile *f) { while (f->curr_token.kind != Token_Semicolon) { AstNode *arg = parse_expr(f, false); - if (f->curr_token.kind == Token_Eq) { - Token eq = expect_token(f, Token_Eq); - AstNode *value = parse_value(f); - arg = ast_field_value(f, arg, value, eq); - } + // if (f->curr_token.kind == Token_Eq) { + // Token eq = expect_token(f, Token_Eq); + // AstNode *value = parse_value(f); + // arg = ast_field_value(f, arg, value, eq); + // } array_add(&results, arg); if (f->curr_token.kind != Token_Comma || diff --git a/src/parser.hpp b/src/parser.hpp new file mode 100644 index 000000000..f73afe40a --- /dev/null +++ b/src/parser.hpp @@ -0,0 +1,545 @@ +struct AstNode; +struct Scope; +struct Type; +struct Entity; +struct DeclInfo; + + +enum ParseFileError { + ParseFile_None, + + ParseFile_WrongExtension, + ParseFile_InvalidFile, + ParseFile_EmptyFile, + ParseFile_Permission, + ParseFile_NotFound, + ParseFile_InvalidToken, + + ParseFile_Count, +}; + +struct CommentGroup { + Array list; // Token_Comment +}; + + +enum ImportedFileKind { + ImportedFile_Normal, + ImportedFile_Shared, + ImportedFile_Init, +}; + +struct ImportedFile { + ImportedFileKind kind; + String path; + String rel_path; + TokenPos pos; // import + isize index; +}; + + +struct AstFile { + isize id; + String fullpath; + gbArena arena; + Tokenizer tokenizer; + Array tokens; + isize curr_token_index; + Token curr_token; + Token prev_token; // previous non-comment + + // >= 0: In Expression + // < 0: In Control Clause + // NOTE(bill): Used to prevent type literals in control clauses + isize expr_level; + bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases + bool in_foreign_block; + bool allow_type; + isize when_level; + + Array decls; + ImportedFileKind file_kind; + bool is_global_scope; + Array imports_and_exports; // 'import' 'using import' 'export' + + + AstNode * curr_proc; + isize scope_level; + Scope * scope; // NOTE(bill): Created in checker + DeclInfo * decl_info; // NOTE(bill): Created in checker + + + CommentGroup lead_comment; // Comment (block) before the decl + CommentGroup line_comment; // Comment after the semicolon + CommentGroup docs; // current docs + Array comments; // All the comments! + + +#define PARSER_MAX_FIX_COUNT 6 + isize fix_count; + TokenPos fix_prev_pos; +}; + + +struct Parser { + String init_fullpath; + Array files; + Array imports; + isize total_token_count; + isize total_line_count; + gbMutex file_add_mutex; + gbMutex file_decl_mutex; +}; + +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_require_results = 1<<4, +}; + +enum ProcCallingConvention { + ProcCC_Invalid = 0, + ProcCC_Odin, + ProcCC_Contextless, + ProcCC_CDecl, + ProcCC_StdCall, + ProcCC_FastCall, + + // TODO(bill): Add extra calling conventions + // ProcCC_VectorCall, + // ProcCC_ClrCall, + + ProcCC_ForeignBlockDefault = -1, +}; + +enum StmtStateFlag { + StmtStateFlag_bounds_check = 1<<0, + StmtStateFlag_no_bounds_check = 1<<1, +}; + +enum FieldFlag { + FieldFlag_NONE = 0, + FieldFlag_ellipsis = 1<<0, + FieldFlag_using = 1<<1, + FieldFlag_no_alias = 1<<2, + FieldFlag_c_vararg = 1<<3, + + FieldFlag_in = 1<<5, + + + FieldFlag_Results = 1<<16, + + // FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_in, + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg, + FieldFlag_Struct = FieldFlag_using, +}; + +enum StmtAllowFlag { + StmtAllowFlag_None = 0, + StmtAllowFlag_In = 1<<0, + StmtAllowFlag_Label = 1<<1, +}; + + + +Array make_ast_node_array(AstFile *f, isize init_capacity = 8) { + Array a; + array_init(&a, heap_allocator(), init_capacity); + return a; +} + + +// NOTE(bill): This massive define is so it is possible to create a discriminated union (and extra debug info) +// for the AstNode. I personally prefer discriminated unions over subtype polymorphism as I can preallocate +// all the nodes and even memcpy in a different kind of node +#define AST_NODE_KINDS \ + AST_NODE_KIND(Ident, "identifier", struct { \ + Token token; \ + Entity *entity; \ + }) \ + AST_NODE_KIND(Implicit, "implicit", Token) \ + AST_NODE_KIND(Undef, "undef", Token) \ + AST_NODE_KIND(BasicLit, "basic literal", struct { \ + Token token; \ + }) \ + AST_NODE_KIND(BasicDirective, "basic directive", struct { \ + Token token; \ + String name; \ + }) \ + AST_NODE_KIND(Ellipsis, "ellipsis", struct { \ + Token token; \ + AstNode *expr; \ + }) \ + AST_NODE_KIND(ProcGroup, "procedure group", struct { \ + Token token; \ + Token open; \ + Token close; \ + Array args; \ + }) \ + AST_NODE_KIND(ProcLit, "procedure literal", struct { \ + AstNode *type; \ + AstNode *body; \ + u64 tags; \ + ProcInlining inlining; \ + }) \ + AST_NODE_KIND(CompoundLit, "compound literal", struct { \ + AstNode *type; \ + Array elems; \ + Token open, close; \ + }) \ +AST_NODE_KIND(_ExprBegin, "", i32) \ + AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ + AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \ + AST_NODE_KIND(RunExpr, "run expression", struct { Token token, name; AstNode *expr; }) \ + AST_NODE_KIND(UnaryExpr, "unary expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(BinaryExpr, "binary expression", struct { Token op; AstNode *left, *right; } ) \ + AST_NODE_KIND(ParenExpr, "parentheses expression", struct { AstNode *expr; Token open, close; }) \ + AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \ + AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \ + AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(SliceExpr, "slice expression", struct { \ + AstNode *expr; \ + Token open, close; \ + Token interval; \ + AstNode *low, *high; \ + }) \ + AST_NODE_KIND(CallExpr, "call expression", struct { \ + AstNode * proc; \ + Array args; \ + Token open; \ + Token close; \ + Token ellipsis; \ + }) \ + AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \ + AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \ + AST_NODE_KIND(TypeAssertion, "type assertion", struct { AstNode *expr; Token dot; AstNode *type; }) \ + AST_NODE_KIND(TypeCast, "type cast", struct { Token token; AstNode *type, *expr; }) \ +AST_NODE_KIND(_ExprEnd, "", i32) \ +AST_NODE_KIND(_StmtBegin, "", i32) \ + AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \ + AST_NODE_KIND(EmptyStmt, "empty statement", struct { Token token; }) \ + AST_NODE_KIND(ExprStmt, "expression statement", struct { AstNode *expr; } ) \ + AST_NODE_KIND(TagStmt, "tag statement", struct { \ + Token token; \ + Token name; \ + AstNode *stmt; \ + }) \ + AST_NODE_KIND(AssignStmt, "assign statement", struct { \ + Token op; \ + Array lhs, rhs; \ + }) \ + AST_NODE_KIND(IncDecStmt, "increment decrement statement", struct { \ + Token op; \ + AstNode *expr; \ + }) \ +AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ + AST_NODE_KIND(BlockStmt, "block statement", struct { \ + Array stmts; \ + Token open, close; \ + }) \ + AST_NODE_KIND(IfStmt, "if statement", struct { \ + Token token; \ + AstNode *init; \ + AstNode *cond; \ + AstNode *body; \ + AstNode *else_stmt; \ + }) \ + AST_NODE_KIND(WhenStmt, "when statement", struct { \ + Token token; \ + AstNode *cond; \ + AstNode *body; \ + AstNode *else_stmt; \ + bool is_cond_determined; \ + bool determined_cond; \ + }) \ + AST_NODE_KIND(ReturnStmt, "return statement", struct { \ + Token token; \ + Array results; \ + }) \ + AST_NODE_KIND(ForStmt, "for statement", struct { \ + Token token; \ + AstNode *label; \ + AstNode *init; \ + AstNode *cond; \ + AstNode *post; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(RangeStmt, "range statement", struct { \ + Token token; \ + AstNode *label; \ + AstNode *val0; \ + AstNode *val1; \ + Token in_token; \ + AstNode *expr; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(CaseClause, "case clause", struct { \ + Token token; \ + Array list; \ + Array stmts; \ + Entity *implicit_entity; \ + }) \ + AST_NODE_KIND(SwitchStmt, "switch statement", struct { \ + Token token; \ + AstNode *label; \ + AstNode *init; \ + AstNode *tag; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(TypeSwitchStmt, "type switch statement", struct { \ + Token token; \ + AstNode *label; \ + AstNode *tag; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ + AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \ + AST_NODE_KIND(UsingStmt, "using statement", struct { \ + Token token; \ + Array list; \ + }) \ + AST_NODE_KIND(UsingInStmt, "using in statement", struct { \ + Token using_token; \ + Array list; \ + Token in_token; \ + AstNode *expr; \ + }) \ + AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ + Token string; \ + AstNode *operand; \ + }) \ + AST_NODE_KIND(AsmStmt, "assembly statement", struct { \ + Token token; \ + bool is_volatile; \ + Token open, close; \ + Token code_string; \ + AstNode *output_list; \ + AstNode *input_list; \ + AstNode *clobber_list; \ + isize output_count, input_count, clobber_count; \ + }) \ + AST_NODE_KIND(PushContext, "context <- statement", struct { \ + Token token; \ + AstNode *expr; \ + AstNode *body; \ + }) \ +AST_NODE_KIND(_ComplexStmtEnd, "", i32) \ +AST_NODE_KIND(_StmtEnd, "", i32) \ +AST_NODE_KIND(_DeclBegin, "", i32) \ + AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \ + AST_NODE_KIND(ForeignBlockDecl, "foreign block declaration", struct { \ + Token token; \ + AstNode * foreign_library; \ + Token open, close; \ + Array decls; \ + Array attributes; \ + bool been_handled; \ + CommentGroup docs; \ + }) \ + AST_NODE_KIND(Label, "label", struct { \ + Token token; \ + AstNode *name; \ + }) \ + AST_NODE_KIND(ValueDecl, "value declaration", struct { \ + Array names; \ + AstNode * type; \ + Array values; \ + bool is_using; \ + bool is_mutable; \ + bool been_handled; \ + Array attributes; \ + CommentGroup docs; \ + CommentGroup comment; \ + }) \ + AST_NODE_KIND(ImportDecl, "import declaration", struct { \ + AstFile *file; \ + Token token; \ + Token relpath; \ + String fullpath; \ + Token import_name; \ + bool is_using; \ + bool been_handled; \ + Array using_in_list; \ + CommentGroup docs; \ + CommentGroup comment; \ + }) \ + AST_NODE_KIND(ExportDecl, "export declaration", struct { \ + AstFile *file; \ + Token token; \ + Token relpath; \ + String fullpath; \ + bool been_handled; \ + Array using_in_list; \ + CommentGroup docs; \ + CommentGroup comment; \ + }) \ + AST_NODE_KIND(ForeignImportDecl, "foreign import declaration", struct { \ + Token token; \ + Token filepath; \ + Token library_name; \ + String base_dir; \ + String collection_name; \ + String fullpath; \ + bool been_handled; \ + CommentGroup docs; \ + CommentGroup comment; \ + }) \ +AST_NODE_KIND(_DeclEnd, "", i32) \ + AST_NODE_KIND(Attribute, "attribute", struct { \ + Token token; \ + AstNode *type; \ + Array elems; \ + Token open, close; \ + }) \ + AST_NODE_KIND(Field, "field", struct { \ + Array names; \ + AstNode * type; \ + AstNode * default_value; \ + u32 flags; \ + CommentGroup docs; \ + CommentGroup comment; \ + }) \ + AST_NODE_KIND(FieldList, "field list", struct { \ + Token token; \ + Array list; \ + }) \ + AST_NODE_KIND(UnionField, "union field", struct { \ + AstNode *name; \ + AstNode *list; \ + }) \ +AST_NODE_KIND(_TypeBegin, "", i32) \ + AST_NODE_KIND(TypeType, "type", struct { \ + Token token; \ + AstNode *specialization; \ + }) \ + AST_NODE_KIND(HelperType, "helper type", struct { \ + Token token; \ + AstNode *type; \ + }) \ + AST_NODE_KIND(AliasType, "alias type", struct { \ + Token token; \ + AstNode *type; \ + }) \ + AST_NODE_KIND(PolyType, "polymorphic type", struct { \ + Token token; \ + AstNode *type; \ + AstNode *specialization; \ + }) \ + AST_NODE_KIND(ProcType, "procedure type", struct { \ + Token token; \ + AstNode *params; \ + AstNode *results; \ + u64 tags; \ + ProcCallingConvention calling_convention; \ + bool generic; \ + }) \ + AST_NODE_KIND(PointerType, "pointer type", struct { \ + Token token; \ + AstNode *type; \ + }) \ + AST_NODE_KIND(ArrayType, "array type", struct { \ + Token token; \ + AstNode *count; \ + AstNode *elem; \ + }) \ + AST_NODE_KIND(DynamicArrayType, "dynamic array type", struct { \ + Token token; \ + AstNode *elem; \ + }) \ + AST_NODE_KIND(StructType, "struct type", struct { \ + Token token; \ + Array fields; \ + isize field_count; \ + AstNode * polymorphic_params; \ + bool is_packed; \ + bool is_raw_union; \ + AstNode * align; \ + }) \ + AST_NODE_KIND(UnionType, "union type", struct { \ + Token token; \ + Array variants; \ + AstNode * align; \ + }) \ + AST_NODE_KIND(EnumType, "enum type", struct { \ + Token token; \ + AstNode * base_type; \ + Array fields; /* FieldValue */ \ + }) \ + AST_NODE_KIND(BitFieldType, "bit field type", struct { \ + Token token; \ + Array fields; /* FieldValue with : */ \ + AstNode * align; \ + }) \ + AST_NODE_KIND(MapType, "map type", struct { \ + Token token; \ + AstNode *count; \ + AstNode *key; \ + AstNode *value; \ + }) \ +AST_NODE_KIND(_TypeEnd, "", i32) + +enum AstNodeKind { + AstNode_Invalid, +#define AST_NODE_KIND(_kind_name_, ...) GB_JOIN2(AstNode_, _kind_name_), + AST_NODE_KINDS +#undef AST_NODE_KIND + AstNode_Count, +}; + +String const ast_node_strings[] = { + {cast(u8 *)"invalid node", gb_size_of("invalid node")}, +#define AST_NODE_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1}, + AST_NODE_KINDS +#undef AST_NODE_KIND +}; + +#define AST_NODE_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(AstNode, _kind_name_); + AST_NODE_KINDS +#undef AST_NODE_KIND + +struct AstNode { + AstNodeKind kind; + u32 stmt_state_flags; + AstFile * file; + Scope * scope; + + union { +#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_; + AST_NODE_KINDS +#undef AST_NODE_KIND + }; +}; + + +#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_)) +#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_); +#ifndef case_end +#define case_end } break; +#endif + + +gb_inline bool is_ast_node_expr(AstNode *node) { + return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1); +} +gb_inline bool is_ast_node_stmt(AstNode *node) { + return gb_is_between(node->kind, AstNode__StmtBegin+1, AstNode__StmtEnd-1); +} +gb_inline bool is_ast_node_complex_stmt(AstNode *node) { + return gb_is_between(node->kind, AstNode__ComplexStmtBegin+1, AstNode__ComplexStmtEnd-1); +} +gb_inline bool is_ast_node_decl(AstNode *node) { + return gb_is_between(node->kind, AstNode__DeclBegin+1, AstNode__DeclEnd-1); +} +gb_inline bool is_ast_node_type(AstNode *node) { + return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1); +} +gb_inline bool is_ast_node_when_stmt(AstNode *node) { + return node->kind == AstNode_WhenStmt; +} + diff --git a/src/types.cpp b/src/types.cpp index eb673232f..d515d9390 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -158,6 +158,7 @@ struct TypeStruct { bool is_polymorphic; \ bool is_poly_specialized; \ bool has_proc_default_values; \ + bool has_named_results; \ isize specialization_count; \ ProcCallingConvention calling_convention; \ }) \