Compare commits

..

10 commits

Author SHA1 Message Date
WanderingPenwing 537b5157e6 readme 2024-12-14 14:51:37 +01:00
WanderingPenwing fbe708e507 cleaned 2024-12-14 14:45:59 +01:00
WanderingPenwing dedb49bba0 while loop 2024-12-14 14:30:25 +01:00
WanderingPenwing bbb817854a changed file structure 2024-12-14 13:34:46 +01:00
WanderingPenwing fb79b520d4 display bloc 2024-12-13 18:26:58 +01:00
WanderingPenwing 1969e1bd8c if x) 2024-12-13 18:11:16 +01:00
WanderingPenwing 27fba9c4bf basic bloc structure 2024-12-13 17:09:35 +01:00
WanderingPenwing 371cbb6db1 mod tests 2024-12-13 16:52:29 +01:00
WanderingPenwing a401c65c35 pretty ui 2024-12-13 16:18:49 +01:00
WanderingPenwing 16d6ff8311 colors 2024-12-13 13:49:51 +01:00
19 changed files with 1231 additions and 1098 deletions

32
README.md Normal file
View file

@ -0,0 +1,32 @@
# Pendragon
Un language de programmation avec la syntaxe d'un texte français.
## Pour l'instant
- La partie pendragon/ transforme le fichier '.dr' en AST (arbre de syntaxe)
- La parte sophie/ interprète l'AST
## Objectif
- La partie hurle/ pour convertir l'AST en IR de LLVM
- Compilation avec LLVM
## Exemple
Voici un exemple de code :
```
Définis A comme entier. Définis B comme entier.
Modifie B avec un.
Définis N comme entier.
Modifie N avec trente.
Tant que N est supérieur à zéro,
Modifie N avec N moins un.
Affiche A.
Affiche B.
Modifie A avec A plus B.
Modifie B avec A plus B.
Affiche "Fin".
Nota Bene : Ceci est un programme qui affiche deux fois N nombres de la suite de Fibonacci.
```

View file

@ -1 +1 @@
{"categories":[{"name":"todo","content":[{"name":"if","description":"implémente if avec des guillemets de délimitation","id":1},{"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":"bug","content":[]},{"name":"to test","content":[{"name":"tests mod","description":"// Hello there","id":3},{"name":"test variable","description":"// Hello there","id":2}]},{"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":"compilation","description":"// Hello there","id":3},{"name":"warning bloc vide","description":"// Hello there","id":2},{"name":"warning variable inutilisée","description":"// Hello there","id":1},{"name":"scope variable","description":"// Hello there","id":6},{"name":"else","description":"// Hello there","id":7},{"name":"break","description":"// Hello there","id":8},{"name":"continue","description":"// Hello there","id":9}]},{"name":"done","content":[]},{"name":"bug","content":[{"name":"erreur de comparaison sans \"est\"","description":"// Hello there","id":1}]},{"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":"+","content":[]}]}

239
src/debug/display.rs Normal file
View file

