changed file structure

This commit is contained in:
WanderingPenwing 2024-12-14 13:34:46 +01:00
parent fb79b520d4
commit bbb817854a
14 changed files with 760 additions and 725 deletions

View file

@ -1 +1 @@
{"categories":[{"name":"todo","content":[{"name":"verifie si bloc pas vide","description":"// Hello there","id":2},{"name":"scope variable","description":"// Hello there","id":6},{"name":"while","description":"implémente un while","id":2},{"name":"else","description":"// Hello there","id":7},{"name":"break","description":"// Hello there","id":8},{"name":"continue","description":"// Hello there","id":9},{"name":"compilation","description":"// Hello there","id":3}]},{"name":"in progress","content":[]},{"name":"done","content":[{"name":"comparaison","description":"// Hello there","id":2},{"name":"booleen type","description":"// Hello there","id":4},{"name":"soixante-dix","description":"// Hello there","id":1},{"name":"parenthese comparaison","description":"// Hello there","id":1},{"name":"test comparaison","description":"// Hello there","id":4},{"name":"error ligne compilation","description":"// Hello there","id":2},{"name":"commentaires","description":"// Hello there","id":3},{"name":"compile time verification","description":"odre des termes rpn","id":1},{"name":"test multiple space in texte","description":"// Hello there","id":3},{"name":"test puis in texte","description":"// Hello there","id":2},{"name":"enchainement puis","description":"// Hello there","id":5},{"name":"guillemet mal ferme","description":"// Hello there","id":4},{"name":"display element","description":"for better print","id":6},{"name":"puis à la ligne, alinéa","description":"// Hello there","id":1},{"name":"teste double tiret","description":"// Hello there","id":2},{"name":"teste puis seul","description":"tout seul, seul devant, seul fin","id":3},{"name":"erreur calcul bool/nombre","description":"affiche element precedent lorsque mauvais enchainement","id":1},{"name":"multiple compilation error","description":"// Hello there","id":1},{"name":"tests mod","description":"// Hello there","id":3},{"name":"if","description":"implémente if avec des guillemets de délimitation","id":1}]},{"name":"bug","content":[]},{"name":"to test","content":[{"name":"test variable","description":"// Hello there","id":2},{"name":"teste if","description":"// Hello there","id":3}]},{"name":"bonus","content":[{"name":"stop cheating with \"-\"","description":"// Hello there","id":5},{"name":"affiche ligne et position erreur","description":"// Hello there","id":1},{"name":"pour numero de ligne execution","description":"sauvegarde numero de ligne dans la commande\n\ncommande.ligne(12)","id":3},{"name":"standardizer erreur","description":"regarder les texte répétés","id":1}]},{"name":"+","content":[]}]}
{"categories":[{"name":"todo","content":[{"name":"verifie si bloc pas vide","description":"// Hello there","id":2},{"name":"scope variable","description":"// Hello there","id":6},{"name":"while","description":"implémente un while","id":2},{"name":"else","description":"// Hello there","id":7},{"name":"break","description":"// Hello there","id":8},{"name":"continue","description":"// Hello there","id":9},{"name":"compilation","description":"// Hello there","id":3},{"name":"separe interpretation","description":"// Hello there","id":1},{"name":"programme not in pendragon self ?","description":"// Hello there","id":2}]},{"name":"in progress","content":[]},{"name":"done","content":[{"name":"comparaison","description":"// Hello there","id":2},{"name":"booleen type","description":"// Hello there","id":4},{"name":"soixante-dix","description":"// Hello there","id":1},{"name":"parenthese comparaison","description":"// Hello there","id":1},{"name":"test comparaison","description":"// Hello there","id":4},{"name":"error ligne compilation","description":"// Hello there","id":2},{"name":"commentaires","description":"// Hello there","id":3},{"name":"compile time verification","description":"odre des termes rpn","id":1},{"name":"test multiple space in texte","description":"// Hello there","id":3},{"name":"test puis in texte","description":"// Hello there","id":2},{"name":"enchainement puis","description":"// Hello there","id":5},{"name":"guillemet mal ferme","description":"// Hello there","id":4},{"name":"display element","description":"for better print","id":6},{"name":"puis à la ligne, alinéa","description":"// Hello there","id":1},{"name":"teste double tiret","description":"// Hello there","id":2},{"name":"teste puis seul","description":"tout seul, seul devant, seul fin","id":3},{"name":"erreur calcul bool/nombre","description":"affiche element precedent lorsque mauvais enchainement","id":1},{"name":"multiple compilation error","description":"// Hello there","id":1},{"name":"tests mod","description":"// Hello there","id":3},{"name":"if","description":"implémente if avec des guillemets de délimitation","id":1}]},{"name":"bug","content":[]},{"name":"to test","content":[{"name":"test variable","description":"// Hello there","id":2},{"name":"teste if","description":"// Hello there","id":3}]},{"name":"bonus","content":[{"name":"stop cheating with \"-\"","description":"// Hello there","id":5},{"name":"affiche ligne et position erreur","description":"// Hello there","id":1},{"name":"pour numero de ligne execution","description":"sauvegarde numero de ligne dans la commande\n\ncommande.ligne(12)","id":3},{"name":"standardizer erreur","description":"regarder les texte répétés","id":1}]},{"name":"+","content":[]}]}

View file

