diff --git a/src/main.rs b/src/main.rs index 0b0b127..b91adb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,164 +1,8 @@ use std::env; use std::fs; -use std::fmt; -use std::collections::HashMap; -mod nombres; - -enum ErreurSophie { - CommandeInconnue(String), - PhraseVide, - ManqueArgument, - OrthographeNombre(String), - MauvaisArgument(String), - DesequilibreParenthese, - VariableInconnue(String), - MauvaisType(String), -} - -#[derive(PartialEq, Debug)] -enum Variable { - Entier(usize), - Texte(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), - 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), - Self::MauvaisType(attendu) => write!(f, "La variable est du mauvais type, {}.", attendu), - } - } -} - -struct Sophie { - variables: HashMap, -} - -impl Sophie { - fn new() -> Self { - Self { - variables: HashMap::new(), - } - } - fn execute(&mut self, 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 self.execute_phrase(phrase) { - Ok(_) => {}, - Err(raison) => { - eprintln!("Erreur phrase {} : {}", index_phrase + 1, raison); - return - } - } - } - } - - fn execute_phrase(&mut self, 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] { - "Définie" => { - self.definie(parties[1])?; - } - "Modifie" => { - self.modifie(parties[1])?; - }, - "Affiche" => { - self.affiche(parties[1])?; - }, - "Demande" => { - self.demande(parties[1])?; - } - autre_commande => { - return Err(ErreurSophie::CommandeInconnue(autre_commande.to_string())) - } - }; - Ok(()) - } - - fn definie(&mut self, arguments: &str) -> Result<(), ErreurSophie> { - let (variable_nom, variable_type) = nom_de_variable(arguments, "comme")?; - - let contenu = match variable_type.as_str() { - "entier" => Variable::Entier(0), - "texte" => Variable::Texte("".to_string()), - _ => return Err(ErreurSophie::MauvaisArgument("type de variable inconnu".into())), - }; - - self.variables.insert(variable_nom, contenu); - Ok(()) - } - - fn modifie(&mut self, arguments: &str) -> Result<(), ErreurSophie> { - let (variable_nom, contenu) = nom_de_variable(arguments, "avec")?; - if !self.variables.contains_key(&variable_nom) { - return Err(ErreurSophie::VariableInconnue(variable_nom)) - } - - let valeur = self.operation(&contenu)?; - self.variables.insert(variable_nom, Variable::Entier(valeur)); - - Ok(()) - } - - fn affiche(&self, 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 = self.operation(argument)?; - texte += &nombres::nombre_comme_texte(resultat); - } - } - println!("{}", texte); - Ok(()) - } - - fn demande(&self, arguments: &str) -> Result<(), ErreurSophie> { - println!("- demande : {}", arguments); - Ok(()) - } -} - -fn nom_de_variable(arguments: &str, separateur: &str) -> Result<(String, String), ErreurSophie> { - let parties: Vec<&str> = arguments.splitn(2, separateur).collect(); - let nom_variable: String = parties[0].trim().to_string(); - if parties.len() == 1 { - return Err(ErreurSophie::ManqueArgument) - } - let Some(first_char) = nom_variable.chars().next() else { - return Err(ErreurSophie::MauvaisArgument("il n'y a pas de variable".to_string())) - }; - if !first_char.is_uppercase() { - return Err(ErreurSophie::MauvaisArgument("il manque une majuscule à la variable".to_string())) - } - Ok((nom_variable, parties[1].trim().to_string())) -} +mod sophie; +use sophie::*; fn main() { let arguments: Vec = env::args().collect(); @@ -180,125 +24,3 @@ fn main() { } } } - - -// ------------------------------------------------------------------------- - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn teste_conversion_nombres_texte() { - 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 - Ok(nombre) => { - assert_eq!(*i, nombre, "Nombre inexact : {}, texte : {}", i, texte); - } - Err(raison) => { - panic!("Conversion échouée pour : {}, avec l'erreur : {}", i, raison); - } - } - } - } - - #[test] - fn teste_somme() { - for (a, b) in [(0, 0), (5, 7), (1467,45678), (1001, 0), (72_036_854_775_807usize, 14_036_567_775_807usize)] { - let texte_a = nombres::nombre_comme_texte(a); - let texte_b = nombres::nombre_comme_texte(b); - let sophie = Sophie::new(); - let resultat = sophie.operation(&format!("{} plus {}", texte_a, texte_b)); - match resultat { // Convert text back to number - Ok(nombre) => { - assert_eq!(a+b, nombre, "Résultat inexact pour {}+{} : {}", a, b, nombre); - } - Err(raison) => { - panic!("Conversion échouée pour : ({},{}), avec l'erreur : {}", a, b, raison); - } - } - } - } - - #[test] - fn teste_definition_variable() { - let mut sophie = Sophie::new(); - let resultat = sophie.execute_phrase("Définie Variable comme entier"); - match resultat { - Ok(_) => { - assert_eq!(sophie.variables["Variable"], Variable::Entier(0), "Variable mal définie"); - } - Err(raison) => { - panic!("Définition de variable échouée : {}", raison); - } - } - } - - #[test] - fn teste_modification_variable() { - let mut sophie = Sophie::new(); - if let Err(raison) = sophie.execute_phrase("Définie Variable comme entier") { - panic!("Définition de variable échouée : {}", raison); - } - let a = 2345678; - let resultat = sophie.execute_phrase(&format!("Modifie Variable avec {} ", nombres::nombre_comme_texte(a))); - match resultat { - Ok(_) => { - assert_eq!(sophie.variables["Variable"], Variable::Entier(a), "Variable mal modifiée"); - } - Err(raison) => { - panic!("Modification de variable échouée : {}", raison); - } - } - } - - #[test] - fn teste_operation_variable() { - let mut sophie = Sophie::new(); - if let Err(raison) = sophie.execute_phrase("Définie Variable comme entier") { - panic!("Définition de variable échouée : {}", raison); - } - let a = 2345678; - if let Err(raison) = sophie.execute_phrase(&format!("Modifie Variable avec {} ", nombres::nombre_comme_texte(a))) { - panic!("Modification de variable échouée : {}", raison); - } - let b = 987654; - let resultat = sophie.operation(&format!("Variable plus {}", nombres::nombre_comme_texte(b))); - - match resultat { - Ok(nombre) => { - assert_eq!(nombre, a+b, "Echec de la somme d'un entier et d'une variable, attendais {}, a reçu {}", a+b, nombre); - } - Err(raison) => { - panic!("Opération de variable échouée : {}", raison); - } - } - } - - #[test] - fn teste_maths() { - let sophie = Sophie::new(); - 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, 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/sophie/mod.rs b/src/sophie/mod.rs new file mode 100644 index 0000000..ecad60d --- /dev/null +++ b/src/sophie/mod.rs @@ -0,0 +1,162 @@ +use std::fmt; +use std::collections::HashMap; + +pub mod nombres; + +#[cfg(test)] +mod tests; + +pub enum ErreurSophie { + CommandeInconnue(String), + PhraseVide, + ManqueArgument, + OrthographeNombre(String), + MauvaisArgument(String), + DesequilibreParenthese, + VariableInconnue(String), + MauvaisType(String), +} + +#[derive(PartialEq, Debug)] +pub enum Variable { + Entier(usize), + Texte(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), + 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), + Self::MauvaisType(attendu) => write!(f, "La variable est du mauvais type, {}.", attendu), + } + } +} + +pub struct Sophie { + variables: HashMap, +} + +impl Sophie { + pub fn new() -> Self { + Self { + variables: HashMap::new(), + } + } + pub fn execute(&mut self, 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 self.execute_phrase(phrase) { + Ok(_) => {}, + Err(raison) => { + eprintln!("Erreur phrase {} : {}", index_phrase + 1, raison); + return + } + } + } + } + + fn execute_phrase(&mut self, 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] { + "Définie" => { + self.definie(parties[1])?; + } + "Modifie" => { + self.modifie(parties[1])?; + }, + "Affiche" => { + self.affiche(parties[1])?; + }, + "Demande" => { + self.demande(parties[1])?; + } + autre_commande => { + return Err(ErreurSophie::CommandeInconnue(autre_commande.to_string())) + } + }; + Ok(()) + } + + fn definie(&mut self, arguments: &str) -> Result<(), ErreurSophie> { + let (variable_nom, variable_type) = nom_de_variable(arguments, "comme")?; + + let contenu = match variable_type.as_str() { + "entier" => Variable::Entier(0), + "texte" => Variable::Texte("".to_string()), + _ => return Err(ErreurSophie::MauvaisArgument("type de variable inconnu".into())), + }; + + self.variables.insert(variable_nom, contenu); + Ok(()) + } + + fn modifie(&mut self, arguments: &str) -> Result<(), ErreurSophie> { + let (variable_nom, contenu) = nom_de_variable(arguments, "avec")?; + if !self.variables.contains_key(&variable_nom) { + return Err(ErreurSophie::VariableInconnue(variable_nom)) + } + + let valeur = self.operation(&contenu)?; + self.variables.insert(variable_nom, Variable::Entier(valeur)); + + Ok(()) + } + + fn affiche(&self, 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 = self.operation(argument)?; + texte += &nombres::nombre_comme_texte(resultat); + } + } + println!("{}", texte); + Ok(()) + } + + fn demande(&self, arguments: &str) -> Result<(), ErreurSophie> { + println!("- demande : {}", arguments); + Ok(()) + } +} + +fn nom_de_variable(arguments: &str, separateur: &str) -> Result<(String, String), ErreurSophie> { + let parties: Vec<&str> = arguments.splitn(2, separateur).collect(); + let nom_variable: String = parties[0].trim().to_string(); + if parties.len() == 1 { + return Err(ErreurSophie::ManqueArgument) + } + let Some(first_char) = nom_variable.chars().next() else { + return Err(ErreurSophie::MauvaisArgument("il n'y a pas de variable".to_string())) + }; + if !first_char.is_uppercase() { + return Err(ErreurSophie::MauvaisArgument("il manque une majuscule à la variable".to_string())) + } + Ok((nom_variable, parties[1].trim().to_string())) +} diff --git a/src/nombres.rs b/src/sophie/nombres.rs similarity index 100% rename from src/nombres.rs rename to src/sophie/nombres.rs diff --git a/src/sophie/tests.rs b/src/sophie/tests.rs new file mode 100644 index 0000000..63878aa --- /dev/null +++ b/src/sophie/tests.rs @@ -0,0 +1,112 @@ +use super::*; + +#[test] +fn teste_conversion_nombres_texte() { + 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 + Ok(nombre) => { + assert_eq!(*i, nombre, "Nombre inexact : {}, texte : {}", i, texte); + } + Err(raison) => { + panic!("Conversion échouée pour : {}, avec l'erreur : {}", i, raison); + } + } + } +} + +#[test] +fn teste_somme() { + for (a, b) in [(0, 0), (5, 7), (1467,45678), (1001, 0), (72_036_854_775_807usize, 14_036_567_775_807usize)] { + let texte_a = nombres::nombre_comme_texte(a); + let texte_b = nombres::nombre_comme_texte(b); + let sophie = Sophie::new(); + let resultat = sophie.operation(&format!("{} plus {}", texte_a, texte_b)); + match resultat { // Convert text back to number + Ok(nombre) => { + assert_eq!(a+b, nombre, "Résultat inexact pour {}+{} : {}", a, b, nombre); + } + Err(raison) => { + panic!("Conversion échouée pour : ({},{}), avec l'erreur : {}", a, b, raison); + } + } + } +} + +#[test] +fn teste_definition_variable() { + let mut sophie = Sophie::new(); + let resultat = sophie.execute_phrase("Définie Variable comme entier"); + match resultat { + Ok(_) => { + assert_eq!(sophie.variables["Variable"], Variable::Entier(0), "Variable mal définie"); + } + Err(raison) => { + panic!("Définition de variable échouée : {}", raison); + } + } +} + +#[test] +fn teste_modification_variable() { + let mut sophie = Sophie::new(); + if let Err(raison) = sophie.execute_phrase("Définie Variable comme entier") { + panic!("Définition de variable échouée : {}", raison); + } + let a = 2345678; + let resultat = sophie.execute_phrase(&format!("Modifie Variable avec {} ", nombres::nombre_comme_texte(a))); + match resultat { + Ok(_) => { + assert_eq!(sophie.variables["Variable"], Variable::Entier(a), "Variable mal modifiée"); + } + Err(raison) => { + panic!("Modification de variable échouée : {}", raison); + } + } +} + +#[test] +fn teste_operation_variable() { + let mut sophie = Sophie::new(); + if let Err(raison) = sophie.execute_phrase("Définie Variable comme entier") { + panic!("Définition de variable échouée : {}", raison); + } + let a = 2345678; + if let Err(raison) = sophie.execute_phrase(&format!("Modifie Variable avec {} ", nombres::nombre_comme_texte(a))) { + panic!("Modification de variable échouée : {}", raison); + } + let b = 987654; + let resultat = sophie.operation(&format!("Variable plus {}", nombres::nombre_comme_texte(b))); + match resultat { + Ok(nombre) => { + assert_eq!(nombre, a+b, "Echec de la somme d'un entier et d'une variable, attendais {}, a reçu {}", a+b, nombre); + } + Err(raison) => { + panic!("Opération de variable échouée : {}", raison); + } + } +} + +#[test] +fn teste_maths() { + let sophie = Sophie::new(); + 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, 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); + } + } +}