@ -0,0 +1,239 @@
use std::fmt;
use std::time::Duration;
use crate::pendragon::structure::*;
use crate::sophie;
pub const TEXTE_ROUGE: &str = "\x1b[31m";
pub const TEXTE_VERT: &str = "\x1b[32m";
//pub const TEXTE_JAUNE: &str = "\x1b[33m";
pub const TEXTE_BLEU: &str = "\x1b[34m";
pub const TEXTE_GRIS: &str = "\x1b[37m";
pub const TEXTE_NORMAL: &str = "\x1b[0m";
pub fn message_compilation(chemin_de_fichier: &str) {
println!("\n- Compilation de {}'{}'{}...", TEXTE_VERT, chemin_de_fichier, TEXTE_NORMAL);
}
pub fn message_compilation_echec() {
eprintln!("\n{}x Échec de la compilation.{}",TEXTE_ROUGE, TEXTE_NORMAL);
}
pub fn message_compilation_ok(temps: Duration) {
println!("{}✓ Compilation Ok.{} ({:.2?}){}", TEXTE_VERT, TEXTE_GRIS, temps, TEXTE_NORMAL);
}
pub fn message_execution(chemin_de_fichier: &str) {
println!("- Exécution de {}'{}'{}...\n", TEXTE_VERT, chemin_de_fichier, TEXTE_NORMAL);
}
pub fn message_execution_echec() {
eprintln!("\n{}x Échec de l'exécution.{}",TEXTE_ROUGE, TEXTE_NORMAL);
}
pub fn message_execution_ok(temps: Duration) {
println!("\n{}✓ Exécution Ok.{} ({:.2?}){}", TEXTE_VERT, TEXTE_GRIS, temps, TEXTE_NORMAL);
}
pub struct ErreurCompilation {
index_ligne: usize,
ligne: String,
erreur: ErreurPendragon,
}
impl ErreurCompilation {
pub fn nouvelle(index_ligne: usize, ligne: String, erreur: ErreurPendragon) -> Self {
Self {
index_ligne,
ligne,
erreur,
}
}
#[cfg(test)]
pub fn raison(&self) -> ErreurPendragon {
self.erreur.clone()
}
}
impl fmt::Display for ErreurCompilation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
write!(f, "{}Erreur :{} {}\n{}ligne {} : {}{}", TEXTE_ROUGE, TEXTE_NORMAL, self.erreur, TEXTE_GRIS, self.index_ligne + 1, self.ligne, TEXTE_NORMAL)
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum ErreurPendragon {
CommandeInconnue(String),
BlocInconnu(String),
ManqueArgument,
MauvaisArgument(String),
MauvaiseIndentation(String),
ManquePonctuation,
NombreInvalide(String),
CalculEntier(String),
OrdreCalculEntier(String, String, String),
TexteInvalide(String),
BooleenInvalide(String),
ComparaisonInvalide(String),
CalculBooleen(String),
OrdreCalculBooleen(String, String, String),
VariableInconnue(String),
MauvaisType(String, String, String),
Lecture(String),
}
impl fmt::Display for ErreurPendragon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::CommandeInconnue(commande) => write!(f, "La commande \"{}\" est inconnue.", commande),
Self::BlocInconnu(bloc) => write!(f, "Le bloc \"{}\" est inconnu.", bloc),
Self::ManqueArgument => write!(f, "Il manque un argument."),
Self::MauvaisArgument(message) => write!(f, "La commande a reçu un mauvais argument, {}.", message),
Self::MauvaiseIndentation(message) => write!(f, "L'indentation est mauvaise, {}.", message),
Self::ManquePonctuation => write!(f, "Il manque la ponctuation de la phrase."),
Self::NombreInvalide(nombre) => write!(f, "Le nombre \"{}\" est mal orthographié.", nombre),
Self::CalculEntier(raison) => write!(f, "Calcul entier échoué, {}.", raison),
Self::OrdreCalculEntier(manque, precedent, suivant) => write!(f, "Calcul entier échoué, il manque un {} entre '{}' et '{}'.", manque, precedent, suivant),
Self::TexteInvalide(raison) => write!(f, "Le texte est invalide, {}.", raison),
Self::BooleenInvalide(booleen) => write!(f, "Le booleen \"{}\" est invalide.", booleen),
Self::CalculBooleen(raison) => write!(f, "Calcul booleen échoué, {}.", raison),
Self::ComparaisonInvalide(raison) => write!(f, "La comparaison est invalide, {}.", raison),
Self::OrdreCalculBooleen(manque, precedent, suivant) => write!(f, "Calcul boolen échoué, il manque un {} entre '{}' et '{}'.", manque, precedent, suivant),
Self::VariableInconnue(nom) => write!(f, "La variable \"{}\" est inconnue.", nom),
Self::MauvaisType(nom, type_variable, type_attendu) => write!(f, "La {} est du mauvais type ({}), attendais {}.", nom, type_variable, type_attendu),
Self::Lecture(raison) => write!(f, "Lecture d'entrées utilisateur impossible : {}.", raison),
}
}
}
impl fmt::Display for Commande {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Definis(nom, type_element) => write!(f, "{}{}{}:{}", TEXTE_VERT, nom, TEXTE_NORMAL, type_element.nom()),
Self::Demande(nom) => write!(f, "{}{}{}?", TEXTE_VERT, nom, TEXTE_NORMAL),
Self::Modifie(nom, expression) => write!(f,"{}{}{}={}[{}{}{}]{}", TEXTE_VERT, nom, TEXTE_NORMAL, TEXTE_ROUGE, TEXTE_NORMAL, liste_element(&expression), TEXTE_ROUGE, TEXTE_NORMAL),
Self::Affiche(expression) => write!(f, "#{}[{}{}{}]{}", TEXTE_ROUGE, TEXTE_NORMAL, liste_element(&expression), TEXTE_ROUGE, TEXTE_NORMAL),
}
}
}
impl fmt::Display for Element {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Entier(nombre) => write!(f, "{}", sophie::nombre::nombre_comme_texte(*nombre)),
Self::Texte(texte) => write!(f, "\"{}\"", texte),
Self::Booleen(booleen) => write!(f, "{}", sophie::booleen::booleen_comme_texte(*booleen)),
Self::Variable(nom, type_variable) => write!(f, "{}{}{}:{}", TEXTE_VERT, nom, TEXTE_NORMAL, type_variable.nom()),
Self::Operateur(operateur) => write!(f, "{}", operateur),
Self::Comparaison(comparaison) => write!(f, "{}", comparaison),
}
}
}
impl fmt::Display for Comparaison {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
let mut texte_membre_a: String = String::new();
for element in &self.membre_a {
texte_membre_a += &format!("{} ", element);
}
let mut texte_membre_b: String = String::new();
for element in &self.membre_b {
texte_membre_b += &format!(" {}", element);
}
let comparaison = if let Some(type_comparaison) = &self.type_comparaison {
format!("{}", type_comparaison)
} else {
"?".to_string()
};
write!(f, "({}{}{})",
texte_membre_a,
comparaison,
texte_membre_b,
)
}
}
impl fmt::Display for TypeComparaison {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Egal => write!(f, "=="),
Self::Different => write!(f, "!="),
Self::SuperieurEgal => write!(f, ">="),
Self::InferieurEgal => write!(f, "<="),
Self::Superieur => write!(f, ">"),
Self::Inferieur => write!(f, "<"),
}
}
}
impl fmt::Display for Operateur {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Ou => write!(f, "ou"),
Self::Et => write!(f, "et"),
Self::Non => write!(f, "non"),
Self::ParentheseBooleen => write!(f, "["),
Self::Puis => write!(f, "~"),
Self::Plus => write!(f, "+"),
Self::Moins => write!(f, "-"),
Self::Fois => write!(f, "x"),
Self::Divise => write!(f, "/"),
Self::ParentheseEntier => write!(f, "("),
}
}
}
impl fmt::Display for Programme {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
let mut texte: String = format!("{}variables : {:?}{}\n\n", TEXTE_GRIS, self.variables, TEXTE_NORMAL);
for phrase in &self.contenu {
texte += &format!("{}\n", phrase);
}
write!(f, "{}", texte)
}
}
impl fmt::Display for Phrase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Commande(commande) => write!(f, "{}", commande),
Self::Bloc(bloc) => write!(f, "{}", bloc),
}
}
}
impl fmt::Display for Bloc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
let mut texte: String = format!("|{}| : {}[{}", liste_element(&self.condition), TEXTE_BLEU, TEXTE_NORMAL);
for (index, phrase) in self.contenu.iter().enumerate() {
texte += &format!("{}", phrase);
if index < self.contenu.len() - 1 {
texte += &format!("{},{} ", TEXTE_GRIS, TEXTE_NORMAL);
}
}
write!(f, "{}{}]{}", texte, TEXTE_BLEU, TEXTE_NORMAL)
}
}
fn liste_element(expression: &Vec<Element>) -> String {
let mut texte = String::new();
for (index, element) in expression.iter().enumerate() {
texte += &format!("{}", element);
if index < expression.len() - 1 {
texte += &format!("{},{} ", TEXTE_GRIS, TEXTE_NORMAL);
}
}
texte
}

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

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

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

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

View file

