From 0f76ad666adaec0e078b51a8a40890f34d679915 Mon Sep 17 00:00:00 2001 From: PedroEdiaz Date: Thu, 19 Jun 2025 21:57:18 -0600 Subject: [PATCH] Pass section --- decode.odin | 73 +++++++++++++++++++++++++++++++----------------- mustache.odin | 77 +++++++++++++++++++++++++++------------------------ 2 files changed, 88 insertions(+), 62 deletions(-) diff --git a/decode.odin b/decode.odin index 63abbed..fd95ec9 100644 --- a/decode.odin +++ b/decode.odin @@ -4,21 +4,48 @@ import "base:runtime" import "core:reflect" import "core:strings" import "core:mem" -import "core:fmt" import "core:log" -decode :: proc( v: any, key: string ) -> any { - v := reflect.any_base(v) - ti := type_info_of(v.id) +@private +_clean_html :: proc(s: string) -> string { + ret: strings.Builder - #partial switch i in runtime.type_info_base(ti).variant { + for c in s do switch c { + case '<': + strings.write_string(&ret, "<") + case '>': + strings.write_string(&ret, ">") + case '&': + strings.write_string(&ret, "&") + case '"': + strings.write_string(&ret, """) + case '\'': + strings.write_string(&ret, "'") + case: + strings.write_rune(&ret, c) + } + + return strings.to_string(ret) +} + +decode :: proc( v: any, key: string ) -> any { + ti := type_info_of(v.id) + variant := runtime.type_info_base(ti).variant + + #partial switch i in variant { case runtime.Type_Info_Enumerated_Array: return decode(any{v.data, ti.id}, key) case runtime.Type_Info_Union: return decode(any{v.data, reflect.union_variant_typeid(v) }, key) + } + if key == "." { + return v + } + + #partial switch i in variant { case runtime.Type_Info_Struct: newkey, err := strings.split_after_n(key, ".", 2) @@ -74,36 +101,31 @@ decode :: proc( v: any, key: string ) -> any { } } - return "" + return nil } - if key == "." { - return v - } - - return "" -} - -decode_string :: proc( v: any, key: string ) -> string { - return fmt.tprintf("%v", decode(v, key)) + return nil } @private -mustache_section :: proc( r: ^strings.Reader, v: any, key: string, inv: bool = false ) -> string { - tmp := mustache(r, v, key) +mustache_section :: proc( r: ^strings.Reader, v, p: any, section_key: string, inv: bool = false ) -> string { + tmp := mustache(r, v, section_key) + defer delete(tmp) + + tmp2 := mustache(tmp, p, section_key) if inv { - return tmp + return tmp2 } - defer delete(tmp) + delete(tmp2) return "" } -section :: proc( r: ^strings.Reader, v: any, key: string, inv: bool = false ) -> string { +section :: proc( r: ^strings.Reader, v: any, section_key: string, inv: bool = false ) -> string { save := r.i - t := reflect.any_base(decode(v, key)) + t := reflect.any_base(decode(v, section_key)) ti := type_info_of(t.id) #partial switch i in runtime.type_info_base(ti).variant { @@ -113,7 +135,7 @@ section :: proc( r: ^strings.Reader, v: any, key: string, inv: bool = false ) -> elem :=reflect.index(t, i); strings.reader_seek(r, save, .Start) - tmp := mustache_section(r, elem, key, !inv) + tmp := mustache_section(r, elem, v, section_key, !inv) defer delete(tmp) ret = strings.concatenate({ret, tmp}) @@ -122,13 +144,12 @@ section :: proc( r: ^strings.Reader, v: any, key: string, inv: bool = false ) -> case runtime.Type_Info_Boolean: b, _ := reflect.as_bool(t) - return mustache_section(r, t, key, b~inv) + return mustache_section(r, t, v, section_key, b~inv) } if t == nil { - return mustache_section(r, t, key, inv) + return mustache_section(r, t, v, section_key, inv) } - return mustache_section(r, t, key, !inv) + return mustache_section(r, t, v, section_key, !inv) } - diff --git a/mustache.odin b/mustache.odin index d05768e..02eef85 100644 --- a/mustache.odin +++ b/mustache.odin @@ -1,6 +1,6 @@ package mustache -import "core:log" +import "core:fmt" import "core:strings" state :: enum { @@ -11,31 +11,21 @@ state :: enum { } mustache :: proc{mustache_reader, mustache_string} -/* -Input: -- fmt: A string with placeholders in the form `{{key}}`. -- dict: A map from string to string, where each key corresponds to a placeholder in the format string. -Returns: -A new string with all placeholders replaced by their corresponding values from -the dictionary. If a key is missing in the dictionary, the placeholder is -replaces with an empty string. -*/ -mustache_string :: proc(fmt: string, v: any , end_block: string = "") -> string { +mustache_string :: proc(fmt: string, data: any , section_key: string = "") -> string { r : strings.Reader strings.reader_init(&r, fmt) - return mustache(&r, v, end_block) + return mustache(&r, data, section_key) } -mustache_reader :: proc(r: ^strings.Reader, v: any, end_block: string = "" ) -> string { +mustache_reader :: proc(r: ^strings.Reader, data: any, section_key: string = "" ) -> string { /* - template works as a state machine, it manipulates `b` (returned string) - and `key` (placeholder string), according to the states. No error - returns are needed because if `key` is not close it writes it as - it's not a placeholder + This is the main parser for mustache templates, it's a recursive decent parser, that works as a state machine, + it manipulates the processed template with data, `ret`, and the key to element on data `key`, according to the states. + This approach let us process the data as fast as posible. */ - b, key: strings.Builder + ret, key: strings.Builder defer strings.builder_destroy(&key) s:= state.writing @@ -53,7 +43,7 @@ mustache_reader :: proc(r: ^strings.Reader, v: any, end_block: string = "" ) -> case .open_bracket: s=.reading_key case .close_bracket: - strings.write_string(&b, "}{" ) + strings.write_string(&ret, "}{" ) s=.writing case .writing: s=.open_bracket @@ -63,7 +53,7 @@ mustache_reader :: proc(r: ^strings.Reader, v: any, end_block: string = "" ) -> case '}': switch s { case .open_bracket: - strings.write_string(&b, "{}" ) + strings.write_string(&ret, "{}" ) s=.writing case .close_bracket: // Work with key @@ -77,18 +67,33 @@ mustache_reader :: proc(r: ^strings.Reader, v: any, end_block: string = "" ) -> switch skey[0] { case '/': - if skey[1:] == end_block { - return strings.to_string(b) + if skey[1:] == section_key { + return strings.to_string(ret) } case '#': - strings.write_string(&b, section(r, v, skey[1:]) ) + strings.write_string(&ret, section(r, data, skey[1:]) ) case '^': - strings.write_string(&b, section(r, v, skey[1:], true) ) + strings.write_string(&ret, section(r, data, skey[1:], true) ) + case '&': + strings.write_string(&ret, fmt.tprintf("%v",decode(data, skey[1:])) ) case: - strings.write_string(&b, decode_string(v, skey) ) + dec := decode(data, skey) + + if dec == nil { + // If not decoded write as key + strings.write_string(&ret, "{{" ) + strings.write_string(&ret, skey ) + strings.write_string(&ret, "}}" ) + break + } + + clean := _clean_html(fmt.tprintf("%v", dec)) + defer delete(clean) + + strings.write_string(&ret, clean) } case .writing: - strings.write_rune(&b, '}' ) + strings.write_rune(&ret, '}' ) s=.writing case .reading_key: s=.close_bracket @@ -96,15 +101,15 @@ mustache_reader :: proc(r: ^strings.Reader, v: any, end_block: string = "" ) -> case: switch s { case .open_bracket: - strings.write_rune(&b, '{' ) - strings.write_rune(&b, c ) + strings.write_rune(&ret, '{' ) + strings.write_rune(&ret, c ) s=.writing case .close_bracket: strings.write_rune(&key, '}' ) strings.write_rune(&key, c ) s=.reading_key case .writing: - strings.write_rune(&b, c ) + strings.write_rune(&ret, c ) s=.writing case .reading_key: strings.write_rune(&key, c ) @@ -114,18 +119,18 @@ mustache_reader :: proc(r: ^strings.Reader, v: any, end_block: string = "" ) -> switch s { case .open_bracket: - strings.write_rune(&b, '{' ) + strings.write_rune(&ret, '{' ) case .reading_key: - strings.write_string(&b,"{{") - strings.write_string(&b,strings.to_string(key)) + strings.write_string(&ret,"{{") + strings.write_string(&ret,strings.to_string(key)) case .close_bracket: - strings.write_string(&b,"{{") - strings.write_string(&b,strings.to_string(key)) - strings.write_rune(&b, '}' ) + strings.write_string(&ret,"{{") + strings.write_string(&ret,strings.to_string(key)) + strings.write_rune(&ret, '}' ) case .writing: } - return strings.to_string(b) + return strings.to_string(ret) } /*