diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2024-09-19 12:36:43 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-19 12:36:43 +0100 |
| commit | 6bbeb0a243b37f03a15671038a0ec65cdd66981e (patch) | |
| tree | 77be66ea3c8078cc2c4b11ec9839818587dfcd3b /src/parser.cpp | |
| parent | a52b1e03219034d13f8f1f1335b54c0678cc7822 (diff) | |
| parent | 29fedc1808c347774971666ecc90b44e1900dc90 (diff) | |
Merge pull request #4203 from karl-zylinski/file-tags-without-comments
Make tags use #+ syntax instead of //+
Diffstat (limited to 'src/parser.cpp')
| -rw-r--r-- | src/parser.cpp | 183 |
1 files changed, 129 insertions, 54 deletions
diff --git a/src/parser.cpp b/src/parser.cpp index 56bea8131..520a23c5a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4967,7 +4967,7 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { } if (f->in_when_statement) { - syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '//+build' tags"); + syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '#+build' tags"); } if (kind != ImportDecl_Standard) { @@ -5355,6 +5355,12 @@ gb_internal Ast *parse_stmt(AstFile *f) { s = ast_empty_stmt(f, token); expect_semicolon(f); return s; + + case Token_FileTag: + // This is always an error because all valid file tags will have been processed in `parse_file` already. + // Any remaining file tags must be past the package line and thus invalid. + syntax_error(token, "Lines starting with #+ (file tags) are only allowed before the package line."); + return ast_bad_stmt(f, token, f->curr_token); } // Error correction statements @@ -6086,7 +6092,7 @@ gb_internal String build_tag_get_token(String s, String *out) { } gb_internal bool parse_build_tag(Token token_for_pos, String s) { - String const prefix = str_lit("+build"); + String const prefix = str_lit("build"); GB_ASSERT(string_starts_with(s, prefix)); s = string_trim_whitespace(substring(s, prefix.len, s.len)); @@ -6099,6 +6105,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { while (s.len > 0) { bool this_kind_correct = true; + bool this_kind_os_seen = false; + bool this_kind_arch_seen = false; + int num_tokens = 0; + do { String p = string_trim_whitespace(build_tag_get_token(s, &s)); if (p.len == 0) break; @@ -6124,7 +6134,18 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { TargetOsKind os = get_target_os_from_string(p); TargetArchKind arch = get_target_arch_from_string(p); + num_tokens += 1; + + // Catches 'windows linux', which is an impossible combination. + // Also catches usage of more than two things within a comma separated group. + if (num_tokens > 2 || (this_kind_os_seen && os != TargetOs_Invalid) || (this_kind_arch_seen && arch != TargetArch_Invalid)) { + syntax_error(token_for_pos, "Invalid build tag: Missing ',' before '%.*s'. Format: '#+build linux, windows amd64, darwin'", LIT(p)); + break; + } + if (os != TargetOs_Invalid) { + this_kind_os_seen = true; + GB_ASSERT(arch == TargetArch_Invalid); if (is_notted) { this_kind_correct = this_kind_correct && (os != build_context.metrics.os); @@ -6132,6 +6153,8 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { this_kind_correct = this_kind_correct && (os == build_context.metrics.os); } } else if (arch != TargetArch_Invalid) { + this_kind_arch_seen = true; + if (is_notted) { this_kind_correct = this_kind_correct && (arch != build_context.metrics.arch); } else { @@ -6171,7 +6194,7 @@ gb_internal String vet_tag_get_token(String s, String *out) { gb_internal u64 parse_vet_tag(Token token_for_pos, String s) { - String const prefix = str_lit("+vet"); + String const prefix = str_lit("vet"); GB_ASSERT(string_starts_with(s, prefix)); s = string_trim_whitespace(substring(s, prefix.len, s.len)); @@ -6276,7 +6299,7 @@ gb_internal isize calc_decl_count(Ast *decl) { } gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s) { - String const prefix = str_lit("+build-project-name"); + String const prefix = str_lit("build-project-name"); GB_ASSERT(string_starts_with(s, prefix)); s = string_trim_whitespace(substring(s, prefix.len, s.len)); if (s.len == 0) { @@ -6320,6 +6343,48 @@ gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s return any_correct; } +gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) { + if (string_starts_with(lc, str_lit("build-project-name"))) { + if (!parse_build_project_directory_tag(tok, lc)) { + return false; + } + } else if (string_starts_with(lc, str_lit("build"))) { + if (!parse_build_tag(tok, lc)) { + return false; + } + } else if (string_starts_with(lc, str_lit("vet"))) { + f->vet_flags = parse_vet_tag(tok, lc); + f->vet_flags_set = true; + } else if (string_starts_with(lc, str_lit("ignore"))) { + return false; + } else if (string_starts_with(lc, str_lit("private"))) { + f->flags |= AstFile_IsPrivatePkg; + String command = string_trim_starts_with(lc, str_lit("private ")); + command = string_trim_whitespace(command); + if (lc == "private") { + f->flags |= AstFile_IsPrivatePkg; + } else if (command == "package") { + f->flags |= AstFile_IsPrivatePkg; + } else if (command == "file") { + f->flags |= AstFile_IsPrivateFile; + } + } else if (lc == "lazy") { + if (build_context.ignore_lazy) { + // Ignore + } else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) { + // Ignore + } else { + f->flags |= AstFile_IsLazy; + } + } else if (lc == "no-instrumentation") { + f->flags |= AstFile_NoInstrumentation; + } else { + error(tok, "Unknown tag '%.*s'", LIT(lc)); + } + + return true; +} + gb_internal bool parse_file(Parser *p, AstFile *f) { if (f->tokens.count == 0) { return true; @@ -6338,9 +6403,34 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { CommentGroup *docs = f->lead_comment; + Array<Token> tags = array_make<Token>(temporary_allocator()); + bool first_invalid_token_set = false; + Token first_invalid_token = {}; + + while (f->curr_token.kind != Token_package && f->curr_token.kind != Token_EOF) { + if (f->curr_token.kind == Token_Comment) { + consume_comment_groups(f, f->prev_token); + } else if (f->curr_token.kind == Token_FileTag) { + array_add(&tags, f->curr_token); + advance_token(f); + } else { + if (!first_invalid_token_set) { + first_invalid_token_set = true; + first_invalid_token = f->curr_token; + } + + advance_token(f); + } + } + if (f->curr_token.kind != Token_package) { ERROR_BLOCK(); - syntax_error(f->curr_token, "Expected a package declaration at the beginning of the file"); + + // The while loop above scanned until it found the package token. If we never + // found one, then make this error appear on the first invalid token line. + Token t = first_invalid_token_set ? first_invalid_token : f->curr_token; + syntax_error(t, "Expected a package declaration at the beginning of the file"); + // IMPORTANT NOTE(bill): this is technically a race condition with the suggestion, but it's ony a suggession // so in practice is should be "fine" if (f->pkg && f->pkg->name != "") { @@ -6349,18 +6439,16 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { return false; } + // There was an OK package declaration. But there some invalid token was hit before the package declaration. + if (first_invalid_token_set) { + syntax_error(first_invalid_token, "Expected only comments or lines starting with '#+' before the package declaration"); + return false; + } + f->package_token = expect_token(f, Token_package); if (f->package_token.kind != Token_package) { return false; } - if (docs != nullptr) { - TokenPos end = token_pos_end(docs->list[docs->list.count-1]); - if (end.line == f->package_token.pos.line || end.line+1 == f->package_token.pos.line) { - // Okay - } else { - docs = nullptr; - } - } Token package_name = expect_token_after(f, Token_Ident, "package"); if (package_name.kind == Token_Ident) { @@ -6374,53 +6462,40 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { } f->package_name = package_name.string; - if (!f->pkg->is_single_file && docs != nullptr && docs->list.count > 0) { - for (Token const &tok : docs->list) { - GB_ASSERT(tok.kind == Token_Comment); - String str = tok.string; - if (string_starts_with(str, str_lit("//"))) { + // TODO: Shouldn't single file only matter for build tags? no-instrumentation for example + // should be respected even when in single file mode. + if (!f->pkg->is_single_file) { + if (docs != nullptr && docs->list.count > 0) { + for (Token const &tok : docs->list) { + GB_ASSERT(tok.kind == Token_Comment); + String str = tok.string; + + if (!string_starts_with(str, str_lit("//"))) { + continue; + } + String lc = string_trim_whitespace(substring(str, 2, str.len)); - if (lc.len > 0 && lc[0] == '+') { - if (string_starts_with(lc, str_lit("+build-project-name"))) { - if (!parse_build_project_directory_tag(tok, lc)) { - return false; - } - } else if (string_starts_with(lc, str_lit("+build"))) { - if (!parse_build_tag(tok, lc)) { - return false; - } - } else if (string_starts_with(lc, str_lit("+vet"))) { - f->vet_flags = parse_vet_tag(tok, lc); - f->vet_flags_set = true; - } else if (string_starts_with(lc, str_lit("+ignore"))) { + if (string_starts_with(lc, str_lit("+"))) { + syntax_warning(tok, "'//+' is deprecated: Use '#+' instead"); + String lt = substring(lc, 1, lc.len); + if (parse_file_tag(lt, tok, f) == false) { return false; - } else if (string_starts_with(lc, str_lit("+private"))) { - f->flags |= AstFile_IsPrivatePkg; - String command = string_trim_starts_with(lc, str_lit("+private ")); - command = string_trim_whitespace(command); - if (lc == "+private") { - f->flags |= AstFile_IsPrivatePkg; - } else if (command == "package") { - f->flags |= AstFile_IsPrivatePkg; - } else if (command == "file") { - f->flags |= AstFile_IsPrivateFile; - } - } else if (lc == "+lazy") { - if (build_context.ignore_lazy) { - // Ignore - } else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) { - // Ignore - } else { - f->flags |= AstFile_IsLazy; - } - } else if (lc == "+no-instrumentation") { - f->flags |= AstFile_NoInstrumentation; - } else { - warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc)); } } } } + + for (Token const &tok : tags) { + GB_ASSERT(tok.kind == Token_FileTag); + String str = tok.string; + + if (string_starts_with(str, str_lit("#+"))) { + String lt = string_trim_whitespace(substring(str, 2, str.len)); + if (parse_file_tag(lt, tok, f) == false) { + return false; + } + } + } } Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment); |