@ -4,6 +4,9 @@ use std::time::Instant;
mod pendragon; 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();
@ -13,7 +16,8 @@ fn main() {
return return
} }
let debug_mode = arguments.contains(&"--debug".to_string()); let mode_debug = arguments.contains(&"-d".to_string());
let mode_interprete = arguments.contains(&"-i".to_string());
let chemin_de_fichier = &arguments[1]; let chemin_de_fichier = &arguments[1];
let mut pendragon = Pendragon::nouveau(); let mut pendragon = Pendragon::nouveau();
@ -21,31 +25,33 @@ 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 : {}", raison); eprintln!("{}Fichier illisible :{} {}", display::TEXTE_ROUGE, raison, display::TEXTE_NORMAL);
return return
} }
println!("# Compilation de '{}'.", 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()) {
eprintln!("\n{}", raison); for erreur in raison {
eprintln!("\n# Échec de la compilation."); eprintln!("\n{}", erreur);
}
display::message_compilation_echec();
return return
} }
println!("# Compilation Ok. ({:.2?})\n", debut.elapsed()); display::message_compilation_ok(debut.elapsed());
if debug_mode { if mode_debug {
println!("{}\n", pendragon.programme); println!("\n{}\n", pendragon.programme);
} }
if !mode_interprete {
return
println!("# Exécution de '{}'.\n", 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);
eprintln!("\n# Échec de l'exécution."); display::message_execution_echec();
return return
} }
display::message_execution_ok(debut.elapsed());
println!("\n# Exécution Ok. ({:.2?})", debut.elapsed());
} }

View file

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

View file

@ -1,159 +0,0 @@
use std::fmt;
use super::*;
pub struct ErreurCompilation {
index_ligne: usize,
erreur: ErreurPendragon,
}
impl ErreurCompilation {
pub fn nouvelle(index_ligne: usize, erreur: ErreurPendragon) -> Self {
Self {
index_ligne,
erreur,
}
}
pub fn raison(&self) -> ErreurPendragon {
self.erreur.clone()
}
}
impl fmt::Display for ErreurCompilation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
write!(f, "Erreur ligne {} : {}", self.index_ligne + 1, self.erreur)
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum ErreurPendragon {
CommandeInconnue(String),
ManqueArgument,
MauvaisArgument(String),
ManquePonctuation,
NombreInvalide(String),
CalculEntier(String),
OrdreCalculEntier(String, String, String),
TexteInvalide(String),
BooleenInvalide(String),
ComparaisonInvalide(String),
CalculBooleen(String),
OrdreCalculBooleen(String, String, String),
VariableInconnue(String),
MauvaisType(String, String, String),
Lecture(String),
}
impl fmt::Display for ErreurPendragon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::CommandeInconnue(commande) => write!(f, "La commande \"{}\" est inconnue.", commande),
Self::ManqueArgument => write!(f, "Il manque un argument."),
Self::MauvaisArgument(message) => write!(f, "La commande a reçu un mauvais argument, {}.", message),
Self::ManquePonctuation => write!(f, "Il manque la ponctuation de la phrase."),
Self::NombreInvalide(nombre) => write!(f, "Le nombre \"{}\" est mal orthographié.", nombre),
Self::CalculEntier(raison) => write!(f, "Calcul entier échoué, {}.", raison),
Self::OrdreCalculEntier(manque, precedent, suivant) => write!(f, "Calcul entier échoué, il manque un {} entre '{}' et '{}'.", manque, precedent, suivant),
Self::TexteInvalide(raison) => write!(f, "Le texte est invalide, {}.", raison),
Self::BooleenInvalide(booleen) => write!(f, "Le booleen \"{}\" est invalide.", booleen),
Self::CalculBooleen(raison) => write!(f, "Calcul booleen échoué, {}.", raison),
Self::ComparaisonInvalide(raison) => write!(f, "La comparaison est invalide, {}.", raison),
Self::OrdreCalculBooleen(manque, precedent, suivant) => write!(f, "Calcul boolen échoué, il manque un {} entre '{}' et '{}'.", manque, precedent, suivant),
Self::VariableInconnue(nom) => write!(f, "La variable \"{}\" est inconnue.", nom),
Self::MauvaisType(nom, type_variable, type_attendu) => write!(f, "La {} est du mauvais type ({}), attendais {}.", nom, type_variable, type_attendu),
Self::Lecture(raison) => write!(f, "Lecture d'entrées utilisateur impossible : {}.", raison),
}
}
}
impl fmt::Display for Commande {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Definis(nom, type_element) => write!(f, "Definis {} comme {}.", nom, type_element.nom()),
Self::Demande(nom) => write!(f, "Demande {}.", nom),
Self::Modifie(nom, expression) => write!(f, "Modifie {} avec {:?}.", nom, expression),
Self::Affiche(expression) => write!(f, "Affiche {:?}.", expression),
}
}
}
impl fmt::Display for Element {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Entier(nombre) => write!(f, "{}", nombre::nombre_comme_texte(*nombre)),
Self::Texte(texte) => write!(f, "\"{}\"", texte),
Self::Booleen(booleen) => write!(f, "{}", 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),
}
}
}
impl fmt::Display for Comparaison {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
let mut texte_membre_a: String = String::new();
for element in &self.membre_a {
texte_membre_a += &format!("{} ", element);
}
let mut texte_membre_b: String = String::new();
for element in &self.membre_b {
texte_membre_b += &format!(" {}", element);
}
write!(f, "({}{:?}{})",
texte_membre_a,
self.type_comparaison,
texte_membre_b,
)
}
}
impl fmt::Display for TypeComparaison {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Egal => write!(f, "égal à"),
Self::Different => write!(f, "différent de"),
Self::SuperieurEgal => write!(f, "supérieur ou égal à"),
Self::InferieurEgal => write!(f, "inférieur ou égal à"),
Self::Superieur => write!(f, "supérieur à"),
Self::Inferieur => write!(f, "inférieur à"),
}
}
}
impl fmt::Display for Operateur {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
match self {
Self::Ou => write!(f, "ou"),
Self::Et => write!(f, "et"),
Self::Non => write!(f, "non"),
Self::ParentheseBooleen => write!(f, "["),
Self::Puis => write!(f, ";"),
Self::Plus => write!(f, "+"),
Self::Moins => write!(f, "-"),
Self::Fois => write!(f, "*"),
Self::Divise => write!(f, "/"),
Self::ParentheseEntier => write!(f, "("),
}
}
}
impl fmt::Display for Programme {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {//'
let mut texte: String = format!("variables : {:?}", self.variables);
for (index, commande) in self.commandes.iter().enumerate() {
texte += &format!("\n#{:2}-{}", index+1, commande);
}
write!(f, "{}", texte)
}
}

