aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/odin/printer/document.odin2
-rw-r--r--tools/odinfmt/flag/flag.odin216
-rw-r--r--tools/odinfmt/main.odin97
3 files changed, 32 insertions, 283 deletions
diff --git a/src/odin/printer/document.odin b/src/odin/printer/document.odin
index f3ef61e..5fba1b9 100644
--- a/src/odin/printer/document.odin
+++ b/src/odin/printer/document.odin
@@ -447,7 +447,7 @@ format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p:
if v.amount > 0 {
flush_line_suffix(builder, &suffix_builder)
// ensure we strip any misplaced trailing whitespace
- for builder.buf[len(builder.buf)-1] == ' ' {
+ for len(builder.buf) > 0 && builder.buf[len(builder.buf) - 1] == ' ' {
pop(&builder.buf)
}
for i := 0; i < v.amount; i += 1 {
diff --git a/tools/odinfmt/flag/flag.odin b/tools/odinfmt/flag/flag.odin
deleted file mode 100644
index b52e359..0000000
--- a/tools/odinfmt/flag/flag.odin
+++ /dev/null
@@ -1,216 +0,0 @@
-package flag
-
-import "base: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,
- offset: uintptr,
-}
-
-Flag_Context :: struct {
- seen_flags: map[string]Flag,
-}
-
-parse_args :: proc(ctx: ^Flag_Context, args: []string) -> Flag_Error {
-
- using runtime;
-
- args := args;
-
- for {
- 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: b64 = true;
- mem.copy(flag.data, &tmp, flag.type.size);
- flag.parsed = true;
- continue;
- } else if value == "" { // must be in the next argument
- 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 = raw_data(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 && len(union_type.variants) == 1 {
- flag.optional = true;
- flag.offset = union_type.tag_offset + offsets[i];
- flag.tag_ptr = rawptr(flag.offset + uintptr(v.data));
- 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;
-
- size := type_info_of(v.id).size;
-
- 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, size - int(v.offset));
- } 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 a829be6..3470ca5 100644
--- a/tools/odinfmt/main.odin
+++ b/tools/odinfmt/main.odin
@@ -1,48 +1,24 @@
package odinfmt
import "core:encoding/json"
+import "core:flags"
import "core:fmt"
+import "core:io"
import "core:mem"
import "core:odin/tokenizer"
import "core:os"
import "core:path/filepath"
import "core:strings"
import "core:time"
-import "flag"
import "src:odin/format"
import "src:odin/printer"
Args :: struct {
- write: Maybe(bool) `flag:"w" usage:"write the new format to file"`,
- stdin: Maybe(bool) `flag:"stdin" usage:"formats code from standard input"`,
+ write: bool `args:"name=w" usage:"write the new format to file"`,
+ stdin: bool `usage:"formats code from standard input"`,
+ path: string `args:"pos=0" usage:"set the file or directory to format"`,
}
-print_help :: proc(args: []string) {
- fmt.eprintln("usage: odinfmt [-w {filepath}] [-stdin]")
-}
-
-print_arg_error :: proc(args: []string, error: flag.Flag_Error) {
- switch error {
- case .None:
- print_help(args)
- case .No_Base_Struct:
- fmt.eprintln(args[0], "no base struct")
- case .Arg_Error:
- fmt.eprintln(args[0], "argument error")
- case .Arg_Unsupported_Field_Type:
- fmt.eprintln(args[0], "argument: unsupported field type")
- case .Arg_Not_Defined:
- fmt.eprintln(args[0], "argument: no defined")
- case .Arg_Non_Optional:
- fmt.eprintln(args[0], "argument: non optional")
- case .Value_Parse_Error:
- fmt.eprintln(args[0], "argument: value parse error")
- case .Tag_Error:
- fmt.eprintln(args[0], "argument: tag error")
- }
-}
-
-
format_file :: proc(filepath: string, config: printer.Config, allocator := context.allocator) -> (string, bool) {
if data, ok := os.read_entire_file(filepath, allocator); ok {
return format.format(filepath, string(data), config, {.Optional_Semicolons}, allocator)
@@ -76,29 +52,18 @@ main :: proc() {
init_global_temporary_allocator(mem.Megabyte * 20) //enough space for the walk
args: Args
+ flags.parse_or_exit(&args, os.args)
- if len(os.args) < 2 {
- print_help(os.args)
- os.exit(1)
- }
-
- // When running `$ odinfmt some_odin_file.odin`, the file shouldn't be passed to
- // `flag.parse`. But, when running `$ odinfmt -stdin`, we do want to pass `-stdin` to
- // `flag.parse`. Need to check if last arg is a flag, and if so, include it in the
- // args to parse.
- args_to_parse := os.args[1:len(os.args) - 1]
- path := os.args[len(os.args) - 1]
- if strings.has_prefix(os.args[len(os.args) - 1], "-") {
- args_to_parse = os.args[1:]
- }
-
- if len(path) <= 1 {
- path = "." // if no file was specified, use current directory as the starting path to look for `odinfmt.json`
- }
-
- if res := flag.parse(args, args_to_parse); res != .None {
- print_arg_error(os.args, res)
- os.exit(1)
+ // only allow the path to not be specified when formatting from stdin
+ if args.path == "" {
+ if args.stdin {
+ // use current directory as the starting path to look for `odinfmt.json`
+ args.path = "."
+ } else {
+ fmt.fprint(os.stderr, "Missing path to format\n")
+ flags.write_usage(os.stream_from_handle(os.stderr), Args, os.args[0])
+ os.exit(1)
+ }
}
tick_time := time.tick_now()
@@ -107,9 +72,9 @@ main :: proc() {
watermark := 0
- config := format.find_config_file_or_default(path)
+ config := format.find_config_file_or_default(args.path)
- if _, ok := args.stdin.(bool); ok {
+ if args.stdin {
data := make([dynamic]byte, arena_allocator)
for {
@@ -128,28 +93,28 @@ main :: proc() {
}
write_failure = !ok
- } else if os.is_file(path) {
- if _, ok := args.write.(bool); ok {
- backup_path := strings.concatenate({path, "_bk"})
+ } else if os.is_file(args.path) {
+ if args.write {
+ backup_path := strings.concatenate({args.path, "_bk"})
defer delete(backup_path)
- if data, ok := format_file(path, config, arena_allocator); ok {
- os.rename(path, backup_path)
+ if data, ok := format_file(args.path, config, arena_allocator); ok {
+ os.rename(args.path, backup_path)
- if os.write_entire_file(path, transmute([]byte)data) {
+ if os.write_entire_file(args.path, transmute([]byte)data) {
os.remove(backup_path)
}
} else {
- fmt.eprintf("Failed to write %v", path)
+ fmt.eprintf("Failed to write %v", args.path)
write_failure = true
}
} else {
- if data, ok := format_file(path, config, arena_allocator); ok {
+ if data, ok := format_file(args.path, config, arena_allocator); ok {
fmt.println(data)
}
}
- } else if os.is_dir(path) {
- filepath.walk(path, walk_files, nil)
+ } else if os.is_dir(args.path) {
+ filepath.walk(args.path, walk_files, nil)
for file in files {
fmt.println(file)
@@ -158,7 +123,7 @@ main :: proc() {
defer delete(backup_path)
if data, ok := format_file(file, config, arena_allocator); ok {
- if _, ok := args.write.(bool); ok {
+ if args.write {
os.rename(file, backup_path)
if os.write_entire_file(file, transmute([]byte)data) {
@@ -184,9 +149,9 @@ main :: proc() {
)
fmt.printf("Peak memory used: %v \n", watermark / mem.Megabyte)
} else {
- fmt.eprintf("%v is neither a directory nor a file \n", path)
+ fmt.eprintf("%v is neither a directory nor a file \n", args.path)
os.exit(1)
}
os.exit(1 if write_failure else 0)
-} \ No newline at end of file
+}