From 42b8fa388b6300c1b05b123c2ac10b0ebbf6e42f Mon Sep 17 00:00:00 2001 From: Matthieu Dubuget Date: Sun, 10 Apr 2022 11:58:17 +0200 Subject: [PATCH] WiP --- README.md | 4 +- bin/nbr.ml | 6 +- dune-project | 18 ++-- lib/dune | 4 +- lib/nombres.ml | 236 ++++++++++++++++++++++++++++++++++++++----------- nombres.opam | 12 ++- 6 files changed, 216 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index a1d378d..b62fbea 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ Nombres ========= -Convert ints into french. +Convert integers into french. -Provided as a 1-function library, and a basic command-line utility (`nbr` — will +Provided as a 1-function library and a basic command-line utility (`nbr` — will certainly be renamed to something longer and less likely to name clash). More advanced functionnalities could be added when needed. Here are some diff --git a/bin/nbr.ml b/bin/nbr.ml index 6dd5298..78ca6d2 100644 --- a/bin/nbr.ml +++ b/bin/nbr.ml @@ -4,14 +4,16 @@ let rec parse_and_print () = input_line stdin |> function | "q" | "Q" -> () | "h" | "?" | "H" -> - print_endline "Je n'accepte que des entiers positifs. Et pas trop grands…\n'q' ou 'Q' pour quitter."; + print_endline + "Je n'accepte que des entiers positifs. Et pas trop grands…\n\ + 'q' ou 'Q' pour quitter."; flush stdout; parse_and_print () | str -> str |> int_of_string |> (fun i -> match i with - | i -> i |> Nombres.nombre_of_int |> print_endline + | i -> i |> Nombres.nombre |> print_endline | exception _ -> ()) |> parse_and_print diff --git a/dune-project b/dune-project index 199baac..c36e81f 100644 --- a/dune-project +++ b/dune-project @@ -1,14 +1,22 @@ -(lang dune 2.8) +(lang dune 3.0) +(name nombres) +(version 0.9) (generate_opam_files true) -(name nombres) + +(source (github ttamttam/nombres)) +(license ISC) +(authors "Matthieu Dubuget") +(maintainers "matthieu.dubuget@gmail.com") + (package - (name nombres) + (name nombres) + (synopsis "Conversion of integers to french.") (description -"\| Convert ints into french. +"\| Convert integers into french. "\| "\| Some references: "\| - https://leconjugueur.lefigaro.fr/frlesnombres.php "\| - https://www.miakinen.net/vrac/nombres "\| - https://www.dcode.fr/ecriture-nombre-lettres -)) \ No newline at end of file +)) diff --git a/lib/dune b/lib/dune index 7c65f97..82a22d0 100644 --- a/lib/dune +++ b/lib/dune @@ -1,2 +1,4 @@ (library - (name nombres)) + (name nombres) + (public_name nombres) + (libraries fmt)) diff --git a/lib/nombres.ml b/lib/nombres.ml index 922c0d6..b8cc54a 100644 --- a/lib/nombres.ml +++ b/lib/nombres.ml @@ -1,56 +1,188 @@ -let rec nombre_of_int = function - | 0 -> "zéro" - | 1 -> "un" - | 2 -> "deux" - | 3 -> "trois" - | 4 -> "quatre" - | 5 -> "cinq" - | 6 -> "six" - | 7 -> "sept" - | 8 -> "huit" - | 9 -> "neuf" - | 10 -> "dix" - | 11 -> "onze" - | 12 -> "douze" - | 13 -> "treize" - | 14 -> "quatorze" - | 15 -> "quinze" - | 16 -> "seize" - | 20 -> "vingt" - | 30 -> "trente" - | 40 -> "quarante" - | 50 -> "cinquante" - | 60 -> "soixante" - | 80 -> "quatre-vingts" - | 100 -> "cent" - | 1000 -> "mille" - | n when n < 70 -> jusqua_70 n - | n when n < 80 -> jusqua_100 "soixante" n - | n when n < 100 -> jusqua_100 "quatre-vingt" n - | n when n < 1_000 -> jusqua_1000 n - | n when n < 1_000_000 -> jusqua_1000000 n - | _ -> "--------------------" +type options = + | Septante + | Huitante + | Nonante + | Belgique (* Septante Nonante *) + | VVF (* Septante Huitante Nonante *) -and jusqua_70 n = - let dizaine = nombre_of_int (n / 10 * 10) in - let unite = n mod 10 in - [ dizaine; nombre_of_int unite ] - |> String.concat (if unite = 1 then " et " else "-") +let int_of_options = function + | Septante -> 0x1 + | Huitante -> 0x2 + | Nonante -> 0x8 + | Belgique -> 0x1 lor 0x8 + | VVF -> 0x1 lor 0x2 lor 0x8 -and jusqua_100 dizaine n = - let unite = n mod 10 in - [ dizaine; nombre_of_int (n mod 20) ] - |> String.concat (if unite = 1 && n < 80 then " et " else "-") +type mode = [ `Belgique | `VVF | `France ] -and jusqua_1000 n = - let centaine = n / 100 and reste = n mod 100 in - let cent = if centaine > 1 && reste = 0 then "cents" else "cent" in - ((if centaine = 1 then [ cent ] else [ nombre_of_int centaine; cent ]) - @ if reste = 0 then [] else [ nombre_of_int reste ]) - |> String.concat " " +let options_of_mode = function + | `Belgique -> [ Belgique ] + | `VVF -> [ VVF ] + | `France -> [] -and jusqua_1000000 n = - let milliers = n / 1000 and reste = n mod 1000 in - ((if milliers = 1 then [ "mille" ] else [ nombre_of_int milliers; "mille" ]) - @ if reste = 0 then [] else [ nombre_of_int reste ]) - |> String.concat " " +let int_of_mode m = + m |> options_of_mode |> List.map int_of_options + |> ListLabels.fold_left ~f:( + ) ~init:0 + +let mode_of_int = function + | 0 -> `France + | 1 -> `Belgique + | 2 -> `VVF + | _ -> assert false + +let opts options lst = List.exists (fun opt -> List.mem opt options) lst + +let nombre ?(options = []) n = + let has_opts = opts options in + if n = min_int then invalid_arg "Nombre hors domaine" + else + (* if debug then Fmt.pr "@[nombre %d:@," n; *) + let rec schu = + [| + ""; + "un"; + "deux"; + "trois"; + "quatre"; + "cinq"; + "six"; + "sept"; + "huit"; + "neuf"; + "dix"; + "onze"; + "douze"; + "treize"; + "quatorze"; + "quinze"; + "seize"; + |] + and schd = + [| + ""; + "dix"; + "vingt"; + "trente"; + "quarante"; + "cinquante"; + "soixante"; + "soixante"; + |] + and nombre_of_int ?(singular = false) ?(accu = []) current () = + match current with + | 0 -> + if accu <> [] then accu |> List.rev |> String.concat " " + else "zéro" + | n when n < 0 -> nombre_of_int ~accu (-n) () + | n when n < 17 -> unroll (schu.(n) :: accu) + | n when n mod 10 = 0 && n < 61 -> unroll (schd.(n / 10) :: accu) + | 80 -> + let nom, noms = + if has_opts [ Huitante; VVF ] then ("huitante", "huitante") + else ("quatre-vingt", "quatre-vingts") + in + unroll ((if singular then nom else noms) :: accu) + | 100 -> unroll ("cent" :: accu) + | 1000 -> unroll ("mille" :: accu) + | n when n < 70 -> + let dizaines = nombre_of_int (n / 10 * 10) () in + let unites = n mod 10 in + let accu = + ([ + dizaines; + (if unites = 1 then " et " else "-"); + nombre_of_int unites (); + ] + |> String.concat "") + :: accu + in + unroll accu + | n when n < 80 -> + let unites = n mod 10 in + let accu = + if has_opts [ Septante; Belgique; VVF ] then + ([ + (if n < 70 then "soixante" else "septante"); + (if unites = 1 && n < 80 then " et " + else if unites > 0 then "-" + else ""); + (if unites > 0 then nombre_of_int unites () else ""); + ] + |> String.concat "") + :: accu + else + ([ + "soixante"; + (if unites = 1 && n < 80 then " et " else "-"); + nombre_of_int (n mod 20) (); + ] + |> String.concat "") + :: accu + in + unroll accu + | n when n < 100 -> + let unites = n mod 10 in + let accu = + if has_opts [ Huitante; VVF ] && n < 90 then + ([ + "huitante"; + (if unites = 1 then " et " else if unites > 0 then "-" else ""); + (if unites > 0 then nombre_of_int unites () else ""); + ] + |> String.concat "") + :: accu + else if has_opts [ Nonante; VVF; Belgique ] && n >= 90 then + ([ + "nonante"; + (if unites = 1 then " et " else if unites > 0 then "-" else ""); + (if unites > 0 then nombre_of_int unites () else ""); + ] + |> String.concat "") + :: accu + else + ([ + "quatre-vingt"; + (if unites = 1 && n < 80 then " et " else "-"); + nombre_of_int (n mod 20) (); + ] + |> String.concat "") + :: accu + in + unroll accu + | n when n < 1_000 -> jusqua_x ~singular accu 100 ~nom:"cent" ~un:false n + | n when n < 1_000_000 -> jusqua_x accu 1000 ~nom:"mille" ~un:false n + | n when n < 1_000_000_000 -> + jusqua_x accu 1_000_000 ~nom:"million" ~un:true n + | n when n < 1_000_000_000_000 -> + jusqua_x accu 1_000_000_000 ~nom:"milliard" ~un:true n + | n when n < 1_000_000_000_000_000 -> + jusqua_x accu 1_000_000_000_000 ~nom:"billion" ~un:true n + | n when n < 1_000_000_000_000_000_000 -> + jusqua_x accu 1_000_000_000_000_000 ~nom:"billiard" ~un:true n + | n when n <= max_int -> + jusqua_x accu 1_000_000_000_000_000_000 ~nom:"trillion" ~un:true n + | _ -> assert false + and unroll accu = List.rev accu |> String.concat " " + and jusqua_x ?(singular = false) accu lim ~nom ~un n = + let un = if un then "un" else "" in + let noms = + match (nom, singular) with + | "mille", _ -> "mille" + | nom, true -> nom + | nom, false -> nom ^ "s" + in + let count = n / lim and reste = n mod lim in + + let accu = + match count with + | 1 -> nom :: un :: accu |> List.filter (( <> ) "") + | _ -> + let noms = if lim = 100 && reste <> 0 then nom else noms in + noms + :: nombre_of_int ~singular:(singular || lim = 1000) count () + :: accu + in + + nombre_of_int ~singular ~accu reste () + in + + nombre_of_int n () diff --git a/nombres.opam b/nombres.opam index 46de998..7d1231d 100644 --- a/nombres.opam +++ b/nombres.opam @@ -1,15 +1,22 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" +version: "0.9" +synopsis: "Conversion of integers to french." description: """ -Convert ints into french. +Convert integers into french. Some references: - https://leconjugueur.lefigaro.fr/frlesnombres.php - https://www.miakinen.net/vrac/nombres - https://www.dcode.fr/ecriture-nombre-lettres """ +maintainer: ["matthieu.dubuget@gmail.com"] +authors: ["Matthieu Dubuget"] +license: "ISC" +homepage: "https://github.com/ttamttam/nombres" +bug-reports: "https://github.com/ttamttam/nombres/issues" depends: [ - "dune" {>= "2.8"} + "dune" {>= "3.0"} "odoc" {with-doc} ] build: [ @@ -26,3 +33,4 @@ build: [ "@doc" {with-doc} ] ] +dev-repo: "git+https://github.com/ttamttam/nombres.git"