diff options
Diffstat (limited to 'src/parser.cpp')
| -rw-r--r-- | src/parser.cpp | 524 |
1 files changed, 301 insertions, 223 deletions
diff --git a/src/parser.cpp b/src/parser.cpp index a377b773c..10aa10119 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -51,6 +51,7 @@ Token ast_token(Ast *node) { case Ast_ReturnStmt: return node->ReturnStmt.token; case Ast_ForStmt: return node->ForStmt.token; case Ast_RangeStmt: return node->RangeStmt.token; + case Ast_InlineRangeStmt: return node->InlineRangeStmt.inline_token; case Ast_CaseClause: return node->CaseClause.token; case Ast_SwitchStmt: return node->SwitchStmt.token; case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; @@ -143,6 +144,7 @@ Ast *clone_ast(Ast *node) { case Ast_ProcLit: n->ProcLit.type = clone_ast(n->ProcLit.type); n->ProcLit.body = clone_ast(n->ProcLit.body); + n->ProcLit.where_clauses = clone_ast_array(n->ProcLit.where_clauses); break; case Ast_CompoundLit: n->CompoundLit.type = clone_ast(n->CompoundLit.type); @@ -257,6 +259,12 @@ Ast *clone_ast(Ast *node) { n->RangeStmt.expr = clone_ast(n->RangeStmt.expr); n->RangeStmt.body = clone_ast(n->RangeStmt.body); break; + case Ast_InlineRangeStmt: + n->InlineRangeStmt.val0 = clone_ast(n->InlineRangeStmt.val0); + n->InlineRangeStmt.val1 = clone_ast(n->InlineRangeStmt.val1); + n->InlineRangeStmt.expr = clone_ast(n->InlineRangeStmt.expr); + n->InlineRangeStmt.body = clone_ast(n->InlineRangeStmt.body); + break; case Ast_CaseClause: n->CaseClause.list = clone_ast_array(n->CaseClause.list); n->CaseClause.stmts = clone_ast_array(n->CaseClause.stmts); @@ -341,10 +349,12 @@ Ast *clone_ast(Ast *node) { n->StructType.fields = clone_ast_array(n->StructType.fields); n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params); n->StructType.align = clone_ast(n->StructType.align); + n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses); break; case Ast_UnionType: n->UnionType.variants = clone_ast_array(n->UnionType.variants); n->UnionType.polymorphic_params = clone_ast(n->UnionType.polymorphic_params); + n->UnionType.where_clauses = clone_ast_array(n->UnionType.where_clauses); break; case Ast_EnumType: n->EnumType.base_type = clone_ast(n->EnumType.base_type); @@ -417,7 +427,7 @@ void syntax_error(Ast *node, char *fmt, ...) { bool ast_node_expect(Ast *node, AstKind kind) { if (node->kind != kind) { - error(node, "Expected %.*s, got %.*s", LIT(ast_strings[kind]), LIT(ast_strings[node->kind])); + syntax_error(node, "Expected %.*s, got %.*s", LIT(ast_strings[kind]), LIT(ast_strings[node->kind])); return false; } return true; @@ -578,6 +588,7 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; + result->BasicLit.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -605,11 +616,13 @@ Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast return result; } -Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags) { +Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_ProcLit); result->ProcLit.type = type; result->ProcLit.body = body; result->ProcLit.tags = tags; + result->ProcLit.where_token = where_token; + result->ProcLit.where_clauses = where_clauses; return result; } @@ -748,6 +761,18 @@ Ast *ast_range_stmt(AstFile *f, Token token, Ast *val0, Ast *val1, Token in_toke return result; } +Ast *ast_inline_range_stmt(AstFile *f, Token inline_token, Token for_token, Ast *val0, Ast *val1, Token in_token, Ast *expr, Ast *body) { + Ast *result = alloc_ast_node(f, Ast_InlineRangeStmt); + result->InlineRangeStmt.inline_token = inline_token; + result->InlineRangeStmt.for_token = for_token; + result->InlineRangeStmt.val0 = val0; + result->InlineRangeStmt.val1 = val1; + result->InlineRangeStmt.in_token = in_token; + result->InlineRangeStmt.expr = expr; + result->InlineRangeStmt.body = body; + return result; +} + Ast *ast_switch_stmt(AstFile *f, Token token, Ast *init, Ast *tag, Ast *body) { Ast *result = alloc_ast_node(f, Ast_SwitchStmt); result->SwitchStmt.token = token; @@ -805,13 +830,14 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) { return result; } -Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, - CommentGroup *docs, CommentGroup *comment) { +Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, Token tag, + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_Field); result->Field.names = names; result->Field.type = type; result->Field.default_value = default_value; result->Field.flags = flags; + result->Field.tag = tag; result->Field.docs = docs; result->Field.comment = comment; return result; @@ -898,7 +924,8 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, - Ast *align) { + Ast *align, + Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); result->StructType.token = token; result->StructType.fields = fields; @@ -907,17 +934,22 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; result->StructType.align = align; + result->StructType.where_token = where_token; + result->StructType.where_clauses = where_clauses; return result; } -Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil) { +Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, + Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = variants; result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; result->UnionType.no_nil = no_nil; + result->UnionType.where_token = where_token; + result->UnionType.where_clauses = where_clauses; return result; } @@ -1118,6 +1150,17 @@ Token advance_token(AstFile *f) { return prev; } +bool peek_token_kind(AstFile *f, TokenKind kind) { + for (isize i = f->curr_token_index+1; i < f->tokens.count; i++) { + Token tok = f->tokens[i]; + if (kind != Token_Comment && tok.kind == Token_Comment) { + continue; + } + return tok.kind == kind; + } + return false; +} + Token expect_token(AstFile *f, TokenKind kind) { Token prev = f->curr_token; if (prev.kind != kind) { @@ -1302,7 +1345,8 @@ bool is_semicolon_optional_for_node(AstFile *f, Ast *s) { case Ast_UnionType: case Ast_EnumType: case Ast_BitFieldType: - return true; + // Require semicolon within a procedure body + return f->curr_proc == nullptr; case Ast_ProcLit: return true; @@ -1336,10 +1380,6 @@ void expect_semicolon(AstFile *f, Ast *s) { return; } - switch (f->curr_token.kind) { - case Token_EOF: - return; - } if (s != nullptr) { if (prev_token.pos.line != f->curr_token.pos.line) { @@ -1352,12 +1392,21 @@ void expect_semicolon(AstFile *f, Ast *s) { case Token_CloseParen: case Token_else: return; + case Token_EOF: + if (is_semicolon_optional_for_node(f, s)) { + return; + } + break; } } String node_string = ast_strings[s->kind]; syntax_error(prev_token, "Expected ';' after %.*s, got %.*s", - LIT(node_string), LIT(token_strings[prev_token.kind])); + LIT(node_string), LIT(token_strings[f->curr_token.kind])); } else { + switch (f->curr_token.kind) { + case Token_EOF: + return; + } syntax_error(prev_token, "Expected ';'"); } fix_advance_to_next_stmt(f); @@ -1461,8 +1510,11 @@ Ast *parse_value(AstFile *f) { if (f->curr_token.kind == Token_OpenBrace) { return parse_literal_value(f, nullptr); } - - Ast *value = parse_expr(f, false); + Ast *value; + bool prev_allow_range = f->allow_range; + f->allow_range = true; + value = parse_expr(f, false); + f->allow_range = prev_allow_range; return value; } @@ -1614,7 +1666,7 @@ void check_polymorphic_params_for_type(AstFile *f, Ast *polymorphic_params, Toke for_array(i, field->Field.names) { Ast *name = field->Field.names[i]; if (name->kind == Ast_PolyType) { - error(name, "Polymorphic names are not needed for %.*s parameters", LIT(token.string)); + syntax_error(name, "Polymorphic names are not needed for %.*s parameters", LIT(token.string)); return; // TODO(bill): Err multiple times or just the once? } } @@ -1687,7 +1739,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { operand = ast_bad_expr(f, token, f->curr_token); } operand->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ else if (name.string == "file") { return ast_basic_directive(f, token, name.string); + } */ else if (name.string == "file") { + return ast_basic_directive(f, token, name.string); } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); @@ -1796,15 +1849,41 @@ Ast *parse_operand(AstFile *f, bool lhs) { } Ast *type = parse_proc_type(f, token); + Token where_token = {}; + Array<Ast *> where_clauses = {}; + u64 tags = 0; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + parse_proc_tags(f, &tags); + if ((tags & ProcTag_require_results) != 0) { + syntax_error(f->curr_token, "#require_results has now been replaced as an attribute @(require_results) on the declaration"); + tags &= ~ProcTag_require_results; + } + GB_ASSERT(type->kind == Ast_ProcType); + type->ProcType.tags = tags; if (f->allow_type && f->expr_level < 0) { + if (tags != 0) { + syntax_error(token, "A procedure type cannot have suffix tags"); + } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); + } return type; } - u64 tags = type->ProcType.tags; - if (allow_token(f, Token_Undef)) { - return ast_proc_lit(f, type, nullptr, tags); + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---)"); + } + return ast_proc_lit(f, type, nullptr, tags, where_token, where_clauses); } else if (f->curr_token.kind == Token_OpenBrace) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1812,7 +1891,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = parse_body(f); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1820,11 +1899,14 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = convert_stmt_to_body(f, parse_stmt(f)); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } if (tags != 0) { - syntax_error(token, "A procedure type cannot have tags"); + syntax_error(token, "A procedure type cannot have suffix tags"); + } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); } return type; @@ -1847,10 +1929,6 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_typeid: { Token token = expect_token(f, Token_typeid); - // Ast *specialization = nullptr; - // if (allow_token(f, Token_Quo)) { - // specialization = parse_type(f); - // } return ast_typeid_type(f, token, nullptr); } break; @@ -1952,6 +2030,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(token, "'#raw_union' cannot also be '#packed'"); } + Token where_token = {}; + Array<Ast *> where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize name_count = 0; @@ -1964,7 +2054,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align, where_token, where_clauses); } break; case Token_union: { @@ -2005,6 +2095,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { } } + Token where_token = {}; + Array<Ast *> where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "union"); while (f->curr_token.kind != Token_CloseBrace && @@ -2020,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token close = expect_token(f, Token_CloseBrace); - return ast_union_type(f, token, variants, polymorphic_params, align, no_nil); + return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, where_token, where_clauses); } break; case Token_enum: { @@ -2091,7 +2193,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { bool prev_allow_range = f->allow_range; f->allow_range = true; - elem = parse_expr(f, false); + elem = parse_expr(f, true); f->allow_range = prev_allow_range; if (allow_token(f, Token_Semicolon)) { underlying = parse_type(f); @@ -2383,17 +2485,18 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_LtEq: case Token_GtEq: return 5; + case Token_in: case Token_notin: - if (f->expr_level >= 0 || f->allow_in_expr) { - return 6; + if (f->expr_level < 0 && !f->allow_in_expr) { + return 0; } - return 0; + /*fallthrough*/ case Token_Add: case Token_Sub: case Token_Or: case Token_Xor: - return 7; + return 6; case Token_Mul: case Token_Quo: case Token_Mod: @@ -2402,7 +2505,7 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_AndNot: case Token_Shl: case Token_Shr: - return 8; + return 7; } return 0; } @@ -2649,7 +2752,7 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { allow_token(f, Token_in); bool prev_allow_range = f->allow_range; f->allow_range = true; - Ast *expr = parse_expr(f, false); + Ast *expr = parse_expr(f, true); f->allow_range = prev_allow_range; auto rhs = array_make<Ast *>(heap_allocator(), 0, 1); @@ -2740,7 +2843,8 @@ Ast *parse_results(AstFile *f, bool *diverging) { Array<Ast *> empty_names = {}; auto list = array_make<Ast *>(heap_allocator(), 0, 1); Ast *type = parse_type(f); - array_add(&list, ast_field(f, empty_names, type, nullptr, 0, nullptr, nullptr)); + Token tag = {}; + array_add(&list, ast_field(f, empty_names, type, nullptr, 0, tag, nullptr, nullptr)); return ast_field_list(f, begin_token, list); } @@ -2795,8 +2899,6 @@ Ast *parse_proc_type(AstFile *f, Token proc_token) { results = parse_results(f, &diverging); u64 tags = 0; - parse_proc_tags(f, &tags); - bool is_generic = false; for_array(i, params->FieldList.list) { @@ -3095,6 +3197,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi Ast *type = nullptr; Ast *default_value = nullptr; + Token tag = {}; expect_token_after(f, Token_Colon, "field list"); if (f->curr_token.kind != Token_Eq) { @@ -3132,16 +3235,25 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value"); } + if (type != nullptr && default_value == nullptr) { + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + if ((allowed_flags & FieldFlag_Tags) == 0) { + syntax_error(tag, "Field tags are only allowed within structures"); + } + } + } + parse_expect_field_separator(f, type); - Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment); + Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); while (f->curr_token.kind != follow && f->curr_token.kind != Token_EOF) { CommentGroup *docs = f->lead_comment; - u32 set_flags = parse_field_prefixes(f); + Token tag = {}; Array<Ast *> names = parse_ident_list(f, allow_poly_names); if (names.count == 0) { syntax_error(f->curr_token, "Empty field declaration"); @@ -3184,9 +3296,18 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value"); } + if (type != nullptr && default_value == nullptr) { + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + if ((allowed_flags & FieldFlag_Tags) == 0) { + syntax_error(tag, "Field tags are only allowed within structures"); + } + } + } + bool ok = parse_expect_field_separator(f, param); - Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment); + Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); if (!ok) { @@ -3210,8 +3331,8 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi token.pos = ast_token(type).pos; names[0] = ast_ident(f, token); u32 flags = check_field_prefixes(f, list.count, allowed_flags, list[i].flags); - - Ast *param = ast_field(f, names, list[i].node, nullptr, flags, docs, f->line_comment); + Token tag = {}; + Ast *param = ast_field(f, names, list[i].node, nullptr, flags, tag, docs, f->line_comment); array_add(¶ms, param); } @@ -3758,9 +3879,55 @@ Ast *parse_stmt(AstFile *f) { Token token = f->curr_token; switch (token.kind) { // Operands - case Token_context: // Also allows for `context =` case Token_inline: + if (peek_token_kind(f, Token_for)) { + Token inline_token = expect_token(f, Token_inline); + Token for_token = expect_token(f, Token_for); + Ast *val0 = nullptr; + Ast *val1 = nullptr; + Token in_token = {}; + Ast *expr = nullptr; + Ast *body = nullptr; + + bool bad_stmt = false; + + if (f->curr_token.kind != Token_in) { + Array<Ast *> idents = parse_ident_list(f, false); + switch (idents.count) { + case 1: + val0 = idents[0]; + break; + case 2: + val0 = idents[0]; + val1 = idents[1]; + break; + default: + syntax_error(for_token, "Expected either 1 or 2 identifiers"); + bad_stmt = true; + break; + } + } + in_token = expect_token(f, Token_in); + + bool prev_allow_range = f->allow_range; + f->allow_range = true; + expr = parse_expr(f, true); + f->allow_range = prev_allow_range; + + if (allow_token(f, Token_do)) { + body = convert_stmt_to_body(f, parse_stmt(f)); + } else { + body = parse_block_stmt(f, false); + } + if (bad_stmt) { + return ast_bad_stmt(f, inline_token, f->curr_token); + } + return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body); + } + /* fallthrough */ case Token_no_inline: + case Token_context: // Also allows for `context =` + case Token_proc: case Token_Ident: case Token_Integer: case Token_Float: @@ -3808,34 +3975,6 @@ Ast *parse_stmt(AstFile *f) { return s; } - // case Token_static: { - // CommentGroup *docs = f->lead_comment; - // Token token = expect_token(f, Token_static); - - // Ast *decl = nullptr; - // Array<Ast *> list = parse_lhs_expr_list(f); - // if (list.count == 0) { - // syntax_error(token, "Illegal use of 'static' statement"); - // expect_semicolon(f, nullptr); - // return ast_bad_stmt(f, token, f->curr_token); - // } - - // expect_token_after(f, Token_Colon, "identifier list"); - // decl = parse_value_decl(f, list, docs); - - // if (decl != nullptr && decl->kind == Ast_ValueDecl) { - // if (decl->ValueDecl.is_mutable) { - // decl->ValueDecl.is_static = true; - // } else { - // error(token, "'static' may only be currently used with variable declaration"); - // } - // return decl; - // } - - // syntax_error(token, "Illegal use of 'static' statement"); - // return ast_bad_stmt(f, token, f->curr_token); - // } break; - case Token_using: { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_using); @@ -3909,12 +4048,10 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "assert") { Ast *t = ast_basic_directive(f, hash_token, tag); return ast_expr_stmt(f, parse_call_expr(f, t)); - } /* else if (name.string == "no_deferred") { - s = parse_stmt(f); - s->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ - - if (tag == "include") { + } else if (tag == "panic") { + Ast *t = ast_basic_directive(f, hash_token, tag); + return ast_expr_stmt(f, parse_call_expr(f, t)); + } else if (tag == "include") { syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?"); s = ast_bad_stmt(f, token, f->curr_token); } else { @@ -4037,7 +4174,6 @@ bool init_parser(Parser *p) { map_init(&p->package_map, heap_allocator()); array_init(&p->packages, heap_allocator()); array_init(&p->package_imports, heap_allocator()); - array_init(&p->files_to_process, heap_allocator()); gb_mutex_init(&p->file_add_mutex); gb_mutex_init(&p->file_decl_mutex); return true; @@ -4060,7 +4196,6 @@ void destroy_parser(Parser *p) { #endif array_free(&p->packages); array_free(&p->package_imports); - array_free(&p->files_to_process); string_set_destroy(&p->imported_files); map_destroy(&p->package_map); gb_mutex_destroy(&p->file_add_mutex); @@ -4077,19 +4212,32 @@ void parser_add_package(Parser *p, AstPackage *pkg) { if (found) { GB_ASSERT(pkg->files.count > 0); AstFile *f = pkg->files[0]; - error(f->package_token, "Non-unique package name '%.*s'", LIT(pkg->name)); + syntax_error(f->package_token, "Non-unique package name '%.*s'", LIT(pkg->name)); GB_ASSERT((*found)->files.count > 0); TokenPos pos = (*found)->files[0]->package_token.pos; - gb_printf_err("\tpreviously declared at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + error_line("\tpreviously declared at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); } else { map_set(&p->package_map, key, pkg); } } } +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file); + +WORKER_TASK_PROC(parser_worker_proc) { + ParserWorkerData *wd = cast(ParserWorkerData *)data; + ParseFileError err = process_imported_file(wd->parser, wd->imported_file); + return cast(isize)err; +} + + void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) { - ImportedFile f = {pkg, fi, pos, p->files_to_process.count}; - array_add(&p->files_to_process, f); + // TODO(bill): Use a better allocator + ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; + auto wd = gb_alloc_item(heap_allocator(), ParserWorkerData); + wd->parser = p; + wd->imported_file = f; + thread_pool_add_task(&parser_thread_pool, parser_worker_proc, wd); } @@ -4140,22 +4288,22 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path, switch (rd_err) { case ReadDirectory_InvalidPath: - error(pos, "Invalid path: %.*s", LIT(rel_path)); + syntax_error(pos, "Invalid path: %.*s", LIT(rel_path)); return false; case ReadDirectory_NotExists: - error(pos, "Path does not exist: %.*s", LIT(rel_path)); + syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path)); return false; case ReadDirectory_Permission: - error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); return false; case ReadDirectory_NotDir: - error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); + syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); return false; case ReadDirectory_Empty: - error(pos, "Empty directory: %.*s", LIT(rel_path)); + syntax_error(pos, "Empty directory: %.*s", LIT(rel_path)); return false; case ReadDirectory_Unknown: - error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); return false; } @@ -4257,7 +4405,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, String file_str = {}; if (colon_pos == 0) { - error(node, "Expected a collection name"); + syntax_error(node, "Expected a collection name"); return false; } @@ -4272,32 +4420,19 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, if (has_windows_drive) { String sub_file_path = substring(file_str, 3, file_str.len); if (!is_import_path_valid(sub_file_path)) { - error(node, "Invalid import path: '%.*s'", LIT(file_str)); + syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } } else if (!is_import_path_valid(file_str)) { - error(node, "Invalid import path: '%.*s'", LIT(file_str)); + syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } - if (is_package_name_reserved(file_str)) { - *path = file_str; - return true; - } - - if (file_mutex) gb_mutex_lock(file_mutex); - defer (if (file_mutex) gb_mutex_unlock(file_mutex)); - - - if (node->kind == Ast_ForeignImportDecl) { - node->ForeignImportDecl.collection_name = collection_name; - } - if (collection_name.len > 0) { if (collection_name == "system") { if (node->kind != Ast_ForeignImportDecl) { - error(node, "The library collection 'system' is restrict for 'foreign_library'"); + syntax_error(node, "The library collection 'system' is restrict for 'foreign_library'"); return false; } else { *path = file_str; @@ -4305,7 +4440,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, } } else if (!find_library_collection_path(collection_name, &base_dir)) { // NOTE(bill): It's a naughty name - error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); + syntax_error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); return false; } } else { @@ -4324,6 +4459,20 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, #endif } + + if (is_package_name_reserved(file_str)) { + *path = file_str; + return true; + } + + if (file_mutex) gb_mutex_lock(file_mutex); + defer (if (file_mutex) gb_mutex_unlock(file_mutex)); + + + if (node->kind == Ast_ForeignImportDecl) { + node->ForeignImportDecl.collection_name = collection_name; + } + if (has_windows_drive) { *path = file_str; } else { @@ -4412,7 +4561,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> array_add(&fl->fullpaths, fullpath); } if (fl->fullpaths.count == 0) { - error(decls[i], "No foreign paths found"); + syntax_error(decls[i], "No foreign paths found"); decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]); goto end; } @@ -4461,7 +4610,7 @@ bool parse_build_tag(Token token_for_pos, String s) { is_notted = true; p = substring(p, 1, p.len); if (p.len == 0) { - error(token_for_pos, "Expected a build platform after '!'"); + syntax_error(token_for_pos, "Expected a build platform after '!'"); break; } } @@ -4490,7 +4639,7 @@ bool parse_build_tag(Token token_for_pos, String s) { } } if (os == TargetOs_Invalid && arch == TargetArch_Invalid) { - error(token_for_pos, "Invalid build tag platform: %.*s", LIT(p)); + syntax_error(token_for_pos, "Invalid build tag platform: %.*s", LIT(p)); break; } } @@ -4536,11 +4685,11 @@ bool parse_file(Parser *p, AstFile *f) { Token package_name = expect_token_after(f, Token_Ident, "package"); if (package_name.kind == Token_Ident) { if (package_name.string == "_") { - error(package_name, "Invalid package name '_'"); + syntax_error(package_name, "Invalid package name '_'"); } else if (f->pkg->kind != Package_Runtime && package_name.string == "runtime") { - error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); + syntax_error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); } else if (is_package_name_reserved(package_name.string)) { - error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); + syntax_error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); } } f->package_name = package_name.string; @@ -4590,9 +4739,9 @@ bool parse_file(Parser *p, AstFile *f) { } -ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file) { AstPackage *pkg = imported_file.pkg; - FileInfo *fi = &imported_file.fi; + FileInfo const *fi = &imported_file.fi; TokenPos pos = imported_file.pos; AstFile *file = gb_alloc_item(heap_allocator(), AstFile); @@ -4607,38 +4756,35 @@ ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { if (err != ParseFile_None) { if (err == ParseFile_EmptyFile) { if (fi->fullpath == p->init_fullpath) { - error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); + syntax_error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); gb_exit(1); } - goto skip; - } + } else { + switch (err) { + case ParseFile_WrongExtension: + syntax_error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); + break; + case ParseFile_InvalidFile: + syntax_error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); + break; + case ParseFile_Permission: + syntax_error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); + break; + case ParseFile_NotFound: + syntax_error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); + break; + case ParseFile_InvalidToken: + syntax_error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); + break; + case ParseFile_EmptyFile: + syntax_error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); + break; + } - switch (err) { - case ParseFile_WrongExtension: - error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); - break; - case ParseFile_InvalidFile: - error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); - break; - case ParseFile_Permission: - error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); - break; - case ParseFile_NotFound: - error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); - break; - case ParseFile_InvalidToken: - error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); - break; - case ParseFile_EmptyFile: - error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); - break; + return err; } - - return err; } - -skip: if (parse_file(p, file)) { gb_mutex_lock(&p->file_add_mutex); defer (gb_mutex_unlock(&p->file_add_mutex)); @@ -4648,42 +4794,33 @@ skip: if (pkg->name.len == 0) { pkg->name = file->package_name; } else if (file->tokens.count > 0 && pkg->name != file->package_name) { - error(file->package_token, "Different package name, expected '%.*s', got '%.*s'", LIT(pkg->name), LIT(file->package_name)); + syntax_error(file->package_token, "Different package name, expected '%.*s', got '%.*s'", LIT(pkg->name), LIT(file->package_name)); } p->total_line_count += file->tokenizer.line_count; p->total_token_count += file->tokens.count; } + return ParseFile_None; } -GB_THREAD_PROC(parse_worker_file_proc) { - if (thread == nullptr) return 0; - auto *p = cast(Parser *)thread->user_data; - isize index = thread->user_index; - gb_mutex_lock(&p->file_add_mutex); - auto file_to_process = p->files_to_process[index]; - gb_mutex_unlock(&p->file_add_mutex); - ParseFileError err = process_imported_file(p, file_to_process); - return cast(isize)err; -} - ParseFileError parse_packages(Parser *p, String init_filename) { GB_ASSERT(init_filename.text[init_filename.len] == 0); - // char *fullpath_str = gb_path_get_full_name(heap_allocator(), cast(char const *)&init_filename[0]); - // String init_fullpath = string_trim_whitespace(make_string_c(fullpath_str)); + isize thread_count = gb_max(build_context.thread_count, 1); + isize worker_count = thread_count-1; // NOTE(bill): The main thread will also be used for work + thread_pool_init(&parser_thread_pool, heap_allocator(), worker_count, "ParserWork"); + String init_fullpath = path_to_full_path(heap_allocator(), init_filename); if (!path_is_directory(init_fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(init_fullpath, ext)) { - gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } } - TokenPos init_pos = {}; if (!build_context.generate_docs) { String s = get_fullpath_core(heap_allocator(), str_lit("runtime")); @@ -4693,76 +4830,17 @@ ParseFileError parse_packages(Parser *p, String init_filename) { try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init); p->init_fullpath = init_fullpath; -#if 1 - isize thread_count = gb_max(build_context.thread_count, 1); - if (thread_count > 1) { - isize volatile curr_import_index = 0; - isize initial_file_count = p->files_to_process.count; - // NOTE(bill): Make sure that these are in parsed in this order - for (isize i = 0; i < initial_file_count; i++) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - curr_import_index++; - } - - auto worker_threads = array_make<gbThread>(heap_allocator(), thread_count); - defer (array_free(&worker_threads)); - - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - gb_thread_init(t); - } - defer (for_array(i, worker_threads) { - gb_thread_destroy(&worker_threads[i]); - }); + thread_pool_start(&parser_thread_pool); + thread_pool_wait_to_process(&parser_thread_pool); - auto errors = array_make<ParseFileError>(heap_allocator(), 0, 16); - - for (;;) { - bool are_any_alive = false; - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - if (gb_thread_is_running(t)) { - are_any_alive = true; - } else if (curr_import_index < p->files_to_process.count) { - auto curr_err = cast(ParseFileError)t->return_value; - if (curr_err != ParseFile_None) { - array_add(&errors, curr_err); - } else { - t->user_index = curr_import_index; - curr_import_index++; - gb_thread_start(t, parse_worker_file_proc, p); - are_any_alive = true; - } - } - } - if (!are_any_alive && curr_import_index >= p->files_to_process.count) { - break; - } - } - - if (errors.count > 0) { - return errors[errors.count-1]; - } - } else { - for_array(i, p->files_to_process) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - } - } -#else - for_array(i, p->files_to_process) { - ImportedFile f = p->files_to_process[i]; - ParseFileError err = process_imported_file(p, f); + // NOTE(bill): Get the last error and use that + for (isize i = parser_thread_pool.task_tail-1; i >= 0; i--) { + WorkerTask *task = &parser_thread_pool.tasks[i]; + ParseFileError err = cast(ParseFileError)task->result; if (err != ParseFile_None) { return err; } } -#endif return ParseFile_None; } |