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::fmt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use super::*;
|
use crate::pendragon::structure::*;
|
||||||
|
use crate::sophie;
|
||||||
|
|
||||||
pub const TEXTE_ROUGE: &str = "\x1b[31m";
|
pub const TEXTE_ROUGE: &str = "\x1b[31m";
|
||||||
pub const TEXTE_VERT: &str = "\x1b[32m";
|
pub const TEXTE_VERT: &str = "\x1b[32m";
|
||||||
|
@ -130,9 +131,9 @@ impl fmt::Display for Commande {
|
||||||
impl fmt::Display for Element {
|
impl fmt::Display for Element {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
|
||||||
match self {
|
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::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::Variable(nom, type_variable) => write!(f, "{}:{}", nom, type_variable.nom()),
|
||||||
Self::Operateur(operateur) => write!(f, "{}", operateur),
|
Self::Operateur(operateur) => write!(f, "{}", operateur),
|
||||||
Self::Comparaison(comparaison) => write!(f, "{}.", comparaison),
|
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;
|
mod pendragon;
|
||||||
use pendragon::*;
|
use pendragon::*;
|
||||||
|
mod sophie;
|
||||||
|
mod debug;
|
||||||
|
use debug::display;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let arguments: Vec<String> = env::args().collect();
|
let arguments: Vec<String> = env::args().collect();
|
||||||
|
@ -21,32 +24,32 @@ fn main() {
|
||||||
let lecture = fs::read_to_string(chemin_de_fichier);
|
let lecture = fs::read_to_string(chemin_de_fichier);
|
||||||
|
|
||||||
if let Err(raison) = lecture {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
debug::message_compilation(chemin_de_fichier);
|
display::message_compilation(chemin_de_fichier);
|
||||||
let debut = Instant::now();
|
let debut = Instant::now();
|
||||||
if let Err(raison) = pendragon.compile(lecture.unwrap()) {
|
if let Err(raison) = pendragon.compile(lecture.unwrap()) {
|
||||||
for erreur in raison {
|
for erreur in raison {
|
||||||
eprintln!("\n{}", erreur);
|
eprintln!("\n{}", erreur);
|
||||||
}
|
}
|
||||||
debug::message_compilation_echec();
|
display::message_compilation_echec();
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
debug::message_compilation_ok(debut.elapsed());
|
display::message_compilation_ok(debut.elapsed());
|
||||||
|
|
||||||
if debug_mode {
|
if debug_mode {
|
||||||
println!("\n{}\n", pendragon.programme);
|
println!("\n{}\n", pendragon.programme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
debug::message_execution(chemin_de_fichier);
|
display::message_execution(chemin_de_fichier);
|
||||||
let debut = Instant::now();
|
let debut = Instant::now();
|
||||||
if let Err(raison) = pendragon.programme.execute() {
|
if let Err(raison) = pendragon.programme.execute() {
|
||||||
eprintln!("\nErreur : {}", raison);
|
eprintln!("\nErreur : {}", raison);
|
||||||
debug::message_execution_echec();
|
display::message_execution_echec();
|
||||||
return
|
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)
|
(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> {
|
pub fn texte_comme_booleen(texte: &str) -> Result<Element, ErreurPendragon> {
|
||||||
match texte {
|
match texte {
|
||||||
"vrai" => Ok(Element::Booleen(true)),
|
"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 nombre;
|
||||||
pub mod texte;
|
pub mod texte;
|
||||||
pub mod booleen;
|
pub mod booleen;
|
||||||
pub mod structure;
|
pub mod structure;
|
||||||
use structure::*;
|
use structure::*;
|
||||||
pub mod debug;
|
|
||||||
use debug::*;
|
|
||||||
|
|
||||||
pub struct Pendragon {
|
pub struct Pendragon {
|
||||||
pub programme: Programme,
|
pub programme: Programme,
|
||||||
|
@ -72,7 +71,7 @@ impl Pendragon {
|
||||||
}
|
}
|
||||||
Err(raison) => {
|
Err(raison) => {
|
||||||
erreurs.push(ErreurCompilation::nouvelle(index_ligne, ligne.into(), 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;
|
indentation_niveau += 1;
|
||||||
|
@ -113,7 +112,7 @@ impl Pendragon {
|
||||||
}
|
}
|
||||||
|
|
||||||
match parties[0] {
|
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())),
|
autre => Err(ErreurPendragon::BlocInconnu(autre.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const NOMS_UNITES: [&str; 10] = ["", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf"];
|
pub 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"];
|
pub 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"];
|
pub 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"];
|
pub const NOMS_SEPARATEURS: [&str; 7] = ["", "mille", "million", "milliard", "billion", "billiard", "trillion"];
|
||||||
const UNION: &str = "-";
|
pub const UNION: &str = "-";
|
||||||
|
|
||||||
impl Pendragon {
|
impl Pendragon {
|
||||||
pub fn elements_nombre(&self, arguments: &str) -> Result<Vec<Element>, ErreurPendragon> {
|
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> {
|
pub fn texte_comme_nombre(texte: &str) -> Result<Element, ErreurPendragon> {
|
||||||
if texte == "zéro" {
|
if texte == "zéro" {
|
||||||
return Ok(Element::Entier(0))
|
return Ok(Element::Entier(0))
|
||||||
|
@ -360,97 +223,3 @@ fn texte_comme_petit_nombre(texte: &str) -> Result<usize, ErreurPendragon> {
|
||||||
|
|
||||||
Ok(nombre)
|
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 std::collections::HashMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -55,29 +54,22 @@ impl Programme {
|
||||||
}
|
}
|
||||||
Ok(())
|
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 struct Bloc {
|
||||||
pub condition: Vec<Element>,
|
pub condition: Vec<Element>,
|
||||||
|
pub repete: bool,
|
||||||
pub contenu: Vec<Phrase>,
|
pub contenu: Vec<Phrase>,
|
||||||
|
pub contenu_sinon: Vec<Phrase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bloc {
|
impl Bloc {
|
||||||
pub fn nouveau(condition: Vec<Element>) -> Self {
|
pub fn nouveau(condition: Vec<Element>, repete: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
condition,
|
condition,
|
||||||
|
repete,
|
||||||
contenu: vec![],
|
contenu: vec![],
|
||||||
|
contenu_sinon: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,21 +77,16 @@ impl Bloc {
|
||||||
self.contenu.push(Phrase::Commande(commande));
|
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) {
|
pub fn ajoute_bloc(&mut self, bloc: Bloc) {
|
||||||
self.contenu.push(Phrase::Bloc(bloc));
|
self.contenu.push(Phrase::Bloc(bloc));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&self, variables: &mut HashMap<String, Element>) -> Result<(), ErreurPendragon> {
|
pub fn ajoute_bloc_sinon(&mut self, bloc: Bloc) {
|
||||||
if !booleen::calcule_booleen(self.condition.clone(), variables)? {
|
self.contenu_sinon.push(Phrase::Bloc(bloc));
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
for phrase in &self.contenu {
|
|
||||||
match phrase {
|
|
||||||
Phrase::Commande(commande) => commande.execute(variables)?,
|
|
||||||
Phrase::Bloc(bloc) => bloc.execute(variables)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,32 +102,6 @@ pub enum Commande {
|
||||||
Affiche(Vec<Element>),
|
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)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum TypeElement {
|
pub enum TypeElement {
|
||||||
Entier,
|
Entier,
|
||||||
|
@ -181,22 +142,6 @@ impl TypeElement {
|
||||||
Self::Booleen => booleen::texte_comme_booleen(texte),
|
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)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
@ -219,28 +164,6 @@ impl Element {
|
||||||
Self::Operateur(operateur) => operateur.type_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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -301,33 +224,6 @@ impl Comparaison {
|
||||||
}
|
}
|
||||||
return Err(ErreurPendragon::ComparaisonInvalide(format!("voulait comparer {} avec {}", element.type_element().nom(), type_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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
|
@ -80,124 +80,3 @@ impl Pendragon {
|
||||||
Err(raison)
|
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