diff options
Diffstat (limited to 'lib/naming/lang.ml')
| -rw-r--r-- | lib/naming/lang.ml | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/lib/naming/lang.ml b/lib/naming/lang.ml new file mode 100644 index 0000000..147a49b --- /dev/null +++ b/lib/naming/lang.ml @@ -0,0 +1,109 @@ +module OI = Colitur_kernel.Overlay_ini + +module SM = Map.Make (String) + +type table = string SM.t + +type t = { + code : string; + fallback_code : string option; + celebration : table; + weekday : table; + month : table; + season : table; + rank : table; + colour : table; + term : table; + chain : t option; (** consulted when this table misses *) +} + +let empty_table = SM.empty + +let rec lookup t sel key = + match SM.find_opt key (sel t) with + | Some v -> Some v + | None -> ( match t.chain with Some b -> lookup b sel key | None -> None) + +(* A miss returns the KEY, never "". See lang.mli for why. *) +let get t sel key = match lookup t sel key with Some v -> v | None -> key + +let celebration t k = get t (fun x -> x.celebration) k +let season t k = get t (fun x -> x.season) k +let rank t k = get t (fun x -> x.rank) k +let colour t k = get t (fun x -> x.colour) k +let term t k = get t (fun x -> x.term) k + +let weekday_key = [| "sunday"; "monday"; "tuesday"; "wednesday"; "thursday"; "friday"; "saturday" |] + +(* weekday/month deliberately do NOT go through [get]: their SM key is a + presentation detail (an English day-name word for weekday, a numeral for + month), not the value a miss should degrade to. [get]'s generic + echo-the-search-key fallback would leak that word ("sunday") out of an + empty/raw table instead of the documented numeral -- [month] never showed + the bug because its key already equals [string_of_int n], but [weekday] + did, and it broke [Lang.raw]'s own identity contract (0 = Sunday, not + "sunday"). Falling back to [string_of_int n] directly keeps [month] + byte-identical and fixes [weekday]. *) +let weekday t n = + if n < 0 || n > 6 then string_of_int n + else match lookup t (fun x -> x.weekday) weekday_key.(n) with Some v -> v | None -> string_of_int n + +let month t n = + if n < 1 || n > 12 then string_of_int n + else match lookup t (fun x -> x.month) (string_of_int n) with Some v -> v | None -> string_of_int n + +let code t = t.code +let fallback_code t = t.fallback_code + +let raw = + { code = "raw"; fallback_code = None; celebration = empty_table; weekday = empty_table; + month = empty_table; season = empty_table; rank = empty_table; colour = empty_table; + term = empty_table; chain = None } + +let with_fallback t base = { t with chain = Some base } + +let of_string text = + match OI.parse_sections text with + | Error e -> Error e + | Ok sections -> + (* Merge EVERY section sharing [name], not just the first: a hand-edited + 595-entry language file (Tasks 3/4's la.ini) WILL grow duplicate + [section] headers as contributors append entries over time -- a + second [celebration] block is the natural way to paste in a new + batch of names. Taking only the first match (the original + [List.find_opt] here) silently dropped every later block; the + failure then surfaces as a coverage report claiming those slugs have + "no Latin name", with nothing pointing back at the parser. Folding + over all matching sections, in file order, keeps this consistent + with the existing within-section behaviour below (last [SM.add] + wins): a key repeated across two blocks resolves to the later one, + exactly what a reader expects when appending to an INI file. *) + let find name = + List.fold_left + (fun m (s : OI.section) -> + if s.OI.name = name then List.fold_left (fun m (k, v) -> SM.add k v m) m s.OI.fields else m) + empty_table sections + in + let meta = find "meta" in + (match SM.find_opt "lang" meta with + | None -> Error "language file has no [meta] lang = <code>" + | Some code -> + Ok + { code; + fallback_code = SM.find_opt "fallback" meta; + celebration = find "celebration"; + weekday = find "weekday"; + month = find "month"; + season = find "season"; + rank = find "rank"; + colour = find "colour"; + term = find "term"; + chain = None }) + +let keys t = + let qualify prefix m = SM.bindings m |> List.map (fun (k, v) -> (prefix ^ "." ^ k, v)) in + List.concat + [ qualify "celebration" t.celebration; qualify "weekday" t.weekday; + qualify "month" t.month; qualify "season" t.season; qualify "rank" t.rank; + qualify "colour" t.colour; qualify "term" t.term ] + |> List.sort compare |