View file

@ -1,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,
@ -19,30 +18,67 @@ impl Pendragon {
} }
} }
pub fn compile(&mut self, contenu: String) -> Result<(), ErreurCompilation> { pub fn compile(&mut self, contenu: String) -> Result<(), Vec<ErreurCompilation>> {
let texte: Vec<&str> = contenu.split('\n').collect(); let texte: Vec<&str> = contenu.split('\n').collect();
let mut erreurs: Vec<ErreurCompilation> = vec![];
let mut indentation_niveau: usize = 0;
let mut pile_bloc: Vec<Bloc> = vec![];
for (index_ligne, ligne) in texte.iter().enumerate() { for (index_ligne, ligne) in texte.iter().enumerate() {
let indentation_ligne = ligne.chars().take_while(|&c| c == '\t').count();
let ligne = ligne.trim(); let ligne = ligne.trim();
let phrases: Vec<&str> = ligne.split_inclusive(|c| c == ',' || c == '.').collect(); let phrases: Vec<&str> = ligne.split_inclusive(|c| c == ',' || c == '.').collect();
let Some(derniere_phrase) = phrases.last() else { let Some(derniere_phrase) = phrases.last() else {
continue continue
}; };
if !derniere_phrase.ends_with('.') && !derniere_phrase.ends_with(',') { if !derniere_phrase.ends_with('.') && !derniere_phrase.ends_with(',') {
return Err(ErreurCompilation::nouvelle(index_ligne, ErreurPendragon::ManquePonctuation)) erreurs.push(ErreurCompilation::nouvelle(index_ligne, ligne.into(), ErreurPendragon::ManquePonctuation))
} }
while indentation_ligne < indentation_niveau {
let Some(bloc_actuel) = pile_bloc.pop() else {
erreurs.push(ErreurCompilation::nouvelle(index_ligne, ligne.into(), ErreurPendragon::MauvaiseIndentation(format!("croyais être à {} niveau", indentation_niveau)),));
indentation_niveau = 0;
continue;
};
if let Some(bloc_precedent) = pile_bloc.last_mut() {
bloc_precedent.ajoute_bloc(bloc_actuel);
} else {
self.programme.ajoute_bloc(bloc_actuel);
}
indentation_niveau -= 1;
}
for phrase in phrases { for phrase in phrases {
if phrase.ends_with(".") { if phrase.ends_with(".") {
if phrase.replace(" ", "").starts_with("NotaBene:") { if phrase.replace(" ", "").starts_with("NotaBene:") {
continue continue
} }
match self.compile_commande(&phrase[..phrase.len() - 1]) { match self.compile_commande(&phrase[..phrase.len() - 1]) {
Ok(commande) => self.programme.ajoute_commande(commande), Ok(commande) => {
Err(raison) => return Err(ErreurCompilation::nouvelle(index_ligne, raison)), if let Some(bloc_actuel) = pile_bloc.last_mut() {
bloc_actuel.ajoute_commande(commande);
} else {
self.programme.ajoute_commande(commande);
}
}
Err(raison) => erreurs.push(ErreurCompilation::nouvelle(index_ligne, ligne.into(), raison)),
} }
continue; continue;
} }
println!("todo : {}", phrase); match self.compile_bloc(&phrase[..phrase.len() - 1]) {
Ok(bloc) => {
pile_bloc.push(bloc);
} }
Err(raison) => {
erreurs.push(ErreurCompilation::nouvelle(index_ligne, ligne.into(), raison));
pile_bloc.push(Bloc::nouveau(vec![Element::Booleen(false)], false));
}
}
indentation_niveau += 1;
}
}
if erreurs.len() > 0 {
return Err(erreurs)
} }
Ok(()) Ok(())
} }
@ -53,7 +89,7 @@ impl Pendragon {
if parties.len() == 1 { if parties.len() == 1 {
return Err(ErreurPendragon::ManqueArgument) return Err(ErreurPendragon::ManqueArgument)
} }
if parties[1].contains("Définis") || parties[1].contains("Modifie") || parties[1].contains("Affiche") || parties[1].contains("Demande") { if contient_mot_cle(parties[1]) {
return Err(ErreurPendragon::ManquePonctuation) return Err(ErreurPendragon::ManquePonctuation)
} }
match parties[0] { match parties[0] {
@ -65,6 +101,23 @@ impl Pendragon {
} }
} }
fn compile_bloc(&mut self, phrase: &str) -> Result<Bloc, ErreurPendragon> {
let phrase = phrase.trim().replace("Tant que", "Tant-que");
let parties: Vec<&str> = phrase.splitn(2, ' ').collect();
if parties.len() == 1 {
return Err(ErreurPendragon::ManqueArgument)
}
if contient_mot_cle(parties[1]) {
return Err(ErreurPendragon::ManquePonctuation)
}
match parties[0] {
"Tant-que" => Ok(Bloc::nouveau(self.elements_booleen(parties[1])?, true)),
"Si" => Ok(Bloc::nouveau(self.elements_booleen(parties[1])?, false)),
autre => Err(ErreurPendragon::BlocInconnu(autre.into())),
}
}
fn affiche(&self, arguments: &str) -> Result<Commande, ErreurPendragon> { fn affiche(&self, arguments: &str) -> Result<Commande, ErreurPendragon> {
Ok(Commande::Affiche(self.elements_texte(arguments)?)) Ok(Commande::Affiche(self.elements_texte(arguments)?))
} }
@ -99,6 +152,14 @@ impl Pendragon {
} }
} }
fn contient_mot_cle(texte: &str) -> bool {
texte.contains("Définis") ||
texte.contains("Modifie") ||
texte.contains("Affiche") ||
texte.contains("Demande") ||
texte.contains("Si")
}
fn nom_de_variable(arguments: &str, separateur: &str) -> Result<(String, String), ErreurPendragon> { fn nom_de_variable(arguments: &str, separateur: &str) -> Result<(String, String), ErreurPendragon> {
let parties = if separateur == "" { let parties = if separateur == "" {
vec![arguments, ""] vec![arguments, ""]
@ -126,7 +187,6 @@ pub fn format_de_variable(nom: &str) -> bool {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::collections::HashMap;
use super::*; use super::*;
#[test] #[test]
@ -144,8 +204,11 @@ mod test {
]; ];
for commentaire in commentaires { for commentaire in commentaires {
match pendragon.compile(commentaire.into()) { match pendragon.compile(commentaire.into()) {
Ok(_) => assert_eq!(pendragon.programme.commandes.len(), 0, "Le commentaire '{}' ne devrait pas générer de commande", commentaire), Ok(_) => assert_eq!(pendragon.programme.contenu.len(), 0, "Le commentaire '{}' ne devrait pas générer de commande", commentaire),
Err(raison) => panic!("Erreur de compilation du commentaire '{}' : {}", commentaire, raison) Err(erreurs) => {
let affichage_erreurs = erreurs.iter().map(|item| format!("{}", item.raison())).collect::<Vec<_>>().join("\n\n");
panic!("Erreur de compilation du commentaire '{}' : \n{}", commentaire, affichage_erreurs)
}
} }
} }
} }
@ -164,32 +227,95 @@ mod test {
"NNotaBene:ceci n'est pas un commentaire." "NNotaBene:ceci n'est pas un commentaire."
]; ];
for commentaire in commentaires { for commentaire in commentaires {
let Err(erreur) = pendragon.compile(commentaire.into()) else { let Err(erreurs) = pendragon.compile(commentaire.into()) else {
panic!("Ne devrait pas pouvoir compiler un commentaire invalide '{}'", commentaire); panic!("Ne devrait pas pouvoir compiler un commentaire invalide '{}'", commentaire);
}; };
let ErreurPendragon::CommandeInconnue(_) = erreur.raison() else { if erreurs.len() > 1 {
panic!("Erreur inattendue de compilation du commentaire '{}' : {}", commentaire, erreur.raison()); let affichage_erreurs = erreurs.iter().map(|item| format!("{}", item.raison())).collect::<Vec<_>>().join("\n\n");
panic!("Plus d'erreurs que prévu pour le commentaire '{}' : {:?}", commentaire, affichage_erreurs)
}
let ErreurPendragon::CommandeInconnue(_) = erreurs[0].raison() else {
panic!("Erreur inattendue de compilation du commentaire '{}' : {}", commentaire, erreurs[0].raison());
}; };
} }
} }
#[test] #[test]
fn ponctuation_valide() { fn ponctuation_valide() {
panic!("todo"); let mut pendragon = Pendragon::nouveau();
let texte = "aah.\noooh.uuuh,\nna,\nbududu.bababa.\naaaaaaaaaaa,sssssss,";
let Err(erreurs) = pendragon.compile(texte.into()) else {
panic!("Il devrait y avoir des erreurs");
};
for erreur in erreurs {
if let ErreurPendragon::ManquePonctuation = erreur.raison() {
panic!("Erreur : manque ponctuation");
}
}
} }
#[test] #[test]
fn ponctuation_invalide() { fn ponctuation_invalide() {
panic!("todo"); let mut pendragon = Pendragon::nouveau();
let textes = [
"Aaaaa",
"aaaa.\nooooo\n",
"aaaa Définis."
];
for texte in textes {
let Err(erreurs) = pendragon.compile(texte.into()) else {
panic!("Ne devrait pas pouvoir compiler un texte invalide '{}'", texte);
};
let mut manque_ponctuation = false;
for erreur in erreurs {
if let ErreurPendragon::ManquePonctuation = erreur.raison() {
manque_ponctuation = true;
}
}
if !manque_ponctuation {
panic!("Il devrait y avoir une erreur de ponctuation dans le texte '{}'", texte);
}
}
} }
#[test] #[test]
fn commande_valide() { fn commande_valide() {
panic!("todo"); let mut pendragon = Pendragon::nouveau();
let texte = "Affiche.\nDemande.\nModifie.Définis.";
let Err(erreurs) = pendragon.compile(texte.into()) else {
panic!("Il devrait y avoir des erreurs");
};
for erreur in erreurs {
if let ErreurPendragon::CommandeInconnue(_) = erreur.raison() {
panic!("Erreur : commande invalide");
}
}
} }
#[test] #[test]
fn commande_invalide() { fn commande_invalide() {
panic!("todo"); let mut pendragon = Pendragon::nouveau();
let textes = [
"Definis a.",
"définis b.",
"modifie c.",
"affiche d.",
"aaaa e."
];
for texte in textes {
let Err(erreurs) = pendragon.compile(texte.into()) else {
panic!("Ne devrait pas pouvoir compiler un texte invalide '{}'", texte);
};
let mut commande_inconnue = false;
for erreur in &erreurs {
if let ErreurPendragon::CommandeInconnue(_) = erreur.raison() {
commande_inconnue = true;
}
}
if !commande_inconnue {
let affichage_erreurs = erreurs.iter().map(|item| format!("{}", item.raison())).collect::<Vec<_>>().join("\n\n");
panic!("La commande devrait être inconnue '{}', erreurs : \n{}", texte, affichage_erreurs);
}
}
} }
} }

