From 92821300e4aef1b647de50ae74c0f374879e8a30 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Mon, 12 Aug 2024 15:21:46 +0200 Subject: [PATCH 1/7] Add a file tag parser to core:odin/parser --- core/odin/parser/file_tags.odin | 256 ++++++++++++++++++++++++++++ tests/core/odin/test_file_tags.odin | 133 +++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 core/odin/parser/file_tags.odin create mode 100644 tests/core/odin/test_file_tags.odin diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin new file mode 100644 index 00000000000..229f87f1c8b --- /dev/null +++ b/core/odin/parser/file_tags.odin @@ -0,0 +1,256 @@ +package odin_parser + +import "base:runtime" +import "core:strings" + +import "../ast" + +Private_Flag :: enum { + Public, + Package, + File, +} + +Odin_OS_Type :: runtime.Odin_OS_Type +Odin_Arch_Type :: runtime.Odin_Arch_Type + +Odin_OS_Types :: bit_set[Odin_OS_Type] +Odin_Arch_Types :: bit_set[Odin_Arch_Type] + +Build_Kind :: struct { + os: Odin_OS_Types, + arch: Odin_Arch_Types, +} + +File_Tags :: struct { + build_project_name: []string, + build: []Build_Kind, + private: Private_Flag, + ignore: bool, + lazy: bool, + no_instrumentation: bool, +} + +ALL_ODIN_OS_TYPES :: Odin_OS_Types{ + .Windows, + .Darwin, + .Linux, + .Essence, + .FreeBSD, + .OpenBSD, + .NetBSD, + .Haiku, + .WASI, + .JS, + .Orca, + .Freestanding, +} +ALL_ODIN_ARCH_TYPES :: Odin_Arch_Types{ + .amd64, + .i386, + .arm32, + .arm64, + .wasm32, + .wasm64p32, +} + +ODIN_OS_NAMES :: [Odin_OS_Type]string{ + .Unknown = "", + .Windows = "windows", + .Darwin = "darwin", + .Linux = "linux", + .Essence = "essence", + .FreeBSD = "freebsd", + .OpenBSD = "openbsd", + .NetBSD = "netbsd", + .Haiku = "haiku", + .WASI = "wasi", + .JS = "js", + .Orca = "orca", + .Freestanding = "freestanding", +} + +ODIN_ARCH_NAMES :: [Odin_Arch_Type]string{ + .Unknown = "", + .amd64 = "amd64", + .i386 = "i386", + .arm32 = "arm32", + .arm64 = "arm64", + .wasm32 = "wasm32", + .wasm64p32 = "wasm64p32", +} + +@require_results +get_build_os_from_string :: proc(str: string) -> Odin_OS_Type { + for os_name, os in ODIN_OS_NAMES { + if strings.equal_fold(os_name, str) { + return os + } + } + return .Unknown +} +@require_results +get_build_arch_from_string :: proc(str: string) -> Odin_Arch_Type { + for arch_name, arch in ODIN_ARCH_NAMES { + if strings.equal_fold(arch_name, str) { + return arch + } + } + return .Unknown +} + +parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { + if file.docs == nil { + return + } + + next_char :: proc(src: string, i: ^int) -> (ch: u8) { + if i^ < len(src) { + ch = src[i^] + } + i^ += 1 + return + } + skip_whitespace :: proc(src: string, i: ^int) { + for { + switch next_char(src, i) { + case ' ', '\t': + continue + case: + i^ -= 1 + return + } + } + } + skip_rest_of_line :: proc(src: string, i: ^int) { + for { + switch next_char(src, i) { + case '\n', 0: + return + case: + continue + } + } + } + scan_value :: proc(src: string, i: ^int) -> string { + start := i^ + for { + switch next_char(src, i) { + case ' ', '\t', '\n', '\r', 0, ',': + i^ -= 1 + return src[start:i^] + case: + continue + } + } + } + + build_kinds: [dynamic]Build_Kind + defer shrink(&build_kinds) + + build_project_names: [dynamic]string + defer shrink(&build_project_names) + + for comment in file.docs.list { + if len(comment.text) < 3 || comment.text[:2] != "//" { + continue + } + text := comment.text[2:] + i := 0 + + skip_whitespace(text, &i) + + if next_char(text, &i) == '+' { + switch scan_value(text, &i) { + case "ignore": + tags.ignore = true + case "lazy": + tags.lazy = true + case "no-instrumentation": + tags.no_instrumentation = true + case "private": + skip_whitespace(text, &i) + switch scan_value(text, &i) { + case "file": + tags.private = .File + case "package", "": + tags.private = .Package + } + case "build-project-name": + values_loop: for { + skip_whitespace(text, &i) + + name_start := i + + switch next_char(text, &i) { + case 0, '\n': + i -= 1 + break values_loop + case '!': + // include ! in the name + case: + i -= 1 + } + + scan_value(text, &i) + append(&build_project_names, text[name_start:i]) + } + case "build": + kinds_loop: for { + os_positive: Odin_OS_Types + os_negative: Odin_OS_Types + + arch_positive: Odin_Arch_Types + arch_negative: Odin_Arch_Types + + defer append(&build_kinds, Build_Kind{ + os = (os_positive == {} ? ALL_ODIN_OS_TYPES : os_positive) -os_negative, + arch = (arch_positive == {} ? ALL_ODIN_ARCH_TYPES : arch_positive)-arch_negative, + }) + + for { + skip_whitespace(text, &i) + + is_notted: bool + switch next_char(text, &i) { + case 0, '\n': + i -= 1 + break kinds_loop + case ',': + continue kinds_loop + case '!': + is_notted = true + case: + i -= 1 + } + + value := scan_value(text, &i) + + if value == "ignore" { + tags.ignore = true + } else if os := get_build_os_from_string(value); os != .Unknown { + if is_notted { + os_negative += {os} + } else { + os_positive += {os} + } + } else if arch := get_build_arch_from_string(value); arch != .Unknown { + if is_notted { + arch_negative += {arch} + } else { + arch_positive += {arch} + } + } + } + } + } + } + + skip_rest_of_line(text, &i) + } + + tags.build = build_kinds[:] + tags.build_project_name = build_project_names[:] + + return +} diff --git a/tests/core/odin/test_file_tags.odin b/tests/core/odin/test_file_tags.odin new file mode 100644 index 00000000000..181fac4e616 --- /dev/null +++ b/tests/core/odin/test_file_tags.odin @@ -0,0 +1,133 @@ +package test_core_odin_parser + +import "base:runtime" +import "core:testing" +import "core:slice" +import "core:odin/ast" +import "core:odin/parser" + +@test +test_parse_file_tags :: proc(t: ^testing.T) { + context.allocator = context.temp_allocator + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + + Test_Case :: struct { + src: string, + tags: parser.File_Tags, + } + + test_cases := []Test_Case{ + {// [0] + src = ``, + tags = {}, + }, {// [1] + src = ` +package main + `, + tags = {}, + }, {// [2] + src = ` +//+build linux, darwin, freebsd, openbsd, netbsd, haiku +//+build arm32, arm64 +package main + `, + tags = { + build = { + {os = {.Linux}, arch = parser.ALL_ODIN_ARCH_TYPES}, + {os = {.Darwin}, arch = parser.ALL_ODIN_ARCH_TYPES}, + {os = {.FreeBSD}, arch = parser.ALL_ODIN_ARCH_TYPES}, + {os = {.OpenBSD}, arch = parser.ALL_ODIN_ARCH_TYPES}, + {os = {.NetBSD}, arch = parser.ALL_ODIN_ARCH_TYPES}, + {os = {.Haiku}, arch = parser.ALL_ODIN_ARCH_TYPES}, + {os = parser.ALL_ODIN_OS_TYPES, arch = {.arm32}}, + {os = parser.ALL_ODIN_OS_TYPES, arch = {.arm64}}, + }, + }, + }, {// [3] + src = ` +// +private +//+lazy +// +no-instrumentation +//+ignore +// some other comment +package main + `, + tags = { + private = .Package, + no_instrumentation = true, + lazy = true, + ignore = true, + }, + }, {// [4] + src = ` +//+build-project-name foo !bar +//+build js wasm32, js wasm64p32 +package main + `, + tags = { + build_project_name = {"foo", "!bar"}, + build = { + { + os = {.JS}, + arch = {.wasm32}, + }, { + os = {.JS}, + arch = {.wasm64p32}, + }, + }, + }, + }, + } + + expect :: proc(t: ^testing.T, ok: bool, name: string, i: int, expected, actual: $T, loc := #caller_location) { + testing.expectf(t, ok, + "[%d] expected %s:\n\e[0;32m%#v\e[0m, actual:\n\e[0;31m%#v\e[0m", + i, name, expected, actual, loc=loc + ) + } + + for test_case, i in test_cases { + + file := ast.File{ + fullpath = "test.odin", + src = test_case.src, + } + + p := parser.default_parser() + ok := parser.parse_file(&p, &file) + + testing.expect(t, ok, "bad parse") + + tags := parser.parse_file_tags(file) + + expect(t, + slice.equal(test_case.tags.build_project_name, tags.build_project_name), + "build_project_name", i, test_case.tags.build_project_name, tags.build_project_name, + ) + + expect(t, + slice.equal(test_case.tags.build, tags.build), + "build", i, test_case.tags.build, tags.build, + ) + + expect(t, + test_case.tags.private == tags.private, + "private", i, test_case.tags.private, tags.private, + ) + + expect(t, + test_case.tags.ignore == tags.ignore, + "ignore", i, test_case.tags.ignore, tags.ignore, + ) + + expect(t, + test_case.tags.lazy == tags.lazy, + "lazy", i, test_case.tags.lazy, tags.lazy, + ) + + expect(t, + test_case.tags.no_instrumentation == tags.no_instrumentation, + "no_instrumentation", i, test_case.tags.no_instrumentation, tags.no_instrumentation, + ) + } +} From 78624603741611936025f33ed31458833f4bc8fb Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Mon, 12 Aug 2024 15:21:46 +0200 Subject: [PATCH 2/7] Remove useless code --- core/odin/parser/file_tags.odin | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin index 229f87f1c8b..371af8055e8 100644 --- a/core/odin/parser/file_tags.odin +++ b/core/odin/parser/file_tags.odin @@ -122,16 +122,6 @@ parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { } } } - skip_rest_of_line :: proc(src: string, i: ^int) { - for { - switch next_char(src, i) { - case '\n', 0: - return - case: - continue - } - } - } scan_value :: proc(src: string, i: ^int) -> string { start := i^ for { @@ -245,8 +235,6 @@ parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { } } } - - skip_rest_of_line(text, &i) } tags.build = build_kinds[:] From 39bdf4d7104d7e4ea246c7b854db4791fb5fdcc6 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Mon, 12 Aug 2024 15:24:49 +0200 Subject: [PATCH 3/7] Add missing require_results attr --- core/odin/parser/file_tags.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin index 371af8055e8..f2a5da83b6a 100644 --- a/core/odin/parser/file_tags.odin +++ b/core/odin/parser/file_tags.odin @@ -99,6 +99,7 @@ get_build_arch_from_string :: proc(str: string) -> Odin_Arch_Type { return .Unknown } +@require_results parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { if file.docs == nil { return From a10f9880204a0e9d7b2b68afa77efe191ce62142 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 29 Aug 2024 21:12:33 +0200 Subject: [PATCH 4/7] Move some types to runtime, use reflection instead of lut --- base/runtime/core.odin | 29 ++++++++++ core/odin/parser/file_tags.odin | 90 ++++++----------------------- tests/core/odin/test_file_tags.odin | 16 ++--- 3 files changed, 56 insertions(+), 79 deletions(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index cc80e68a57f..90e049b0cfa 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -546,10 +546,23 @@ Odin_OS_Type :: type_of(ODIN_OS) arm64, wasm32, wasm64p32, + riscv64, } */ Odin_Arch_Type :: type_of(ODIN_ARCH) +Odin_Arch_Types :: bit_set[Odin_Arch_Type] + +ALL_ODIN_ARCH_TYPES :: Odin_Arch_Types{ + .amd64, + .i386, + .arm32, + .arm64, + .wasm32, + .wasm64p32, + .riscv64, +} + /* // Defined internally by the compiler Odin_Build_Mode_Type :: enum int { @@ -573,6 +586,22 @@ Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE) */ Odin_Endian_Type :: type_of(ODIN_ENDIAN) +Odin_OS_Types :: bit_set[Odin_OS_Type] + +ALL_ODIN_OS_TYPES :: Odin_OS_Types{ + .Windows, + .Darwin, + .Linux, + .Essence, + .FreeBSD, + .OpenBSD, + .NetBSD, + .Haiku, + .WASI, + .JS, + .Orca, + .Freestanding, +} /* // Defined internally by the compiler diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin index f2a5da83b6a..cce34b2da24 100644 --- a/core/odin/parser/file_tags.odin +++ b/core/odin/parser/file_tags.odin @@ -2,6 +2,7 @@ package odin_parser import "base:runtime" import "core:strings" +import "core:reflect" import "../ast" @@ -11,15 +12,9 @@ Private_Flag :: enum { File, } -Odin_OS_Type :: runtime.Odin_OS_Type -Odin_Arch_Type :: runtime.Odin_Arch_Type - -Odin_OS_Types :: bit_set[Odin_OS_Type] -Odin_Arch_Types :: bit_set[Odin_Arch_Type] - Build_Kind :: struct { - os: Odin_OS_Types, - arch: Odin_Arch_Types, + os: runtime.Odin_OS_Types, + arch: runtime.Odin_Arch_Types, } File_Tags :: struct { @@ -31,69 +26,22 @@ File_Tags :: struct { no_instrumentation: bool, } -ALL_ODIN_OS_TYPES :: Odin_OS_Types{ - .Windows, - .Darwin, - .Linux, - .Essence, - .FreeBSD, - .OpenBSD, - .NetBSD, - .Haiku, - .WASI, - .JS, - .Orca, - .Freestanding, -} -ALL_ODIN_ARCH_TYPES :: Odin_Arch_Types{ - .amd64, - .i386, - .arm32, - .arm64, - .wasm32, - .wasm64p32, -} - -ODIN_OS_NAMES :: [Odin_OS_Type]string{ - .Unknown = "", - .Windows = "windows", - .Darwin = "darwin", - .Linux = "linux", - .Essence = "essence", - .FreeBSD = "freebsd", - .OpenBSD = "openbsd", - .NetBSD = "netbsd", - .Haiku = "haiku", - .WASI = "wasi", - .JS = "js", - .Orca = "orca", - .Freestanding = "freestanding", -} - -ODIN_ARCH_NAMES :: [Odin_Arch_Type]string{ - .Unknown = "", - .amd64 = "amd64", - .i386 = "i386", - .arm32 = "arm32", - .arm64 = "arm64", - .wasm32 = "wasm32", - .wasm64p32 = "wasm64p32", -} - @require_results -get_build_os_from_string :: proc(str: string) -> Odin_OS_Type { - for os_name, os in ODIN_OS_NAMES { - if strings.equal_fold(os_name, str) { - return os +get_build_os_from_string :: proc(str: string) -> runtime.Odin_OS_Type { + fields := reflect.enum_fields_zipped(runtime.Odin_OS_Type) + for os in fields { + if strings.equal_fold(os.name, str) { + return runtime.Odin_OS_Type(os.value) } } return .Unknown } @require_results -get_build_arch_from_string :: proc(str: string) -> Odin_Arch_Type { - for arch_name, arch in ODIN_ARCH_NAMES { - if strings.equal_fold(arch_name, str) { - return arch +get_build_arch_from_string :: proc(str: string) -> runtime.Odin_Arch_Type { + fields := reflect.enum_fields_zipped(runtime.Odin_Arch_Type) + for os in fields { + if strings.equal_fold(os.name, str) { + return runtime.Odin_Arch_Type(os.value) } } return .Unknown @@ -188,15 +136,15 @@ parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { } case "build": kinds_loop: for { - os_positive: Odin_OS_Types - os_negative: Odin_OS_Types + os_positive: runtime.Odin_OS_Types + os_negative: runtime.Odin_OS_Types - arch_positive: Odin_Arch_Types - arch_negative: Odin_Arch_Types + arch_positive: runtime.Odin_Arch_Types + arch_negative: runtime.Odin_Arch_Types defer append(&build_kinds, Build_Kind{ - os = (os_positive == {} ? ALL_ODIN_OS_TYPES : os_positive) -os_negative, - arch = (arch_positive == {} ? ALL_ODIN_ARCH_TYPES : arch_positive)-arch_negative, + os = (os_positive == {} ? runtime.ALL_ODIN_OS_TYPES : os_positive) -os_negative, + arch = (arch_positive == {} ? runtime.ALL_ODIN_ARCH_TYPES : arch_positive)-arch_negative, }) for { diff --git a/tests/core/odin/test_file_tags.odin b/tests/core/odin/test_file_tags.odin index 181fac4e616..a9f10b833f7 100644 --- a/tests/core/odin/test_file_tags.odin +++ b/tests/core/odin/test_file_tags.odin @@ -33,14 +33,14 @@ package main `, tags = { build = { - {os = {.Linux}, arch = parser.ALL_ODIN_ARCH_TYPES}, - {os = {.Darwin}, arch = parser.ALL_ODIN_ARCH_TYPES}, - {os = {.FreeBSD}, arch = parser.ALL_ODIN_ARCH_TYPES}, - {os = {.OpenBSD}, arch = parser.ALL_ODIN_ARCH_TYPES}, - {os = {.NetBSD}, arch = parser.ALL_ODIN_ARCH_TYPES}, - {os = {.Haiku}, arch = parser.ALL_ODIN_ARCH_TYPES}, - {os = parser.ALL_ODIN_OS_TYPES, arch = {.arm32}}, - {os = parser.ALL_ODIN_OS_TYPES, arch = {.arm64}}, + {os = {.Linux}, arch = runtime.ALL_ODIN_ARCH_TYPES}, + {os = {.Darwin}, arch = runtime.ALL_ODIN_ARCH_TYPES}, + {os = {.FreeBSD}, arch = runtime.ALL_ODIN_ARCH_TYPES}, + {os = {.OpenBSD}, arch = runtime.ALL_ODIN_ARCH_TYPES}, + {os = {.NetBSD}, arch = runtime.ALL_ODIN_ARCH_TYPES}, + {os = {.Haiku}, arch = runtime.ALL_ODIN_ARCH_TYPES}, + {os = runtime.ALL_ODIN_OS_TYPES, arch = {.arm32}}, + {os = runtime.ALL_ODIN_OS_TYPES, arch = {.arm64}}, }, }, }, {// [3] From f93779d4257af2535ac4e1089a3c5932c33fd3f9 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 29 Aug 2024 22:08:01 +0200 Subject: [PATCH 5/7] Parse build_project_name as `[][]string` --- core/odin/parser/file_tags.odin | 50 +++++++++++++------- tests/core/odin/test_file_tags.odin | 73 +++++++++++++++-------------- 2 files changed, 69 insertions(+), 54 deletions(-) diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin index cce34b2da24..514e1b046cb 100644 --- a/core/odin/parser/file_tags.odin +++ b/core/odin/parser/file_tags.odin @@ -18,7 +18,7 @@ Build_Kind :: struct { } File_Tags :: struct { - build_project_name: []string, + build_project_name: [][]string, build: []Build_Kind, private: Private_Flag, ignore: bool, @@ -48,7 +48,9 @@ get_build_arch_from_string :: proc(str: string) -> runtime.Odin_Arch_Type { } @require_results -parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { +parse_file_tags :: proc(file: ast.File, allocator := context.allocator) -> (tags: File_Tags) { + context.allocator = allocator + if file.docs == nil { return } @@ -87,7 +89,10 @@ parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { build_kinds: [dynamic]Build_Kind defer shrink(&build_kinds) - build_project_names: [dynamic]string + build_project_name_strings: [dynamic]string + defer shrink(&build_project_name_strings) + + build_project_names: [dynamic][]string defer shrink(&build_project_names) for comment in file.docs.list { @@ -116,23 +121,32 @@ parse_file_tags :: proc(file: ast.File) -> (tags: File_Tags) { tags.private = .Package } case "build-project-name": - values_loop: for { - skip_whitespace(text, &i) - - name_start := i - - switch next_char(text, &i) { - case 0, '\n': - i -= 1 - break values_loop - case '!': - // include ! in the name - case: - i -= 1 + groups_loop: for { + index_start := len(build_project_name_strings) + + defer append(&build_project_names, build_project_name_strings[index_start:]) + + for { + skip_whitespace(text, &i) + name_start := i + + switch next_char(text, &i) { + case 0, '\n': + i -= 1 + break groups_loop + case ',': + continue groups_loop + case '!': + // include ! in the name + case: + i -= 1 + } + + scan_value(text, &i) + append(&build_project_name_strings, text[name_start:i]) } - scan_value(text, &i) - append(&build_project_names, text[name_start:i]) + append(&build_project_names, build_project_name_strings[index_start:]) } case "build": kinds_loop: for { diff --git a/tests/core/odin/test_file_tags.odin b/tests/core/odin/test_file_tags.odin index a9f10b833f7..f321d78d33b 100644 --- a/tests/core/odin/test_file_tags.odin +++ b/tests/core/odin/test_file_tags.odin @@ -3,6 +3,7 @@ package test_core_odin_parser import "base:runtime" import "core:testing" import "core:slice" +import "core:log" import "core:odin/ast" import "core:odin/parser" @@ -60,12 +61,12 @@ package main }, }, {// [4] src = ` -//+build-project-name foo !bar +//+build-project-name foo !bar, baz //+build js wasm32, js wasm64p32 package main `, tags = { - build_project_name = {"foo", "!bar"}, + build_project_name = {{"foo", "!bar"}, {"baz"}}, build = { { os = {.JS}, @@ -79,11 +80,9 @@ package main }, } - expect :: proc(t: ^testing.T, ok: bool, name: string, i: int, expected, actual: $T, loc := #caller_location) { - testing.expectf(t, ok, - "[%d] expected %s:\n\e[0;32m%#v\e[0m, actual:\n\e[0;31m%#v\e[0m", - i, name, expected, actual, loc=loc - ) + error_expected :: proc(name: string, i: int, expected, actual: $T, loc := #caller_location) { + log.errorf("[%d] expected %s:\n\e[0;32m%#v\e[0m, actual:\n\e[0;31m%#v\e[0m", + i, name, expected, actual, location=loc) } for test_case, i in test_cases { @@ -100,34 +99,36 @@ package main tags := parser.parse_file_tags(file) - expect(t, - slice.equal(test_case.tags.build_project_name, tags.build_project_name), - "build_project_name", i, test_case.tags.build_project_name, tags.build_project_name, - ) - - expect(t, - slice.equal(test_case.tags.build, tags.build), - "build", i, test_case.tags.build, tags.build, - ) - - expect(t, - test_case.tags.private == tags.private, - "private", i, test_case.tags.private, tags.private, - ) - - expect(t, - test_case.tags.ignore == tags.ignore, - "ignore", i, test_case.tags.ignore, tags.ignore, - ) - - expect(t, - test_case.tags.lazy == tags.lazy, - "lazy", i, test_case.tags.lazy, tags.lazy, - ) - - expect(t, - test_case.tags.no_instrumentation == tags.no_instrumentation, - "no_instrumentation", i, test_case.tags.no_instrumentation, tags.no_instrumentation, - ) + + build_project_name_the_same: bool + check: if len(test_case.tags.build_project_name) == len(tags.build_project_name) { + for tag, i in test_case.tags.build_project_name { + slice.equal(tag, tags.build_project_name[i]) or_break check + } + build_project_name_the_same = true + } + if !build_project_name_the_same { + error_expected("build_project_name", i, test_case.tags.build_project_name, tags.build_project_name) + } + + if !slice.equal(test_case.tags.build, tags.build) { + error_expected("build", i, test_case.tags.build, tags.build,) + } + + if test_case.tags.private != tags.private { + error_expected("private", i, test_case.tags.private, tags.private) + } + + if test_case.tags.ignore != tags.ignore { + error_expected("ignore", i, test_case.tags.ignore, tags.ignore) + } + + if test_case.tags.lazy != tags.lazy { + error_expected("lazy", i, test_case.tags.lazy, tags.lazy) + } + + if test_case.tags.no_instrumentation != tags.no_instrumentation { + error_expected("no_instrumentation", i, test_case.tags.no_instrumentation, tags.no_instrumentation) + } } } From a28d574272c1deb294f5ec8a30eeb6581601a71a Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 29 Aug 2024 23:02:49 +0200 Subject: [PATCH 6/7] Add and test match_build_tags --- core/odin/parser/file_tags.odin | 32 ++++++++++++ tests/core/odin/test_file_tags.odin | 80 ++++++++++++++++++----------- 2 files changed, 83 insertions(+), 29 deletions(-) diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin index 514e1b046cb..b12c3b5fd6a 100644 --- a/core/odin/parser/file_tags.odin +++ b/core/odin/parser/file_tags.odin @@ -205,3 +205,35 @@ parse_file_tags :: proc(file: ast.File, allocator := context.allocator) -> (tags return } + +Build_Target :: struct { + os: runtime.Odin_OS_Type, + arch: runtime.Odin_Arch_Type, + project_name: string, +} + +@require_results +match_build_tags :: proc(file_tags: File_Tags, target: Build_Target) -> bool { + + project_name_correct := len(target.project_name) == 0 || len(file_tags.build_project_name) == 0 + + for group in file_tags.build_project_name { + group_correct := true + for name in group { + if name[0] == '!' { + group_correct &&= target.project_name != name[1:] + } else { + group_correct &&= target.project_name == name + } + } + project_name_correct ||= group_correct + } + + os_and_arch_correct := len(file_tags.build) == 0 + + for kind in file_tags.build { + os_and_arch_correct ||= target.os in kind.os && target.arch in kind.arch + } + + return !file_tags.ignore && project_name_correct && os_and_arch_correct +} diff --git a/tests/core/odin/test_file_tags.odin b/tests/core/odin/test_file_tags.odin index f321d78d33b..b2b378c9d8c 100644 --- a/tests/core/odin/test_file_tags.odin +++ b/tests/core/odin/test_file_tags.odin @@ -13,8 +13,12 @@ test_parse_file_tags :: proc(t: ^testing.T) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() Test_Case :: struct { - src: string, - tags: parser.File_Tags, + src: string, + tags: parser.File_Tags, + matching_targets: []struct{ + target: parser.Build_Target, + result: bool, + }, } test_cases := []Test_Case{ @@ -26,6 +30,9 @@ test_parse_file_tags :: proc(t: ^testing.T) { package main `, tags = {}, + matching_targets = { + {{.Windows, .amd64, "foo"}, true}, + }, }, {// [2] src = ` //+build linux, darwin, freebsd, openbsd, netbsd, haiku @@ -44,6 +51,11 @@ package main {os = runtime.ALL_ODIN_OS_TYPES, arch = {.arm64}}, }, }, + matching_targets = { + {{.Linux, .amd64, "foo"}, true}, + {{.Windows, .arm64, "foo"}, true}, + {{.Windows, .amd64, "foo"}, false}, + }, }, {// [3] src = ` // +private @@ -59,6 +71,9 @@ package main lazy = true, ignore = true, }, + matching_targets = { + {{.Linux, .amd64, "foo"}, false}, + }, }, {// [4] src = ` //+build-project-name foo !bar, baz @@ -77,14 +92,14 @@ package main }, }, }, + matching_targets = { + {{.JS, .wasm32, "foo"}, true}, + {{.JS, .wasm64p32, "baz"}, true}, + {{.JS, .wasm64p32, "bar"}, false}, + }, }, } - error_expected :: proc(name: string, i: int, expected, actual: $T, loc := #caller_location) { - log.errorf("[%d] expected %s:\n\e[0;32m%#v\e[0m, actual:\n\e[0;31m%#v\e[0m", - i, name, expected, actual, location=loc) - } - for test_case, i in test_cases { file := ast.File{ @@ -107,28 +122,35 @@ package main } build_project_name_the_same = true } - if !build_project_name_the_same { - error_expected("build_project_name", i, test_case.tags.build_project_name, tags.build_project_name) - } - - if !slice.equal(test_case.tags.build, tags.build) { - error_expected("build", i, test_case.tags.build, tags.build,) - } - - if test_case.tags.private != tags.private { - error_expected("private", i, test_case.tags.private, tags.private) - } - - if test_case.tags.ignore != tags.ignore { - error_expected("ignore", i, test_case.tags.ignore, tags.ignore) - } - - if test_case.tags.lazy != tags.lazy { - error_expected("lazy", i, test_case.tags.lazy, tags.lazy) - } - - if test_case.tags.no_instrumentation != tags.no_instrumentation { - error_expected("no_instrumentation", i, test_case.tags.no_instrumentation, tags.no_instrumentation) + testing.expectf(t, build_project_name_the_same, + "[%d] file_tags.build_project_name expected:\n%#v, got:\n%#v", + i, test_case.tags.build_project_name, tags.build_project_name) + + testing.expectf(t, slice.equal(test_case.tags.build, tags.build), + "[%d] file_tags.build expected:\n%#v, got:\n%#v", + i, test_case.tags.build, tags.build) + + testing.expectf(t, test_case.tags.private == tags.private, + "[%d] file_tags.private expected:\n%v, got:\n%v", + i, test_case.tags.private, tags.private) + + testing.expectf(t, test_case.tags.ignore == tags.ignore, + "[%d] file_tags.ignore expected:\n%v, got:\n%v", + i, test_case.tags.ignore, tags.ignore) + + testing.expectf(t, test_case.tags.lazy == tags.lazy, + "[%d] file_tags.lazy expected:\n%v, got:\n%v", + i, test_case.tags.lazy, tags.lazy) + + testing.expectf(t, test_case.tags.no_instrumentation == tags.no_instrumentation, + "[%d] file_tags.no_instrumentation expected:\n%v, got:\n%v", + i, test_case.tags.no_instrumentation, tags.no_instrumentation) + + for target in test_case.matching_targets { + matches := parser.match_build_tags(test_case.tags, target.target) + testing.expectf(t, matches == target.result, + "[%d] Expected parser.match_build_tags(%#v) == %v, got %v", + i, target.target, target.result, matches) } } } From 655610ec879de4dd56cb3a7f58925f18471c93d2 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 29 Aug 2024 23:06:50 +0200 Subject: [PATCH 7/7] Satisfy vet --- tests/core/odin/test_file_tags.odin | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/core/odin/test_file_tags.odin b/tests/core/odin/test_file_tags.odin index b2b378c9d8c..4af1afc75c5 100644 --- a/tests/core/odin/test_file_tags.odin +++ b/tests/core/odin/test_file_tags.odin @@ -3,7 +3,6 @@ package test_core_odin_parser import "base:runtime" import "core:testing" import "core:slice" -import "core:log" import "core:odin/ast" import "core:odin/parser" @@ -100,14 +99,14 @@ package main }, } - for test_case, i in test_cases { + for test_case, test_case_i in test_cases { file := ast.File{ fullpath = "test.odin", src = test_case.src, } - p := parser.default_parser() + p := parser.default_parser() ok := parser.parse_file(&p, &file) testing.expect(t, ok, "bad parse") @@ -124,33 +123,33 @@ package main } testing.expectf(t, build_project_name_the_same, "[%d] file_tags.build_project_name expected:\n%#v, got:\n%#v", - i, test_case.tags.build_project_name, tags.build_project_name) + test_case_i, test_case.tags.build_project_name, tags.build_project_name) testing.expectf(t, slice.equal(test_case.tags.build, tags.build), "[%d] file_tags.build expected:\n%#v, got:\n%#v", - i, test_case.tags.build, tags.build) + test_case_i, test_case.tags.build, tags.build) testing.expectf(t, test_case.tags.private == tags.private, "[%d] file_tags.private expected:\n%v, got:\n%v", - i, test_case.tags.private, tags.private) + test_case_i, test_case.tags.private, tags.private) testing.expectf(t, test_case.tags.ignore == tags.ignore, "[%d] file_tags.ignore expected:\n%v, got:\n%v", - i, test_case.tags.ignore, tags.ignore) + test_case_i, test_case.tags.ignore, tags.ignore) testing.expectf(t, test_case.tags.lazy == tags.lazy, "[%d] file_tags.lazy expected:\n%v, got:\n%v", - i, test_case.tags.lazy, tags.lazy) + test_case_i, test_case.tags.lazy, tags.lazy) testing.expectf(t, test_case.tags.no_instrumentation == tags.no_instrumentation, "[%d] file_tags.no_instrumentation expected:\n%v, got:\n%v", - i, test_case.tags.no_instrumentation, tags.no_instrumentation) + test_case_i, test_case.tags.no_instrumentation, tags.no_instrumentation) for target in test_case.matching_targets { matches := parser.match_build_tags(test_case.tags, target.target) testing.expectf(t, matches == target.result, "[%d] Expected parser.match_build_tags(%#v) == %v, got %v", - i, target.target, target.result, matches) + test_case_i, target.target, target.result, matches) } } }