better structuure

This commit is contained in:
WanderingPenwing 2024-12-10 13:24:36 +01:00
parent 797b9991ea
commit 7ad8b8106b
8 changed files with 580 additions and 377 deletions

View file

@ -12,12 +12,25 @@ fn main() {
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);

View file

@ -1,79 +1,119 @@
use super::Pendragon;
use super::ErreurPendragon;
use super::Element;
use super::*;
impl Pendragon {
pub fn condition(&self, arguments: &str) -> Result<bool, ErreurPendragon> {
self.condition_elementaire(arguments)
}
pub fn elements_booleen(&self, arguments: &str) -> Result<Vec<Element>, 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<Element> = Vec::new();
let mut pile_operateurs: Vec<Operateur> = Vec::new();
pub fn condition_elementaire(&self, texte: &str) -> Result<bool, ErreurPendragon> {
let mut expression: Vec<String> = texte.split(" ").map(String::from).collect();
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);
}
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<bool, ErreurPendragon> {
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);
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 {
return Err(ErreurPendragon::VariableInconnue(texte.to_string()))
break;
}
}
texte_comme_booleen(texte)
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));
}
}
}
while let Some(operateur) = pile_operateurs.pop() {
expression.push(Element::Operateur(operateur));
}
Ok(expression)
}
}
pub fn affiche_booleen(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<String, ErreurPendragon> {
let booleen = calcule_booleen(expression.clone(), variables)?;
Ok(booleen_comme_texte(booleen))
}
pub fn calcule_booleen(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<bool, ErreurPendragon> {
let mut pile: Vec<bool> = 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::MauvaisType(nom.into(), variable.type_element().nom(), "booleen".into()))
}
}
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<bool, ErreurPendragon> {
pub fn texte_comme_booleen(texte: &str) -> Result<Element, ErreurPendragon> {
match texte {
"vrai" => Ok(true),
"faux" => Ok(false),
"vrai" => Ok(Element::Booleen(true)),
"faux" => Ok(Element::Booleen(false)),
_ => Err(ErreurPendragon::BooleenInvalide(texte.into())),
}
}

57
src/pendragon/debug.rs Normal file
View file

@ -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)
}
}

View file

@ -1,4 +1,3 @@
use std::io;
use std::collections::HashMap;
pub mod nombre;
@ -6,149 +5,93 @@ 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<String, Element>,
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<Vec<Commande>, 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<Commande> = 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<Commande, ErreurPendragon> {
fn compile_phrase(&mut self, phrase: &str) -> Result<Commande, ErreurPendragon> {
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<Commande, ErreurPendragon> {
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<Commande, ErreurPendragon> {
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<Commande, ErreurPendragon> {
let (variable_nom, contenu) = self.nom_de_variable(arguments, "avec")?;
let variable = self.recupere_variable(&variable_nom)?;
let (variable_nom, contenu) = nom_de_variable(arguments, "avec")?;
let variable = self.programme.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)?),
let elements = match variable {
TypeElement::Entier => self.elements_nombre(&contenu)?,
TypeElement::Texte => self.elements_texte(&contenu)?,
TypeElement::Booleen => self.elements_booleen(&contenu)?,
};
self.variables.insert(variable_nom, valeur);
let commande = Modifie(variable_nom, Expression::avec_arguments(variable.type_element(), arguments)?),
Ok(commande)
Ok(Commande::Modifie(variable_nom, elements))
}
fn demande(&mut self, arguments: &str) -> Result<Commande, ErreurPendragon> {
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()))
Ok(Commande::Demande(variable_nom.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<Element, ErreurPendragon> {
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> {
fn nom_de_variable(arguments: &str, separateur: &str) -> Result<(String, String), ErreurPendragon> {
let parties = if separateur == "" {
vec![arguments, ""]
} else {
@ -161,5 +104,14 @@ impl Pendragon {
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
}

View file

@ -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<usize, ErreurPendragon> {
//return self.operation_elementaire(arguments);
pub fn elements_nombre(&self, arguments: &str) -> Result<Vec<Element>, 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<String> = texte.split(" ").map(String::from).collect();
let elements_texte: Vec<&str> = texte.split(" ").collect();
let mut expression: Vec<Element> = Vec::new();
let mut pile_operateurs: Vec<Operateur> = Vec::new();
while expression.contains(&"ouvre-la-parenthese".to_string()) {
let mut ouverture: Option<usize> = None;
let mut fermeture: Option<usize> = 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);
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;
}
}
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);
pile_operateurs.push(Operateur::Plus);
}
}
if expression.contains(&"ferme-la-parenthese".to_string()) {
return Err(ErreurPendragon::DesequilibreParenthese);
}
self.operation_elementaire(&expression.join(" "))
}
pub fn operation_elementaire(&self, arguments: &str) -> Result<usize, ErreurPendragon> {
let texte = arguments.replace("divisé par", "divise-par");
let mut expression: Vec<String> = 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(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);
}
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<usize, ErreurPendragon> {
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);
"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 {
return Err(ErreurPendragon::VariableInconnue(texte.to_string()))
break;
}
}
texte_comme_nombre(texte)
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 let Some(operateur) = pile_operateurs.pop() {
expression.push(Element::Operateur(operateur));
}
Ok(expression)
}
}
pub fn affiche_nombre(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<String, ErreurPendragon> {
let nombre = calcule_nombre(expression.clone(), variables)?;
Ok(nombre_comme_texte(nombre))
}
pub fn calcule_nombre(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<usize, ErreurPendragon> {
let mut pile: Vec<usize> = Vec::new();
for element in expression {
if let Element::Entier(nombre) = element {
pile.push(nombre);
continue;
}
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::MauvaisType(nom.into(), variable.type_element().nom(), "entier".into()))
}
}
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<usize, ErreurPendragon> {
pub fn texte_comme_nombre(texte: &str) -> Result<Element, ErreurPendragon> {
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<usize, ErreurPendragon> {
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<usize, ErreurPendragon> {

View file

@ -1,43 +1,90 @@
use std::fmt;
use std::io;
use std::collections::HashMap;
use super::*;
pub struct Programme {
pub variables: HashMap<String, TypeElement>,
pub commandes: Vec<Commande>,
}
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<TypeElement, ErreurPendragon> {
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<String, Element> = 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<Element>
}
impl Expression {
fn nouvelle(type_expression: TypeElement) -> Self {
Self {
type_expression,
contenu: vec![]
}
}
fn avec_arguments(type_expression: TypeElement, arguments: &str) -> Result<Self, ErreurPendragon> {
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<Element>),
Affiche(Vec<Element>),
}
#[derive(PartialEq, Debug, Clone)]
@ -55,6 +102,47 @@ impl TypeElement {
Self::Booleen => "booléen".into(),
}
}
pub fn texte_comme_type(texte: &str) -> Result<Self, ErreurPendragon> {
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<Element, ErreurPendragon> {
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<Element, ErreurPendragon> {
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,
}
}
}

View file

@ -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<String, ErreurPendragon> {
let liste_arguments: Vec<&str> = arguments.split(',').collect();
pub fn elements_texte(&self, arguments: &str) -> Result<Vec<Element>, ErreurPendragon> {
let mut expression: Vec<Element> = Vec::new();
let mut texte = "".to_string();
for argument in liste_arguments {
let argument: &str = argument.trim();
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<Element>, variables: &HashMap<String, Element>) -> Result<String, ErreurPendragon> {
let mut pile: Vec<Element> = 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)
}

14
test.dr
View file

@ -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.