flags: Rename varg to overflow, let it be renamed with config

This commit is contained in:
Feoramund
2025-06-09 13:02:05 -04:00
parent 2e199c669f
commit 6dee422700
9 changed files with 66 additions and 62 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ TAG_USAGE :: "usage"
UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>" UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
INTERNAL_VARIADIC_FLAG :: "varg" INTERNAL_OVERFLOW_FLAG :: #config(ODIN_CORE_FLAGS_OVERFLOW_FLAG, "overflow")
RESERVED_HELP_FLAG :: "help" RESERVED_HELP_FLAG :: "help"
RESERVED_HELP_FLAG_SHORT :: "h" RESERVED_HELP_FLAG_SHORT :: "h"
+5 -1
View File
@@ -22,10 +22,14 @@ The format is similar to the Odin binary's way of handling compiler flags.
Unhandled Arguments: Unhandled Arguments:
All unhandled positional arguments are placed into the `varg` field on a All unhandled positional arguments are placed into the `overflow` field on a
struct, if it exists. In UNIX-style parsing, the existence of a `--` on the struct, if it exists. In UNIX-style parsing, the existence of a `--` on the
command line will also pass all arguments afterwards into this field. command line will also pass all arguments afterwards into this field.
If desired, the name of the field may be changed from `overflow` to any string
by setting the `ODIN_CORE_FLAGS_OVERFLOW_FLAG` compile-time config option with
`-define:ODIN_CORE_FLAGS_OVERFLOW_FLAG=<name>`.
Struct Tags: Struct Tags:
+1 -1
View File
@@ -4,7 +4,7 @@ import "core:os"
Parse_Error_Reason :: enum { Parse_Error_Reason :: enum {
None, None,
// An extra positional argument was given, and there is no `varg` field. // An extra positional argument was given, and there is no `overflow` field.
Extra_Positional, Extra_Positional,
// The underlying type does not support the string value it is being set to. // The underlying type does not support the string value it is being set to.
Bad_Value, Bad_Value,
+1 -1
View File
@@ -114,7 +114,7 @@ main :: proc() {
verbose: bool `usage:"Show verbose output."`, verbose: bool `usage:"Show verbose output."`,
debug: bool `args:"hidden" usage:"print debug info"`, debug: bool `args:"hidden" usage:"print debug info"`,
varg: [dynamic]string `usage:"Any extra arguments go here."`, overflow: [dynamic]string `usage:"Any extra arguments go here."`,
} }
opt: Options opt: Options
+2 -2
View File
@@ -33,9 +33,9 @@ push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: stri
field, index, has_pos_assigned := get_field_by_pos(model, pos) field, index, has_pos_assigned := get_field_by_pos(model, pos)
if !has_pos_assigned { if !has_pos_assigned {
when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) { when intrinsics.type_has_field(T, INTERNAL_OVERFLOW_FLAG) {
// Add it to the fallback array. // Add it to the fallback array.
field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG) field = reflect.struct_field_by_name(T, INTERNAL_OVERFLOW_FLAG)
} else { } else {
return Parse_Error { return Parse_Error {
.Extra_Positional, .Extra_Positional,
+1 -1
View File
@@ -95,7 +95,7 @@ parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (
// `--`, and only `--`. // `--`, and only `--`.
// Everything from now on will be treated as an argument. // Everything from now on will be treated as an argument.
future_args = max(int) future_args = max(int)
current_flag = INTERNAL_VARIADIC_FLAG current_flag = INTERNAL_OVERFLOW_FLAG
return return
} }
} }
+2 -2
View File
@@ -80,7 +80,7 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.", fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
model_type, field.name, loc = loc) model_type, field.name, loc = loc)
fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.", fmt.assertf(field.name != INTERNAL_OVERFLOW_FLAG, "%T.%s is defined as required. This is disallowed.",
model_type, field.name, loc = loc) model_type, field.name, loc = loc)
if len(requirement) > 0 { if len(requirement) > 0 {
@@ -113,7 +113,7 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
if length, is_manifold := get_struct_subtag(args_tag, SUBTAG_MANIFOLD); is_manifold { if length, is_manifold := get_struct_subtag(args_tag, SUBTAG_MANIFOLD); is_manifold {
fmt.assertf(!has_pos, fmt.assertf(!has_pos,
"%T.%s has both `%s` and `%s` defined. This is disallowed.\n\tSuggestion: Use a dynamic array field named `%s` to accept unspecified positional arguments.", "%T.%s has both `%s` and `%s` defined. This is disallowed.\n\tSuggestion: Use a dynamic array field named `%s` to accept unspecified positional arguments.",
model_type, field.name, SUBTAG_POS, SUBTAG_MANIFOLD, INTERNAL_VARIADIC_FLAG, loc = loc) model_type, field.name, SUBTAG_POS, SUBTAG_MANIFOLD, INTERNAL_OVERFLOW_FLAG, loc = loc)
if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok { if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
fmt.assertf(value > 0, fmt.assertf(value > 0,
+8 -8
View File
@@ -38,10 +38,10 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
// POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ... // POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ...
// //
sort_flags :: proc(i, j: Flag) -> slice.Ordering { sort_flags :: proc(i, j: Flag) -> slice.Ordering {
// `varg` goes to the end. // `overflow` goes to the end.
if i.name == INTERNAL_VARIADIC_FLAG { if i.name == INTERNAL_OVERFLOW_FLAG {
return .Greater return .Greater
} else if j.name == INTERNAL_VARIADIC_FLAG { } else if j.name == INTERNAL_OVERFLOW_FLAG {
return .Less return .Less
} }
@@ -147,7 +147,7 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
case runtime.Type_Info_Dynamic_Array: case runtime.Type_Info_Dynamic_Array:
requirement_spec := describe_array_requirements(flag) requirement_spec := describe_array_requirements(flag)
if flag.is_manifold || flag.name == INTERNAL_VARIADIC_FLAG { if flag.is_manifold || flag.name == INTERNAL_OVERFLOW_FLAG {
if flag.manifold_length == 0 { if flag.manifold_length == 0 {
flag.type_description = fmt.tprintf("<%v, ...>%s", flag.type_description = fmt.tprintf("<%v, ...>%s",
specific_type_info.elem.id, specific_type_info.elem.id,
@@ -177,7 +177,7 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
} }
} }
if flag.name == INTERNAL_VARIADIC_FLAG { if flag.name == INTERNAL_OVERFLOW_FLAG {
flag.full_length = len(flag.type_description) flag.full_length = len(flag.type_description)
} else if flag.is_boolean { } else if flag.is_boolean {
flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description) flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description)
@@ -201,13 +201,13 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
strings.write_string(&builder, program) strings.write_string(&builder, program)
for flag in visible_flags { for flag in visible_flags {
if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_VARIADIC_FLAG) { if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_OVERFLOW_FLAG) {
continue continue
} }
strings.write_byte(&builder, ' ') strings.write_byte(&builder, ' ')
if flag.name == INTERNAL_VARIADIC_FLAG { if flag.name == INTERNAL_OVERFLOW_FLAG {
strings.write_string(&builder, "...") strings.write_string(&builder, "...")
continue continue
} }
@@ -252,7 +252,7 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
strings.write_byte(&builder, '\t') strings.write_byte(&builder, '\t')
if flag.name == INTERNAL_VARIADIC_FLAG { if flag.name == INTERNAL_OVERFLOW_FLAG {
strings.write_string(&builder, flag.type_description) strings.write_string(&builder, flag.type_description)
} else { } else {
strings.write_string(&builder, flag_prefix) strings.write_string(&builder, flag_prefix)
+45 -45
View File
@@ -454,44 +454,44 @@ test_arrays :: proc(t: ^testing.T) {
@(test) @(test)
test_varargs :: proc(t: ^testing.T) { test_varargs :: proc(t: ^testing.T) {
S :: struct { S :: struct {
varg: [dynamic]string, overflow: [dynamic]string,
} }
s: S s: S
args := [?]string { "abc", "foo", "bar" } args := [?]string { "abc", "foo", "bar" }
result := flags.parse(&s, args[:]) result := flags.parse(&s, args[:])
defer delete(s.varg) defer delete(s.overflow)
testing.expect_value(t, result, nil) testing.expect_value(t, result, nil)
testing.expect_value(t, len(s.varg), 3) testing.expect_value(t, len(s.overflow), 3)
if len(s.varg) < 3 { if len(s.overflow) < 3 {
return return
} }
testing.expect_value(t, s.varg[0], "abc") testing.expect_value(t, s.overflow[0], "abc")
testing.expect_value(t, s.varg[1], "foo") testing.expect_value(t, s.overflow[1], "foo")
testing.expect_value(t, s.varg[2], "bar") testing.expect_value(t, s.overflow[2], "bar")
} }
@(test) @(test)
test_mixed_varargs :: proc(t: ^testing.T) { test_mixed_varargs :: proc(t: ^testing.T) {
S :: struct { S :: struct {
input: string `args:"pos=0"`, input: string `args:"pos=0"`,
varg: [dynamic]string, overflow: [dynamic]string,
} }
s: S s: S
args := [?]string { "abc", "foo", "bar" } args := [?]string { "abc", "foo", "bar" }
result := flags.parse(&s, args[:]) result := flags.parse(&s, args[:])
defer delete(s.varg) defer delete(s.overflow)
testing.expect_value(t, result, nil) testing.expect_value(t, result, nil)
testing.expect_value(t, len(s.varg), 2) testing.expect_value(t, len(s.overflow), 2)
if len(s.varg) < 2 { if len(s.overflow) < 2 {
return return
} }
testing.expect_value(t, s.input, "abc") testing.expect_value(t, s.input, "abc")
testing.expect_value(t, s.varg[0], "foo") testing.expect_value(t, s.overflow[0], "foo")
testing.expect_value(t, s.varg[1], "bar") testing.expect_value(t, s.overflow[1], "bar")
} }
@(test) @(test)
@@ -718,23 +718,23 @@ test_tags_required_limit_max :: proc(t: ^testing.T) {
test_tags_pos_out_of_order :: proc(t: ^testing.T) { test_tags_pos_out_of_order :: proc(t: ^testing.T) {
S :: struct { S :: struct {
a: int `args:"pos=2"`, a: int `args:"pos=2"`,
varg: [dynamic]int, overflow: [dynamic]int,
} }
s: S s: S
args := [?]string { "1", "2", "3", "4" } args := [?]string { "1", "2", "3", "4" }
result := flags.parse(&s, args[:]) result := flags.parse(&s, args[:])
defer delete(s.varg) defer delete(s.overflow)
testing.expect_value(t, result, nil) testing.expect_value(t, result, nil)
testing.expect_value(t, len(s.varg), 3) testing.expect_value(t, len(s.overflow), 3)
if len(s.varg) < 3 { if len(s.overflow) < 3 {
return return
} }
testing.expect_value(t, s.a, 3) testing.expect_value(t, s.a, 3)
testing.expect_value(t, s.varg[0], 1) testing.expect_value(t, s.overflow[0], 1)
testing.expect_value(t, s.varg[1], 2) testing.expect_value(t, s.overflow[1], 2)
testing.expect_value(t, s.varg[2], 4) testing.expect_value(t, s.overflow[2], 4)
} }
@(test) @(test)
@@ -899,7 +899,7 @@ test_pos_nonoverlap :: proc(t: ^testing.T) {
@(test) @(test)
test_pos_many_args :: proc(t: ^testing.T) { test_pos_many_args :: proc(t: ^testing.T) {
S :: struct { S :: struct {
varg: [dynamic]int, overflow: [dynamic]int,
a: int `args:"pos=0,required"`, a: int `args:"pos=0,required"`,
b: int `args:"pos=64,required"`, b: int `args:"pos=64,required"`,
c: int `args:"pos=66,required"`, c: int `args:"pos=66,required"`,
@@ -908,7 +908,7 @@ test_pos_many_args :: proc(t: ^testing.T) {
s: S s: S
args: [dynamic]string args: [dynamic]string
defer delete(s.varg) defer delete(s.overflow)
for i in 0 ..< 130 { append(&args, fmt.aprintf("%i", 1 + i)) } for i in 0 ..< 130 { append(&args, fmt.aprintf("%i", 1 + i)) }
defer { defer {
@@ -922,14 +922,14 @@ test_pos_many_args :: proc(t: ^testing.T) {
testing.expect_value(t, result, nil) testing.expect_value(t, result, nil)
testing.expect_value(t, s.a, 1) testing.expect_value(t, s.a, 1)
for i in 1 ..< 63 { testing.expect_value(t, s.varg[i], 2 + i) } for i in 1 ..< 63 { testing.expect_value(t, s.overflow[i], 2 + i) }
testing.expect_value(t, s.b, 65) testing.expect_value(t, s.b, 65)
testing.expect_value(t, s.varg[63], 66) testing.expect_value(t, s.overflow[63], 66)
testing.expect_value(t, s.c, 67) testing.expect_value(t, s.c, 67)
testing.expect_value(t, s.varg[64], 68) testing.expect_value(t, s.overflow[64], 68)
testing.expect_value(t, s.varg[65], 69) testing.expect_value(t, s.overflow[65], 69)
testing.expect_value(t, s.varg[66], 70) testing.expect_value(t, s.overflow[66], 70)
for i in 67 ..< 126 { testing.expect_value(t, s.varg[i], 4 + i) } for i in 67 ..< 126 { testing.expect_value(t, s.overflow[i], 4 + i) }
testing.expect_value(t, s.d, 130) testing.expect_value(t, s.d, 130)
} }
@@ -1135,7 +1135,7 @@ test_unix_positional :: proc(t: ^testing.T) {
@(test) @(test)
test_unix_positional_with_manifold :: proc(t: ^testing.T) { test_unix_positional_with_manifold :: proc(t: ^testing.T) {
S :: struct { S :: struct {
varg: [dynamic]int, overflow: [dynamic]int,
v: [dynamic]int `args:"manifold"`, v: [dynamic]int `args:"manifold"`,
} }
s: S s: S
@@ -1144,18 +1144,18 @@ test_unix_positional_with_manifold :: proc(t: ^testing.T) {
result := flags.parse(&s, args[:], .Unix) result := flags.parse(&s, args[:], .Unix)
defer { defer {
delete(s.varg) delete(s.overflow)
delete(s.v) delete(s.v)
} }
testing.expect_value(t, result, nil) testing.expect_value(t, result, nil)
testing.expect_value(t, len(s.varg), 1) testing.expect_value(t, len(s.overflow), 1)
testing.expect_value(t, len(s.v), 2) testing.expect_value(t, len(s.v), 2)
} }
@(test) @(test)
test_unix_double_dash_varargs :: proc(t: ^testing.T) { test_unix_double_dash_varargs :: proc(t: ^testing.T) {
S :: struct { S :: struct {
varg: [dynamic]string, overflow: [dynamic]string,
i: int, i: int,
} }
s: S s: S
@@ -1164,19 +1164,19 @@ test_unix_double_dash_varargs :: proc(t: ^testing.T) {
result := flags.parse(&s, args[:], .Unix) result := flags.parse(&s, args[:], .Unix)
defer { defer {
delete(s.varg) delete(s.overflow)
} }
testing.expect_value(t, result, nil) testing.expect_value(t, result, nil)
testing.expect_value(t, len(s.varg), 3) testing.expect_value(t, len(s.overflow), 3)
testing.expect_value(t, s.i, 3) testing.expect_value(t, s.i, 3)
if len(s.varg) != 3 { if len(s.overflow) != 3 {
return return
} }
testing.expect_value(t, s.varg[0], "hellope") testing.expect_value(t, s.overflow[0], "hellope")
testing.expect_value(t, s.varg[1], "-i") testing.expect_value(t, s.overflow[1], "-i")
testing.expect_value(t, s.varg[2], "5") testing.expect_value(t, s.overflow[2], "5")
} }
@(test) @(test)
@@ -1200,17 +1200,17 @@ test_unix_no_value :: proc(t: ^testing.T) {
@(test) @(test)
test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) { test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) {
S :: struct { S :: struct {
varg: [dynamic]cstring, overflow: [dynamic]cstring,
} }
s: S s: S
args := [?]string { "Hellope", "world!" } args := [?]string { "Hellope", "world!" }
result := flags.parse(&s, args[:]) result := flags.parse(&s, args[:])
defer { defer {
for v in s.varg { for v in s.overflow {
delete(v) delete(v)
} }
delete(s.varg) delete(s.overflow)
} }
testing.expect_value(t, result, nil) testing.expect_value(t, result, nil)
} }
@@ -1428,7 +1428,7 @@ very nicely.
debug: bool `args:"hidden" usage:"print debug info"`, debug: bool `args:"hidden" usage:"print debug info"`,
verbose: bool, verbose: bool,
varg: [dynamic]string, overflow: [dynamic]string,
} }
builder := strings.builder_make() builder := strings.builder_make()
@@ -1441,7 +1441,7 @@ very nicely.
@(test) @(test)
test_usage_write_unix :: proc(t: ^testing.T) { test_usage_write_unix :: proc(t: ^testing.T) {
Expected_Output :: `Usage: Expected_Output :: `Usage:
varg required-number [number] [name] --bars --bots --foos --gadgets --manifold-flag --widgets [--array] [--count] [--greek] [--verbose] ... overflow required-number [number] [name] --bars --bots --foos --gadgets --manifold-flag --widgets [--array] [--count] [--greek] [--verbose] ...
Flags: Flags:
--required-number <int>, required | some number --required-number <int>, required | some number
--number <int> | some other number --number <int> | some other number
@@ -1493,12 +1493,12 @@ very nicely.
debug: bool `args:"hidden" usage:"print debug info"`, debug: bool `args:"hidden" usage:"print debug info"`,
verbose: bool, verbose: bool,
varg: [dynamic]string, overflow: [dynamic]string,
} }
builder := strings.builder_make() builder := strings.builder_make()
defer strings.builder_destroy(&builder) defer strings.builder_destroy(&builder)
writer := strings.to_stream(&builder) writer := strings.to_stream(&builder)
flags.write_usage(writer, S, "varg", .Unix) flags.write_usage(writer, S, "overflow", .Unix)
testing.expect_value(t, strings.to_string(builder), Expected_Output) testing.expect_value(t, strings.to_string(builder), Expected_Output)
} }