From e05d23d4098fd8d58dd8718971586a9143452117 Mon Sep 17 00:00:00 2001 From: Simon Kellet Date: Mon, 6 Jul 2026 20:09:08 +0100 Subject: [PATCH] renamed to fleg (name pending) --- README.md | 16 ++--- fleg/flag.odin | 162 +++++++++++++++++++++++++++++++++++++++++++++++++ main.odin | 19 +++--- 3 files changed, 179 insertions(+), 18 deletions(-) create mode 100644 fleg/flag.odin diff --git a/README.md b/README.md index 3497ff7..41dd524 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,19 @@ This a very simple implementation of Go-like flags in Odin ## How to use ```odin - import "myflag" + import "fleg" isRunning: bool isNumber: int isString: string isFloat: f32 - myflag.BoolVar(&isRunning, "isRunning", true, "A select piece of code is running!") - myflag.IntVar(&isNumber, "isNumber", 3434, "A number!") - myflag.StringVar(&isString, "isString", "Hellope!", "A string!") - myflag.Float32Var(&isFloat, "isFloat", 3.1415, "A float!") + fleg.BoolVar(&isRunning, "isRunning", true, "A select piece of code is running!") + fleg.IntVar(&isNumber, "isNumber", 3434, "A number!") + fleg.StringVar(&isString, "isString", "Hellope!", "A string!") + fleg.Float32Var(&isFloat, "isFloat", 3.1415, "A float!") - myflag.parse_flags() + fleg.parse_flags() ``` ***Supports: bool, int, string, f32*** @@ -63,6 +63,6 @@ If needed, you are able to setup a custom allocator like so: arena_alloc := vmem.arena_allocator(&arena) defer vmem.arena_destroy(&arena) - myflag.init_custom_allocator(arena_alloc) - defer myflag.destroy() //clean up flags + fleg.init_custom_allocator(arena_alloc) + defer fleg.destroy() //clean up flags ``` \ No newline at end of file diff --git a/fleg/flag.odin b/fleg/flag.odin new file mode 100644 index 0000000..076917b --- /dev/null +++ b/fleg/flag.odin @@ -0,0 +1,162 @@ +#+vet explicit-allocators +package fleg + +import "base:runtime" +import "core:fmt" +import "core:os" +import "core:strconv" +import "core:strings" + +FORCE_HELP_ON_EMPTY_ARGS := false + +Flag_Value_Ptr :: union { + ^bool, + ^int, + ^string, + ^f32, + ^f64, +} + +Flag :: struct { + name: string, + value: Flag_Value_Ptr, + usage: string, + parsed: bool, // sck: flag to check if it has been parsed +} + +all_flags: [dynamic]Flag +flag_allocator: runtime.Allocator + +// sck: Used to create a customer allocator +init_custom_allocator :: proc(allocator: runtime.Allocator) { + destroy() // sck: make sure to destory the old allocations + + flag_allocator = allocator + all_flags = make([dynamic]Flag, flag_allocator) +} + +destroy :: proc() { + delete(all_flags) + all_flags = {} // sck: needed? +} + +print_flags :: proc() { + for f in all_flags { + fmt.printfln("%s: %v: %s", f.name, f.value, f.usage) + } +} + +print_usage :: proc() { + fmt.println("\tUsage: ") + for f in all_flags { + switch v in f.value { + case ^bool: + fmt.printfln("\t-%s:\n\t\t%s (default: %v)\n", f.name, f.usage, v^) + case ^int: + fmt.printfln("\t-%s:\n\t\t%s (default: %d)\n", f.name, f.usage, v^) + case ^string: + fmt.printfln("\t-%s:\n\t\t%s (default: %s)\n", f.name, f.usage, v^) + case ^f32: + fmt.printfln("\t-%s:\n\t\t%s (default: %f)\n", f.name, f.usage, v^) + case ^f64: + fmt.printfln("\t-%s:\n\t\t%s (default: %f)\n", f.name, f.usage, v^) + } + } + os.exit(0) +} + +BoolVar :: proc(ptr: ^bool, name: string, default: bool, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +IntVar :: proc(ptr: ^int, name: string, default: int, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +StringVar :: proc(ptr: ^string, name: string, default: string, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +Float32Var :: proc(ptr: ^f32, name: string, default: f32, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +Float64Var :: proc(ptr: ^f64, name: string, default: f64, usage: string) { + ptr^ = default + append(&all_flags, Flag{name = name, value = ptr, usage = usage}) +} + +parse_flags :: proc() { + if len(os.args) < 2 && FORCE_HELP_ON_EMPTY_ARGS {print_usage()} + + for &a in os.args { + if a == "-help" || a == "--help" {print_usage()} + if a == os.args[0] {continue} + + for &f in all_flags { + if f.parsed {continue} + a = strings.trim_prefix(a, "-") + + name := a + value: string + + split := strings.index_any(a, "=") + if split >= 0 { + name = a[:split] + value = a[split + 1:] + } else { + name = a + } + + if name == f.name && !f.parsed { + f.parsed = true + switch &v in f.value { + case ^bool: + if value == "" {break} + parsed, ok := strconv.parse_bool(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted bool", f.name, value) + os.exit(1) + } + v^ = parsed + + case ^int: + if value == "" {break} + parsed, ok := strconv.parse_int(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted int", f.name, value) + os.exit(1) + } + v^ = parsed + + case ^string: + if value == "" {break} + // TODO: validate string? + v^ = value + + case ^f32: + if value == "" {break} + parsed, ok := strconv.parse_f32(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted f32", f.name, value) + os.exit(1) + } + v^ = parsed + + case ^f64: + if value == "" {break} + parsed, ok := strconv.parse_f64(value) + if !ok { + fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted f64", f.name, value) + os.exit(1) + } + v^ = parsed + } + } + } + } +} diff --git a/main.odin b/main.odin index 1acab58..7ecb59c 100644 --- a/main.odin +++ b/main.odin @@ -1,8 +1,7 @@ -package flag +package main import "core:fmt" -import vmem "core:mem/virtual" -import "myflag" +import "fleg" main :: proc(){ /* @@ -12,20 +11,20 @@ main :: proc(){ arena_alloc := vmem.arena_allocator(&arena) defer vmem.arena_destroy(&arena) //clean - myflag.init_custom_allocator(arena_alloc) + fleg.init_custom_allocator(arena_alloc) */ - defer myflag.destroy() + defer fleg.destroy() isRunning: bool isNumber: int isString: string isFloat: f32 - myflag.BoolVar(&isRunning, "isRunning", true, "A select piece of code is running!") - myflag.IntVar(&isNumber, "isNumber", 3434, "A number!") - myflag.StringVar(&isString, "isString", "Hellope!", "A string!") - myflag.Float32Var(&isFloat, "isFloat", 3.1415, "A float!") - myflag.parse_flags() + fleg.BoolVar(&isRunning, "isRunning", true, "A select piece of code is running!") + fleg.IntVar(&isNumber, "isNumber", 3434, "A number!") + fleg.StringVar(&isString, "isString", "Hellope!", "A string!") + fleg.Float32Var(&isFloat, "isFloat", 3.1415, "A float!") + fleg.parse_flags() fmt.println("-------MAIN--------") fmt.println(isRunning)