txt.odin (1557B)
1 /* 2 Package txt is the few readings of plain text that several packages 3 share: the words a name is made of, a plural made singular, and the JSON 4 object inside an answer. 5 */ 6 package txt 7 8 import "core:strings" 9 10 // split_words breaks a name into the words it is made of: a capital 11 // starts a word, an underscore ends one, as snake_case languages put the 12 // next word after it. 13 split_words :: proc(name: string, allocator := context.allocator) -> []string { 14 words := make([dynamic]string, allocator) 15 start := 0 16 for i in 0 ..< len(name) { 17 c := name[i] 18 if i > 0 && ((c >= 'A' && c <= 'Z') || c == '_') { 19 if i > start { 20 append(&words, name[start:i]) 21 } 22 start = i + 1 if c == '_' else i 23 } 24 } 25 if start < len(name) { 26 append(&words, name[start:]) 27 } 28 return words[:] 29 } 30 31 // depluralise drops a trailing s, except where dropping it would leave 32 // another: class stays class. 33 depluralise :: proc(w: string) -> string { 34 if len(w) > 3 && strings.has_suffix(w, "s") && w[len(w) - 2] != 's' { 35 return w[:len(w) - 1] 36 } 37 return w 38 } 39 40 // object finds the JSON object in a text that has prose around it: from 41 // the first brace to the last. Only a provider that can enforce a schema 42 // returns bare JSON; the rest wrap it in whatever they were minded to 43 // say, and a compiler prints its own prose before its JSON when it cannot 44 // even start. 45 object :: proc(s: string) -> (string, bool) { 46 start := strings.index_byte(s, '{') 47 end := strings.last_index_byte(s, '}') 48 if start < 0 || end < start { 49 return "", false 50 } 51 return s[start:end + 1], true 52 }