internal_assignment.odin 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. //+private
  2. package flags
  3. import "base:intrinsics"
  4. @require import "base:runtime"
  5. import "core:container/bit_array"
  6. @require import "core:fmt"
  7. @require import "core:mem"
  8. import "core:reflect"
  9. @require import "core:strconv"
  10. @require import "core:strings"
  11. // Push a positional argument onto a data struct, checking for specified
  12. // positionals first before adding it to a fallback field.
  13. @(optimization_mode="favor_size")
  14. push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
  15. if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
  16. // The max index is set, which means we're out of space.
  17. // Add one free bit by setting the index above to false.
  18. bit_array.set(&parser.filled_pos, 1 + parser.filled_pos.max_index, false)
  19. }
  20. pos: int = ---
  21. {
  22. iter := bit_array.make_iterator(&parser.filled_pos)
  23. ok: bool
  24. pos, ok = bit_array.iterate_by_unset(&iter)
  25. // This may be an allocator error.
  26. assert(ok, "Unable to find a free spot in the positional bit_array.")
  27. }
  28. field, index, has_pos_assigned := get_field_by_pos(model, pos)
  29. if !has_pos_assigned {
  30. when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) {
  31. // Add it to the fallback array.
  32. field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG)
  33. } else {
  34. return Parse_Error {
  35. .Extra_Positional,
  36. fmt.tprintf("Got extra positional argument `%s` with nowhere to store it.", arg),
  37. }
  38. }
  39. }
  40. ptr := cast(rawptr)(cast(uintptr)model + field.offset)
  41. args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
  42. field_name := get_field_name(field)
  43. error = parse_and_set_pointer_by_type(ptr, arg, field.type, args_tag)
  44. #partial switch &specific_error in error {
  45. case Parse_Error:
  46. specific_error.message = fmt.tprintf("Unable to set positional #%i (%s) of type %v to `%s`.%s%s",
  47. pos,
  48. field_name,
  49. field.type,
  50. arg,
  51. " " if len(specific_error.message) > 0 else "",
  52. specific_error.message)
  53. case nil:
  54. bit_array.set(&parser.filled_pos, pos)
  55. bit_array.set(&parser.fields_set, index)
  56. }
  57. return
  58. }
  59. register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int) {
  60. if pos, ok := get_field_pos(field); ok {
  61. bit_array.set(&parser.filled_pos, pos)
  62. }
  63. bit_array.set(&parser.fields_set, index)
  64. }
  65. // Set a `-flag` argument, Odin-style.
  66. @(optimization_mode="favor_size")
  67. set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
  68. // We make a special case for help requests.
  69. switch name {
  70. case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
  71. return Help_Request{}
  72. }
  73. field, index := get_field_by_name(model, name) or_return
  74. #partial switch specific_type_info in field.type.variant {
  75. case runtime.Type_Info_Boolean:
  76. ptr := cast(^bool)(cast(uintptr)model + field.offset)
  77. ptr^ = true
  78. case:
  79. return Parse_Error {
  80. .Bad_Value,
  81. fmt.tprintf("Unable to set `%s` of type %v to true.", name, field.type),
  82. }
  83. }
  84. register_field(parser, field, index)
  85. return
  86. }
  87. // Set a `-flag` argument, UNIX-style.
  88. @(optimization_mode="favor_size")
  89. set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
  90. // We make a special case for help requests.
  91. switch name {
  92. case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
  93. return 0, Help_Request{}
  94. }
  95. field, index := get_field_by_name(model, name) or_return
  96. #partial switch specific_type_info in field.type.variant {
  97. case runtime.Type_Info_Boolean:
  98. ptr := cast(^bool)(cast(uintptr)model + field.offset)
  99. ptr^ = true
  100. case runtime.Type_Info_Dynamic_Array:
  101. future_args = 1
  102. if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
  103. if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic {
  104. // Variadic arrays may specify how many arguments they consume at once.
  105. // Otherwise, they take everything that's left.
  106. if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
  107. future_args = cast(int)value
  108. } else {
  109. future_args = max(int)
  110. }
  111. }
  112. }
  113. case:
  114. // `--flag`, waiting on its value.
  115. future_args = 1
  116. }
  117. register_field(parser, field, index)
  118. return
  119. }
  120. // Set a `-flag:option` argument.
  121. @(optimization_mode="favor_size")
  122. set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
  123. field, index := get_field_by_name(model, name) or_return
  124. if len(option) == 0 {
  125. return Parse_Error {
  126. .No_Value,
  127. fmt.tprintf("Setting `%s` to an empty value is meaningless.", name),
  128. }
  129. }
  130. // Guard against incorrect syntax.
  131. #partial switch specific_type_info in field.type.variant {
  132. case runtime.Type_Info_Map:
  133. return Parse_Error {
  134. .No_Value,
  135. fmt.tprintf("Unable to set `%s` of type %v to `%s`. Are you missing an `=`? The correct format is `map:key=value`.", name, field.type, option),
  136. }
  137. }
  138. ptr := cast(rawptr)(cast(uintptr)model + field.offset)
  139. args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
  140. error = parse_and_set_pointer_by_type(ptr, option, field.type, args_tag)
  141. #partial switch &specific_error in error {
  142. case Parse_Error:
  143. specific_error.message = fmt.tprintf("Unable to set `%s` of type %v to `%s`.%s%s",
  144. name,
  145. field.type,
  146. option,
  147. " " if len(specific_error.message) > 0 else "",
  148. specific_error.message)
  149. case nil:
  150. register_field(parser, field, index)
  151. }
  152. return
  153. }
  154. // Set a `-map:key=value` argument.
  155. @(optimization_mode="favor_size")
  156. set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
  157. field, index := get_field_by_name(model, name) or_return
  158. #partial switch specific_type_info in field.type.variant {
  159. case runtime.Type_Info_Map:
  160. key := key
  161. key_ptr := cast(rawptr)&key
  162. key_cstr: cstring
  163. if reflect.is_cstring(specific_type_info.key) {
  164. // We clone the key here, because it's liable to be a slice of an
  165. // Odin string, and we need to put a NUL terminator in it.
  166. key_cstr = strings.clone_to_cstring(key)
  167. key_ptr = &key_cstr
  168. }
  169. defer if key_cstr != nil {
  170. delete(key_cstr)
  171. }
  172. raw_map := (^runtime.Raw_Map)(cast(uintptr)model + field.offset)
  173. hash := specific_type_info.map_info.key_hasher(key_ptr, runtime.map_seed(raw_map^))
  174. backing_alloc := false
  175. elem_backing: []byte
  176. value_ptr: rawptr
  177. if raw_map.allocator.procedure == nil {
  178. raw_map.allocator = context.allocator
  179. } else {
  180. value_ptr = runtime.__dynamic_map_get(raw_map,
  181. specific_type_info.map_info,
  182. hash,
  183. key_ptr,
  184. )
  185. }
  186. if value_ptr == nil {
  187. alloc_error: runtime.Allocator_Error = ---
  188. elem_backing, alloc_error = mem.alloc_bytes(specific_type_info.value.size, specific_type_info.value.align)
  189. if elem_backing == nil {
  190. return Parse_Error {
  191. alloc_error,
  192. "Failed to allocate element backing for map value.",
  193. }
  194. }
  195. backing_alloc = true
  196. value_ptr = raw_data(elem_backing)
  197. }
  198. args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
  199. error = parse_and_set_pointer_by_type(value_ptr, value, specific_type_info.value, args_tag)
  200. #partial switch &specific_error in error {
  201. case Parse_Error:
  202. specific_error.message = fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.%s%s",
  203. name,
  204. field.type,
  205. key,
  206. value,
  207. " " if len(specific_error.message) > 0 else "",
  208. specific_error.message)
  209. }
  210. if backing_alloc {
  211. runtime.__dynamic_map_set(raw_map,
  212. specific_type_info.map_info,
  213. hash,
  214. key_ptr,
  215. value_ptr,
  216. )
  217. delete(elem_backing)
  218. }
  219. register_field(parser, field, index)
  220. return
  221. }
  222. return Parse_Error {
  223. .Bad_Value,
  224. fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.", name, field.type, key, value),
  225. }
  226. }