aboutsummaryrefslogtreecommitdiff
path: root/src/parser.cpp
diff options
context:
space:
mode:
authorChris Heyes <rumcode@icloud.com>2019-11-01 19:18:33 +0000
committerChris Heyes <rumcode@icloud.com>2019-11-01 19:18:33 +0000
commit153e7525b5d64f12deb318d50aee1d9dbeb1fc8e (patch)
treeb8ac88c0788af31bcb3c5041d78306c2120714f6 /src/parser.cpp
parentd85893954dccc7833e3d954db641d9fd2a3c7451 (diff)
parent44a303e5778fb8564964d53523634f34f8589489 (diff)
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src/parser.cpp')
-rw-r--r--src/parser.cpp524
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(&params, 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(&params, 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(&params, 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;
}