ENSISA 1A
Année 2025–2026
Semestre 1

De zéro à λ en C — Correction

Barème : 20 points. Les réponses ci-dessous détaillent le barème et les attendus pour chaque question.

Partie 1 — Questions de cours (4 pts)

Q1. (1 pt)

Réponse

Barème : 0,5 pt pour taille/format/plage, 0,5 pt pour l'overflow silencieux.

Q2 — Structures chaînées et capture d'état. (1 pt)

Réponse

Barème : 0,3 pt définition, 0,4 pt représentation, 0,3 pt gel.

Q3 — Insertion vs modification dans une liste chaînée. (1 pt)

Réponse

Exemple :

(let x 10)
(let f (lambda () (let x 20)))  (f) x   → 10  (nouveau binding local)
(let g (lambda () (set! x 20))) (g) x   → 20  (mutation du x original)

Barème : 0,3 pt let, 0,3 pt set!, 0,4 pt exemple correct.

Q4. (1 pt)

Réponse

RPN de (3 + 4) × (5 − 2) :

3 4 + 5 2 − ×

Barème : 0,4 pt explication, 0,6 pt RPN correcte.

Partie 2 — Analyse de code C (5 pts)

Q5. (2 pts)

Réponse

Ce que calcule mystere :

Trace pour n=5 :

Appelntemp (lu)res*pretour
m(5)510101520
m(4)4641010
m(3)33164
m(2)21031
m(1)110

Affichage : r = 20, x = 15

Barème : 0,5 pt explication, 1 pt tracé correct, 0,5 pt affichage.

Q6. (1,5 pts)

Réponse

Trace détaillée de la pile d'appels pour mystere(3, &x) :

┌─ mystere(3, &x)─────────────────────────────┐
│ n=3  p→x  temp=⊘  res=⊘                     │
│   ┌─ mystere(2, &temp)───────────────────┐  │
│   │ n=2  p→temp  tmp=⊘  res=⊘            │  │
│   │   ┌─ mystere(1, &tmp) ────────────┐  │  │
│   │   │ n=1  p→tmp                     │  │  │
│   │   │ *p = 1  ← tmp = 1             │  │  │
│   │   │ return 0                       │  │  │
│   │   └────────────────────────────────┘  │  │
│   │ tmp=1, res=0                          │  │
│   │ *p = 2 + 1 = 3  ← temp = 3           │  │
│   │ return 0 + 1 = 1                      │  │
│   └───────────────────────────────────────┘  │
│ temp=3, res=1                                │
│ *p = 3 + 3 = 6  ← x = 6                     │
│ return 1 + 3 = 4                             │
└──────────────────────────────────────────────┘
⇒ r = 4, x = 6

Barème : 0,5 pt par niveau correct de la pile.

Q7. (1,5 pts)

Réponse

int mystere_iter(int n, int *p) {
    int ret = 0;
    *p = 0;
    for (int i = 1; i <= n; i++) {
        *p = *p + i;          /* T(i) = T(i-1) + i */
        if (i < n)
            ret += *p;        /* somme des T(1)…T(n-1) */
    }
    return ret;
}

Vérification pour n=5 :

