Merge remote-tracking branch 'refs/remotes/origin/mustacheless' into mustacheless

This commit is contained in:
PedroEdiaz
2025-06-18 16:34:17 -06:00
2 changed files with 129 additions and 59 deletions

View File

@@ -1,30 +1,69 @@
package mustache package mustache
import "base:runtime"
import "core:reflect" import "core:reflect"
import "core:strings"
import "core:log" import "core:log"
import "core:fmt"
decode :: proc( v: any, key: string ) -> string { decode :: proc( v: any, key: string ) -> any {
if v == nil do return "" if v == nil do return ""
ti := runtime.type_info_base(type_info_of(v.id)) v := reflect.any_base(v)
a := any{v.data, ti.id} ti := type_info_of(v.id)
#partial switch info in ti.variant { switch {
case reflect.is_struct(ti):
newkey, err := strings.split_after_n(key, ".", 2)
case runtime.Type_Info_Struct: if len(newkey) != 2 || err != nil {
return decode( reflect.struct_field_value_by_name(v, key), ".") return decode( reflect.struct_field_value_by_name(v, key), "." )
case runtime.Type_Info_String:
switch s in a {
case string:
return string(s)
case cstring:
return string(s)
} }
return decode( reflect.struct_field_value_by_name(v, newkey[0][:len(newkey[0])-1]), newkey[1] )
case reflect.is_string(ti):
if key != "." do return ""
return v
case reflect.is_integer(ti):
if key != "." do return ""
return v
case reflect.is_enum(ti):
return decode(any{v.data, ti.id}, key)
case reflect.is_slice(ti):
if key != "." do return ""
return v
case:
log.warnf("%v", ti)
} }
return "" return ""
} }
decode_string :: proc( v: any, key: string ) -> string {
return fmt.tprintf("%v", decode(v, key))
}
preprocess :: proc( r: ^strings.Reader, v: any, key: string ) -> string {
if v == nil do return ""
save := r.i
t := reflect.any_base(decode(v, key))
ti := type_info_of(t.id)
ret : string
switch {
case reflect.is_slice(ti):
for i in 0..<reflect.length(t) {
elem :=reflect.index(t, i);
strings.reader_seek(r, save, .Start)
tmp := mustache(r, elem, key)
defer delete(tmp)
ret = strings.concatenate({ret, tmp})
log.infof(ret)
}
}
return ret
}

View File

@@ -1,8 +1,8 @@
package mustache package mustache
import "core:log"
import "core:strings" import "core:strings"
@private
state :: enum { state :: enum {
writing, writing,
reading_key, reading_key,
@@ -10,6 +10,7 @@ state :: enum {
close_bracket, close_bracket,
} }
mustache :: proc{mustache_reader, mustache_string}
/* /*
Input: Input:
- fmt: A string with placeholders in the form `{{key}}`. - fmt: A string with placeholders in the form `{{key}}`.
@@ -20,7 +21,13 @@ 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 the dictionary. If a key is missing in the dictionary, the placeholder is
replaces with an empty string. replaces with an empty string.
*/ */
mustache :: proc(fmt: string, v: any ) -> string { mustache_string :: proc(fmt: string, v: any ) -> string {
r : strings.Reader
strings.reader_init(&r, fmt)
return mustache(&r, v, "")
}
mustache_reader :: proc(r: ^strings.Reader, v: any, end_block: string ) -> string {
/* /*
template works as a state machine, it manipulates `b` (returned string) template works as a state machine, it manipulates `b` (returned string)
and `key` (placeholder string), according to the states. No error and `key` (placeholder string), according to the states. No error
@@ -33,49 +40,73 @@ mustache :: proc(fmt: string, v: any ) -> string {
s:= state.writing s:= state.writing
for c in fmt do switch c { for {
case '{': c, _, err := strings.reader_read_rune(r);
switch s {
case .open_bracket: if err != nil {
s=.reading_key break
case .close_bracket:
strings.write_string(&b, "}{" )
s=.writing
case .writing:
s=.open_bracket
case .reading_key:
strings.write_rune(&key, '{' )
} }
case '}':
switch s { switch c {
case .open_bracket: case '{':
strings.write_string(&b, "{}" ) switch s {
s=.writing case .open_bracket:
case .close_bracket: s=.reading_key
strings.write_string(&b, decode(v, strings.to_string(key) )) case .close_bracket:
strings.builder_reset(&key) strings.write_string(&b, "}{" )
s=.writing s=.writing
case .writing: case .writing:
strings.write_rune(&b, '}' ) s=.open_bracket
s=.writing case .reading_key:
case .reading_key: strings.write_rune(&key, '{' )
s=.close_bracket }
} case '}':
case: switch s {
switch s { case .open_bracket:
case .open_bracket: strings.write_string(&b, "{}" )
strings.write_rune(&b, '{' ) s=.writing
strings.write_rune(&b, c ) case .close_bracket:
s=.writing // Work with key
case .close_bracket: skey := strings.to_string(key)
strings.write_rune(&key, '}' ) strings.builder_reset(&key)
strings.write_rune(&key, c ) s=.writing
s=.reading_key
case .writing: if len(skey) == 0 {
strings.write_rune(&b, c ) break
s=.writing }
case .reading_key:
strings.write_rune(&key, c ) switch skey[0] {
case '/':
if skey[1:] == end_block {
return strings.to_string(b)
}
case '#':
strings.write_string(&b, preprocess(r, v, skey[1:]) )
case:
strings.write_string(&b, decode_string(v, skey) )
}
case .writing:
strings.write_rune(&b, '}' )
s=.writing
case .reading_key:
s=.close_bracket
}
case:
switch s {
case .open_bracket:
strings.write_rune(&b, '{' )
strings.write_rune(&b, 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 )
s=.writing
case .reading_key:
strings.write_rune(&key, c )
}
} }
} }