affiche et addition

This commit is contained in:
WanderingPenwing 2024-12-06 23:43:35 +01:00
parent 73d53f33eb
commit 8020b189ae
7 changed files with 354 additions and 6 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "sophie"
version = "0.1.0"

8
Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "sophie"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

18
shell.nix Normal file
View file

@ -0,0 +1,18 @@
{ pkgs ? import <nixpkgs> { overlays = [ (import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz)) ]; },}:
with pkgs;
mkShell {
nativeBuildInputs = with xorg; [
pkg-config
] ++ [
cargo
rustc
];
buildInputs = [
latest.rustChannels.stable.rust
xorg.libX11
xorg.libXi
xorg.libXtst
libevdev
];
}

View file

@ -1,13 +1,119 @@
use std::env;
use std::fs;
use std::fmt;
mod nombres;
enum ErreurSophie {
CommandeInconnue(String),
PhraseVide,
ManqueArgument,
OrthographeNombre(String),
}
impl fmt::Display for ErreurSophie {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CommandeInconnue(commande) => write!(f, "La commande \"{}\" est inconnue.", commande),
Self::PhraseVide => write!(f, "La phrase est vide."),
Self::ManqueArgument => write!(f, "Il manque un argument."),
Self::OrthographeNombre(nombre) => write!(f, "Le nombre \"{}\" est mal orthographié.", nombre),
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let arguments: Vec<String> = env::args().collect();
let file_path = &args[1];
if arguments.len() < 2 {
eprintln!("Utilisation : sophie <FILE>");
return
}
let chemin_de_fichier = &arguments[1];
let contents = fs::read_to_string(file_path)
.expect("Should have been able to read the file");
println!("content : \n{}", contents);
match fs::read_to_string(chemin_de_fichier) {
Ok(contenu) => {
execute(contenu);
}
Err(raison) => {
eprintln!("Fichier illisible : {}", raison);
}
}
}
fn execute(contenu: String) {
let contenu_propre = contenu.replace("\n", "");
let mut texte: Vec<&str> = contenu_propre.split('.').collect();
texte.pop(); // remove empty phrase after last dot
for (index_phrase, phrase) in texte.iter().enumerate() {
match execute_phrase(phrase) {
Ok(_) => {},
Err(raison) => {
eprintln!("Erreur phrase {} : {}", index_phrase + 1, raison);
return
}
}
}
}
fn execute_phrase(phrase: &str) -> Result<(), ErreurSophie> {
let phrase = phrase.trim();
let parties: Vec<&str> = phrase.splitn(2, ' ').collect();
if parties.is_empty() {
return Err(ErreurSophie::PhraseVide)
}
if parties.len() == 1 {
return Err(ErreurSophie::ManqueArgument)
}
match parties[0] {
"Modifie" => {
modifie(parties[1])?;
},
"Affiche" => {
affiche(parties[1])?;
},
"Demande" => {
demande(parties[1])?;
}
autre_commande => {
return Err(ErreurSophie::CommandeInconnue(autre_commande.to_string()))
}
};
Ok(())
}
fn modifie(arguments: &str) -> Result<(), ErreurSophie> {
println!("- modifie : {}", arguments);
Ok(())
}
fn affiche(arguments: &str) -> Result<(), ErreurSophie> {
let liste_arguments: Vec<&str> = arguments.split(',').collect();
let mut texte = "".to_string();
for argument in liste_arguments {
let argument: &str = argument.trim();
if argument.starts_with('"') {
if argument.ends_with('"') {
texte += &argument[1..argument.len()-1];
}
} else {
let resultat = nombres::operation(argument)?;
texte += &nombres::nombre_comme_texte(resultat);
}
}
println!("{}", texte);
Ok(())
}
fn demande(arguments: &str) -> Result<(), ErreurSophie> {
println!("- demande : {}", arguments);
Ok(())
}

206
src/nombres.rs Normal file
View file

