Files
Geneweb/lib/translate.ml
2024-03-05 22:01:20 +01:00

309 lines
9.5 KiB
OCaml

(* $Id: translate.ml,v 5.9 2007-09-12 09:58:44 ddr Exp $ *)
(* Copyright (c) 1998-2007 INRIA *)
module Buff = Buff.Make ()
let skip_lang s =
let rec loop i =
if i = String.length s then None
else match s.[i] with 'a' .. 'z' | '-' -> loop (i + 1) | _ -> Some i
in
loop
let inline lang macro_char macro s =
let lang = lang ^ ":" in
let derived_lang =
try
let i = String.index lang '-' in
String.sub lang 0 i ^ ":"
with Not_found -> ""
in
let rec loop alt_version bol i =
if i = String.length s then
match alt_version with
| Some s -> (s, true)
| None -> ("..........", false)
else if bol then
match skip_lang s i with
| Some j when s.[j] = ':' ->
let curr_lang = String.sub s i (j + 1 - i) in
if curr_lang = lang || curr_lang = derived_lang || curr_lang = "en:"
then
let s, i =
let j = if s.[j + 1] = ' ' then j + 1 else j in
let rec loop len j =
if j = String.length s then (Buff.get len, j)
else if s.[j] = '\n' then
if j + 1 < String.length s && s.[j + 1] = ' ' then
let j =
let rec loop j =
if j < String.length s && s.[j] = ' ' then loop (j + 1)
else j
in
loop (j + 1)
in
loop (Buff.store len '\n') j
else (Buff.get len, j)
else if s.[j] = macro_char then
loop (Buff.mstore len (macro s.[j + 1])) (j + 2)
else loop (Buff.store len s.[j]) (j + 1)
in
loop 0 (j + 1)
in
if curr_lang = lang then (s, false)
else
let alt_version =
if curr_lang = derived_lang then Some s
else if alt_version = None then Some s
else alt_version
in
loop alt_version true i
else loop alt_version (s.[i] = '\n') (i + 1)
| _ -> loop alt_version (s.[i] = '\n') (i + 1)
else loop alt_version (s.[i] = '\n') (i + 1)
in
loop None true 0
let language_name ?(sep = '/') lang lang_def =
let str = lang_def in
let len = String.length lang in
let rec loop beg i =
if i = String.length str && i = beg then lang
else if i = String.length str || str.[i] = sep then
if
i > beg + len + 1
&& str.[beg + len] = '='
&& String.sub str beg len = lang
then String.sub str (beg + len + 1) (i - beg - len - 1)
else if i = String.length str then lang
else loop (i + 1) (i + 1)
else loop beg (i + 1)
in
loop 0 0
(* eval *)
let erase str i j = String.sub str 0 i ^ String.sub str j (String.length str - j)
(*
* eval_set scans strings of the form @(x) where x is a list of characters
* meaning a predicate to set for each character. Fills [set], the set of
* predicates. Treats also the special case for @(&) = delete the next
* character if any.
*)
let eval_set str =
let rec loop set str i =
if i + 3 < String.length str then
if
str.[i] = '@'
&& str.[i + 1] = '('
&& str.[i + 3] <> '?'
&& str.[i + 3] <> '-'
then
if str.[i + 2] = '&' && str.[i + 3] = ')' && i + 4 < String.length str
then loop set (erase str i (i + 5)) i
else
let set, j =
let rec loop set i =
if i < String.length str then
if str.[i] <> ')' then loop (str.[i] :: set) (i + 1)
else (set, i + 1)
else (set, i)
in
loop set (i + 2)
in
loop set (erase str i j) i
else loop set str (i + 1)
else (set, str)
in
loop [] str 0
let rec apply_expr set str i =
if i + 1 < String.length str && str.[i + 1] = '?' then
if List.mem str.[i] set then
let str = erase str i (i + 2) in
let str, i = apply_expr set str i in
if i < String.length str && str.[i] = ':' then
let str, j = apply_expr set str (i + 1) in
(erase str i j, i)
else (str, i)
else
let str, j = apply_expr set str (i + 2) in
let str = erase str i j in
if i < String.length str && str.[i] = ':' then
let str = erase str i (i + 1) in
apply_expr set str i
else (str, i)
else if i < String.length str && (str.[i] = ':' || str.[i] = ')') then (str, i)
else apply_expr set str (i + 1)
(*
* eval_app scans strings matching expressions between @( and ).
* an expression is:
* - a [character] followed by "?", an [expression] and possibly ":" and
* [another expression]
* - any [string] not holding ":"
* The [character] is a predicate. If defined, the first expression is
* evaluated, else it is the second one. The evaluation of a string is
* itself.
*
* ex: p?e:m?A?en:er:w?e:n?es
* In this example, if m and A are only defined predicates:
* p not being defined, it is m?A?en:er:w?e:n?es
* m being defined, it is A?en:er
* A being defined, it is en
* This example shows how to display adjectives in German, where
* m is for masculine, w for feminine and n for neuter
*)
let eval_app set str =
let rec loop str i =
if i + 3 < String.length str then
if str.[i] = '@' && str.[i + 1] = '(' && str.[i + 3] <> '-' then
let str = erase str i (i + 2) in
let str, i = apply_expr set str i in
if i < String.length str then
if str.[i] = ')' then loop (erase str i (i + 1)) i else loop str i
else str
else loop str (i + 1)
else str
in
loop str 0
(*
* eval_shift scans strings matching:
* @(#-) shifting # words of the left after the next word.
* @(#--) shifting # words of the left to the end.
* ex:
* before: "Une avec un diamant@(3-) bague"
* after: "Une bague avec un diamant"
* before: "Sie haben geworfen@(1--) einen kurzen Bogen"
* after: "Sie haben einen kurzen Bogen geworfen"
*)
let rec eval_shift s =
let t = Bytes.make (String.length s) '#' in
let rec loop changed i j =
if
i + 4 < String.length s
&& s.[i] = '@'
&& s.[i + 1] = '('
&& s.[i + 3] = '-'
then
let nleft = Char.code s.[i + 2] - Char.code '0' in
let to_the_end = s.[i + 4] = '-' in
let k = if to_the_end then i + 5 else i + 4 in
if k < String.length s && s.[k] = ')' then
let l =
let rec loop nleft l =
if l > 0 then
if s.[l] = ' ' then
if nleft <= 1 then l + 1 else loop (nleft - 1) (l - 1)
else loop nleft (l - 1)
else 0
in
loop nleft (i - 1)
in
let len = i - l in
let j = j - len in
let k = k + 1 in
let i = if k < String.length s && s.[k] = ' ' then k + 1 else k in
let i, j =
if to_the_end then
let rec loop i j =
if i < String.length s then (
Bytes.set t j s.[i];
loop (i + 1) (j + 1))
else
let j =
if j > 0 && Bytes.get t (j - 1) <> ' ' then (
Bytes.set t j ' ';
j + 1)
else j
in
String.blit s l t j len;
(i, j + len)
in
loop i j
else
let rec loop i j =
if i < String.length s then
if s.[i] = ' ' then (
Bytes.set t j ' ';
String.blit s l t (j + 1) len;
(i, j + 1 + len))
else (
Bytes.set t j s.[i];
loop (i + 1) (j + 1))
else if k < String.length s && s.[k] = ' ' then (
Bytes.set t j ' ';
String.blit s l t (j + 1) len;
(i, j + 1 + len))
else (
String.blit s l t j len;
(i, j + len))
in
loop i j
in
loop true i j
else (
Bytes.set t j s.[i];
loop changed (i + 1) (j + 1))
else if i < String.length s then (
Bytes.set t j s.[i];
loop changed (i + 1) (j + 1))
else if changed then eval_shift (Bytes.sub_string t 0 j)
else Bytes.sub_string t 0 j
in
loop false 0 0
let rec eval str =
if not (String.contains str '@') then str
else
let str = eval_rec str in
let set, str = eval_set str in
let str = eval_app set str in
eval_shift str
and eval_rec str =
let rec loop str i =
if i = String.length str then str
else if
i + 3 < String.length str
&& str.[i] = '@'
&& str.[i + 1] = '('
&& str.[i + 2] = '@'
then
let j =
let rec loop j =
if j < String.length str then
if str.[j] = '(' then
let j = loop (j + 1) in
loop (j + 1)
else if str.[j] = ')' then j
else loop (j + 1)
else j
in
loop (i + 2)
in
if j = String.length str then str
else
let j =
if j > String.length str then
Option.value
~default:(String.length str - 1)
(String.rindex_opt str ')')
else j
in
let sstr = eval (String.sub str (i + 2) (j - i - 2)) in
let k = i + String.length sstr in
let str =
String.sub str 0 i ^ sstr
^ String.sub str (j + 1) (String.length str - j - 1)
in
loop str k
else loop str (i + 1)
in
loop str 0