diff --git a/src/main.rs b/src/main.rs index 49f1422..927d63a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,8 @@ enum ErreurSophie { ManqueArgument(String), OrthographeNombre(String), MauvaisArgument(String), + DesequilibreParenthese, + VariableInconnue(String), } impl fmt::Display for ErreurSophie { @@ -20,7 +22,9 @@ impl fmt::Display for ErreurSophie { Self::PhraseVide => write!(f, "La phrase est vide."), Self::ManqueArgument(commande) => write!(f, "Il manque un argument pour \"{}\".", commande), Self::OrthographeNombre(nombre) => write!(f, "Le nombre \"{}\" est mal orthographié.", nombre), - Self::MauvaisArgument(message) => write!(f, "La commande a reçu un mauvais argument, {}.", message) + Self::MauvaisArgument(message) => write!(f, "La commande a reçu un mauvais argument, {}.", message), + Self::DesequilibreParenthese => write!(f, "Les parenthèses sont déséquilibrés."), + Self::VariableInconnue(nom) => write!(f, "La variable \"{}\" est inconnue", nom), } } } @@ -123,31 +127,6 @@ impl Sophie { println!("- demande : {}", arguments); Ok(()) } - - pub fn operation(&self, arguments: &str) -> Result { - let somme_texte: Vec<&str> = arguments.split("plus").collect(); - let mut somme : usize = 0; - for somme_element in somme_texte { - let somme_element_propre: &str = somme_element.trim(); - let produit_texte: Vec<&str> = somme_element_propre.split("fois").collect(); - - let mut produit : usize = 1; - for produit_element in produit_texte { - let produit_element_propre: &str = produit_element.trim(); - let Some(first_char) = produit_element_propre.chars().next() else { - return Err(ErreurSophie::MauvaisArgument("il y a un argument vide pour l'operation".to_string())) - }; - let nombre = if first_char.is_uppercase() { - self.variables[produit_element_propre] - } else { - nombres::texte_comme_nombre(produit_element_propre)? - }; - produit *= nombre; - } - somme += produit; - } - Ok(somme) - } } fn main() { @@ -172,6 +151,8 @@ fn main() { } +// ------------------------------------------------------------------------- + #[cfg(test)] // Compile and run only during testing mod tests { @@ -179,7 +160,6 @@ mod tests { #[test] fn teste_conversion_nombres_texte() { - // Test on a limited set of numbers to ensure feasibility for i in [0, 1, 42, 123, 999, 1031, 1_001_091, 72_036_854_775_807usize].iter() { let texte = nombres::nombre_comme_texte(*i); // Convert number to text match nombres::texte_comme_nombre(&texte) { // Convert text back to number @@ -231,7 +211,7 @@ mod tests { let mut sophie = Sophie::new(); let a = 2345678; let b = 987654; - let phrase = format!("Modifie Variable avec {}", nombres::nombre_comme_texte(a)); + let phrase = format!("Modifie Variable avec {} ", nombres::nombre_comme_texte(a)); if let Err(raison) = sophie.execute_phrase(&phrase) { panic!("Execution échouée pour \"{}\", avec l'erreur : {}", phrase, raison); } @@ -248,12 +228,23 @@ mod tests { } #[test] - fn teste_multiplication() { + fn teste_maths() { let sophie = Sophie::new(); - let resultat = sophie.operation("trois fois deux plus quatre fois sept"); + let a = 2345678; + let b = 987654; + let c = 34523456; + let d = 45678; + let e = 2; + let resultat = sophie.operation(&format!("{} fois {} plus ouvre la parenthèse {} moins {} ferme la parenthèse divisé par {}", + nombres::nombre_comme_texte(a), + nombres::nombre_comme_texte(b), + nombres::nombre_comme_texte(c), + nombres::nombre_comme_texte(d), + nombres::nombre_comme_texte(e) + )); match resultat { Ok(nombre) => { - assert_eq!(nombre, 34, "Echec de la multiplication de 3*2+4*7, got {}", nombre); + assert_eq!(nombre, a*b+(c-d)/e, "Echec de l'opération mathématique, got {}", nombre); } Err(raison) => { panic!("Execution échouée pour multiplication, avec l'erreur : {}", raison); diff --git a/src/nombres.rs b/src/nombres.rs index 93a31ff..5acaee3 100644 --- a/src/nombres.rs +++ b/src/nombres.rs @@ -1,4 +1,5 @@ use super::ErreurSophie; +use super::Sophie; 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"]; @@ -6,6 +7,107 @@ const NOMS_DIZAINES: [&str; 9] = ["", "dix", "vingt", "trente", "quarante", "cin const NOMS_SEPARATEURS: [&str; 7] = ["", "mille", "million", "milliard", "billion", "billiard", "trillion"]; const UNION: &str = "-"; +impl Sophie { + pub fn operation(&self, arguments: &str) -> Result { + //return self.operation_elementaire(arguments); + let texte = arguments + .replace("ouvre la parenthèse", "ouvre-la-parenthese") + .replace("ferme la parenthèse", "ferme-la-parenthese") + .replace("divisé par", "divise-par"); + let mut expression: Vec = texte.split(" ").map(String::from).collect(); + + while expression.contains(&"ouvre-la-parenthese".to_string()) { + let mut ouverture: Option = None; + let mut fermeture: Option = None; + for index in 0..expression.len() { + if expression[index] == "ouvre-la-parenthese" { + ouverture = Some(index); + } + if expression[index] == "ferme-la-parenthese" && ouverture.is_some() { + fermeture = Some(index); + break; + } + } + let Some(index_ouverture) = ouverture else { + return Err(ErreurSophie::DesequilibreParenthese); + }; + let Some(index_fermeture) = fermeture else { + return Err(ErreurSophie::DesequilibreParenthese); + }; + let contenu: String = expression[(index_ouverture+1)..(index_fermeture)].join(" "); + let nombre = self.operation_elementaire(&contenu)?; + let nombre_texte = nombre_comme_texte(nombre); + expression[index_ouverture] = nombre_texte; + for _ in 0..(index_fermeture-index_ouverture) { + expression.remove(index_ouverture+1); + } + } + if expression.contains(&"ferme-la-parenthese".to_string()) { + return Err(ErreurSophie::DesequilibreParenthese); + } + self.operation_elementaire(&expression.join(" ")) + } + + pub fn operation_elementaire(&self, arguments: &str) -> Result { + let texte = arguments.replace("divisé par", "divise-par"); + let mut expression: Vec = texte.split(" ").map(String::from).collect(); + + let mut index = 0; + while index < expression.len() { + if expression[index] != "fois" && expression[index] != "divise-par" { + index += 1; + continue; + } + if index == 0 || index == expression.len() - 1 { + return Err(ErreurSophie::ManqueArgument(expression[index].to_string())); + } + let a = self.texte_comme_nombre(&expression[index - 1])?; + let b = self.texte_comme_nombre(&expression[index + 1])?; + let produit = if expression[index] == "fois" {a*b} else {a/b}; + let produit_texte: String = nombre_comme_texte(produit); + index -= 1; + expression[index] = produit_texte; + expression.remove(index + 1); + expression.remove(index + 1); + } + + let mut index = 0; + while index < expression.len() { + if expression[index] != "plus" && expression[index] != "moins" { + index += 1; + continue; + } + if index == 0 || index == expression.len() - 1 { + return Err(ErreurSophie::ManqueArgument(expression[index].to_string())); + } + let a = self.texte_comme_nombre(&expression[index - 1])?; + let b = self.texte_comme_nombre(&expression[index + 1])?; + let somme = if expression[index] == "plus" {a+b} else {a-b}; + let somme_texte: String = nombre_comme_texte(somme); + index -= 1; + expression[index] = somme_texte; + expression.remove(index + 1); + expression.remove(index + 1); + } + + if expression.len() > 1 { + return Err(ErreurSophie::MauvaisArgument("expression mathématique".to_string())) + } + self.texte_comme_nombre(&expression[0]) + } + + fn texte_comme_nombre(&self, texte: &str) -> Result { + if texte.chars().next().map_or(false, |c| c.is_uppercase()) { + if self.variables.contains_key(texte) { + return Ok(self.variables[texte]); + } else { + return Err(ErreurSophie::VariableInconnue(texte.to_string())) + } + } + texte_comme_nombre(texte) + } +} + pub fn nombre_comme_texte(nombre: usize) -> String { if nombre == 0 { return "zéro".to_string() diff --git a/test.sp b/test.sp index ba52671..1f10ada 100644 --- a/test.sp +++ b/test.sp @@ -1 +1,2 @@ +Affiche dix plus sept fois sept. Affiche deux fois ouvre la parenthèse quatre plus seize ferme la parenthèse. diff --git a/todo b/todo new file mode 100644 index 0000000..3f3c089 --- /dev/null +++ b/todo @@ -0,0 +1,2 @@ +variable types +variable = Variable::Entier(usize)