View file

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

View file

@ -1,23 +1,26 @@
use std::io;
use std::collections::HashMap; use std::collections::HashMap;
use super::*; use super::*;
pub struct Programme { pub struct Programme {
pub variables: HashMap<String, TypeElement>, pub variables: HashMap<String, TypeElement>,
pub commandes: Vec<Commande>, pub contenu: Vec<Phrase>,
} }
impl Programme { impl Programme {
pub fn nouveau() -> Self { pub fn nouveau() -> Self {
Self { Self {
variables: HashMap::new(), variables: HashMap::new(),
commandes: vec![], contenu: vec![],
} }
} }
pub fn ajoute_commande(&mut self, commande: Commande) { pub fn ajoute_commande(&mut self, commande: Commande) {
self.commandes.push(commande); self.contenu.push(Phrase::Commande(commande));
}
pub fn ajoute_bloc(&mut self, bloc: Bloc) {
self.contenu.push(Phrase::Bloc(bloc));
} }
pub fn ajoute_variable(&mut self, nom: String, type_variable: TypeElement) -> Result<(), ErreurPendragon> { pub fn ajoute_variable(&mut self, nom: String, type_variable: TypeElement) -> Result<(), ErreurPendragon> {
@ -51,35 +54,37 @@ impl Programme {
} }
Ok(()) Ok(())
} }
}
pub fn execute(&self) -> Result<(), ErreurPendragon> { pub struct Bloc {
let mut variables_globales: HashMap<String, Element> = HashMap::new(); pub condition: Vec<Element>,
for commande in &self.commandes { pub repete: bool,
match commande { pub contenu: Vec<Phrase>,
Commande::Definis(nom, type_element) => { }
variables_globales.insert(nom.to_string(), type_element.comme_element());
} impl Bloc {
Commande::Demande(nom) => { pub fn nouveau(condition: Vec<Element>, repete: bool) -> Self {
let valeur = variables_globales[nom].type_element().demande_valeur(&nom)?; Self {
variables_globales.insert(nom.to_string(), valeur); condition,
} repete,
Commande::Modifie(nom, expression) => { contenu: vec![],
let valeur = match variables_globales[nom].type_element() {
TypeElement::Entier => Element::Entier(nombre::calcule_nombre(expression.clone(), &variables_globales)?),
TypeElement::Texte => Element::Texte(texte::calcule_texte(expression.clone(), &variables_globales)?),
TypeElement::Booleen => Element::Booleen(booleen::calcule_booleen(expression.clone(), &variables_globales)?),
};
variables_globales.insert(nom.to_string(), valeur);
}
Commande::Affiche(expression) => {
println!("{}", texte::calcule_texte(expression.to_vec(), &variables_globales)?);
} }
} }
pub fn ajoute_commande(&mut self, commande: Commande) {
self.contenu.push(Phrase::Commande(commande));
} }
Ok(())
pub fn ajoute_bloc(&mut self, bloc: Bloc) {
self.contenu.push(Phrase::Bloc(bloc));
} }
} }
pub enum Phrase {
Bloc(Bloc),
Commande(Commande),
}
pub enum Commande { pub enum Commande {
Definis(String, TypeElement), Definis(String, TypeElement),
Demande(String), Demande(String),
@ -127,22 +132,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)]
@ -165,28 +154,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)]
@ -247,33 +214,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)]

