diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1a1f565 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "sophie" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..15b8556 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sophie" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..f00f0a0 --- /dev/null +++ b/shell.nix @@ -0,0 +1,18 @@ +{ pkgs ? import { overlays = [ (import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz)) ]; },}: +with pkgs; + +mkShell { + nativeBuildInputs = with xorg; [ + pkg-config + ] ++ [ + cargo + rustc + ]; + buildInputs = [ + latest.rustChannels.stable.rust + xorg.libX11 + xorg.libXi + xorg.libXtst + libevdev + ]; +} diff --git a/src/main.rs b/src/main.rs index caaedaf..6ee73b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,119 @@ use std::env; use std::fs; +use std::fmt; + +mod nombres; + +enum ErreurSophie { + CommandeInconnue(String), + PhraseVide, + ManqueArgument, + OrthographeNombre(String), +} + +impl fmt::Display for ErreurSophie { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CommandeInconnue(commande) => write!(f, "La commande \"{}\" est inconnue.", commande), + Self::PhraseVide => write!(f, "La phrase est vide."), + Self::ManqueArgument => write!(f, "Il manque un argument."), + Self::OrthographeNombre(nombre) => write!(f, "Le nombre \"{}\" est mal orthographié.", nombre), + } + } +} fn main() { - let args: Vec = env::args().collect(); + let arguments: Vec = env::args().collect(); - let file_path = &args[1]; + if arguments.len() < 2 { + eprintln!("Utilisation : sophie "); + return + } + + let chemin_de_fichier = &arguments[1]; - let contents = fs::read_to_string(file_path) - .expect("Should have been able to read the file"); - - println!("content : \n{}", contents); + match fs::read_to_string(chemin_de_fichier) { + Ok(contenu) => { + execute(contenu); + } + Err(raison) => { + eprintln!("Fichier illisible : {}", raison); + } + } } + +fn execute(contenu: String) { + let contenu_propre = contenu.replace("\n", ""); + let mut texte: Vec<&str> = contenu_propre.split('.').collect(); + texte.pop(); // remove empty phrase after last dot + for (index_phrase, phrase) in texte.iter().enumerate() { + match execute_phrase(phrase) { + Ok(_) => {}, + Err(raison) => { + eprintln!("Erreur phrase {} : {}", index_phrase + 1, raison); + return + } + } + } +} + +fn execute_phrase(phrase: &str) -> Result<(), ErreurSophie> { + let phrase = phrase.trim(); + let parties: Vec<&str> = phrase.splitn(2, ' ').collect(); + + if parties.is_empty() { + return Err(ErreurSophie::PhraseVide) + } + + if parties.len() == 1 { + return Err(ErreurSophie::ManqueArgument) + } + + match parties[0] { + "Modifie" => { + modifie(parties[1])?; + }, + "Affiche" => { + affiche(parties[1])?; + }, + "Demande" => { + demande(parties[1])?; + } + autre_commande => { + return Err(ErreurSophie::CommandeInconnue(autre_commande.to_string())) + } + }; + Ok(()) +} + +fn modifie(arguments: &str) -> Result<(), ErreurSophie> { + println!("- modifie : {}", arguments); + Ok(()) +} + +fn affiche(arguments: &str) -> Result<(), ErreurSophie> { + let liste_arguments: Vec<&str> = arguments.split(',').collect(); + + let mut texte = "".to_string(); + + for argument in liste_arguments { + let argument: &str = argument.trim(); + if argument.starts_with('"') { + if argument.ends_with('"') { + texte += &argument[1..argument.len()-1]; + } + } else { + let resultat = nombres::operation(argument)?; + texte += &nombres::nombre_comme_texte(resultat); + } + } + println!("{}", texte); + Ok(()) +} + +fn demande(arguments: &str) -> Result<(), ErreurSophie> { + println!("- demande : {}", arguments); + Ok(()) +} + + diff --git a/src/nombres.rs b/src/nombres.rs new file mode 100644 index 0000000..9a1bb21 --- /dev/null +++ b/src/nombres.rs @@ -0,0 +1,206 @@ +use super::ErreurSophie; + +const NOMS_UNITES: [&str; 10] = ["", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf"]; +const NOMS_UNITES_DIX: [&str; 10] = ["dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf"]; +const NOMS_DIZAINES: [&str; 9] = ["", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "x", "quatre-vingts"]; +const NOMS_SEPARATEURS: [&str; 7] = ["", "mille", "million", "milliard", "billion", "billiard", "trillion"]; +const UNION: &str = "-"; + +pub fn nombre_comme_texte(nombre: usize) -> String { + if nombre == 0 { + return "zéro".to_string() + } + if nombre >= 10usize.pow(18) { + return "infini".to_string() + } + let mut groupes: Vec = vec![]; + let mut nombre = nombre; + while nombre > 0 { + groupes.insert(0, nombre % 1000); + nombre /= 1000; + } + let mut chaine: String = "".to_string(); + + for index in 0..groupes.len() { + if groupes[index] == 0 { + continue + } + let pluriel: &str = if (groupes.len() - index - 1 > 1) && groupes[index] > 1 { + "s" + } else { + "" + }; + if index < groupes.len() - 1 { + let union = if index > 0 {UNION} else {""}; + let chiffre = if groupes.len() - index - 1 == 1 && groupes[index] == 1 { // un mille + "".to_string() + } else { + petit_nombre_comme_texte(groupes[index]) + UNION + }; + chaine += &format!("{}{}{}{}", + union, + chiffre, + NOMS_SEPARATEURS[groupes.len() - index - 1], + pluriel, + ); + } else { + let union = if index > 0 {UNION} else {""}; + chaine += union; + chaine += &petit_nombre_comme_texte(groupes[index]); + } + } + chaine +} + +fn petit_nombre_comme_texte(nombre: usize) -> String { + let nombre = nombre.clamp(0, 999); + let centaine = nombre / 100; + let dizaine = (nombre % 100) / 10; + let unité = nombre % 10; + + let décalage_dizaine = if [1, 7, 9].contains(&dizaine) {1} else {0}; + + let centaine_texte = if centaine > 1 { + format!("{}{}cent", NOMS_UNITES[centaine], UNION) + } else if centaine > 0 { + "cent".to_string() + } else { + "".to_string() + }; + + let dizaine_union = if centaine > 0 && dizaine > 0 { + UNION.to_string() + } else { + "".to_string() + }; + + let dizaine_texte = NOMS_DIZAINES[dizaine - décalage_dizaine]; + + let séparation = if unité == 1 && ![0, 1, 8, 9].contains(&dizaine) {UNION.to_string() + "et"} else {"".to_string()}; + + let unité_union = if nombre - unité > 0 && unité > 0 { + UNION.to_string() + } else { + "".to_string() + }; + let unité_texte = if [1, 7, 9].contains(&dizaine) { + unité_union + NOMS_UNITES_DIX[unité] + } else { + unité_union + NOMS_UNITES[unité] + }; + + format!("{}{}{}{}{}", centaine_texte, dizaine_union, dizaine_texte, séparation, unité_texte) +} + +pub fn texte_comme_nombre(texte: &str) -> Result { + let pluriel = format!("s{}", UNION); + let mut petits_nombres_texte: Vec<&str> = vec![]; + let mut texte_modifie = texte; + let mut dernier_separateur = 0; + for (index, separateur_texte) in NOMS_SEPARATEURS.iter().enumerate() { + if index == 0 { + continue + } + let mille: bool = texte_modifie.starts_with("mille"); + let texte_separe: Vec<&str> = texte_modifie.split(separateur_texte).collect(); + if texte_separe.len() > 2 { + return Err(ErreurSophie::OrthographeNombre(texte.to_string())) + } + if texte_separe.len() > 1 { + let petit_nombre_texte = texte_separe[1] + .trim_start_matches(&pluriel) + .trim_start_matches(UNION) + .trim_end_matches(UNION); + petits_nombres_texte.push(petit_nombre_texte); + texte_modifie = if mille { + "un" + } else { + texte_separe[0] + }; + dernier_separateur = index; + } else if !petits_nombres_texte.is_empty() { + petits_nombres_texte.push(""); + } + } + let petit_nombre_texte = texte_modifie + .trim_start_matches(&pluriel) + .trim_start_matches(UNION) + .trim_end_matches(UNION); + petits_nombres_texte.insert(dernier_separateur, petit_nombre_texte); + + let mut nombre: usize = 0; + + for (index, petit_nombre_texte) in petits_nombres_texte.iter().enumerate() { + let petit_nombre = texte_comme_petit_nombre(petit_nombre_texte)?; + nombre += petit_nombre * 1000usize.pow(index as u32); + } + + Ok(nombre) +} + +fn texte_comme_petit_nombre(texte: &str) -> Result { + let elements: Vec<&str> = texte.split(UNION).collect(); + + let mut nombre = 0; + let mut dernier_chiffre_texte = ""; + + for chiffre_texte in elements { + if chiffre_texte == "cent" { + if nombre == 0 { + nombre = 1; + } + if nombre >= 100 { + return Err(ErreurSophie::OrthographeNombre(texte.to_string())) + } + nombre *= 100; + dernier_chiffre_texte = chiffre_texte; + continue + } + if chiffre_texte == "vingts" { + if dernier_chiffre_texte == "quatre" { + nombre += 80 - 4; + dernier_chiffre_texte = chiffre_texte; + continue + } else { + return Err(ErreurSophie::OrthographeNombre(texte.to_string())) + } + } + if let Some(chiffre) = NOMS_UNITES.iter().position(|&s| s == chiffre_texte) { + if nombre%10 > 0 { + return Err(ErreurSophie::OrthographeNombre(texte.to_string())) + } + nombre += chiffre; + dernier_chiffre_texte = chiffre_texte; + continue + } + if let Some(chiffre) = NOMS_DIZAINES.iter().position(|&s| s == chiffre_texte) { + if nombre%100 > 0 && chiffre != 1 { + return Err(ErreurSophie::OrthographeNombre(texte.to_string())) + } + nombre += chiffre*10; + dernier_chiffre_texte = chiffre_texte; + continue + } + if let Some(chiffre) = NOMS_UNITES_DIX.iter().position(|&s| s == chiffre_texte) { + if nombre%10 > 0 { + return Err(ErreurSophie::OrthographeNombre(texte.to_string())) + } + nombre += 10 + chiffre; + dernier_chiffre_texte = chiffre_texte; + continue + } + return Err(ErreurSophie::OrthographeNombre(texte.to_string())) + } + + Ok(nombre) +} + +pub fn operation(arguments: &str) -> Result { + let somme_texte: Vec<&str> = arguments.split("plus").collect(); + let mut somme : usize = 0; + for element in somme_texte { + let element_propre: &str = element.trim(); + somme += texte_comme_nombre(element_propre)? + } + Ok(somme) +} diff --git a/test.sp b/test.sp new file mode 100644 index 0000000..6849387 --- /dev/null +++ b/test.sp @@ -0,0 +1,2 @@ +Affiche "Nombre 1 : ", deux-million plus mille-sept-cent-trente-deux. +Affiche "Nombre 2 : ", sept-cent plus cinq-cent-quarante-quatre.