diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2024-08-30 11:58:18 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-30 11:58:18 +0100 |
| commit | 773703bc83c05eb799359d6d65d32717796c19e0 (patch) | |
| tree | 945970e7ef45f1e5e301760c4e3e7af88d1e949e /core | |
| parent | a4e865f90bd3a6f7e5ff004d691863827d0b9399 (diff) | |
| parent | 655610ec879de4dd56cb3a7f58925f18471c93d2 (diff) | |
Merge pull request #4009 from thetarnav/file-tag-parser
Add a file tag parser to core:odin/parser
Diffstat (limited to 'core')
| -rw-r--r-- | core/odin/parser/file_tags.odin | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin new file mode 100644 index 000000000..b12c3b5fd --- /dev/null +++ b/core/odin/parser/file_tags.odin @@ -0,0 +1,239 @@ +package odin_parser + +import "base:runtime" +import "core:strings" +import "core:reflect" + +import "../ast" + +Private_Flag :: enum { + Public, + Package, + File, +} + +Build_Kind :: struct { + os: runtime.Odin_OS_Types, + arch: runtime.Odin_Arch_Types, +} + +File_Tags :: struct { + build_project_name: [][]string, + build: []Build_Kind, + private: Private_Flag, + ignore: bool, + lazy: bool, + no_instrumentation: bool, +} + +@require_results +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) -> 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 +} + +@require_results +parse_file_tags :: proc(file: ast.File, allocator := context.allocator) -> (tags: File_Tags) { + context.allocator = allocator + + 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 + } + } + } + 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_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 { + 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": + 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]) + } + + append(&build_project_names, build_project_name_strings[index_start:]) + } + case "build": + kinds_loop: for { + os_positive: runtime.Odin_OS_Types + os_negative: runtime.Odin_OS_Types + + arch_positive: runtime.Odin_Arch_Types + arch_negative: runtime.Odin_Arch_Types + + defer append(&build_kinds, Build_Kind{ + 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 { + 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} + } + } + } + } + } + } + } + + tags.build = build_kinds[:] + tags.build_project_name = build_project_names[:] + + 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 +} |