View file

@ -1,137 +0,0 @@
//#[test]
//fn teste_definition_variable() {
// let mut sophie = Pendragon::new();
// let resultat = sophie.execute_phrase("Définis Element comme entier");
// match resultat {
// Ok(_) => {
// assert_eq!(sophie.variables["Element"], Element::Entier(0), "Element mal définie");
// }
// Err(raison) => {
// panic!("Définition de variable échouée : {}", raison);
// }
// }
//}
//
//#[test]
//fn teste_modification_variable() {
// let mut sophie = Pendragon::new();
// if let Err(raison) = sophie.execute_phrase("Définis Element comme entier") {
// panic!("Définition de variable échouée : {}", raison);
// }
// let a = 2345678;
// let resultat = sophie.execute_phrase(&format!("Modifie Element avec {} ", nombre::nombre_comme_texte(a)));
// match resultat {
// Ok(_) => {
// assert_eq!(sophie.variables["Element"], Element::Entier(a), "Element mal modifiée");
// }
// Err(raison) => {
// panic!("Modification de variable échouée : {}", raison);
// }
// }
//}
//
//#[test]
//fn teste_operation_variable() {
// let mut sophie = Pendragon::new();
// if let Err(raison) = sophie.execute_phrase("Définis Element comme entier") {
// panic!("Définition de variable échouée : {}", raison);
// }
// let a = 2345678;
// if let Err(raison) = sophie.execute_phrase(&format!("Modifie Element avec {} ", nombre::nombre_comme_texte(a))) {
// panic!("Modification de variable échouée : {}", raison);
// }
// let b = 987654;
// let resultat = sophie.operation(&format!("Element plus {}", nombre::nombre_comme_texte(b)));
// match resultat {
// Ok(nombre) => {
// assert_eq!(nombre, a+b, "Echec de la somme d'un entier et d'une variable, attendais {}, a reçu {}", a+b, nombre);
// }
// Err(raison) => {
// panic!("Opération de variable échouée : {}", raison);
// }
// }
//}
//#[test]
//fn teste_texte() {
// let mut sophie = Pendragon::new();
// if let Err(raison) = sophie.execute_phrase("Définis A comme entier") {
// panic!("Définition de variable échouée : {}", raison);
// }
// let a = 2345678;
// if let Err(raison) = sophie.execute_phrase(&format!("Modifie A avec {} ", nombre::nombre_comme_texte(a))) {
// panic!("Modification de variable échouée : {}", raison);
// }
// if let Err(raison) = sophie.execute_phrase("Définis B comme texte") {
// panic!("Définition de variable échouée : {}", raison);
// }
// if let Err(raison) = sophie.execute_phrase("Modifie B avec \"hello there\", \" general\", \" Kenobi\"") {
// panic!("Modification de variable échouée : {}", raison);
// }
// let resultat = sophie.texte("\"Combo : \", B, \" / \", A plus ouvre la parenthèse un plus cinq ferme la parenthèse fois ouvre la parenthèse huit moins un ferme la parenthèse");
//
// match resultat {
// Ok(texte) => assert_eq!(texte, "Combo : hello there general Kenobi / deux-millions-trois-cent-quarante-cinq-mille-sept-cent-vingt", "Texte mal calculé"),
// Err(raison) => panic!("Calcul de texte échoué : {}", raison),
// }
//}
// --------------------------------------------- anti-test
//#[test]
//fn teste_redefinition_variable() {
// let mut sophie = Pendragon::new();
// if let Err(raison) = sophie.execute_phrase("Définis Element comme entier") {
// panic!("Définition de variable échouée : {}", raison);
// };
// let Err(raison) = sophie.execute_phrase("Définis Element comme texte") else {
// panic!("Ne devrais pas pouvoir redéfinir une variable");
// };
// if let ErreurPendragon::MauvaisArgument(ref texte) = raison {
// assert_eq!(texte, "la variable \"Element\" existe déjà", "Définition échouée avec erreur imprévue : {}", raison);
// } else {
// panic!("Définition échouée avec erreur imprévue : {}", raison);
// }
//}
//
//#[test]
//fn teste_echec_modification() {
// let mut sophie = Pendragon::new();
// let resultat = sophie.execute_phrase("Modifie Element avec deux");
// let Err(raison) = resultat else {
// panic!("Ne devrais pas pouvoir modifier une variable non définie");
// };
// if let ErreurPendragon::VariableInconnue(nom) = raison {
// assert_eq!(nom, "Element", "Mauvais nom de variable reconnu : {}", nom);
// } else {
// panic!("Modification échouée avec erreur imprévue : {}", raison);
// }
//}
//
//#[test]
//fn teste_majuscule_variable() {
// let mut sophie = Pendragon::new();
// let resultat = sophie.execute_phrase("Définis variable comme entier");
// let Err(raison) = resultat else {
// panic!("Ne devrais pas pouvoir definir une variable sans majuscule");
// };
// if let ErreurPendragon::MauvaisArgument(explication) = raison {
// assert_eq!(explication, "il manque une majuscule à la variable", "Mauvaise explication : {}", explication);
// } else {
// panic!("Définition échouée avec erreur imprévue : {}", raison);
// }
//}
//
//#[test]
//fn teste_point_phrase() {
// let mut sophie = Pendragon::new();
// let resultat = sophie.execute("Définis Element comme entier".into());
// let Err(raison) = resultat else {
// panic!("Ne devrais pas pouvoir faire de commande sans point à la fin");
// };
// let ErreurPendragon::ManquePoint = raison else {
// panic!("Définition échouée avec erreur imprévue : {}", raison);
// };
//}

