diff --git a/src/sophie/mod.rs b/src/sophie/mod.rs index ecad60d..f770a34 100644 --- a/src/sophie/mod.rs +++ b/src/sophie/mod.rs @@ -1,11 +1,28 @@ use std::fmt; +use std::io; use std::collections::HashMap; pub mod nombres; +pub mod texte; #[cfg(test)] mod tests; +#[derive(PartialEq, Debug)] +pub enum Variable { + Entier(usize), + Texte(String), +} + +impl Variable { + pub fn nom_type(&self) -> String { + match self { + Self::Entier(_) => "entier".into(), + Self::Texte(_) => "texte".into(), + } + } +} + pub enum ErreurSophie { CommandeInconnue(String), PhraseVide, @@ -14,13 +31,8 @@ pub enum ErreurSophie { MauvaisArgument(String), DesequilibreParenthese, VariableInconnue(String), - MauvaisType(String), -} - -#[derive(PartialEq, Debug)] -pub enum Variable { - Entier(usize), - Texte(String), + MauvaisType(String, String, String), + ProblemeTerminal(String), } impl fmt::Display for ErreurSophie { @@ -33,7 +45,8 @@ impl fmt::Display for ErreurSophie { 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), + Self::MauvaisType(nom, type_variable, type_attendu) => write!(f, "La variable {} est du mauvais type ({}), attendais {}.", nom, type_variable, type_attendu), + Self::ProblemeTerminal(probleme) => write!(f, "Problème d'accès terminal : {}.", probleme), } } } @@ -114,41 +127,54 @@ impl Sophie { return Err(ErreurSophie::VariableInconnue(variable_nom)) } - let valeur = self.operation(&contenu)?; - self.variables.insert(variable_nom, Variable::Entier(valeur)); + let valeur = match self.variables[&variable_nom] { + Variable::Entier(_) => Variable::Entier(self.operation(&contenu)?), + Variable::Texte(_) => Variable::Texte(self.texte(&contenu)?), + }; + self.variables.insert(variable_nom, 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); + println!("{}", self.texte(arguments)?); Ok(()) } - fn demande(&self, arguments: &str) -> Result<(), ErreurSophie> { - println!("- demande : {}", arguments); + fn demande(&mut self, arguments: &str) -> Result<(), ErreurSophie> { + let (variable_nom, _) = nom_de_variable(arguments, "")?; + + if !self.variables.contains_key(&variable_nom) { + return Err(ErreurSophie::VariableInconnue(variable_nom)) + } + + println!("Quelle valeur pour {} ?", variable_nom); + let mut reponse: String = String::new(); + if let Err(_) = io::stdin().read_line(&mut reponse) { + return Err(ErreurSophie::ProblemeTerminal("lecture d'entrées utilisateur impossible".into())) + } + + let contenu = reponse.trim(); + + let valeur = match self.variables[&variable_nom] { + Variable::Entier(_) => Variable::Entier(self.operation(contenu)?), + Variable::Texte(_) => Variable::Texte(contenu.into()), + }; + self.variables.insert(variable_nom, valeur); + 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(); + let parties = if separateur == "" { + vec![arguments, ""] + } else { + arguments.splitn(2, separateur).collect() + }; + + let nom_variable = parties[0].trim().to_string(); + if parties.len() == 1 { return Err(ErreurSophie::ManqueArgument) } diff --git a/src/sophie/nombres.rs b/src/sophie/nombres.rs index f2c3061..a4ceda3 100644 --- a/src/sophie/nombres.rs +++ b/src/sophie/nombres.rs @@ -101,7 +101,7 @@ impl Sophie { if texte.chars().next().map_or(false, |c| c.is_uppercase()) { if self.variables.contains_key(texte) { let Variable::Entier(nombre) = self.variables[texte] else { - return Err(ErreurSophie::MauvaisType("attendais entier".to_string())) + return Err(ErreurSophie::MauvaisType(texte.into(), self.variables[texte].nom_type(), "entier".into())) }; return Ok(nombre); } else { @@ -184,7 +184,7 @@ fn petit_nombre_comme_texte(nombre: usize) -> String { 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 && nombre > 16 { + let unité_union = if nombre - unité > 0 && unité > 0 && (nombre%100 > 16 || nombre%100 < 10) { UNION.to_string() } else { "".to_string() @@ -303,5 +303,5 @@ fn texte_comme_petit_nombre(texte: &str) -> Result { return Err(ErreurSophie::OrthographeNombre(texte.to_string())) } - Ok(nombre) + Ok(nombre) } diff --git a/src/sophie/tests.rs b/src/sophie/tests.rs index 63878aa..cbc626a 100644 --- a/src/sophie/tests.rs +++ b/src/sophie/tests.rs @@ -103,10 +103,26 @@ fn teste_maths() { 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); + assert_eq!(nombre, a*b+(c-d)/e, "Echec de l'opération mathématique, résultat : {}", nombre); } Err(raison) => { panic!("Execution échouée pour multiplication, avec l'erreur : {}", raison); } } } + +// --------------------------------------------- anti-test + +#[test] +fn teste_echec_modification() { + let mut sophie = Sophie::new(); + let resultat = sophie.execute_phrase("Modifie Variable avec deux"); + let Err(raison) = resultat else { + panic!("Ne devrais pas pouvoir modifier une variable non définie"); + }; + if let ErreurSophie::VariableInconnue(nom) = raison { + assert_eq!(nom, "Variable", "Mauvais nom de variable reconnu : {}", nom); + } else { + panic!("Modification échouée avec erreur imprévue : {}", raison); + } +} diff --git a/src/sophie/texte.rs b/src/sophie/texte.rs new file mode 100644 index 0000000..af0a9a2 --- /dev/null +++ b/src/sophie/texte.rs @@ -0,0 +1,35 @@ +use super::Sophie; +use super::ErreurSophie; +use super::Variable; +use super::nombres; + +impl Sophie { + pub fn texte(&self, arguments: &str) -> Result { + 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: String = if argument.contains(" ") { + nombres::nombre_comme_texte(self.operation(argument)?) + } else { + if !self.variables.contains_key(argument) { + return Err(ErreurSophie::VariableInconnue(argument.into())) + } + match self.variables[argument] { + Variable::Entier(nombre) => nombres::nombre_comme_texte(nombre), + Variable::Texte(ref contenu) => contenu.to_string(), + } + }; + texte += &resultat; + } + } + Ok(texte) + } +} \ No newline at end of file diff --git a/test.sp b/test.sp index d78321a..bdbadb4 100644 --- a/test.sp +++ b/test.sp @@ -1,3 +1,14 @@ Définie A comme entier. Modifie A avec mille-cinq-cent-cinquante-cinq fois treize. Affiche "A : ", A. +Définie B comme texte. +Modifie B avec "hello there", " general", " Kenobi". +Affiche "Combo : ", B, " / ", ouvre la parenthèse un plus cinq ferme la parenthèse fois ouvre la parenthèse huit moins un ferme la parenthèse. +Modifie B avec deux plus trois. +Demande B. +Affiche "Texte B : ", B. +Demande A. +Modifie A avec A fois deux. +Affiche "fois deux : ", A. + + diff --git a/todo b/todo deleted file mode 100644 index 3f3c089..0000000 --- a/todo +++ /dev/null @@ -1,2 +0,0 @@ -variable types -variable = Variable::Entier(usize)