changed file structure
This commit is contained in:
parent
fb79b520d4
commit
bbb817854a
|
@ -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":[]}]}
|
|
@ -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
5
src/debug/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub mod display;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
318
src/debug/test.rs
Normal file
318
src/debug/test.rs
Normal 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);
|
||||
};
|
||||
}
|
17
src/main.rs
17
src/main.rs
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
68
src/sophie/booleen.rs
Normal 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
148
src/sophie/mod.rs
Normal 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
139
src/sophie/nombre.rs
Normal 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
47
src/sophie/texte.rs
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue