Last active
November 12, 2019 22:47
-
-
Save kevinw/5747511506729338ddbed517a5c64668 to your computer and use it in GitHub Desktop.
@tweak odin attribute
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// An example file using the @tweak attribute. | |
package main | |
using import "core:math/linalg" | |
@tweak | |
editor_settings : struct { | |
grid_offset: Vector3, | |
test_string: string, | |
test_int: int, | |
}; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
show_tweakable_ui :: proc() { | |
for tweakable in all_tweakables { | |
ui_label(tweakable.name); | |
any_ptr := tweakable.ptr(); | |
ui_edit_struct(ctx, tweakable.name, any_ptr.data, type_info_of(any_ptr.id)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package preprocess | |
using import "core:fmt" | |
using import "core:odin/ast" | |
import "core:mem" | |
import "core:os" | |
import "core:odin/parser" | |
import "core:strings" | |
// TODO: This won't work for multiple attributes, and for lots of types of declarations. | |
TWEAKABLE_ATTRIBUTE_NAME :: "tweak"; | |
get_tweakable :: proc (s: ^Stmt) -> (Value_Decl, bool) { | |
value, ok := s.derived.(Value_Decl); | |
if !ok do return {}, false; | |
for attr in value.attributes { | |
if len(attr.elems) == 0 do continue; | |
if len(attr.elems) > 1 { | |
fmt.eprintln("warning: attribute has more than one element"); | |
} | |
attr_ident, attr_ok := attr.elems[0].derived.(Ident); | |
if attr_ok && attr_ident.name == TWEAKABLE_ATTRIBUTE_NAME { | |
return value, true; | |
} | |
} | |
return {}, false; | |
} | |
process_tweakable :: proc(builder: ^strings.Builder, value: Value_Decl) { | |
for name in value.names { | |
switch ident in name.derived { | |
case Ident: | |
identifier_name := ident.name; | |
sbprintf(builder, " { \"%s\", proc() -> any { return %s; } },\n", identifier_name, identifier_name); | |
case: | |
fmt.eprintln("unhandled name type:", ident); | |
} | |
} | |
} | |
parse_odin_file :: proc(filename: string, p: ^parser.Parser) -> bool { | |
assert(len(filename) > 0); | |
assert(p != nil); | |
data, success := os.read_entire_file(filename); | |
if !success { | |
fmt.eprintln("error reading", filename); | |
return false; | |
} | |
pkg := mem.new_clone(ast.Package { | |
kind = .Init, | |
name="main", | |
fullpath=dirname(filename), | |
}); | |
file := mem.new_clone(ast.File{ | |
id=0, pkg=pkg, fullpath=filename, src=data, | |
}); | |
files: [1]^ast.File = { file }; | |
pkg.files = files[:]; | |
res := parser.parse_file(p, file); | |
if !res { | |
fmt.eprintln("error parsing file", filename); | |
return false; | |
} | |
return true; | |
} | |
// TODO: This should be able to accept a package as input, and walk files | |
// recursively in it. Currently it will only parse one odin source file. | |
process_tweakable_file :: proc(input_odin_file: string, output_file: string) -> bool { | |
p := parser.default_parser(); | |
ok := parse_odin_file(input_odin_file, &p); | |
if !ok { | |
fmt.eprintln("could not parse", input_odin_file); | |
return false; | |
} | |
builder := strings.make_builder(context.temp_allocator); | |
sbprintln(&builder, ` | |
// THIS FILE WAS AUTOGENERATED | |
package main | |
Tweakable :: struct { | |
name: string, | |
ptr: proc() -> any, | |
}; | |
all_tweakables := [?]Tweakable {`); | |
for _, index in p.file.decls { | |
if value, is_tweak := get_tweakable(p.file.decls[index]); is_tweak { | |
process_tweakable(&builder, value); | |
} | |
} | |
sbprintln(&builder, "};"); | |
write_file_if_different(output_file, builder.buf[:]); | |
return true; | |
} | |
write_file_if_different :: proc(filename: string, contents: []u8) -> bool { | |
size_on_disk := os.file_size_from_path(filename); | |
needs_write := false; | |
if size_on_disk == -1 do needs_write = true; | |
else if cast(i64)len(contents) != size_on_disk do needs_write = true; | |
else { | |
disk_contents, ok := os.read_entire_file(filename); | |
defer if disk_contents != nil do delete(disk_contents); | |
if !ok do needs_write = true; | |
else if 0 != mem.compare(disk_contents, contents) do needs_write = true; | |
} | |
if !needs_write do return false; | |
os.write_entire_file(filename, contents); | |
fmt.println("-> wrote", filename); | |
return true; | |
} | |
main :: proc() { | |
process_tweakable_file("src/editor.odin", "src/tweakables.odin"); | |
println("preprocessor finished."); | |
} | |
dirname :: proc(path: string, allocator := context.temp_allocator) -> string { | |
assert(strings.index(path, "/") == -1, "need to handle forward slashes in paths"); | |
// if there's no slash, return ./ | |
last_index := strings.last_index(path, "\\"); | |
if last_index == -1 do return strings.clone(".\\", allocator); | |
// if it's already a dir | |
if last_index == len(path) - 1 do return strings.clone(path, allocator); | |
// otherwise, return the dir part, including the slash | |
return strings.clone(path[0:last_index + 1], allocator); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// THIS FILE WAS AUTOGENERATED | |
package main | |
Tweakable :: struct { | |
name: string, | |
ptr: proc() -> any, | |
}; | |
all_tweakables := [?]Tweakable { | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment