diff --git a/src/main.rs b/src/main.rs index 8a3b9b2..04ad141 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,13 +11,26 @@ fn main() { eprintln!("Utilisation : pendragon "); return } + + let debug_mode = arguments.contains(&"--debug".to_string()); let chemin_de_fichier = &arguments[1]; - let mut pendragon = Pendragon::new(); + let mut pendragon = Pendragon::nouveau(); match fs::read_to_string(chemin_de_fichier) { Ok(contenu) => { - let _ = pendragon.compile(contenu); + let Ok(_) = pendragon.compile(contenu) else { + eprintln!("Compilation interrompue"); + return + }; + if debug_mode { + println!("{}\n-----------", pendragon.programme); + } + if let Err(raison) = pendragon.programme.execute() { + eprintln!("Erreur Execution : {}", raison); + return + } + println!("\n# Success"); } Err(raison) => { eprintln!("Fichier illisible : {}", raison); diff --git a/src/pendragon/booleen.rs b/src/pendragon/booleen.rs index d7be372..c2ffab1 100644 --- a/src/pendragon/booleen.rs +++ b/src/pendragon/booleen.rs @@ -1,79 +1,119 @@ -use super::Pendragon; -use super::ErreurPendragon; -use super::Element; +use super::*; impl Pendragon { - pub fn condition(&self, arguments: &str) -> Result { - self.condition_elementaire(arguments) - } + pub fn elements_booleen(&self, arguments: &str) -> Result, ErreurPendragon> { + let texte = arguments + .replace("ouvre la parenthèse", "ouvre-la-parenthese") + .replace("ferme la parenthèse", "ferme-la-parenthese"); + let elements_texte: Vec<&str> = texte.split(" ").collect(); + let mut expression: Vec = Vec::new(); + let mut pile_operateurs: Vec = Vec::new(); - pub fn condition_elementaire(&self, texte: &str) -> Result { - let mut expression: Vec = texte.split(" ").map(String::from).collect(); + for element in elements_texte { + match element { + "vrai" => expression.push(Element::Booleen(true)), + "faux" => expression.push(Element::Booleen(false)), + "non" => pile_operateurs.push(Operateur::Non), + "et" => { + while let Some(operateur) = pile_operateurs.last() { + if *operateur == Operateur::Non || *operateur == Operateur::Et { + expression.push(Element::Operateur(pile_operateurs.pop().unwrap())); + } else { + break; + } + } + pile_operateurs.push(Operateur::Et); + } + "ou" => { + while let Some(operateur) = pile_operateurs.last() { + if *operateur == Operateur::Non || *operateur == Operateur::Et || *operateur == Operateur::Ou { + expression.push(Element::Operateur(pile_operateurs.pop().unwrap())); + } else { + break; + } + } + pile_operateurs.push(Operateur::Ou); + } + "ouvre-la-parenthese" => pile_operateurs.push(Operateur::ParentheseBooleen), + "ferme-la-parenthese" => { + while let Some(operateur) = pile_operateurs.pop() { + if operateur == Operateur::ParentheseBooleen { + break; + } + expression.push(Element::Operateur(operateur)); + } + } + autre => { + if !format_de_variable(autre) { + return Err(ErreurPendragon::MauvaisArgument(format!("{}", autre))) + } + self.programme.variable_est_de_type(autre, TypeElement::Booleen)?; + expression.push(Element::Variable(autre.into(), TypeElement::Booleen)); + } + } + } - let mut index = 0; - while index < expression.len() { - if expression[index] != "non" { - index += 1; - continue; - } - if index == expression.len() - 1 { - return Err(ErreurPendragon::ManqueArgument); - } - let a = self.texte_comme_booleen(&expression[index + 1])?; - expression[index] = booleen_comme_texte(!a); - expression.remove(index + 1); + while let Some(operateur) = pile_operateurs.pop() { + expression.push(Element::Operateur(operateur)); } - let mut index = 0; - while index < expression.len() { - if expression[index] != "et" { - index += 1; - continue; - } - if index == 0 || index == expression.len() - 1 { - return Err(ErreurPendragon::ManqueArgument); - } - let a = self.texte_comme_booleen(&expression[index - 1])?; - let b = self.texte_comme_booleen(&expression[index + 1])?; - index -= 1; - expression[index] = booleen_comme_texte(a && b); - expression.remove(index + 1); - expression.remove(index + 1); - } - let mut index = 0; - while index < expression.len() { - if expression[index] != "ou" { - index += 1; - continue; - } - if index == 0 || index == expression.len() - 1 { - return Err(ErreurPendragon::ManqueArgument); - } - let a = self.texte_comme_booleen(&expression[index - 1])?; - let b = self.texte_comme_booleen(&expression[index + 1])?; - index -= 1; - expression[index] = booleen_comme_texte(a || b); - expression.remove(index + 1); - expression.remove(index + 1); - } - if expression.len() > 1 { - return Err(ErreurPendragon::MauvaisArgument("expression booléenne".to_string())) - } - self.texte_comme_booleen(&expression[0]) - } - pub fn texte_comme_booleen(&self, texte: &str) -> Result { - if texte.chars().next().map_or(false, |c| c.is_uppercase()) { - if self.variables.contains_key(texte) { - let Element::Booleen(booleen) = self.variables[texte] else { - return Err(ErreurPendragon::MauvaisType(texte.into(), self.variables[texte].type_element().nom(), "booleen".into())) - }; - return Ok(booleen); + Ok(expression) + } +} + +pub fn affiche_booleen(expression: Vec, variables: &HashMap) -> Result { + let booleen = calcule_booleen(expression.clone(), variables)?; + Ok(booleen_comme_texte(booleen)) +} + +pub fn calcule_booleen(expression: Vec, variables: &HashMap) -> Result { + let mut pile: Vec = Vec::new(); + + for element in expression { + if let Element::Booleen(booleen) = element { + pile.push(booleen); + continue; + } + if let Element::Variable(nom, _) = element { + let Some(variable) = variables.get(&nom) else { + return Err(ErreurPendragon::VariableInconnue(nom.into())) + }; + if let Element::Booleen(booleen) = variable { + pile.push(*booleen); + continue } else { - return Err(ErreurPendragon::VariableInconnue(texte.to_string())) + return Err(ErreurPendragon::MauvaisType(nom.into(), variable.type_element().nom(), "booleen".into())) } } - texte_comme_booleen(texte) + let Element::Operateur(ref operateur) = element else { + return Err(ErreurPendragon::MauvaisArgument(format!("{:?}, attendais un opérateur", element))) + }; + let Some(booleen_a) = pile.pop() else { + return Err(ErreurPendragon::CalculBooleen("la pile est vide".into())) + }; + match operateur { + Operateur::Non => { + pile.push(!booleen_a); + } + Operateur::Et => { + let Some(booleen_b) = pile.pop() else { + return Err(ErreurPendragon::CalculBooleen("la pile est vide".into())) + }; + pile.push(booleen_a && booleen_b); + } + Operateur::Ou => { + let Some(booleen_b) = pile.pop() else { + return Err(ErreurPendragon::CalculBooleen("la pile est vide".into())) + }; + pile.push(booleen_a || booleen_b); + } + _ => return Err(ErreurPendragon::MauvaisArgument(format!("{:?}, attendais un opérateur booléen", element))) + } } + if pile.len() > 1 { + return Err(ErreurPendragon::CalculBooleen("la pile n'est pas vide".into())) + } + Ok(pile[0]) } pub fn booleen_comme_texte(booleen: bool) -> String { @@ -84,10 +124,10 @@ pub fn booleen_comme_texte(booleen: bool) -> String { } } -pub fn texte_comme_booleen(texte: &str) -> Result { +pub fn texte_comme_booleen(texte: &str) -> Result { match texte { - "vrai" => Ok(true), - "faux" => Ok(false), + "vrai" => Ok(Element::Booleen(true)), + "faux" => Ok(Element::Booleen(false)), _ => Err(ErreurPendragon::BooleenInvalide(texte.into())), } } \ No newline at end of file diff --git a/src/pendragon/debug.rs b/src/pendragon/debug.rs new file mode 100644 index 0000000..f2075a5 --- /dev/null +++ b/src/pendragon/debug.rs @@ -0,0 +1,57 @@ +use std::fmt; +use super::*; + +pub enum ErreurPendragon { + CommandeInconnue(String), + ManqueArgument, + NombreInvalide(String), + BooleenInvalide(String), + TexteInvalide(String), + MauvaisArgument(String), + VariableInconnue(String), + MauvaisType(String, String, String), + ManquePoint, + Lecture(String), + CalculBooleen(String), + CalculEntier(String), +} + +impl fmt::Display for ErreurPendragon { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//' + match self { + Self::CommandeInconnue(commande) => write!(f, "La commande \"{}\" est inconnue.", commande), + Self::ManqueArgument => write!(f, "Il manque un argument."), + Self::NombreInvalide(nombre) => write!(f, "Le nombre \"{}\" est mal orthographié.", nombre), + Self::TexteInvalide(raison) => write!(f, "Le texte est invalide, {}.", raison), + Self::BooleenInvalide(booleen) => write!(f, "Le booleen \"{}\" est mal orthographié.", booleen), + Self::MauvaisArgument(message) => write!(f, "La commande a reçu un mauvais argument, {}.", message), + Self::VariableInconnue(nom) => write!(f, "La variable \"{}\" est inconnue.", nom), + Self::MauvaisType(nom, type_variable, type_attendu) => write!(f, "La variable {} est du mauvais type ({}), attendais {}.", nom, type_variable, type_attendu), + Self::ManquePoint => write!(f, "Il manque un point."), + Self::Lecture(raison) => write!(f, "Lecture d'entrées utilisateur impossible : {}.", raison), + Self::CalculBooleen(raison) => write!(f, "Calcul booleen échoué, {}.", raison), + Self::CalculEntier(raison) => write!(f, "Calcul entier échoué, {}.", raison), + } + } +} + +impl fmt::Display for Commande { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//' + match self { + Self::Definis(nom, type_element) => write!(f, "Definis {} comme {}.", nom, type_element.nom()), + Self::Demande(nom) => write!(f, "Demande {}.", nom), + Self::Modifie(nom, expression) => write!(f, "Modifie {} avec {:?}.", nom, expression), + Self::Affiche(expression) => write!(f, "Affiche {:?}.", expression), + } + } +} + +impl fmt::Display for Programme { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//' + let mut texte: String = format!("variables : {:?}", self.variables); + for (index, commande) in self.commandes.iter().enumerate() { + texte += &format!("\n#{:2}-{}", index+1, commande); + } + write!(f, "{}", texte) + } +} \ No newline at end of file diff --git a/src/pendragon/mod.rs b/src/pendragon/mod.rs index 1f65455..accfadc 100644 --- a/src/pendragon/mod.rs +++ b/src/pendragon/mod.rs @@ -1,4 +1,3 @@ -use std::io; use std::collections::HashMap; pub mod nombre; @@ -6,160 +5,113 @@ pub mod texte; pub mod booleen; pub mod structure; use structure::*; +pub mod debug; +use debug::*; #[cfg(test)] mod tests; pub struct Pendragon { - variables: HashMap, + pub programme: Programme, } impl Pendragon { - pub fn new() -> Self { + pub fn nouveau() -> Self { Self { - variables: HashMap::new(), + programme: Programme::nouveau(), } } - pub fn compile(&mut self, contenu: String) -> Result, ErreurPendragon> { + + pub fn compile(&mut self, contenu: String) -> Result<(), ErreurPendragon> { let contenu_propre = contenu.replace("\n", ""); let mut texte: Vec<&str> = contenu_propre.split('.').collect(); let reste = texte.pop(); if reste != Some("") { - eprintln!("Erreur phrase {} : Il manque un point.", texte.len() + 1); + eprintln!("Erreur Compilation, phrase {} : Il manque un point.", texte.len() + 1); return Err(ErreurPendragon::ManquePoint) } - let mut liste_commandes = Vec = vec![]; for (index_phrase, phrase) in texte.iter().enumerate() { match self.compile_phrase(phrase) { - Ok(commande) => {liste_commandes.push(commande)}, + Ok(commande) => {self.programme.ajoute_commande(commande)}, Err(raison) => { eprintln!("Erreur phrase {} : {}", index_phrase + 1, raison); return Err(raison) } } } - Ok(liste_commande) + Ok(()) } - fn compile_phrase(&self, phrase: &str) -> Result { + fn compile_phrase(&mut self, phrase: &str) -> Result { let phrase = phrase.trim(); let parties: Vec<&str> = phrase.splitn(2, ' ').collect(); - + if parties.len() == 1 { + return Err(ErreurPendragon::ManqueArgument) + } match parties[0] { - "Définis" => { - self.definis(parties[1]) - }, - "Modifie" => { - self.modifie(parties[1]) - }, - "Affiche" => { - self.affiche(parties[1]) - }, - "Demande" => { - self.demande(parties[1]) - } - autre_commande => { - return Err(ErreurPendragon::CommandeInconnue(autre_commande.to_string())) - } + "Définis" => self.definis(parties[1]), + "Modifie" => self.modifie(parties[1]), + "Affiche" => self.affiche(parties[1]), + "Demande" => self.demande(parties[1]), + autre => Err(ErreurPendragon::CommandeInconnue(autre.into())), } } fn affiche(&self, arguments: &str) -> Result { - println!("{}", self.texte(arguments)?); - - let commande = Affiche(Expression::avec_arguments(TypeElement::Texte, arguments)?) - Ok(commande) + Ok(Commande::Affiche(self.elements_texte(arguments)?)) } fn definis(&mut self, arguments: &str) -> Result { - let (variable_nom, variable_type) = self.nom_de_variable(arguments, "comme")?; + let (variable_nom, variable_type_texte) = nom_de_variable(arguments, "comme")?; + let variable_type = TypeElement::texte_comme_type(&variable_type_texte)?; - let possible_variable = self.recupere_variable(&variable_nom); + self.programme.ajoute_variable(variable_nom.clone(), variable_type.clone())?; - let Err(raison) = possible_variable else { - return Err(ErreurPendragon::MauvaisArgument(format!("la variable \"{}\" existe déjà", variable_nom))) - }; - let ErreurPendragon::VariableInconnue(_) = raison else { - return Err(raison) - }; - - let contenu = match variable_type.as_str() { - "entier" => Element::Entier(0), - "texte" => Element::Texte("".to_string()), - "booléen" => Element::Booleen(false), - _ => return Err(ErreurPendragon::MauvaisArgument(format!("type de variable \"{}\" inconnu", variable_type))), - }; - - self.variables.insert(variable_nom, contenu); - - let commande = Definis(variable_nom.into(), contenu.type_element()); - Ok(commande) + Ok(Commande::Definis(variable_nom.into(), variable_type)) } fn modifie(&mut self, arguments: &str) -> Result { - let (variable_nom, contenu) = self.nom_de_variable(arguments, "avec")?; - let variable = self.recupere_variable(&variable_nom)?; - - let valeur = match variable.type_element() { - TypeElement::Entier => Element::Entier(self.operation(&contenu)?), - TypeElement::Texte => Element::Texte(self.texte(&contenu)?), - TypeElement::Booleen => Element::Booleen(self.condition(&contenu)?), - }; - self.variables.insert(variable_nom, valeur); + let (variable_nom, contenu) = nom_de_variable(arguments, "avec")?; + let variable = self.programme.variable(&variable_nom)?; - let commande = Modifie(variable_nom, Expression::avec_arguments(variable.type_element(), arguments)?), - Ok(commande) + let elements = match variable { + TypeElement::Entier => self.elements_nombre(&contenu)?, + TypeElement::Texte => self.elements_texte(&contenu)?, + TypeElement::Booleen => self.elements_booleen(&contenu)?, + }; + + Ok(Commande::Modifie(variable_nom, elements)) } fn demande(&mut self, arguments: &str) -> Result { - let (variable_nom, _) = self.nom_de_variable(arguments, "")?; + let (variable_nom, _) = nom_de_variable(arguments, "")?; + let _ = self.programme.variable(&variable_nom)?; - let _ = self.recupere_variable(&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(ErreurPendragon::ProblemeTerminal("lecture d'entrées utilisateur impossible".into())) - } - - let contenu = reponse.trim(); - - let valeur = match self.variables[&variable_nom].type_element() { - TypeElement::Entier => Element::Entier(nombre::texte_comme_nombre(contenu)?), - TypeElement::Texte => Element::Texte(contenu.into()), - TypeElement::Booleen => Element::Booleen(booleen::texte_comme_booleen(contenu)?) - }; - self.variables.insert(variable_nom, valeur); - - let commande = Demande(variable_nom.into()); - Ok(commande) - } - - fn recupere_variable(&self, nom: &str) -> Result { - let Some(first_char) = nom.chars().next() else { - return Err(ErreurPendragon::MauvaisArgument("il n'y a pas de variable".to_string())) - }; - if !first_char.is_uppercase() { - return Err(ErreurPendragon::MauvaisArgument("il manque une majuscule à la variable".to_string())) - } - if !self.variables.contains_key(nom) { - return Err(ErreurPendragon::VariableInconnue(nom.into())) - } - Ok(self.variables[nom].clone()) - } - - fn nom_de_variable(&self, arguments: &str, separateur: &str) -> Result<(String, String), ErreurPendragon> { - 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(ErreurPendragon::ManqueArgument) - } - Ok((nom_variable, parties[1].trim().to_string())) + Ok(Commande::Demande(variable_nom.into())) } } + +fn nom_de_variable(arguments: &str, separateur: &str) -> Result<(String, String), ErreurPendragon> { + 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(ErreurPendragon::ManqueArgument) + } + Ok((nom_variable, parties[1].trim().to_string())) +} + +pub fn format_de_variable(nom: &str) -> bool { + let Some(first_char) = nom.chars().next() else { + return false + }; + if !first_char.is_uppercase() { + return false + } + true +} diff --git a/src/pendragon/nombre.rs b/src/pendragon/nombre.rs index 72badd4..353f46d 100644 --- a/src/pendragon/nombre.rs +++ b/src/pendragon/nombre.rs @@ -1,6 +1,4 @@ -use super::ErreurPendragon; -use super::Pendragon; -use super::Element; +use super::*; 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"]; @@ -9,107 +7,138 @@ const NOMS_SEPARATEURS: [&str; 7] = ["", "mille", "million", "milliard", "billio const UNION: &str = "-"; impl Pendragon { - pub fn operation(&self, arguments: &str) -> Result { - //return self.operation_elementaire(arguments); + pub fn elements_nombre(&self, arguments: &str) -> Result, ErreurPendragon> { 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(); + let elements_texte: Vec<&str> = texte.split(" ").collect(); + let mut expression: Vec = Vec::new(); + let mut pile_operateurs: Vec = Vec::new(); + + for element in elements_texte { + match element { + "plus" => { + while let Some(operateur) = pile_operateurs.last() { + if *operateur == Operateur::Plus || *operateur == Operateur::Moins || *operateur == Operateur::Fois || *operateur == Operateur::Divise { + expression.push(Element::Operateur(pile_operateurs.pop().unwrap())); + } else { + break; + } + } + pile_operateurs.push(Operateur::Plus); + } + "moins" => { + while let Some(operateur) = pile_operateurs.last() { + if *operateur == Operateur::Plus || *operateur == Operateur::Moins || *operateur == Operateur::Fois || *operateur == Operateur::Divise { + expression.push(Element::Operateur(pile_operateurs.pop().unwrap())); + } else { + break; + } + } + pile_operateurs.push(Operateur::Moins); + } + "fois" => { + while let Some(operateur) = pile_operateurs.last() { + if *operateur == Operateur::Fois || *operateur == Operateur::Divise { + expression.push(Element::Operateur(pile_operateurs.pop().unwrap())); + } else { + break; + } + } + pile_operateurs.push(Operateur::Fois); + } + "divise-par" => { + while let Some(operateur) = pile_operateurs.last() { + if *operateur == Operateur::Fois || *operateur == Operateur::Divise { + expression.push(Element::Operateur(pile_operateurs.pop().unwrap())); + } else { + break; + } + } + pile_operateurs.push(Operateur::Divise); + } + "ouvre-la-parenthese" => pile_operateurs.push(Operateur::ParentheseEntier), + "ferme-la-parenthese" => { + while let Some(operateur) = pile_operateurs.pop() { + if operateur == Operateur::ParentheseEntier { + break; + } + expression.push(Element::Operateur(operateur)); + } + } + autre => { + if format_de_variable(autre) { + self.programme.variable_est_de_type(autre, TypeElement::Entier)?; + expression.push(Element::Variable(autre.into(), TypeElement::Entier)); + } else { + expression.push(texte_comme_nombre(autre)?); + } + } + } + } - 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(ErreurPendragon::DesequilibreParenthese); - }; - let Some(index_fermeture) = fermeture else { - return Err(ErreurPendragon::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); - } + while let Some(operateur) = pile_operateurs.pop() { + expression.push(Element::Operateur(operateur)); } - if expression.contains(&"ferme-la-parenthese".to_string()) { - return Err(ErreurPendragon::DesequilibreParenthese); - } - self.operation_elementaire(&expression.join(" ")) + + Ok(expression) } +} - 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(); +pub fn affiche_nombre(expression: Vec, variables: &HashMap) -> Result { + let nombre = calcule_nombre(expression.clone(), variables)?; + Ok(nombre_comme_texte(nombre)) +} - 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(ErreurPendragon::ManqueArgument); - } - 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); +pub fn calcule_nombre(expression: Vec, variables: &HashMap) -> Result { + let mut pile: Vec = Vec::new(); + + for element in expression { + if let Element::Entier(nombre) = element { + pile.push(nombre); + continue; } - - 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(ErreurPendragon::ManqueArgument); - } - 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(ErreurPendragon::MauvaisArgument("expression mathématique".to_string())) - } - self.texte_comme_nombre(&expression[0]) - } - - pub 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) { - let Element::Entier(nombre) = self.variables[texte] else { - return Err(ErreurPendragon::MauvaisType(texte.into(), self.variables[texte].type_element().nom(), "entier".into())) - }; - return Ok(nombre); + if let Element::Variable(nom, _) = element { + let Some(variable) = variables.get(&nom) else { + return Err(ErreurPendragon::VariableInconnue(nom.into())) + }; + if let Element::Entier(nombre) = variable { + pile.push(*nombre); + continue } else { - return Err(ErreurPendragon::VariableInconnue(texte.to_string())) + return Err(ErreurPendragon::MauvaisType(nom.into(), variable.type_element().nom(), "entier".into())) } } - texte_comme_nombre(texte) + let Element::Operateur(ref operateur) = element else { + return Err(ErreurPendragon::MauvaisArgument(format!("{:?}, attendais un opérateur", element))) + }; + let Some(nombre_a) = pile.pop() else { + return Err(ErreurPendragon::CalculEntier("la pile est vide".into())) + }; + let Some(nombre_b) = pile.pop() else { + return Err(ErreurPendragon::CalculEntier("la pile est vide".into())) + }; + match operateur { + Operateur::Plus => { + pile.push(nombre_b + nombre_b); + } + Operateur::Moins => { + pile.push(nombre_b - nombre_a); + } + Operateur::Fois => { + pile.push(nombre_b * nombre_a); + } + Operateur::Divise => { + pile.push(nombre_b / nombre_a); + } + _ => return Err(ErreurPendragon::MauvaisArgument(format!("{:?}, attendais un opérateur d'entiers", element))) + } } + if pile.len() > 1 { + return Err(ErreurPendragon::CalculEntier("la pile n'est pas vide".into())) + } + Ok(pile[0]) } pub fn nombre_comme_texte(nombre: usize) -> String { @@ -198,9 +227,9 @@ fn petit_nombre_comme_texte(nombre: usize) -> String { format!("{}{}{}{}{}", centaine_texte, dizaine_union, dizaine_texte, séparation, unité_texte) } -pub fn texte_comme_nombre(texte: &str) -> Result { +pub fn texte_comme_nombre(texte: &str) -> Result { if texte == "zéro" { - return Ok(0) + return Ok(Element::Entier(0)) } let pluriel = format!("s{}", UNION); let mut petits_nombres_texte: Vec<&str> = vec![]; @@ -243,7 +272,7 @@ pub fn texte_comme_nombre(texte: &str) -> Result { nombre += petit_nombre * 1000usize.pow((petits_nombres_texte.len() - index - 1) as u32); } - Ok(nombre) + Ok(Element::Entier(nombre)) } fn texte_comme_petit_nombre(texte: &str) -> Result { diff --git a/src/pendragon/structure.rs b/src/pendragon/structure.rs index 0ad14cc..c9efbe7 100644 --- a/src/pendragon/structure.rs +++ b/src/pendragon/structure.rs @@ -1,43 +1,90 @@ -use std::fmt; +use std::io; +use std::collections::HashMap; + +use super::*; + +pub struct Programme { + pub variables: HashMap, + pub commandes: Vec, +} + +impl Programme { + pub fn nouveau() -> Self { + Self { + variables: HashMap::new(), + commandes: vec![], + } + } + + pub fn ajoute_commande(&mut self, commande: Commande) { + self.commandes.push(commande); + } + + pub fn ajoute_variable(&mut self, nom: String, type_variable: TypeElement) -> Result<(), ErreurPendragon> { + let Err(raison) = self.variable(&nom) else { + return Err(ErreurPendragon::MauvaisArgument(format!("la variable \"{}\" existe déjà", nom))) + }; + let ErreurPendragon::VariableInconnue(_) = raison else { + return Err(raison) + }; + self.variables.insert(nom, type_variable); + Ok(()) + } + + pub fn variable(&self, nom: &str) -> Result { + let Some(first_char) = nom.chars().next() else { + return Err(ErreurPendragon::MauvaisArgument("il n'y a pas de variable".to_string())) + }; + if !first_char.is_uppercase() { + return Err(ErreurPendragon::MauvaisArgument("il manque une majuscule à la variable".to_string())) + } + if !self.variables.contains_key(nom) { + return Err(ErreurPendragon::VariableInconnue(nom.into())) + } + Ok(self.variables[nom].clone()) + } + + pub fn variable_est_de_type(&self, nom: &str, type_element: TypeElement) -> Result<(), ErreurPendragon> { + let type_variable = self.variable(nom)?; + if type_variable != type_element { + return Err(ErreurPendragon::MauvaisType(nom.into(), type_variable.nom(), type_element.nom())) + } + Ok(()) + } + + pub fn execute(&self) -> Result<(), ErreurPendragon> { + let mut variables_globales: HashMap = HashMap::new(); + for commande in &self.commandes { + match commande { + Commande::Definis(nom, type_element) => { + variables_globales.insert(nom.to_string(), type_element.comme_element()); + } + Commande::Demande(nom) => { + let valeur = variables_globales[nom].type_element().demande_valeur(&nom)?; + variables_globales.insert(nom.to_string(), valeur); + } + Commande::Modifie(nom, expression) => { + let valeur = match variables_globales[nom].type_element() { + TypeElement::Entier => Element::Entier(nombre::calcule_nombre(expression.clone(), &variables_globales)?), + TypeElement::Texte => Element::Texte(texte::calcule_texte(expression.clone(), &variables_globales)?), + TypeElement::Booleen => Element::Booleen(booleen::calcule_booleen(expression.clone(), &variables_globales)?), + }; + variables_globales.insert(nom.to_string(), valeur); + } + Commande::Affiche(expression) => { + println!("{}", texte::calcule_texte(expression.to_vec(), &variables_globales)?); + } + } + } + Ok(()) + } +} pub enum Commande { Definis(String, TypeElement), Demande(String), - Modifie(String, Expression), - Affiche(Expression), -} - -#[derive(PartialEq, Debug, Clone)] -pub struct Expression { - type_expression: TypeElement, - contenu: Vec -} - -impl Expression { - fn nouvelle(type_expression: TypeElement) -> Self { - Self { - type_expression, - contenu: vec![] - } - } - - fn avec_arguments(type_expression: TypeElement, arguments: &str) -> Result { - let expression = Self { - type_expression, - contenu: vec![] - } - - Ok(expression) - } - - fn ajoute(&mut self, element: Element) -> Result<(), ErreurPendragon> { - let type_element = element.type_element(); - if self.type_expression != type_element { - return Err(ErreurPendragon::MauvaisType("inconnue".into(), type_element.nom(), self.type_expression.nom())) - } - self.contenu.push(element); - Ok(()) - } + Modifie(String, Vec), + Affiche(Vec), } #[derive(PartialEq, Debug, Clone)] @@ -55,6 +102,47 @@ impl TypeElement { Self::Booleen => "booléen".into(), } } + + pub fn texte_comme_type(texte: &str) -> Result { + match texte.trim() { + "entier" => Ok(TypeElement::Entier), + "texte" => Ok(TypeElement::Texte), + "booléen" => Ok(TypeElement::Booleen), + autre => return Err(ErreurPendragon::MauvaisArgument(format!("type de variable \"{}\" inconnu", autre))), + } + } + + pub fn comme_element(&self) -> Element { + match self { + Self::Entier => Element::Entier(0), + Self::Texte => Element::Texte("".into()), + Self::Booleen => Element::Booleen(false), + } + } + + pub fn texte_comme_element(&self, texte: &str) -> Result { + match self { + Self::Entier => nombre::texte_comme_nombre(texte), + Self::Texte => Ok(Element::Texte(texte.into())), + Self::Booleen => booleen::texte_comme_booleen(texte), + } + } + + pub fn demande_valeur(&self, nom: &str) -> Result { + loop { + println!("Quelle valeur pour {} ({}) ?", nom, self.nom()); + + let mut reponse = String::new(); + if let Err(raison) = io::stdin().read_line(&mut reponse) { + return Err(ErreurPendragon::Lecture(format!("{}", raison))); + } + + match self.texte_comme_element(reponse.trim()) { + Ok(element) => return Ok(element), + Err(raison) => eprintln!("Erreur : {}", raison), + } + } + } } #[derive(PartialEq, Debug, Clone)] @@ -63,8 +151,7 @@ pub enum Element { Texte(String), Booleen(bool), Variable(String, TypeElement), - Operateur(String, TypeElement), - Expression(Expression, TypeElement), + Operateur(Operateur), } impl Element { @@ -74,42 +161,31 @@ impl Element { Self::Texte(_) => TypeElement::Texte, Self::Booleen(_) => TypeElement::Booleen, Self::Variable(_, type_element) => type_element.clone(), - Self::Operateur(_, type_element) => type_element.clone(), - Self::Expression(_, type_element) => type_element.clone(), + Self::Operateur(operateur) => operateur.type_element(), } } } -pub enum ErreurPendragon { - CommandeInconnue(String), - PhraseVide, - ManqueArgument, - NombreInvalide(String), - BooleenInvalide(String), - TexteInvalide(String), - MauvaisArgument(String), - DesequilibreParenthese, - VariableInconnue(String), - MauvaisType(String, String, String), - ProblemeTerminal(String), - ManquePoint, +#[derive(Clone, Debug, PartialEq)] +pub enum Operateur { + Ou, + Et, + Non, + ParentheseBooleen, + Virgule, + Plus, + Moins, + Fois, + Divise, + ParentheseEntier, } -impl fmt::Display for ErreurPendragon { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//' +impl Operateur { + pub fn type_element(&self) -> TypeElement { 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::NombreInvalide(nombre) => write!(f, "Le nombre \"{}\" est mal orthographié.", nombre), - Self::TexteInvalide(raison) => write!(f, "Le texte est invalide, {}.", raison), - Self::BooleenInvalide(booleen) => write!(f, "Le booleen \"{}\" est mal orthographié.", booleen), - 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(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), - Self::ManquePoint => write!(f, "Il manque un point."), + Self::Ou | Self::Et | Self::Non | Self::ParentheseBooleen => TypeElement::Booleen, + Self::Virgule => TypeElement::Texte, + Self::Plus | Self::Moins | Self::Fois | Self::Divise | Self::ParentheseEntier => TypeElement::Entier, } - } + } } \ No newline at end of file diff --git a/src/pendragon/texte.rs b/src/pendragon/texte.rs index e4efc02..334d87e 100644 --- a/src/pendragon/texte.rs +++ b/src/pendragon/texte.rs @@ -1,44 +1,78 @@ -use super::Pendragon; -use super::ErreurPendragon; -use super::Element; -use super::nombre; -use super::booleen; +use super::*; impl Pendragon { - 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(); + pub fn elements_texte(&self, arguments: &str) -> Result, ErreurPendragon> { + let mut expression: Vec = Vec::new(); + + for argument in arguments.split(',').map(|arg| arg.trim()) { + if expression.len() > 0 { + expression.push(Element::Operateur(Operateur::Virgule)); + } if argument.starts_with('"') { if argument.ends_with('"') { - texte += &argument[1..argument.len()-1]; + expression.push(Element::Texte(argument[1..argument.len() - 1].into())); } else { return Err(ErreurPendragon::TexteInvalide("guillemet mal refermé".into())) } continue; - } - - if let Ok(nombre) = self.operation(argument) { - texte += &nombre::nombre_comme_texte(nombre); + } + if format_de_variable(argument) && !argument.contains(" ") { + expression.push(Element::Variable(argument.into(), self.programme.variable(argument)?)); continue; } - - if let Ok(booleen) = self.condition(argument) { - texte += &booleen::booleen_comme_texte(booleen); - continue; + if let Ok(elements_nombre) = self.elements_nombre(argument) { + expression.extend(elements_nombre); + } else if let Ok(elements_booleen) = self.elements_booleen(argument) { + expression.extend(elements_booleen); + } else { + return Err(ErreurPendragon::MauvaisArgument(argument.to_string())); } - - let variable = self.recupere_variable(argument)?; - - let Element::Texte(contenu) = variable else { - return Err(ErreurPendragon::MauvaisType(argument.into(), variable.type_element().nom(), "texte".into())) - }; - - texte += &contenu; } - Ok(texte) + expression.push(Element::Operateur(Operateur::Virgule)); + Ok(expression) } +} + +pub fn calcule_texte(expression: Vec, variables: &HashMap) -> Result { + let mut pile: Vec = Vec::new(); + let mut texte: String = String::new(); + + for element in expression { + let Element::Operateur(ref operateur) = element else { + pile.push(element); + continue; + }; + let Operateur::Virgule = operateur else { + pile.push(element); + continue; + }; + let Some(element_pile) = pile.last() else { + continue; + }; + if let TypeElement::Booleen = element_pile.type_element() { + texte += &booleen::affiche_booleen(pile.clone(), variables)?; + continue; + } + if let TypeElement::Entier = element_pile.type_element() { + texte += &nombre::affiche_nombre(pile.clone(), variables)?; + continue; + } + match element_pile { + Element::Texte(contenu) => texte += contenu, + Element::Variable(nom, type_element) => { + let Some(variable) = variables.get(nom) else { + return Err(ErreurPendragon::VariableInconnue(nom.into())) + }; + let Element::Texte(contenu) = variable else { + return Err(ErreurPendragon::MauvaisType(nom.into(), type_element.nom(), "texte".into())) + }; + texte += &contenu; + } + autre => { + return Err(ErreurPendragon::MauvaisArgument(format!("{:?}", autre))) + } + } + pile = Vec::new(); + } + Ok(texte) } \ No newline at end of file diff --git a/test.dr b/test.dr index bcbd5c2..add0e8a 100644 --- a/test.dr +++ b/test.dr @@ -1,7 +1,9 @@ -Définis A comme entier. -Modifie A avec mille-cinq-cent-cinquante-cinq fois treize. -Affiche A. Définis B comme booléen. -Affiche "B ou : ", B ou vrai. -Affiche "B et : ", B et vrai. -Affiche dix fois dix fois dix. +Définis C comme booléen. +Définis D comme booléen. +Modifie B avec vrai et ouvre la parenthèse non C ou D ferme la parenthèse ou faux. +Affiche "B : ", B. +Définis E comme entier. +Modifie E avec mille-cinq-cent-cinquante-cinq fois ouvre la parenthèse un plus six ferme la parenthèse plus quarante-deux. +Affiche mille-cinq-cent-cinquante-cinq fois ouvre la parenthèse un plus six ferme la parenthèse plus quarante-deux. +Affiche "E : ", E fois dix. \ No newline at end of file