@ -1,6 +1,7 @@
use std::fmt;
use std::time::Duration;
use super::*;
use crate::pendragon::structure::*;
use crate::sophie;
pub const TEXTE_ROUGE: &str = "\x1b[31m";
pub const TEXTE_VERT: &str = "\x1b[32m";
@ -130,9 +131,9 @@ impl fmt::Display for Commande {
impl fmt::Display for Element {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Entier(nombre) => write!(f, "{}", nombre::nombre_comme_texte(*nombre)),
Self::Entier(nombre) => write!(f, "{}", sophie::nombre::nombre_comme_texte(*nombre)),
Self::Texte(texte) => write!(f, "\"{}\"", texte),
Self::Booleen(booleen) => write!(f, "{}", booleen::booleen_comme_texte(*booleen)),
Self::Booleen(booleen) => write!(f, "{}", sophie::booleen::booleen_comme_texte(*booleen)),
Self::Variable(nom, type_variable) => write!(f, "{}:{}", nom, type_variable.nom()),
Self::Operateur(operateur) => write!(f, "{}", operateur),
Self::Comparaison(comparaison) => write!(f, "{}.", comparaison),

5
src/debug/mod.rs Normal file
View file

@ -0,0 +1,5 @@
pub mod display;
#[cfg(test)]
mod test;

318
src/debug/test.rs Normal file
View file

@ -0,0 +1,318 @@
use std::collections::HashMap;
use crate::sophie;
use crate::pendragon;
use crate::display::*;
use crate::structure::*;
#[test]
fn calcul_texte() {
let pendragon = pendragon::Pendragon::nouveau();
let a = 2345678;
let b = 987654;
let possible_expression = pendragon.elements_texte(&format!("\"hello\" puis {} fois {} puis \"there\" puis vrai ou faux puis trois puis deux puis alinéa puis retour à la ligne",
sophie::nombre::nombre_comme_texte(a),
sophie::nombre::nombre_comme_texte(b)));
match possible_expression {
Ok(expression) => {
match sophie::texte::calcule_texte(expression, &HashMap::new()) {
Ok(texte) => {
let vrai_texte = format!("hello{}therevraitroisdeux\t\n", sophie::nombre::nombre_comme_texte(a*b));
assert_eq!(texte, vrai_texte, "Calcul d'expression (texte) donne un mauvais résultat : {}", texte);
}
Err(raison) => {
panic!("Calcul d'expression (texte) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (texte) échouée : {}", raison);
}
}
}
#[test]
fn conversion_texte() {
let pendragon = pendragon::Pendragon::nouveau();
let texte = "\"hello aaaa puis AERTY et ou fois six\"";
match pendragon.elements_texte(texte) {
Ok(expression) => {
if expression.len() != 2 {
panic!("L'expression (texte) devrait contenir deux éléments (texte et puis), contient : {:?}", expression);
}
assert_eq!(expression[0], Element::Texte(texte[1..texte.len()-1].into()), "Calcul d'expression (texte) donne un mauvais résultat : {}", texte);
}
Err(raison) => {
panic!("Conversion échouée (texte) : {}", raison);
}
}
}
#[test]
fn erreur_conversion_texte() {
let pendragon = pendragon::Pendragon::nouveau();
let textes = vec![
"trois puis puis un",
"\" test",
"puis",
"un puis",
"puis un",
];
for texte in textes {
let Err(raison) = pendragon.elements_texte(texte) else {
panic!("Ne devrait pas réussir à convertir le texte '{}'", texte);
};
let ErreurPendragon::TexteInvalide(_) = raison else {
panic!("Erreur imprévue pour convertir le texte '{}' : {}", texte, raison);
};
}
}
//----------------------------
#[test]
fn conversion_booleen_texte() {
for b in [true, false].iter() {
let texte = sophie::booleen::booleen_comme_texte(*b); // Convert number to text
match pendragon::booleen::texte_comme_booleen(&texte) { // Convert text back to number
Ok(booleen) => {
assert_eq!(Element::Booleen(*b), booleen, "Booleen inexact : {}, texte : {}", b, texte);
}
Err(raison) => {
panic!("Conversion échouée pour : {}, avec l'erreur : {}", b, raison);
}
}
}
}
#[test]
fn calcul_booleen() {
let pendragon = pendragon::Pendragon::nouveau();
let mut configurations = Vec::new();
for b1 in [true, false] {
for b2 in [true, false] {
for b3 in [true, false] {
for b4 in [true, false] {
for b5 in [true, false] {
configurations.push((b1, b2, b3, b4, b5));
}
}
}
}
}
for configuration in configurations {
let possible_expression = pendragon.elements_booleen(&format!("{} et non ouvre la parenthèse {} ou non {} ferme la parenthèse ou non {} et {}",
sophie::booleen::booleen_comme_texte(configuration.0),
sophie::booleen::booleen_comme_texte(configuration.1),
sophie::booleen::booleen_comme_texte(configuration.2),
sophie::booleen::booleen_comme_texte(configuration.3),
sophie::booleen::booleen_comme_texte(configuration.4)));
match possible_expression {
Ok(expression) => {
match sophie::booleen::calcule_booleen(expression, &HashMap::new()) {
Ok(booleen) => {
let resultat = configuration.0 && !(configuration.1 || !configuration.2) || !configuration.3 && configuration.4;
assert_eq!(booleen, resultat, "Calcul d'expression (booleen) donne un mauvais résultat : {}", booleen);
}
Err(raison) => {
panic!("Calcul d'expression (booleen) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (booleen) échouée : {}", raison);
}
}
}
}
#[test]
fn comparaison_booleen() {
let pendragon = pendragon::Pendragon::nouveau();
for (a, b) in [(1, 4), (2, 2), (3, 1), (0, 3)] {
let possible_expressions = vec![
pendragon.elements_booleen(&format!("non six plus {} est supérieur à deux fois {}", sophie::nombre::nombre_comme_texte(a), sophie::nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("six plus {} est inférieur à deux fois {}", sophie::nombre::nombre_comme_texte(a), sophie::nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("six plus {} est supérieur ou égal à deux fois {}", sophie::nombre::nombre_comme_texte(a), sophie::nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("non six plus {} est inférieur ou égal à deux fois {}", sophie::nombre::nombre_comme_texte(a), sophie::nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("non \"deux\" est égal à \"{}\"", sophie::nombre::nombre_comme_texte(a))),
pendragon.elements_booleen(&format!("\"trois\" est différent de \"{}\"", sophie::nombre::nombre_comme_texte(a))),
];
let bonne_reponses = vec![
!(6+a > 2*b),
(6+a < 2*b),
(6+a >= 2*b),
!(6+a <= 2*b),
!(a == 2),
(a != 3),
];
for index in 0..possible_expressions.len() {
match &possible_expressions[index] {
Ok(expression) => {
match sophie::booleen::calcule_booleen(expression.clone(), &HashMap::new()) {
Ok(booleen) => {
let reponse = bonne_reponses[index];
assert_eq!(booleen, reponse, "Calcul d'expression (booleen) n°{} ({},{}) donne un mauvais résultat : {}, attendais {}", index, a, b, booleen, reponse);
}
Err(raison) => {
panic!("Calcul d'expression (booleen) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (booleen) échouée : {}", raison);
}
}
}
}
}
#[test]
fn combinaison_booleen() {
let pendragon = pendragon::Pendragon::nouveau();
for a in 0..5 {
for b in 0..5 {
for c in 0..5 {
for d in 1..5 {
for e in 0..5 {
let possible_expression = pendragon.elements_booleen(&format!("non ouvre la parenthèse six plus {} ferme la parenthèse est supérieur à deux fois {} et ouvre la parenthèse {} divisé par deux est inférieur à ouvre la parenthèse {} moins un ferme la parenthèse ou non \"deux\" est égal à \"{}\" ferme la parenthèse",
sophie::nombre::nombre_comme_texte(a),
sophie::nombre::nombre_comme_texte(b),
sophie::nombre::nombre_comme_texte(c),
sophie::nombre::nombre_comme_texte(d),
sophie::nombre::nombre_comme_texte(e),
));
let bonne_reponse = !((6+a) > 2*b) && (c/2 < (d-1) || !(e == 2));
match possible_expression {
Ok(expression) => {
match sophie::booleen::calcule_booleen(expression.clone(), &HashMap::new()) {
Ok(booleen) => {
assert_eq!(booleen, bonne_reponse, "Calcul d'expression (booleen) ({},{},{},{},{}) donne un mauvais résultat : {}, attendais {}", a, b, c, d, e, booleen, bonne_reponse);
}
Err(raison) => {
panic!("Calcul d'expression (booleen) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (booleen) échouée : {}", raison);
}
}
}
}
}
}
}
}
#[test]
fn erreur_calcul_booleen() {
let pendragon = pendragon::Pendragon::nouveau();
let textes_invalide = vec![
"et faux",
"vrai et et faux",
"vrai ou ou faux",
"vrai et vrai faux",
"vrai et faux vrai",
"vrai et faux ouvre la parenthèse vrai ou faux ferme la parenthèse",
"vrai et ouvre la parenthèse et vrai ou faux ferme la parenthèse",
"vrai et ouvre la parenthèse vrai ou faux et ferme la parenthèse",
"vrai et ouvre la parenthèse vrai ou faux ferme la parenthèse vrai",
];
for texte in textes_invalide {
let Err(raison) = pendragon.elements_booleen(texte) else {
panic!("Devrait détecter une erreur pour '{}'", texte);
};
let ErreurPendragon::OrdreCalculBooleen(_,_,_) = raison else {
panic!("Devrait détecter une erreur de calcul booléen pour '{}', a déclenché : {}", texte, raison);
};
}
}
// ---------------------
#[test]
fn conversion_nombres_texte() {
for i in [0, 1, 42, 70, 123, 999, 1031, 1_001_091, 72_036_854_775_807usize, 2345678*987654].iter() {
let texte = sophie::nombre::nombre_comme_texte(*i); // Convert number to text
if texte.contains("--") {
panic!("Il y a deux tirets pour {} : {}", i, texte);
}
match pendragon::nombre::texte_comme_nombre(&texte) { // Convert text back to number
Ok(nombre) => {
assert_eq!(Element::Entier(*i), nombre, "Nombre inexact : {}, texte : {}", i, texte);
}
Err(raison) => {
panic!("Conversion échouée pour : {}, avec l'erreur : {}", i, raison);
}
}
}
}
#[test]
fn calcul_nombre() {
let pendragon = pendragon::Pendragon::nouveau();
let a = 2345678;
let b = 987654;
let c = 34523456;
let d = 45678;
let e = 2;
let possible_expression = pendragon.elements_nombre(&format!("{} fois {} plus ouvre la parenthèse {} moins {} ferme la parenthèse divisé par {}",
sophie::nombre::nombre_comme_texte(a),
sophie::nombre::nombre_comme_texte(b),
sophie::nombre::nombre_comme_texte(c),
sophie::nombre::nombre_comme_texte(d),
sophie::nombre::nombre_comme_texte(e)));
match possible_expression {
Ok(expression) => {
match sophie::nombre::calcule_nombre(expression, &HashMap::new()) {
Ok(nombre) => {
assert_eq!(nombre, a*b+(c-d)/e, "Calcul d'expression (entier) donne un mauvais résultat : {}", nombre);
}
Err(raison) => {
panic!("Calcul d'expression (entier) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (entier) échouée : {}", raison);
}
}
}
#[test]
fn erreur_calcul_nombre() {
let pendragon = pendragon::Pendragon::nouveau();
let textes_invalide = vec![
"un un fois un",
"un plus fois un",
"un moins divisé par un",
"un fois un ouvre la parenthèse un plus un ferme la parenthèse",
"un fois ouvre la parenthèse plus un plus un ferme la parenthèse",
"un fois ouvre la parenthèse un plus un fois ferme la parenthèse",
"un fois ouvre la parenthèse un plus un ferme la parenthèse un",
];
for texte in textes_invalide {
let Err(raison) = pendragon.elements_nombre(texte) else {
panic!("Devrait détecter une erreur pour '{}'", texte);
};
let ErreurPendragon::OrdreCalculEntier(_,_,_) = raison else {
panic!("Devrait détecter une erreur de calcul entier pour '{}', a déclenché : {}", texte, raison);
};
}
}
#[test]
fn nombre_invalide_et() {
let pendragon = pendragon::Pendragon::nouveau();
let Err(raison) = pendragon.elements_nombre("et") else {
panic!("Devrait détecter une erreur pour 'et'");
};
let ErreurPendragon::NombreInvalide(_) = raison else {
panic!("Devrait détecter une erreur de nombre invalide pour 'et', a déclenché : {}", raison);
};
}

View file

@ -4,6 +4,9 @@ use std::time::Instant;
mod pendragon;
use pendragon::*;
mod sophie;
mod debug;
use debug::display;
fn main() {
let arguments: Vec<String> = env::args().collect();
@ -21,32 +24,32 @@ fn main() {
let lecture = fs::read_to_string(chemin_de_fichier);
if let Err(raison) = lecture {
eprintln!("{}Fichier illisible :{} {}", debug::TEXTE_ROUGE, raison, debug::TEXTE_NORMAL);
eprintln!("{}Fichier illisible :{} {}", display::TEXTE_ROUGE, raison, display::TEXTE_NORMAL);
return
}
debug::message_compilation(chemin_de_fichier);
display::message_compilation(chemin_de_fichier);
let debut = Instant::now();
if let Err(raison) = pendragon.compile(lecture.unwrap()) {
for erreur in raison {
eprintln!("\n{}", erreur);
}
debug::message_compilation_echec();
display::message_compilation_echec();
return
}
debug::message_compilation_ok(debut.elapsed());
display::message_compilation_ok(debut.elapsed());
if debug_mode {
println!("\n{}\n", pendragon.programme);
}
debug::message_execution(chemin_de_fichier);
display::message_execution(chemin_de_fichier);
let debut = Instant::now();
if let Err(raison) = pendragon.programme.execute() {
eprintln!("\nErreur : {}", raison);
debug::message_execution_echec();
display::message_execution_echec();
return
}
debug::message_execution_ok(debut.elapsed());
display::message_execution_ok(debut.elapsed());
}

View file

@ -213,73 +213,6 @@ fn compare_parentheses(strings: &Vec<String>) -> (usize, usize) {
(ouvre_count, ferme_count)
}
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()))
}
}
if let Element::Comparaison(comparaison) = element {
pile.push(comparaison.calcule(variables)?);
continue
}
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("il reste plusieurs éléments dans la pile".into()))
}
Ok(pile[0])
}
pub fn booleen_comme_texte(booleen: bool) -> String {
if booleen {
"vrai".into()
} else {
"faux".into()
}
}
pub fn texte_comme_booleen(texte: &str) -> Result<Element, ErreurPendragon> {
match texte {
"vrai" => Ok(Element::Booleen(true)),
@ -300,173 +233,3 @@ pub fn texte_comme_comparaison(texte: &str) -> Result<TypeComparaison, ErreurPen
}
}
// -----------------------------------------------------------------------
#[cfg(test)]
mod test {
use std::collections::HashMap;
use super::*;
#[test]
fn conversion_booleen_texte() {
for b in [true, false].iter() {
let texte = booleen_comme_texte(*b); // Convert number to text
match texte_comme_booleen(&texte) { // Convert text back to number
Ok(booleen) => {
assert_eq!(Element::Booleen(*b), booleen, "Booleen inexact : {}, texte : {}", b, texte);
}
Err(raison) => {
panic!("Conversion échouée pour : {}, avec l'erreur : {}", b, raison);
}
}
}
}
#[test]
fn calcul_booleen() {
let pendragon = Pendragon::nouveau();
let mut configurations = Vec::new();
for b1 in [true, false] {
for b2 in [true, false] {
for b3 in [true, false] {
for b4 in [true, false] {
for b5 in [true, false] {
configurations.push((b1, b2, b3, b4, b5));
}
}
}
}
}
for configuration in configurations {
let possible_expression = pendragon.elements_booleen(&format!("{} et non ouvre la parenthèse {} ou non {} ferme la parenthèse ou non {} et {}",
booleen_comme_texte(configuration.0),
booleen_comme_texte(configuration.1),
booleen_comme_texte(configuration.2),
booleen_comme_texte(configuration.3),
booleen_comme_texte(configuration.4)));
match possible_expression {
Ok(expression) => {
match calcule_booleen(expression, &HashMap::new()) {
Ok(booleen) => {
let resultat = configuration.0 && !(configuration.1 || !configuration.2) || !configuration.3 && configuration.4;
assert_eq!(booleen, resultat, "Calcul d'expression (booleen) donne un mauvais résultat : {}", booleen);
}
Err(raison) => {
panic!("Calcul d'expression (booleen) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (booleen) échouée : {}", raison);
}
}
}
}
#[test]
fn comparaison_booleen() {
let pendragon = Pendragon::nouveau();
for (a, b) in [(1, 4), (2, 2), (3, 1), (0, 3)] {
let possible_expressions = vec![
pendragon.elements_booleen(&format!("non six plus {} est supérieur à deux fois {}", nombre::nombre_comme_texte(a), nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("six plus {} est inférieur à deux fois {}", nombre::nombre_comme_texte(a), nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("six plus {} est supérieur ou égal à deux fois {}", nombre::nombre_comme_texte(a), nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("non six plus {} est inférieur ou égal à deux fois {}", nombre::nombre_comme_texte(a), nombre::nombre_comme_texte(b))),
pendragon.elements_booleen(&format!("non \"deux\" est égal à \"{}\"", nombre::nombre_comme_texte(a))),
pendragon.elements_booleen(&format!("\"trois\" est différent de \"{}\"", nombre::nombre_comme_texte(a))),
];
let bonne_reponses = vec![
!(6+a > 2*b),
(6+a < 2*b),
(6+a >= 2*b),
!(6+a <= 2*b),
!(a == 2),
(a != 3),
];
for index in 0..possible_expressions.len() {
match &possible_expressions[index] {
Ok(expression) => {
match calcule_booleen(expression.clone(), &HashMap::new()) {
Ok(booleen) => {
let reponse = bonne_reponses[index];
assert_eq!(booleen, reponse, "Calcul d'expression (booleen) n°{} ({},{}) donne un mauvais résultat : {}, attendais {}", index, a, b, booleen, reponse);
}
Err(raison) => {
panic!("Calcul d'expression (booleen) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (booleen) échouée : {}", raison);
}
}
}
}
}
#[test]
fn combinaison_booleen() {
let pendragon = Pendragon::nouveau();
for a in 0..5 {
for b in 0..5 {
for c in 0..5 {
for d in 1..5 {
for e in 0..5 {
let possible_expression = pendragon.elements_booleen(&format!("non ouvre la parenthèse six plus {} ferme la parenthèse est supérieur à deux fois {} et ouvre la parenthèse {} divisé par deux est inférieur à ouvre la parenthèse {} moins un ferme la parenthèse ou non \"deux\" est égal à \"{}\" ferme la parenthèse",
nombre::nombre_comme_texte(a),
nombre::nombre_comme_texte(b),
nombre::nombre_comme_texte(c),
nombre::nombre_comme_texte(d),
nombre::nombre_comme_texte(e),
));
let bonne_reponse = !((6+a) > 2*b) && (c/2 < (d-1) || !(e == 2));
match possible_expression {
Ok(expression) => {
match calcule_booleen(expression.clone(), &HashMap::new()) {
Ok(booleen) => {
assert_eq!(booleen, bonne_reponse, "Calcul d'expression (booleen) ({},{},{},{},{}) donne un mauvais résultat : {}, attendais {}", a, b, c, d, e, booleen, bonne_reponse);
}
Err(raison) => {
panic!("Calcul d'expression (booleen) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (booleen) échouée : {}", raison);
}
}
}
}
}
}
}
}
#[test]
fn erreur_calcul_booleen() {
let pendragon = Pendragon::nouveau();
let textes_invalide = vec![
"et faux",
"vrai et et faux",
"vrai ou ou faux",
"vrai et vrai faux",
"vrai et faux vrai",
"vrai et faux ouvre la parenthèse vrai ou faux ferme la parenthèse",
"vrai et ouvre la parenthèse et vrai ou faux ferme la parenthèse",
"vrai et ouvre la parenthèse vrai ou faux et ferme la parenthèse",
"vrai et ouvre la parenthèse vrai ou faux ferme la parenthèse vrai",
];
for texte in textes_invalide {
let Err(raison) = pendragon.elements_booleen(texte) else {
panic!("Devrait détecter une erreur pour '{}'", texte);
};
let ErreurPendragon::OrdreCalculBooleen(_,_,_) = raison else {
panic!("Devrait détecter une erreur de calcul booléen pour '{}', a déclenché : {}", texte, raison);
};
}
}
}

View file

@ -1,12 +1,11 @@
use std::collections::HashMap;
use crate::display::ErreurPendragon;
use crate::display::ErreurCompilation;
pub mod nombre;
pub mod texte;
pub mod booleen;
pub mod structure;
use structure::*;
pub mod debug;
use debug::*;
pub struct Pendragon {
pub programme: Programme,
@ -72,7 +71,7 @@ impl Pendragon {
}
Err(raison) => {
erreurs.push(ErreurCompilation::nouvelle(index_ligne, ligne.into(), raison));
pile_bloc.push(Bloc::nouveau(vec![Element::Booleen(false)]));
pile_bloc.push(Bloc::nouveau(vec![Element::Booleen(false)], false));
}
}
indentation_niveau += 1;
@ -113,7 +112,7 @@ impl Pendragon {
}
match parties[0] {
"Si" => Ok(Bloc::nouveau(self.elements_booleen(parties[1])?)),
"Si" => Ok(Bloc::nouveau(self.elements_booleen(parties[1])?, false)),
autre => Err(ErreurPendragon::BlocInconnu(autre.into())),
}
}

View file

@ -1,10 +1,10 @@
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"];
const NOMS_DIZAINES: [&str; 9] = ["", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "x", "quatre-vingts"];
const NOMS_SEPARATEURS: [&str; 7] = ["", "mille", "million", "milliard", "billion", "billiard", "trillion"];
const UNION: &str = "-";
pub const NOMS_UNITES: [&str; 10] = ["", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf"];
pub const NOMS_UNITES_DIX: [&str; 10] = ["dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf"];
pub const NOMS_DIZAINES: [&str; 9] = ["", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "x", "quatre-vingts"];
pub const NOMS_SEPARATEURS: [&str; 7] = ["", "mille", "million", "milliard", "billion", "billiard", "trillion"];
pub const UNION: &str = "-";
impl Pendragon {
pub fn elements_nombre(&self, arguments: &str) -> Result<Vec<Element>, ErreurPendragon> {
@ -112,143 +112,6 @@ impl Pendragon {
}
}
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_a);
}
Operateur::Moins => {
if nombre_b < nombre_a {
return Err(ErreurPendragon::CalculEntier(format!("a essayé de soustraire '{}' à '{}'", nombre::nombre_comme_texte(nombre_a), nombre::nombre_comme_texte(nombre_b))))
}
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 {
if nombre == 0 {
return "zéro".to_string()
}
if nombre >= 10usize.pow(18) {
return "infini".to_string()
}
let mut groupes: Vec<usize> = vec![];
let mut nombre = nombre;
while nombre > 0 {
groupes.insert(0, nombre % 1000);
nombre /= 1000;
}
let mut chaine: String = "".to_string();
for index in 0..groupes.len() {
if groupes[index] == 0 {
continue
}
let pluriel: &str = if (groupes.len() - index - 1 > 1) && groupes[index] > 1 {
"s"
} else {
""
};
if index < groupes.len() - 1 {
let union = if index > 0 {UNION} else {""};
let chiffre = if groupes.len() - index - 1 == 1 && groupes[index] == 1 { // un mille
"".to_string()
} else {
petit_nombre_comme_texte(groupes[index]) + UNION
};
chaine += &format!("{}{}{}{}",
union,
chiffre,
NOMS_SEPARATEURS[groupes.len() - index - 1],
pluriel,
);
} else {
let union = if index > 0 {UNION} else {""};
chaine += union;
chaine += &petit_nombre_comme_texte(groupes[index]);
}
}
chaine
}
fn petit_nombre_comme_texte(nombre: usize) -> String {
let nombre = nombre.clamp(0, 999);
let centaine = nombre / 100;
let dizaine = (nombre % 100) / 10;
let unité = nombre % 10;
let centaine_texte = if centaine > 1 {
format!("{}{}cent", NOMS_UNITES[centaine], UNION)
} else if centaine > 0 {
"cent".to_string()
} else {
"".to_string()
};
let décalage_dizaine = if [1, 7, 9].contains(&dizaine) {1} else {0};
let dizaine_texte = NOMS_DIZAINES[dizaine - décalage_dizaine];
let séparation = if unité == 1 && ![0, 1, 8, 9].contains(&dizaine) {UNION.to_string() + "et"} else {"".to_string()};
let unité_texte = if [1, 7, 9].contains(&dizaine) {NOMS_UNITES_DIX[unité]} else {NOMS_UNITES[unité]};
let mut texte_nombre = format!("{}{}{}{}{}{}", centaine_texte, UNION, dizaine_texte, séparation, UNION, unité_texte);
while texte_nombre.contains("--") {
texte_nombre = texte_nombre.replace("--","-");
}
if texte_nombre.starts_with("-") {
texte_nombre = texte_nombre[1..texte_nombre.len()].to_string();
}
if texte_nombre.ends_with("-") {
texte_nombre = texte_nombre[0..texte_nombre.len()-1].to_string();
}
texte_nombre
}
pub fn texte_comme_nombre(texte: &str) -> Result<Element, ErreurPendragon> {
if texte == "zéro" {
return Ok(Element::Entier(0))
@ -360,97 +223,3 @@ fn texte_comme_petit_nombre(texte: &str) -> Result<usize, ErreurPendragon> {
Ok(nombre)
}
// -----------------------------------------------------------------------
#[cfg(test)]
mod test {
use std::collections::HashMap;
use super::*;
#[test]
fn conversion_nombres_texte() {
for i in [0, 1, 42, 70, 123, 999, 1031, 1_001_091, 72_036_854_775_807usize, 2345678*987654].iter() {
let texte = nombre_comme_texte(*i); // Convert number to text
if texte.contains("--") {
panic!("Il y a deux tirets pour {} : {}", i, texte);
}
match texte_comme_nombre(&texte) { // Convert text back to number
Ok(nombre) => {
assert_eq!(Element::Entier(*i), nombre, "Nombre inexact : {}, texte : {}", i, texte);
}
Err(raison) => {
panic!("Conversion échouée pour : {}, avec l'erreur : {}", i, raison);
}
}
}
}
#[test]
fn calcul_nombre() {
let pendragon = Pendragon::nouveau();
let a = 2345678;
let b = 987654;
let c = 34523456;
let d = 45678;
let e = 2;
let possible_expression = pendragon.elements_nombre(&format!("{} fois {} plus ouvre la parenthèse {} moins {} ferme la parenthèse divisé par {}",
nombre_comme_texte(a),
nombre_comme_texte(b),
nombre_comme_texte(c),
nombre_comme_texte(d),
nombre_comme_texte(e)));
match possible_expression {
Ok(expression) => {
match calcule_nombre(expression, &HashMap::new()) {
Ok(nombre) => {
assert_eq!(nombre, a*b+(c-d)/e, "Calcul d'expression (entier) donne un mauvais résultat : {}", nombre);
}
Err(raison) => {
panic!("Calcul d'expression (entier) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (entier) échouée : {}", raison);
}
}
}
#[test]
fn erreur_calcul_nombre() {
let pendragon = Pendragon::nouveau();
let textes_invalide = vec![
"un un fois un",
"un plus fois un",
"un moins divisé par un",
"un fois un ouvre la parenthèse un plus un ferme la parenthèse",
"un fois ouvre la parenthèse plus un plus un ferme la parenthèse",
"un fois ouvre la parenthèse un plus un fois ferme la parenthèse",
"un fois ouvre la parenthèse un plus un ferme la parenthèse un",
];
for texte in textes_invalide {
let Err(raison) = pendragon.elements_nombre(texte) else {
panic!("Devrait détecter une erreur pour '{}'", texte);
};
let ErreurPendragon::OrdreCalculEntier(_,_,_) = raison else {
panic!("Devrait détecter une erreur de calcul entier pour '{}', a déclenché : {}", texte, raison);
};
}
}
#[test]
fn nombre_invalide_et() {
let pendragon = Pendragon::nouveau();
let Err(raison) = pendragon.elements_nombre("et") else {
panic!("Devrait détecter une erreur pour 'et'");
};
let ErreurPendragon::NombreInvalide(_) = raison else {
panic!("Devrait détecter une erreur de nombre invalide pour 'et', a déclenché : {}", raison);
};
}
}

View file

@ -1,4 +1,3 @@
use std::io;
use std::collections::HashMap;
use super::*;
@ -55,29 +54,22 @@ impl Programme {
}
Ok(())
}
pub fn execute(&self) -> Result<(), ErreurPendragon> {
let mut variables_globales: HashMap<String, Element> = HashMap::new();
for phrase in &self.contenu {
match phrase {
Phrase::Commande(commande) => commande.execute(&mut variables_globales)?,
Phrase::Bloc(bloc) => bloc.execute(&mut variables_globales)?,
}
}
Ok(())
}
}
pub struct Bloc {
pub condition: Vec<Element>,
pub repete: bool,
pub contenu: Vec<Phrase>,
pub contenu_sinon: Vec<Phrase>,
}
impl Bloc {
pub fn nouveau(condition: Vec<Element>) -> Self {
pub fn nouveau(condition: Vec<Element>, repete: bool) -> Self {
Self {
condition,
repete,
contenu: vec![],
contenu_sinon: vec![],
}
}
@ -85,21 +77,16 @@ impl Bloc {
self.contenu.push(Phrase::Commande(commande));
}
pub fn ajoute_commande_sinon(&mut self, commande: Commande) {
self.contenu_sinon.push(Phrase::Commande(commande));
}
pub fn ajoute_bloc(&mut self, bloc: Bloc) {
self.contenu.push(Phrase::Bloc(bloc));
}
pub fn execute(&self, variables: &mut HashMap<String, Element>) -> Result<(), ErreurPendragon> {
if !booleen::calcule_booleen(self.condition.clone(), variables)? {
return Ok(());
}
for phrase in &self.contenu {
match phrase {
Phrase::Commande(commande) => commande.execute(variables)?,
Phrase::Bloc(bloc) => bloc.execute(variables)?,
}
}
Ok(())
pub fn ajoute_bloc_sinon(&mut self, bloc: Bloc) {
self.contenu_sinon.push(Phrase::Bloc(bloc));
}
}
@ -115,32 +102,6 @@ pub enum Commande {
Affiche(Vec<Element>),
}
impl Commande {
fn execute(&self, variables: &mut HashMap<String, Element>) -> Result<(), ErreurPendragon> {
match self {
Commande::Definis(nom, type_element) => {
variables.insert(nom.to_string(), type_element.comme_element());
}
Commande::Demande(nom) => {
let valeur = variables[nom].type_element().demande_valeur(&nom)?;
variables.insert(nom.to_string(), valeur);
}
Commande::Modifie(nom, expression) => {
let valeur = match variables[nom].type_element() {
TypeElement::Entier => Element::Entier(nombre::calcule_nombre(expression.clone(), variables)?),
TypeElement::Texte => Element::Texte(texte::calcule_texte(expression.clone(), variables)?),
TypeElement::Booleen => Element::Booleen(booleen::calcule_booleen(expression.clone(), variables)?),
};
variables.insert(nom.to_string(), valeur);
}
Commande::Affiche(expression) => {
println!("{}", texte::calcule_texte(expression.to_vec(), variables)?);
}
}
Ok(())
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum TypeElement {
Entier,
@ -181,22 +142,6 @@ impl TypeElement {
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)]
@ -219,28 +164,6 @@ impl Element {
Self::Operateur(operateur) => operateur.type_element(),
}
}
pub fn compare(&self, element: Element, comparaison: TypeComparaison) -> Result<bool, ErreurPendragon> {
if let TypeComparaison::Egal = comparaison {
return Ok(*self == element)
}
if let TypeComparaison::Different = comparaison {
return Ok(*self != element)
}
let Self::Entier(nombre_a) = self else {
return Err(ErreurPendragon::ComparaisonInvalide(format!("comparaison numérique avec {}", self)))
};
let Self::Entier(nombre_b) = element else {
return Err(ErreurPendragon::ComparaisonInvalide(format!("comparaison numérique avec {}", element)))
};
match comparaison {
TypeComparaison::SuperieurEgal => Ok(*nombre_a >= nombre_b),
TypeComparaison::InferieurEgal => Ok(*nombre_a <= nombre_b),
TypeComparaison::Superieur => Ok(*nombre_a > nombre_b),
TypeComparaison::Inferieur => Ok(*nombre_a < nombre_b),
_ => Err(ErreurPendragon::ComparaisonInvalide("problème de logique".into())),
}
}
}
#[derive(Clone, Debug, PartialEq)]
@ -301,33 +224,6 @@ impl Comparaison {
}
return Err(ErreurPendragon::ComparaisonInvalide(format!("voulait comparer {} avec {}", element.type_element().nom(), type_comparaison)))
}
pub fn calcule(&self, variables: &HashMap<String, Element>) -> Result<bool, ErreurPendragon> {
let Some(ref comparaison) = self.type_comparaison else {
return Err(ErreurPendragon::ComparaisonInvalide("la comparaison n'a pas de type".into()))
};
let Some(element) = self.membre_a.first() else {
return Err(ErreurPendragon::ComparaisonInvalide("il n'y a pas de premier membre".into()))
};
let (membre_a, membre_b) = match element.type_element() {
TypeElement::Entier => {
let membre_a = Element::Entier(nombre::calcule_nombre(self.membre_a.clone(), variables)?);
let membre_b = Element::Entier(nombre::calcule_nombre(self.membre_b.clone(), variables)?);
(membre_a, membre_b)
}
TypeElement::Texte => {
let membre_a = Element::Texte(texte::calcule_texte(self.membre_a.clone(), variables)?);
let membre_b = Element::Texte(texte::calcule_texte(self.membre_b.clone(), variables)?);
(membre_a, membre_b)
}
TypeElement::Booleen => {
let membre_a = Element::Booleen(booleen::calcule_booleen(self.membre_a.clone(), variables)?);
let membre_b = Element::Booleen(booleen::calcule_booleen(self.membre_b.clone(), variables)?);
(membre_a, membre_b)
}
};
Ok(membre_a.compare(membre_b, comparaison.clone())?)
}
}
#[derive(Clone, Debug, PartialEq)]

View file

@ -79,125 +79,4 @@ impl Pendragon {
};
Err(raison)
}
}
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::Puis = 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)?;
pile = Vec::new();
continue;
}
if let TypeElement::Entier = element_pile.type_element() {
texte += &nombre::affiche_nombre(pile.clone(), variables)?;
pile = Vec::new();
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)
}
// -----------------------------------------------------------------------
#[cfg(test)]
mod test {
use std::collections::HashMap;
use super::*;
#[test]
fn calcul_texte() {
let pendragon = Pendragon::nouveau();
let a = 2345678;
let b = 987654;
let possible_expression = pendragon.elements_texte(&format!("\"hello\" puis {} fois {} puis \"there\" puis vrai ou faux puis trois puis deux puis alinéa puis retour à la ligne",
nombre::nombre_comme_texte(a),
nombre::nombre_comme_texte(b)));
match possible_expression {
Ok(expression) => {
match calcule_texte(expression, &HashMap::new()) {
Ok(texte) => {
let vrai_texte = format!("hello{}therevraitroisdeux\t\n", nombre::nombre_comme_texte(a*b));
assert_eq!(texte, vrai_texte, "Calcul d'expression (texte) donne un mauvais résultat : {}", texte);
}
Err(raison) => {
panic!("Calcul d'expression (texte) échoué, avec l'erreur : {}", raison);
}
}
}
Err(raison) => {
panic!("Détermination d'expression (texte) échouée : {}", raison);
}
}
}
#[test]
fn conversion_texte() {
let pendragon = Pendragon::nouveau();
let texte = "\"hello aaaa puis AERTY et ou fois six\"";
match pendragon.elements_texte(texte) {
Ok(expression) => {
if expression.len() != 2 {
panic!("L'expression (texte) devrait contenir deux éléments (texte et puis), contient : {:?}", expression);
}
assert_eq!(expression[0], Element::Texte(texte[1..texte.len()-1].into()), "Calcul d'expression (texte) donne un mauvais résultat : {}", texte);
}
Err(raison) => {
panic!("Conversion échouée (texte) : {}", raison);
}
}
}
#[test]
fn erreur_conversion_texte() {
let pendragon = Pendragon::nouveau();
let textes = vec![
"trois puis puis un",
"\" test",
"puis",
"un puis",
"puis un",
];
for texte in textes {
let Err(raison) = pendragon.elements_texte(texte) else {
panic!("Ne devrait pas réussir à convertir le texte '{}'", texte);
};
let ErreurPendragon::TexteInvalide(_) = raison else {
panic!("Erreur imprévue pour convertir le texte '{}' : {}", texte, raison);
};
}
}
}

68
src/sophie/booleen.rs Normal file
View file

@ -0,0 +1,68 @@
use super::*;
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()))
}
}
if let Element::Comparaison(comparaison) = element {
pile.push(comparaison.calcule(variables)?);
continue
}
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("il reste plusieurs éléments dans la pile".into()))
}
Ok(pile[0])
}
pub fn booleen_comme_texte(booleen: bool) -> String {
if booleen {
"vrai".into()
} else {
"faux".into()
}
}

148
src/sophie/mod.rs Normal file
View file

@ -0,0 +1,148 @@
use std::io;
use std::collections::HashMap;
use crate::pendragon::structure::*;
use crate::display::ErreurPendragon;
pub mod booleen;
pub mod texte;
pub mod nombre;
impl Programme {
pub fn execute(&self) -> Result<(), ErreurPendragon> {
let mut variables_globales: HashMap<String, Element> = HashMap::new();
for phrase in &self.contenu {
match phrase {
Phrase::Commande(commande) => commande.execute(&mut variables_globales)?,
Phrase::Bloc(bloc) => bloc.execute(&mut variables_globales)?,
}
}
Ok(())
}
}
impl Bloc {
pub fn execute(&self, variables: &mut HashMap<String, Element>) -> Result<(), ErreurPendragon> {
if booleen::calcule_booleen(self.condition.clone(), variables)? {
for phrase in &self.contenu {
match phrase {
Phrase::Commande(commande) => commande.execute(variables)?,
Phrase::Bloc(bloc) => bloc.execute(variables)?,
}
}
while booleen::calcule_booleen(self.condition.clone(), variables)? && self.repete {
for phrase in &self.contenu {
match phrase {
Phrase::Commande(commande) => commande.execute(variables)?,
Phrase::Bloc(bloc) => bloc.execute(variables)?,
}
}
}
} else if self.contenu_sinon.len() > 0 {
for phrase in &self.contenu_sinon {
match phrase {
Phrase::Commande(commande) => commande.execute(variables)?,
Phrase::Bloc(bloc) => bloc.execute(variables)?,
}
}
}
Ok(())
}
}
impl Commande {
fn execute(&self, variables: &mut HashMap<String, Element>) -> Result<(), ErreurPendragon> {
match self {
Commande::Definis(nom, type_element) => {
variables.insert(nom.to_string(), type_element.comme_element());
}
Commande::Demande(nom) => {
let valeur = variables[nom].type_element().demande_valeur(&nom)?;
variables.insert(nom.to_string(), valeur);
}
Commande::Modifie(nom, expression) => {
let valeur = match variables[nom].type_element() {
TypeElement::Entier => Element::Entier(nombre::calcule_nombre(expression.clone(), variables)?),
TypeElement::Texte => Element::Texte(texte::calcule_texte(expression.clone(), variables)?),
TypeElement::Booleen => Element::Booleen(booleen::calcule_booleen(expression.clone(), variables)?),
};
variables.insert(nom.to_string(), valeur);
}
Commande::Affiche(expression) => {
println!("{}", texte::calcule_texte(expression.to_vec(), variables)?);
}
}
Ok(())
}
}
impl TypeElement {
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),
}
}
}
}
impl Element {
pub fn compare(&self, element: Element, comparaison: TypeComparaison) -> Result<bool, ErreurPendragon> {
if let TypeComparaison::Egal = comparaison {
return Ok(*self == element)
}
if let TypeComparaison::Different = comparaison {
return Ok(*self != element)
}
let Self::Entier(nombre_a) = self else {
return Err(ErreurPendragon::ComparaisonInvalide(format!("comparaison numérique avec {}", self)))
};
let Self::Entier(nombre_b) = element else {
return Err(ErreurPendragon::ComparaisonInvalide(format!("comparaison numérique avec {}", element)))
};
match comparaison {
TypeComparaison::SuperieurEgal => Ok(*nombre_a >= nombre_b),
TypeComparaison::InferieurEgal => Ok(*nombre_a <= nombre_b),
TypeComparaison::Superieur => Ok(*nombre_a > nombre_b),
TypeComparaison::Inferieur => Ok(*nombre_a < nombre_b),
_ => Err(ErreurPendragon::ComparaisonInvalide("problème de logique".into())),
}
}
}
impl Comparaison {
pub fn calcule(&self, variables: &HashMap<String, Element>) -> Result<bool, ErreurPendragon> {
let Some(ref comparaison) = self.type_comparaison else {
return Err(ErreurPendragon::ComparaisonInvalide("la comparaison n'a pas de type".into()))
};
let Some(element) = self.membre_a.first() else {
return Err(ErreurPendragon::ComparaisonInvalide("il n'y a pas de premier membre".into()))
};
let (membre_a, membre_b) = match element.type_element() {
TypeElement::Entier => {
let membre_a = Element::Entier(nombre::calcule_nombre(self.membre_a.clone(), variables)?);
let membre_b = Element::Entier(nombre::calcule_nombre(self.membre_b.clone(), variables)?);
(membre_a, membre_b)
}
TypeElement::Texte => {
let membre_a = Element::Texte(texte::calcule_texte(self.membre_a.clone(), variables)?);
let membre_b = Element::Texte(texte::calcule_texte(self.membre_b.clone(), variables)?);
(membre_a, membre_b)
}
TypeElement::Booleen => {
let membre_a = Element::Booleen(booleen::calcule_booleen(self.membre_a.clone(), variables)?);
let membre_b = Element::Booleen(booleen::calcule_booleen(self.membre_b.clone(), variables)?);
(membre_a, membre_b)
}
};
Ok(membre_a.compare(membre_b, comparaison.clone())?)
}
}

139
src/sophie/nombre.rs Normal file
View file

@ -0,0 +1,139 @@
use crate::nombre::*;
use super::*;
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_a);
}
Operateur::Moins => {
if nombre_b < nombre_a {
return Err(ErreurPendragon::CalculEntier(format!("a essayé de soustraire '{}' à '{}'", nombre_comme_texte(nombre_a), nombre_comme_texte(nombre_b))))
}
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 {
if nombre == 0 {
return "zéro".to_string()
}
if nombre >= 10usize.pow(18) {
return "infini".to_string()
}
let mut groupes: Vec<usize> = vec![];
let mut nombre = nombre;
while nombre > 0 {
groupes.insert(0, nombre % 1000);
nombre /= 1000;
}
let mut chaine: String = "".to_string();
for index in 0..groupes.len() {
if groupes[index] == 0 {
continue
}
let pluriel: &str = if (groupes.len() - index - 1 > 1) && groupes[index] > 1 {
"s"
} else {
""
};
if index < groupes.len() - 1 {
let union = if index > 0 {UNION} else {""};
let chiffre = if groupes.len() - index - 1 == 1 && groupes[index] == 1 { // un mille
"".to_string()
} else {
petit_nombre_comme_texte(groupes[index]) + UNION
};
chaine += &format!("{}{}{}{}",
union,
chiffre,
NOMS_SEPARATEURS[groupes.len() - index - 1],
pluriel,
);
} else {
let union = if index > 0 {UNION} else {""};
chaine += union;
chaine += &petit_nombre_comme_texte(groupes[index]);
}
}
chaine
}
fn petit_nombre_comme_texte(nombre: usize) -> String {
let nombre = nombre.clamp(0, 999);
let centaine = nombre / 100;
let dizaine = (nombre % 100) / 10;
let unité = nombre % 10;
let centaine_texte = if centaine > 1 {
format!("{}{}cent", NOMS_UNITES[centaine], UNION)
} else if centaine > 0 {
"cent".to_string()
} else {
"".to_string()
};
let décalage_dizaine = if [1, 7, 9].contains(&dizaine) {1} else {0};
let dizaine_texte = NOMS_DIZAINES[dizaine - décalage_dizaine];
let séparation = if unité == 1 && ![0, 1, 8, 9].contains(&dizaine) {UNION.to_string() + "et"} else {"".to_string()};
let unité_texte = if [1, 7, 9].contains(&dizaine) {NOMS_UNITES_DIX[unité]} else {NOMS_UNITES[unité]};
let mut texte_nombre = format!("{}{}{}{}{}{}", centaine_texte, UNION, dizaine_texte, séparation, UNION, unité_texte);
while texte_nombre.contains("--") {
texte_nombre = texte_nombre.replace("--","-");
}
if texte_nombre.starts_with("-") {
texte_nombre = texte_nombre[1..texte_nombre.len()].to_string();
}
if texte_nombre.ends_with("-") {
texte_nombre = texte_nombre[0..texte_nombre.len()-1].to_string();
}
texte_nombre
}

47
src/sophie/texte.rs Normal file
View file

@ -0,0 +1,47 @@
use super::*;
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::Puis = 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)?;
pile = Vec::new();
continue;
}
if let TypeElement::Entier = element_pile.type_element() {
texte += &nombre::affiche_nombre(pile.clone(), variables)?;
pile = Vec::new();
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)
}