diff --git a/tools/odinfmt/flag/flag.odin b/tools/odinfmt/flag/flag.odin index 543438678..041b69df0 100644 --- a/tools/odinfmt/flag/flag.odin +++ b/tools/odinfmt/flag/flag.odin @@ -1,232 +1,219 @@ -package flag - -import "core:runtime" -import "core:strings" -import "core:reflect" -import "core:fmt" -import "core:mem" -import "core:strconv" - -Flag_Error :: enum { - None, - No_Base_Struct, - Arg_Error, - Arg_Unsupported_Field_Type, - Arg_Not_Defined, - Arg_Non_Optional, - Value_Parse_Error, - Tag_Error, -} - -Flag :: struct { - optional: bool, - type: ^runtime.Type_Info, - data: rawptr, - tag_ptr: rawptr, - parsed: bool, -} - -Flag_Context :: struct { - seen_flags: map [string] Flag, -} - -parse_args :: proc(ctx: ^Flag_Context, args: []string) -> Flag_Error { - - using runtime; - - args := args; - - for true { - - if len(args) == 0 { - return .None; - } - - arg := args[0]; - - if len(arg) < 2 || arg[0] != '-' { - return .Arg_Error; - } - - minus_count := 1; - - if arg[1] == '-' { - minus_count += 1; - - if len(arg) == 2 { - return .Arg_Error; - } - } - - name := arg[minus_count:]; - - if len(name) == 0 { - return .Arg_Error; - } - - args = args[1:]; - - assign_index := strings.index(name, "="); - - value := ""; - - if assign_index > 0 { - value = name[assign_index + 1:]; - name = name[0:assign_index]; - } - - flag := &ctx.seen_flags[name]; - - if flag == nil { - return .Arg_Not_Defined; - } - - if reflect.is_boolean(flag.type) { - tmp := true; - mem.copy(flag.data, &tmp, flag.type.size); - flag.parsed = true; - continue; - } - - //must be in the next argument - else if value == "" { - - if len(args) == 0 { - return .Arg_Error; - } - - value = args[0]; - args = args[1:]; - } - - #partial switch in flag.type.variant { - case Type_Info_Integer: - if v, ok := strconv.parse_int(value); ok { - mem.copy(flag.data, &v, flag.type.size); - } - else { - return .Value_Parse_Error; - } - case Type_Info_String: - raw_string := cast(^mem.Raw_String)flag.data; - raw_string.data = strings.ptr_from_string(value); - raw_string.len = len(value); - case Type_Info_Float: - switch flag.type.size { - case 32: - if v, ok := strconv.parse_f32(value); ok { - mem.copy(flag.data, &v, flag.type.size); - } - else { - return .Value_Parse_Error; - } - case 64: - if v, ok := strconv.parse_f64(value); ok { - mem.copy(flag.data, &v, flag.type.size); - } - else { - return .Value_Parse_Error; - } - } - } - - flag.parsed = true; - } - - - - return .None; -} - -reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error { - using runtime; - - if !reflect.is_struct(type_info_of(v.id)) { - return .No_Base_Struct; - } - - names := reflect.struct_field_names(v.id); - types := reflect.struct_field_types(v.id); - offsets := reflect.struct_field_offsets(v.id); - tags := reflect.struct_field_tags(v.id); - - for name, i in names { - - flag: Flag; - - type := types[i]; - - if named_type, ok := type.variant.(Type_Info_Named); ok { - - if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && union_type.maybe && len(union_type.variants) == 1 { - flag.optional = true; - flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i])); - type = union_type.variants[0]; - } - - else { - return .Arg_Unsupported_Field_Type; - } - - } - - #partial switch in type.variant { - case Type_Info_Integer, Type_Info_String, Type_Info_Boolean, Type_Info_Float: - flag.type = type; - flag.data = rawptr(uintptr(v.data) + uintptr(offsets[i])); - case: - return .Arg_Unsupported_Field_Type; - } - - flag_name: string; - - if value, ok := reflect.struct_tag_lookup(tags[i], "flag"); ok { - flag_name = cast(string)value; - } - - else { - return .Tag_Error; - } - - ctx.seen_flags[flag_name] = flag; - } - - return .None; -} - -parse :: proc(v: any, args: []string) -> Flag_Error { - - if v == nil { - return .None; - } - - ctx: Flag_Context; - - if res := reflect_args_structure(&ctx, v); res != .None { - return res; - } - - if res := parse_args(&ctx, args); res != .None { - return res; - } - - //validate that the required flags were actually set - for k, v in ctx.seen_flags { - - if v.optional && v.parsed { - tag_value : i32 = 1; - mem.copy(v.tag_ptr, &tag_value, 4); //4 constant is probably not portable, but it works for me currently - } - - else if !v.parsed && !v.optional { - return .Arg_Non_Optional; - } - - } - - return .None; -} - -usage :: proc(v: any) -> string { - return "failed"; -} \ No newline at end of file +package flag + +import "core:runtime" +import "core:strings" +import "core:reflect" +import "core:fmt" +import "core:mem" +import "core:strconv" + +Flag_Error :: enum { + None, + No_Base_Struct, + Arg_Error, + Arg_Unsupported_Field_Type, + Arg_Not_Defined, + Arg_Non_Optional, + Value_Parse_Error, + Tag_Error, +} + +Flag :: struct { + optional: bool, + type: ^runtime.Type_Info, + data: rawptr, + tag_ptr: rawptr, + parsed: bool, +} + +Flag_Context :: struct { + seen_flags: map[string]Flag, +} + +parse_args :: proc(ctx: ^Flag_Context, args: []string) -> Flag_Error { + + using runtime; + + args := args; + + for true { + + if len(args) == 0 { + return .None; + } + + arg := args[0]; + + if len(arg) < 2 || arg[0] != '-' { + return .Arg_Error; + } + + minus_count := 1; + + if arg[1] == '-' { + minus_count += 1; + + if len(arg) == 2 { + return .Arg_Error; + } + } + + name := arg[minus_count:]; + + if len(name) == 0 { + return .Arg_Error; + } + + args = args[1:]; + + assign_index := strings.index(name, "="); + + value := ""; + + if assign_index > 0 { + value = name[assign_index + 1:]; + name = name[0:assign_index]; + } + + flag := &ctx.seen_flags[name]; + + if flag == nil { + return .Arg_Not_Defined; + } + + if reflect.is_boolean(flag.type) { + tmp := true; + mem.copy(flag.data, &tmp, flag.type.size); + flag.parsed = true; + continue; + } else + + //must be in the next argument + if value == "" { + + if len(args) == 0 { + return .Arg_Error; + } + + value = args[0]; + args = args[1:]; + } + + #partial switch _ in flag.type.variant { + case Type_Info_Integer: + if v, ok := strconv.parse_int(value); ok { + mem.copy(flag.data, &v, flag.type.size); + } else { + return .Value_Parse_Error; + } + case Type_Info_String: + raw_string := cast(^mem.Raw_String)flag.data; + raw_string.data = strings.ptr_from_string(value); + raw_string.len = len(value); + case Type_Info_Float: + switch flag.type.size { + case 32: + if v, ok := strconv.parse_f32(value); ok { + mem.copy(flag.data, &v, flag.type.size); + } else { + return .Value_Parse_Error; + } + case 64: + if v, ok := strconv.parse_f64(value); ok { + mem.copy(flag.data, &v, flag.type.size); + } else { + return .Value_Parse_Error; + } + } + } + + flag.parsed = true; + } + + return .None; +} + +reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error { + using runtime; + + if !reflect.is_struct(type_info_of(v.id)) { + return .No_Base_Struct; + } + + names := reflect.struct_field_names(v.id); + types := reflect.struct_field_types(v.id); + offsets := reflect.struct_field_offsets(v.id); + tags := reflect.struct_field_tags(v.id); + + for name, i in names { + + flag: Flag; + + type := types[i]; + + if named_type, ok := type.variant.(Type_Info_Named); ok { + + if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && union_type.maybe && len(union_type.variants) == 1 { + flag.optional = true; + flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i])); + type = union_type.variants[0]; + } else { + return .Arg_Unsupported_Field_Type; + } + } + + #partial switch _ in type.variant { + case Type_Info_Integer, Type_Info_String, Type_Info_Boolean, Type_Info_Float: + flag.type = type; + flag.data = rawptr(uintptr(v.data) + uintptr(offsets[i])); + case: + return .Arg_Unsupported_Field_Type; + } + + flag_name: string; + + if value, ok := reflect.struct_tag_lookup(tags[i], "flag"); ok { + flag_name = cast(string)value; + } else { + return .Tag_Error; + } + + ctx.seen_flags[flag_name] = flag; + } + + return .None; +} + +parse :: proc(v: any, args: []string) -> Flag_Error { + + if v == nil { + return .None; + } + + ctx: Flag_Context; + + if res := reflect_args_structure(&ctx, v); res != .None { + return res; + } + + if res := parse_args(&ctx, args); res != .None { + return res; + } + + //validate that the required flags were actually set + for k, v in ctx.seen_flags { + + if v.optional && v.parsed { + tag_value: i32 = 1; + mem.copy(v.tag_ptr, &tag_value, 4); //4 constant is probably not portable, but it works for me currently + } else if !v.parsed && !v.optional { + return .Arg_Non_Optional; + } + } + + return .None; +} + +usage :: proc(v: any) -> string { + return "failed"; +} diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin index 4040fe4c2..29c1f6c1b 100644 --- a/tools/odinfmt/main.odin +++ b/tools/odinfmt/main.odin @@ -1,148 +1,127 @@ -package odinfmt - -import "core:os" -import "core:odin/format" -import "core:fmt" -import "core:strings" -import "core:path/filepath" -import "core:time" -import "core:mem" - -import "flag" - -Args :: struct { - write: Maybe(bool) `flag:"w" usage:"write the new format to file"`, -} - -print_help :: proc() { - -} - -print_arg_error :: proc(error: flag.Flag_Error) { - fmt.println(error); -} - -format_file :: proc(filepath: string) -> ([] u8, bool) { - - if data, ok := os.read_entire_file(filepath); ok { - return format.format(data, format.default_style); - } - - else { - return {}, false; - } - -} - -files: [dynamic] string; - -walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool) { - - if info.is_dir { - return 0, false; - } - - if filepath.ext(info.name) != ".odin" { - return 0, false; - } - - append(&files, strings.clone(info.fullpath)); - - return 0, false; -} - -main :: proc() { - - init_global_temporary_allocator(mem.megabytes(100)); - - args: Args; - - if len(os.args) < 2 { - print_help(); - os.exit(1); - } - - if res := flag.parse(args, os.args[1:len(os.args)-1]); res != .None { - print_arg_error(res); - os.exit(1); - } - - path := os.args[len(os.args)-1]; - - tick_time := time.tick_now(); - - if os.is_file(path) { - - if _, ok := args.write.(bool); ok { - - backup_path := strings.concatenate({path, "_bk"}, context.temp_allocator); - - if data, ok := format_file(path); ok { - - os.rename(path, backup_path); - - if os.write_entire_file(path, data) { - os.remove(backup_path); - } - - } - - else { - fmt.eprintf("failed to write %v", path); - } - - } - - else { - - if data, ok := format_file(path); ok { - fmt.println(transmute(string)data); - } - - } - - } - - else if os.is_dir(path) { - - filepath.walk(path, walk_files); - - for file in files { - - fmt.println(file); - - backup_path := strings.concatenate({file, "_bk"}, context.temp_allocator); - - if data, ok := format_file(file); ok { - - if _, ok := args.write.(bool); ok { - os.rename(file, backup_path); - - if os.write_entire_file(file, data) { - os.remove(backup_path); - } - } - - else { - fmt.println(transmute(string)data); - } - - - } else { - fmt.eprintf("failed to format %v", file); - } - - free_all(context.temp_allocator); - } - - fmt.printf("formatted %v files in %vms", len(files), time.duration_milliseconds(time.tick_lap_time(&tick_time))); - - } - - else{ - fmt.eprintf("%v is neither a directory nor a file \n", path); - os.exit(1); - } - - os.exit(0); -} \ No newline at end of file +package odinfmt + +import "core:os" +import "core:odin/format" +import "core:fmt" +import "core:strings" +import "core:path/filepath" +import "core:time" +import "core:mem" + +import "flag" + +Args :: struct { + write: Maybe(bool) `flag:"w" usage:"write the new format to file"`, +} + +print_help :: proc() { +} + +print_arg_error :: proc(error: flag.Flag_Error) { + fmt.println(error); +} + +format_file :: proc(filepath: string) -> ([]u8, bool) { + + if data, ok := os.read_entire_file(filepath); ok { + return format.format(data, format.default_style); + } else { + return {}, false; + } +} + +files: [dynamic]string; + +walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool) { + + if info.is_dir { + return 0, false; + } + + if filepath.ext(info.name) != ".odin" { + return 0, false; + } + + append(&files, strings.clone(info.fullpath)); + + return 0, false; +} + +main :: proc() { + + init_global_temporary_allocator(mem.megabytes(100)); + + args: Args; + + if len(os.args) < 2 { + print_help(); + os.exit(1); + } + + if res := flag.parse(args, os.args[1:len(os.args) - 1]); res != .None { + print_arg_error(res); + os.exit(1); + } + + path := os.args[len(os.args) - 1]; + + tick_time := time.tick_now(); + + if os.is_file(path) { + + if _, ok := args.write.(bool); ok { + + backup_path := strings.concatenate({path, "_bk"}, context.temp_allocator); + + if data, ok := format_file(path); ok { + + os.rename(path, backup_path); + + if os.write_entire_file(path, data) { + os.remove(backup_path); + } + } else { + fmt.eprintf("failed to write %v", path); + } + } else { + + if data, ok := format_file(path); ok { + fmt.println(transmute(string)data); + } + } + } else if os.is_dir(path) { + + filepath.walk(path, walk_files); + + for file in files { + + fmt.println(file); + + backup_path := strings.concatenate({file, "_bk"}, context.temp_allocator); + + if data, ok := format_file(file); ok { + + if _, ok := args.write.(bool); ok { + os.rename(file, backup_path); + + if os.write_entire_file(file, data) { + os.remove(backup_path); + } + } else { + fmt.println(transmute(string)data); + } + } else { + fmt.eprintf("failed to format %v", file); + } + + free_all(context.temp_allocator); + } + + fmt.printf("formatted %v files in %vms", len(files), time.duration_milliseconds(time.tick_lap_time(&tick_time))); + } else { + fmt.eprintf("%v is neither a directory nor a file \n", path); + os.exit(1); + } + + os.exit(0); +}