@ -0,0 +1,206 @@
use super::ErreurSophie;
const NOMS_UNITES: [&str; 10] = ["", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf"];
const NOMS_UNITES_DIX: [&str; 10] = ["dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf"];
const NOMS_DIZAINES: [&str; 9] = ["", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "x", "quatre-vingts"];
const NOMS_SEPARATEURS: [&str; 7] = ["", "mille", "million", "milliard", "billion", "billiard", "trillion"];
const UNION: &str = "-";
pub 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 décalage_dizaine = if [1, 7, 9].contains(&dizaine) {1} else {0};
let centaine_texte = if centaine > 1 {
format!("{}{}cent", NOMS_UNITES[centaine], UNION)
} else if centaine > 0 {
"cent".to_string()
} else {
"".to_string()
};
let dizaine_union = if centaine > 0 && dizaine > 0 {
UNION.to_string()
} else {
"".to_string()
};
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é_union = if nombre - unité > 0 && unité > 0 {
UNION.to_string()
} else {
"".to_string()
};
let unité_texte = if [1, 7, 9].contains(&dizaine) {
unité_union + NOMS_UNITES_DIX[unité]
} else {
unité_union + NOMS_UNITES[unité]
};
format!("{}{}{}{}{}", centaine_texte, dizaine_union, dizaine_texte, séparation, unité_texte)
}
pub fn texte_comme_nombre(texte: &str) -> Result<usize, ErreurSophie> {
let pluriel = format!("s{}", UNION);
let mut petits_nombres_texte: Vec<&str> = vec![];
let mut texte_modifie = texte;
let mut dernier_separateur = 0;
for (index, separateur_texte) in NOMS_SEPARATEURS.iter().enumerate() {
if index == 0 {
continue
}
let mille: bool = texte_modifie.starts_with("mille");
let texte_separe: Vec<&str> = texte_modifie.split(separateur_texte).collect();
if texte_separe.len() > 2 {
return Err(ErreurSophie::OrthographeNombre(texte.to_string()))
}
if texte_separe.len() > 1 {
let petit_nombre_texte = texte_separe[1]
.trim_start_matches(&pluriel)
.trim_start_matches(UNION)
.trim_end_matches(UNION);
petits_nombres_texte.push(petit_nombre_texte);
texte_modifie = if mille {
"un"
} else {
texte_separe[0]
};
dernier_separateur = index;
} else if !petits_nombres_texte.is_empty() {
petits_nombres_texte.push("");
}
}
let petit_nombre_texte = texte_modifie
.trim_start_matches(&pluriel)
.trim_start_matches(UNION)
.trim_end_matches(UNION);
petits_nombres_texte.insert(dernier_separateur, petit_nombre_texte);
let mut nombre: usize = 0;
for (index, petit_nombre_texte) in petits_nombres_texte.iter().enumerate() {
let petit_nombre = texte_comme_petit_nombre(petit_nombre_texte)?;
nombre += petit_nombre * 1000usize.pow(index as u32);
}
Ok(nombre)
}
fn texte_comme_petit_nombre(texte: &str) -> Result<usize, ErreurSophie> {
let elements: Vec<&str> = texte.split(UNION).collect();
let mut nombre = 0;
let mut dernier_chiffre_texte = "";
for chiffre_texte in elements {
if chiffre_texte == "cent" {
if nombre == 0 {
nombre = 1;
}
if nombre >= 100 {
return Err(ErreurSophie::OrthographeNombre(texte.to_string()))
}
nombre *= 100;
dernier_chiffre_texte = chiffre_texte;
continue
}
if chiffre_texte == "vingts" {
if dernier_chiffre_texte == "quatre" {
nombre += 80 - 4;
dernier_chiffre_texte = chiffre_texte;
continue
} else {
return Err(ErreurSophie::OrthographeNombre(texte.to_string()))
}
}
if let Some(chiffre) = NOMS_UNITES.iter().position(|&s| s == chiffre_texte) {
if nombre%10 > 0 {
return Err(ErreurSophie::OrthographeNombre(texte.to_string()))
}
nombre += chiffre;
dernier_chiffre_texte = chiffre_texte;
continue
}
if let Some(chiffre) = NOMS_DIZAINES.iter().position(|&s| s == chiffre_texte) {
if nombre%100 > 0 && chiffre != 1 {
return Err(ErreurSophie::OrthographeNombre(texte.to_string()))
}
nombre += chiffre*10;
dernier_chiffre_texte = chiffre_texte;
continue
}
if let Some(chiffre) = NOMS_UNITES_DIX.iter().position(|&s| s == chiffre_texte) {
if nombre%10 > 0 {
return Err(ErreurSophie::OrthographeNombre(texte.to_string()))
}
nombre += 10 + chiffre;
dernier_chiffre_texte = chiffre_texte;
continue
}
return Err(ErreurSophie::OrthographeNombre(texte.to_string()))
}
Ok(nombre)
}
pub fn operation(arguments: &str) -> Result<usize, ErreurSophie> {
let somme_texte: Vec<&str> = arguments.split("plus").collect();
let mut somme : usize = 0;
for element in somme_texte {
let element_propre: &str = element.trim();
somme += texte_comme_nombre(element_propre)?
}
Ok(somme)
}

2
test.sp Normal file
View file

@ -0,0 +1,2 @@
Affiche "Nombre 1 : ", deux-million plus mille-sept-cent-trente-deux.
Affiche "Nombre 2 : ", sept-cent plus cinq-cent-quarante-quatre.