parsing.odin 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package flags
  2. @require import "core:container/bit_array"
  3. @require import "core:fmt"
  4. Parsing_Style :: enum {
  5. // Odin-style: `-flag`, `-flag:option`, `-map:key=value`
  6. Odin,
  7. // UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument`
  8. Unix,
  9. }
  10. /*
  11. Parse a slice of command-line arguments into an annotated struct.
  12. *Allocates Using Provided Allocator*
  13. By default, this proc will only allocate memory outside of its lifetime if it
  14. has to append to a dynamic array, set a map value, or set a cstring.
  15. The program is expected to free any allocations on `model` as a result of parsing.
  16. Inputs:
  17. - model: A pointer to an annotated struct with flag definitions.
  18. - args: A slice of strings, usually `os.args[1:]`.
  19. - style: The argument parsing style.
  20. - validate_args: If `true`, will ensure that all required arguments are set if no errors occurred.
  21. - strict: If `true`, will return on first error. Otherwise, parsing continues.
  22. - allocator: (default: context.allocator)
  23. - loc: The caller location for debugging purposes (default: #caller_location)
  24. Returns:
  25. - error: A union of errors; parsing, file open, a help request, or validation.
  26. */
  27. @(optimization_mode="favor_size")
  28. parse :: proc(
  29. model: ^$T,
  30. args: []string,
  31. style: Parsing_Style = .Odin,
  32. validate_args: bool = true,
  33. strict: bool = true,
  34. allocator := context.allocator,
  35. loc := #caller_location,
  36. ) -> (error: Error) {
  37. context.allocator = allocator
  38. validate_structure(model^, style, loc)
  39. parser: Parser
  40. defer {
  41. bit_array.destroy(&parser.filled_pos)
  42. bit_array.destroy(&parser.fields_set)
  43. }
  44. switch style {
  45. case .Odin:
  46. for arg in args {
  47. error = parse_one_odin_arg(model, &parser, arg)
  48. if strict && error != nil {
  49. return
  50. }
  51. }
  52. case .Unix:
  53. // Support for `-flag argument (repeating-argument ...)`
  54. future_args: int
  55. current_flag: string
  56. for i := 0; i < len(args); i += 1 {
  57. #no_bounds_check arg := args[i]
  58. future_args, current_flag, error = parse_one_unix_arg(model, &parser, arg)
  59. if strict && error != nil {
  60. return
  61. }
  62. for starting_future_args := future_args; future_args > 0; future_args -= 1 {
  63. i += 1
  64. if i == len(args) {
  65. if future_args == starting_future_args {
  66. return Parse_Error {
  67. .No_Value,
  68. fmt.tprintf("Expected a value for `%s` but none was given.", current_flag),
  69. }
  70. }
  71. break
  72. }
  73. #no_bounds_check arg = args[i]
  74. error = set_option(model, &parser, current_flag, arg)
  75. if strict && error != nil {
  76. return
  77. }
  78. }
  79. }
  80. }
  81. if error == nil && validate_args {
  82. return validate_arguments(model, &parser)
  83. }
  84. return
  85. }