ENSISA 1A
Année 2025–2026
Semestre 1

De zéro à λ en C — Correction — Sujet B

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,3 pt plages/formats, 0,3 pt -1 en %u, 0,4 pt wrap-around défini.

Q2 — S-expressions : types et représentation. (1 pt)

Réponse

Barème : 0,4 pt les quatre types, 0,6 pt représentation des listes.

Q3 — Évaluation différée. (1 pt)

Réponse

Barème : 0,3 pt quote, 0,3 pt évaluation normale, 0,4 pt exemple.

Q4. (1 pt)

Réponse

Barème : 0,4 pt cycle FDE, 0,3 pt IP, 0,3 pt SP.

Partie 2 — Analyse de code C (5 pts)

Q5. (2 pts)

Réponse

Ce que calcule fibo : le n-ième nombre de Fibonacci (F₀=0, F₁=1, Fₙ = Fₙ₋₁ + Fₙ₋₂). *compteur compte le nombre total d'appels à fibo.

Affichage : r = 5, c = 15

Explication : F₅ = 5. Le nombre d'appels pour Fₙ suit la récurrence C(0)=C(1)=1, C(n)=1+C(n-1)+C(n-2). Pour n=5 : C(5)=15.

Comptage pour n=5 :

fibo(5)
├─ fibo(4)
│  ├─ fibo(3)
│  │  ├─ fibo(2)
│  │  │  ├─ fibo(1) → 1
│  │  │  └─ fibo(0) → 0
│  │  └─ fibo(1) → 1
│  └─ fibo(2)
│     ├─ fibo(1) → 1
│     └─ fibo(0) → 0
└─ fibo(3)
   ├─ fibo(2)
   │  ├─ fibo(1) → 1
   │  └─ fibo(0) → 0
   └─ fibo(1) → 1
Total : 15 appels (1 + 9 sous-appels + 5 feuilles)

Barème : 0,5 pt identification de Fibonacci, 0,5 pt affichage, 1 pt comptage des appels.

Q6. (1,5 pts)

Réponse

Arbre d'appels pour fibo(4, &c) :

fibo(4, &c)     c=1
├─ fibo(3, &c)  c=2
│  ├─ fibo(2, &c)  c=3
│  │  ├─ fibo(1, &c)  c=4 → 1
│  │  └─ fibo(0, &c)  c=5 → 0
│  └─ fibo(1, &c)  c=6 → 1
└─ fibo(2, &c)  c=7
   ├─ fibo(1, &c)  c=8 → 1
   └─ fibo(0, &c)  c=9 → 0

Total : 9 appels pour F₄. Résultat : F₄ = 3 (0+1+1+2+...).

Barème : 0,5 pt structure arborescente, 0,5 pt valeurs n, 0,5 pt compteur.

Q7. (1,5 pts)

Réponse

int fibo_iter(int n, int *compteur) {
    if (n <= 1) {
        (*compteur)++;
        return n;
    }
    int a = 0, b = 1;  /* F₀, F₁ */
    *compteur = 2;     /* appels pour a et b déjà comptés */
    for (int i = 2; i <= n; i++) {
        int tmp = a + b;
        a = b;
        b = tmp;
        (*compteur)++;
    }
    return b;
}

Vérification pour n=5 : F₅ = 5, compteur = 6 (2 + 4 itérations). ✓

Barème : 0,5 pt boucle correcte, 0,5 pt calcul de Fibonacci, 0,5 pt compteur.

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 (list 1 (list 2 3) 4) :

       CONS
       ╱    ╲
     SYM     CONS
    "list"  ╱    ╲
           INT    CONS
            1    ╱    ╲
               CONS    CONS
               ╱    ╲  ╱    ╲
             SYM   CONS INT  NULL
            "list" ╱    ╲  4
                  INT   CONS
                   2   ╱    ╲
                      INT   NULL
                       3

Barème : structure correcte. Barème souple — un schéma lisible suffit.

Q9. (2 pts)

Réponse

int compte_atomes(Sexp *e) {
    if (e == NULL) return 0;
    if (e->type != SEXPR_CONS) return 1;  /* atome (INT ou SYM) */

    int total = 0;
    Sexp *cur = e;
    while (cur != NULL && cur->type == SEXPR_CONS) {
        total += compte_atomes(cur->car);
        cur = cur->cdr;
    }
    return total;
}

Explication : On parcourt la liste chaînée ; pour chaque élément (car), on compte récursivement ses atomes. Les atomes (INT, SYM) renvoient 1, NULL renvoie 0.

Test : '(1 2 (3 4)) → atomes : 1, 2, 3, 4 = 4. ✓

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

4 atomes.

Total : list + 1 + list + 2 + 3 + 4 = 6 atomes

Barème : réponse correcte = 1 pt. (Piège : le list est aussi un atome.)

Partie 4 — VM et bytecode (4 pts)

Q11. (2 pts)

Réponse

Bytecode pour ((10 + 2) × 3) − (8 ÷ 2) :

PUSH 10
PUSH 2
ADD        ; 10 + 2 = 12
PUSH 3
MUL        ; 12 × 3 = 36
PUSH 8
PUSH 2
DIV        ; 8 ÷ 2 = 4
SUB        ; 36 − 4 = 32
PRINT
HALT

État de la pile après chaque instruction :

#InstructionPile (sommet à droite)
1PUSH 10[10]
2PUSH 2[10, 2]
3ADD[12]
4PUSH 3[12, 3]
5MUL[36]
6PUSH 8[36, 8]
7PUSH 2[36, 8, 2]
8DIV[36, 4]
9SUB[32]
10PRINT[]
11HALT[]

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

Q12. (2 pts)

Réponse

int eval_bytecode_v2(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: /* DUP */
            pile[sp] = pile[sp-1];
            sp++;
            break;
        case 2: /* ADD */
            sp--;
            pile[sp-1] += pile[sp];
            break;
        case 3: /* SUB */
            sp--;
            pile[sp-1] -= pile[sp];
            break;
        case 4: /* MUL */
            sp--;
            pile[sp-1] *= pile[sp];
            break;
        case 5: /* DIV */
            sp--;
            if (pile[sp] == 0) {
                printf("Erreur: /0\n");
                return 0;
            }
            pile[sp-1] /= pile[sp];
            break;
        case 6: /* PRINT */
            printf("%d\n", pile[--sp]);
            break;
        case 7: /* 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/DUP, 0,5 pt ADD/SUB/MUL/DIV avec /0, 0,5 pt PRINT/HALT/retour.

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

Q13. (1,5 pts)

Réponse

eval détermine le type d'expression en regardant le champ type de la S-expression :

  1. Entier (SEXPR_INT) → retourne l'entier (valeur immédiate).
  2. Symbole (SEXPR_SYM) → cherche la valeur du symbole dans l'environnement via env_lookup.
  3. Liste vide (NULL) → retourne NULL (ou provoque une erreur selon l'implémentation).
  4. Liste (SEXPR_CONS) → examine le car :
    • Si c'est une forme spéciale (let, set!, lambda, if, quote, begin, define, and/or) → traitement spécifique sans évaluer tous les arguments.
    • Sinon → appel de fonction : évaluer le car (qui doit produire une closure), évaluer les arguments (cdr), puis appeler apply_closure.

Barème : 0,3 pt dispatch par type, 0,4 pt entiers/symboles/NULL, 0,4 pt formes spéciales, 0,4 pt appel de fonction.

Q14. (1,5 pts)

Réponse

Barème : 0,5 pt structure chaînée, 0,5 pt env_lookup, 0,5 pt shadowing.

— Fin de la correction du sujet B —