Ver Fonte

bring over the odinfmt code

Daniel Gavin há 4 anos atrás
pai
commit
3157467e4b
3 ficheiros alterados com 407 adições e 0 exclusões
  1. 35 0
      core/odin/format/format.odin
  2. 232 0
      tools/odinfmt/flag/flag.odin
  3. 140 0
      tools/odinfmt/main.odin

+ 35 - 0
core/odin/format/format.odin

@@ -0,0 +1,35 @@
+package odin_format
+
+import "core:odin/printer"
+import "core:odin/parser"
+import "core:odin/ast"
+
+default_style := printer.default_style;
+
+simplify :: proc(file: ^ast.File) {
+
+}
+
+format :: proc(source: [] u8, config: printer.Config, allocator := context.allocator) -> ([] u8, bool) {
+
+    pkg := ast.Package {
+        kind = .Normal,
+    };
+
+    file := ast.File {
+        pkg = &pkg,
+        src = source,
+    };
+
+    p := parser.default_parser();
+
+    ok := parser.parse_file(&p, &file);
+
+    if !ok || file.syntax_error_count > 0  {
+        return {}, false;
+    }
+
+    prnt := printer.make_printer(config, allocator);
+
+    return transmute([]u8) printer.print(&prnt, &file), true;
+}

+ 232 - 0
tools/odinfmt/flag/flag.odin

@@ -0,0 +1,232 @@
+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";
+}

+ 140 - 0
tools/odinfmt/main.odin

@@ -0,0 +1,140 @@
+package odinfmt
+
+import "core:os"
+import "core:odin/format"
+import "core:fmt"
+import "core:strings"
+import "core:path/filepath"
+
+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() {
+
+    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];
+
+    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);
+                }
+
+
+            }
+
+            free_all(context.temp_allocator);
+        }
+
+        fmt.printf("formatted %v files", len(files));
+
+    }
+
+    else{
+        fmt.eprintf("%v is neither a directory nor a file \n", path);
+        os.exit(1);
+    }
+
+    os.exit(0);
+}