i*p (avant)*p (après)ret
1011
2134
33610
461020
5101520 (pas d'ajout)

Résultat : r=20, x=15. ✓

Barème : 0,5 pt boucle correcte, 0,5 pt mise à jour de *p, 0,5 pt accumulation de ret.

Partie 3 — S-expressions et parcours d'arbre (4 pts)

Dans tout ce qui suit, Sexp est une structure C avec union et champ type.

Q8. (1 pt)

Réponse

Arbre de (+ (* 2 3) (- 10 (/ 8 4))) en paires CONS :

         CONS
        ╱    ╲
      SYM     CONS
      "+"    ╱    ╲
           CONS    CONS
          ╱    ╲  ╱    ╲
        SYM   CONS SYM  CONS
        "*"  ╱    ╲ "-" ╱    ╲
            INT  CONS  INT  CONS
             2  ╱    ╲  10  ╱    ╲
              INT  NULL   CONS  NULL
               3         ╱    ╲
                       SYM    CONS
                       "/"   ╱    ╲
                           INT   CONS
                            8   ╱    ╲
                              INT   NULL
                               4

Barème : structure correcte (symboles, entiers, listes imbriquées). Barème souple — un schéma lisible suffit.

Q9. (2 pts)

Réponse

int profondeur(Sexp *e) {
    if (e == NULL) return 0;
    if (e->type != SEXPR_CONS) return 0;  /* atome → 0 */

    int max_elem = 0;
    Sexp *cur = e;
    while (cur != NULL && cur->type == SEXPR_CONS) {
        int d = profondeur(cur->car);
        if (d > max_elem) max_elem = d;
        cur = cur->cdr;
    }
    return 1 + max_elem;
}

Explication : On parcourt la liste (la « colonne vertébrale » de CONS), on calcule récursivement la profondeur de chaque élément (car), et on prend le max. Un niveau de liste ajoute 1.

Test avec (+ 2 (* 3 4)) : les éléments sont + (0), 2 (0), (* 3 4) (1). Max = 1, donc profondeur = 2. ✓

Barème : 0,5 pt cas de base, 0,5 pt boucle de parcours, 0,5 pt appel récursif, 0,5 pt retour.

Q10. (1 pt)

Réponse

Profondeur = 3

Analyse :

Barème : réponse correcte = 1 pt.

Partie 4 — VM et bytecode (4 pts)

Q11. (2 pts)

Réponse

Bytecode pour (8 − 2 × 3) × 4 :

PUSH 8
PUSH 2
PUSH 3
MUL        ; 2 × 3 = 6
SUB        ; 8 − 6 = 2
PUSH 4
MUL        ; 2 × 4 = 8
PRINT
HALT

État de la pile après chaque instruction :

#InstructionPile (sommet à droite)
1PUSH 8[8]
2PUSH 2[8, 2]
3PUSH 3[8, 2, 3]
4MUL[8, 6]
5SUB[2]
6PUSH 4[2, 4]
7MUL[8]
8PRINT[]
9HALT[]

Barème : 1 pt séquence correcte, 1 pt tableau pile.

Q12. (2 pts)

Réponse

int eval_bytecode(int code[], int taille) {
    int pile[256], sp = 0;

    for (int ip = 0; ip < taille; ) {
        int op = code[ip++];
        switch (op) {
        case 0: /* PUSH v */
            pile[sp++] = code[ip++];
            break;
        case 1: /* ADD */
            sp--;
            pile[sp-1] += pile[sp];
            break;
        case 2: /* SUB */
            sp--;
            pile[sp-1] -= pile[sp];
            break;
        case 3: /* MUL */
            sp--;
            pile[sp-1] *= pile[sp];
            break;
        case 4: /* DIV */
            sp--;
            if (pile[sp] == 0) {
                printf("Erreur: /0\n");
                return 0;
            }
            pile[sp-1] /= pile[sp];
            break;
        case 5: /* PRINT */
            printf("%d\n", pile[--sp]);
            break;
        case 6: /* HALT */
            return sp > 0 ? pile[sp-1] : 0;
        }
    }
    return sp > 0 ? pile[sp-1] : 0;
}

Barème : 0,5 pt structure (pile, sp, boucle), 0,5 pt PUSH/ADD/SUB/MUL, 0,5 pt DIV avec /0, 0,5 pt PRINT/HALT/retour.

Partie 5 — Architecture de l'interpréteur (3 pts)

Q13. (1,5 pts)

Réponse

apply_closure applique une fermeture à des arguments :

  1. Extraire la fermeture : vérifier que c'est une liste (fun (params) body env_snapshot).
  2. Reconstruire la chaîne d'environnements à partir du env_snapshot via env_from_snapshot (parcours et recréation des Env).
  3. Lier les paramètres aux arguments : pour chaque paramètre (dans l'ordre), évaluer l'argument correspondant et l'ajouter au nouvel environnement avec env_bind.
  4. Évaluer le corps dans le nouvel environnement (qui a pour parent la chaîne issue du snapshot).
  5. Retourner le résultat.

Point clé : le corps est évalué dans l'environnement gelé (celui de la création), pas dans l'environnement d'appel → portée lexicale.

Barème : 0,3 pt extraction, 0,3 pt snapshot, 0,5 pt liaison params→args, 0,4 pt évaluation du corps.

Q14. (1,5 pts)

Réponse

Principe du comptage de références :

Pourquoi est-ce nécessaire ?

Sans refcounting : crash (double free ou use-after-free) dès qu'une cellule est partagée entre plusieurs environnements.

Barème : 0,5 pt mécanisme, 0,5 pt nécessité (partage, mutation), 0,5 pt conséquences sans refcounting.

— Fin de la correction —