# Approfondissement — Une calculatrice RPN

## Notre petit langage

Après la calculatrice à pile du carnet `ap1-05-pile-calculatrice`, cet atelier
facultatif propose un autre découpage : tous les symboles de l'expression sont
séparés par des espaces. En *notation polonaise inverse* (RPN), l'opérateur suit
ses deux opérandes : `3 4 +` vaut 7, et `3 4 + 2 *` vaut 14. Les nombres sont des entiers,
éventuellement négatifs ; les opérateurs du socle sont `+`, `-` et `*`.

Le sujet et ses vérifications sont entièrement dans ce notebook. Évaluez
les cellules de haut en bas, après avoir complété le module correspondant.
Les squelettes renvoient `nil` : leurs vérifications échouent tant que vous
ne les avez pas complétés. Les cellules de vérification sont fournies comme
outils ; vous n'avez pas à apprendre leur syntaxe.

## Une pile comme accumulateur

La pile est une liste dont le sommet est à gauche. Empiler `x` revient à
construire `[x | pile]`. Un opérateur retire deux valeurs et remet leur résultat.
Voici la trace de `3 4 + 2 *` :

| Symbole lu | Pile après lecture |
|---|---|
| début | `[]` |
| `3` | `[3]` |
| `4` | `[4, 3]` |
| `+` | `[7]` |
| `2` | `[2, 7]` |
| `*` | `[14]` |

À la fin, un seul nombre doit rester. Dans `[b, a | reste]`, le sommet `b`
est le deuxième opérande : pour une soustraction, nous calculons `a - b`.
Tracez `5 3 -` avant de coder. Quel serait le résultat si vous inversiez les deux ?

## Deux fonctions fournies par Elixir

Le format impose des séparateurs : nous pouvons confier le découpage à
`String.split/1`. Cette fonction ne valide pas l'expression ; le parcours
récursif et les règles de calcul restent à écrire.

```elixir
String.split("12 3 -")
# => ["12", "3", "-"]
```

`Integer.parse/1` lit un préfixe entier et conserve le reste, comme pour la
lecture de tokens d'AP1. Pour reconnaître un symbole entier complet, le reste
doit être vide.

```elixir
{Integer.parse("12"), Integer.parse("-3"),
 Integer.parse("12x"), Integer.parse("chat")}
# => {{12, ""}, {-3, ""}, {12, "x"}, :error}
```

## Bronze — Une expression valide

Écrivez `Rpn.eval/1`, qui renvoie le nombre résultat. Pour ce premier palier,
vous pouvez supposer l'entrée valide. Utilisez la récursion et une pile en
paramètre accumulateur, sans introduire de boucle ni de pipeline.

La fonction d'entrée appelle `parcourir(String.split(texte), [])`.
Pour le parcours, prévoyez un cas de base et une clause par opérateur,
puis le cas d'un nombre. Le motif `["+" | suite]` reconnaît l'opérateur
en tête des symboles ; `[b, a | pile]` extrait ses deux opérandes.
Au cas de base, `[]` et `[resultat]` indiquent que le calcul est terminé.

```elixir
defmodule Rpn do
  def eval(texte) do
    # Lancez le parcours avec une pile vide.
    nil
  end

  # Ajoutez vos clauses de parcourir/2 ici.
end
```

Vérification bronze :

```elixir
for {expression, attendu} <- [
  {"3 4 +", 7}, {"3 4 + 2 *", 14}, {"5 1 2 + 4 * + 3 -", 14},
  {"5 3 -", 2}, {"12 3 -", 9}, {"-3 2 *", -6}, {"42", 42},
  {"  3   4 +  ", 7}
] do
  obtenu = Rpn.eval(expression)
  if obtenu != attendu do
    raise "#{inspect(expression)} : attendu #{attendu}, obtenu #{inspect(obtenu)}"
  end
end
IO.puts("✅ Bronze : calculs et ordre des opérandes")
```

## Argent — Refuser une expression incorrecte

Copiez votre module dans un nouveau module `RpnSur`, pour garder les
vérifications bronze utilisables. `RpnSur.eval/1` renvoie désormais
`{:ok, resultat}` ou `{:error, raison}`. Choisissez des raisons explicites :

- `:operandes_manquants` pour `3 +` ;
- `:symbole_inconnu` pour `3 4 &` ou `12x` ;
- `:pile_finale_invalide` pour une entrée vide ou plusieurs nombres restants.

Les clauses de succès des opérateurs viennent avant leurs clauses d'erreur.
Pour lire un nombre, vous pouvez passer le résultat de `Integer.parse/1`
à une fonction auxiliaire : une clause reconnaît `{nombre, ""}`, l'autre
refuse tout autre résultat. Vous réutilisez ainsi les motifs connus d'AP1 et d'AP2.

```elixir
defmodule RpnSur do
  def eval(texte) do
    # Reprenez votre parcours et ajoutez les cas d'erreur.
    nil
  end
end
```

Vérification argent (après avoir complété ce palier) :

```elixir
for {expression, attendu} <- [
  {"3 4 + 2 *", {:ok, 14}}, {"5 3 -", {:ok, 2}},
  {"12 -3 *", {:ok, -36}}, {"42", {:ok, 42}},
  {"3 +", {:error, :operandes_manquants}},
  {"+", {:error, :operandes_manquants}},
  {"3 -", {:error, :operandes_manquants}},
  {"3 *", {:error, :operandes_manquants}},
  {"3 4 &", {:error, :symbole_inconnu}},
  {"12x", {:error, :symbole_inconnu}},
  {"", {:error, :pile_finale_invalide}},
  {"   ", {:error, :pile_finale_invalide}},
  {"3 4 5 +", {:error, :pile_finale_invalide}}
] do
  obtenu = RpnSur.eval(expression)
  if obtenu != attendu do
    raise "#{inspect(expression)} : attendu #{inspect(attendu)}, obtenu #{inspect(obtenu)}"
  end
end
IO.puts("✅ Argent : succès, erreurs et cas limites")
```

## Or — Une extension au choix

Vous pouvez ajouter la division et son erreur de division par zéro, produire
la trace des piles successives, ou reconnaître les opérateurs unaires `neg`
et `abs`. Fixez le contrat, calculez deux exemples à la main et écrivez les
vérifications correspondantes. Une seule extension bien expliquée suffit.

```elixir
# Votre extension et vos vérifications personnelles.
```

## Votre explication et le rendu

Dans une cellule de texte, tracez `5 3 -`, expliquez le rôle de la pile et
ce qui diminue à chaque appel. Indiquez pourquoi `{12, "x"}` ne suffit pas
à reconnaître un entier complet. Ajoutez au moins deux exemples personnels.

Conservez le module `Rpn` et ses vérifications bronze. Si vous avez fait
l'argent, conservez aussi `RpnSur`. Si vous vous arrêtez au bronze, retirez
le squelette argent et sa cellule de vérification avant de réévaluer tout
le notebook. Précisez le palier atteint et ce qui vous bloque encore.

Enregistrez ce fichier dans `entrainement/` de votre dépôt `ap-homework`.
Ce complément est facultatif et peut être joint dans `AP3/` au rendu unique
du cycle pour un bonus. Les notebooks et les cellules à rendre seront précisés en cours, selon notre avancement. Pour toute question, contactez
[aurelien.noce@minesparis.psl.eu](mailto:aurelien.noce@minesparis.psl.eu).