View file

@ -42,7 +42,6 @@ impl Pendragon {
expression.push(Element::Operateur(Operateur::Puis)); expression.push(Element::Operateur(Operateur::Puis));
} }
expression.extend(self.puis(&expression, &pile_inconnu)?); expression.extend(self.puis(&expression, &pile_inconnu)?);
pile_inconnu = Vec::new();
expression.push(Element::Operateur(Operateur::Puis)); expression.push(Element::Operateur(Operateur::Puis));
Ok(expression) Ok(expression)
} }
@ -81,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);
};
}
}
}

View file

@ -1,54 +0,0 @@
pub enum MotCle {
Definis,
Modifie,
Affiche,
Demande,
Si,
Sinon,
NotaBene,
Plus,
Moins,
Fois,
Divise,
Et,
Ou,
Non,
OuvreParenthese,
FermeParenthese,
Puis,
Alinea,
RetourLigne,
}
pub impl MotCle {
fn comme_texte(&self) -> String {
match Self {
Self::Definis,
Self::Modifie,
Self::Affiche,
Self::Demande,
Self::Si,
Self::Sinon,
Self::NotaBene,
Plus,
Moins,
Fois,
Divise,
Et,
Ou,
Non,
OuvreParenthese,
FermeParenthese,
Puis,
Alinea,
RetourLigne,
}
}
fn depuis_texte(texte: &str) -> Self {
match texte {
}
}

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

@ -0,0 +1,68 @@
use super::*;
pub fn affiche_booleen(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<String, ErreurPendragon> {
let booleen = calcule_booleen(expression.clone(), variables)?;
Ok(booleen_comme_texte(booleen))
}
pub fn calcule_booleen(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<bool, ErreurPendragon> {
let mut pile: Vec<bool> = Vec::new();
for element in expression {
if let Element::Booleen(booleen) = element {
pile.push(booleen);
continue;
}
if let Element::Variable(nom, _) = element {
let Some(variable) = variables.get(&nom) else {
return Err(ErreurPendragon::VariableInconnue(nom.into()))
};
if let Element::Booleen(booleen) = variable {
pile.push(*booleen);
continue
} else {
return Err(ErreurPendragon::MauvaisType(nom.into(), variable.type_element().nom(), "booleen".into()))
}
}
if let Element::Comparaison(comparaison) = element {
pile.push(comparaison.calcule(variables)?);
continue
}
let Element::Operateur(ref operateur) = element else {
return Err(ErreurPendragon::MauvaisArgument(format!("{}, attendais un opérateur", element)))
};
let Some(booleen_a) = pile.pop() else {
return Err(ErreurPendragon::CalculBooleen("la pile est vide".into()))
};
match operateur {
Operateur::Non => {
pile.push(!booleen_a);
}
Operateur::Et => {
let Some(booleen_b) = pile.pop() else {
return Err(ErreurPendragon::CalculBooleen("la pile est vide".into()))
};
pile.push(booleen_a && booleen_b);
}
Operateur::Ou => {
let Some(booleen_b) = pile.pop() else {
return Err(ErreurPendragon::CalculBooleen("la pile est vide".into()))
};
pile.push(booleen_a || booleen_b);
}
_ => return Err(ErreurPendragon::MauvaisArgument(format!("{}, attendais un opérateur booléen", element)))
}
}
if pile.len() > 1 {
return Err(ErreurPendragon::CalculBooleen("il reste plusieurs éléments dans la pile".into()))
}
Ok(pile[0])
}
pub fn booleen_comme_texte(booleen: bool) -> String {
if booleen {
"vrai".into()
} else {
"faux".into()
}
}

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

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

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

@ -0,0 +1,139 @@
use crate::nombre::*;
use super::*;
pub fn affiche_nombre(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<String, ErreurPendragon> {
let nombre = calcule_nombre(expression.clone(), variables)?;
Ok(nombre_comme_texte(nombre))
}
pub fn calcule_nombre(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<usize, ErreurPendragon> {
let mut pile: Vec<usize> = Vec::new();
for element in expression {
if let Element::Entier(nombre) = element {
pile.push(nombre);
continue;
}
if let Element::Variable(nom, _) = element {
let Some(variable) = variables.get(&nom) else {
return Err(ErreurPendragon::VariableInconnue(nom.into()))
};
if let Element::Entier(nombre) = variable {
pile.push(*nombre);
continue
} else {
return Err(ErreurPendragon::MauvaisType(nom.into(), variable.type_element().nom(), "entier".into()))
}
}
let Element::Operateur(ref operateur) = element else {
return Err(ErreurPendragon::MauvaisArgument(format!("{}, attendais un opérateur", element)))
};
let Some(nombre_a) = pile.pop() else {
return Err(ErreurPendragon::CalculEntier("la pile est vide".into()))
};
let Some(nombre_b) = pile.pop() else {
return Err(ErreurPendragon::CalculEntier("la pile est vide".into()))
};
match operateur {
Operateur::Plus => {
pile.push(nombre_b + nombre_a);
}
Operateur::Moins => {
if nombre_b < nombre_a {
return Err(ErreurPendragon::CalculEntier(format!("a essayé de soustraire '{}' à '{}'", nombre_comme_texte(nombre_a), nombre_comme_texte(nombre_b))))
}
pile.push(nombre_b - nombre_a);
}
Operateur::Fois => {
pile.push(nombre_b * nombre_a);
}
Operateur::Divise => {
pile.push(nombre_b / nombre_a);
}
_ => return Err(ErreurPendragon::MauvaisArgument(format!("'{}', attendais un opérateur d'entiers", element)))
}
}
if pile.len() > 1 {
return Err(ErreurPendragon::CalculEntier("la pile n'est pas vide".into()))
}
Ok(pile[0])
}
pub fn nombre_comme_texte(nombre: usize) -> String {
if nombre == 0 {
return "zéro".to_string()
}
if nombre >= 10usize.pow(18) {
return "infini".to_string()
}
let mut groupes: Vec<usize> = vec![];
let mut nombre = nombre;
while nombre > 0 {
groupes.insert(0, nombre % 1000);
nombre /= 1000;
}
let mut chaine: String = "".to_string();
for index in 0..groupes.len() {
if groupes[index] == 0 {
continue
}
let pluriel: &str = if (groupes.len() - index - 1 > 1) && groupes[index] > 1 {
"s"
} else {
""
};
if index < groupes.len() - 1 {
let union = if index > 0 {UNION} else {""};
let chiffre = if groupes.len() - index - 1 == 1 && groupes[index] == 1 { // un mille
"".to_string()
} else {
petit_nombre_comme_texte(groupes[index]) + UNION
};
chaine += &format!("{}{}{}{}",
union,
chiffre,
NOMS_SEPARATEURS[groupes.len() - index - 1],
pluriel,
);
} else {
let union = if index > 0 {UNION} else {""};
chaine += union;
chaine += &petit_nombre_comme_texte(groupes[index]);
}
}
chaine
}
fn petit_nombre_comme_texte(nombre: usize) -> String {
let nombre = nombre.clamp(0, 999);
let centaine = nombre / 100;
let dizaine = (nombre % 100) / 10;
let unité = nombre % 10;
let centaine_texte = if centaine > 1 {
format!("{}{}cent", NOMS_UNITES[centaine], UNION)
} else if centaine > 0 {
"cent".to_string()
} else {
"".to_string()
};
let décalage_dizaine = if [1, 7, 9].contains(&dizaine) {1} else {0};
let dizaine_texte = NOMS_DIZAINES[dizaine - décalage_dizaine];
let séparation = if unité == 1 && ![0, 1, 8, 9].contains(&dizaine) {UNION.to_string() + "et"} else {"".to_string()};
let unité_texte = if [1, 7, 9].contains(&dizaine) {NOMS_UNITES_DIX[unité]} else {NOMS_UNITES[unité]};
let mut texte_nombre = format!("{}{}{}{}{}{}", centaine_texte, UNION, dizaine_texte, séparation, UNION, unité_texte);
while texte_nombre.contains("--") {
texte_nombre = texte_nombre.replace("--","-");
}
if texte_nombre.starts_with("-") {
texte_nombre = texte_nombre[1..texte_nombre.len()].to_string();
}
if texte_nombre.ends_with("-") {
texte_nombre = texte_nombre[0..texte_nombre.len()-1].to_string();
}
texte_nombre
}

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

@ -0,0 +1,47 @@
use super::*;
pub fn calcule_texte(expression: Vec<Element>, variables: &HashMap<String, Element>) -> Result<String, ErreurPendragon> {
let mut pile: Vec<Element> = Vec::new();
let mut texte: String = String::new();
for element in expression {
let Element::Operateur(ref operateur) = element else {
pile.push(element);
continue;
};
let Operateur::Puis = operateur else {
pile.push(element);
continue;
};
let Some(element_pile) = pile.last() else {
continue;
};
if let TypeElement::Booleen = element_pile.type_element() {
texte += &booleen::affiche_booleen(pile.clone(), variables)?;
pile = Vec::new();
continue;
}
if let TypeElement::Entier = element_pile.type_element() {
texte += &nombre::affiche_nombre(pile.clone(), variables)?;
pile = Vec::new();
continue;
}
match element_pile {
Element::Texte(contenu) => texte += contenu,
Element::Variable(nom, type_element) => {
let Some(variable) = variables.get(nom) else {
return Err(ErreurPendragon::VariableInconnue(nom.into()))
};
let Element::Texte(contenu) = variable else {
return Err(ErreurPendragon::MauvaisType(nom.into(), type_element.nom(), "texte".into()))
};
texte += &contenu;
}
autre => {
return Err(ErreurPendragon::MauvaisArgument(format!("{}", autre)))
}
}
pile = Vec::new();
}
Ok(texte)
}

26
test.dr
View file

@ -1,19 +1,17 @@
Définis A comme entier. Définis F comme texte. Définis A comme entier. Définis B comme entier.
Définis B comme entier. Modifie B avec un.
Définis C comme entier.
Nota Bene : A est la variable pour un polynôme.
Modifie A avec dix-sept plus trois. Définis N comme entier.
Modifie C avec six-cent-soixante-douze. Modifie N avec trente.
Modifie B avec trois fois A fois ouvre la parenthèse trois fois A plus C ferme la parenthèse.
Nota Bene : 3*A*(3*A+C).
Affiche "Résultat : " puis B. Tant que N est supérieur à zéro,
Modifie N avec N moins un.
Si A supérieur ou égal à trois, Affiche A.
Affiche B.
Modifie A avec A plus B.
Modifie B avec A plus B.
Définis Bool comme booléen. Affiche "Fin".
Modifie Bool avec vrai.
Affiche vrai et faux.
Affiche alinéa puis trois plus un puis retour à la ligne puis "test". Nota Bene : Ceci est un programme qui affiche deux fois N nombres de la suite de Fibonacci.