10Fermer12
Lionel DebrouxLe 11/01/2009 à 18:19
En L3, on avait implémenté un assembleur (enfin, je veux dire, "un programme pour transformer en binaire du code source écrit en langage d'assemblage" grin) ARM. Pas de linker, on se limitait à un fichier unique par exécution, qui devait contenir tous les labels utilisés.
en utilisant pour la reconnaissance de syntaxe:
* le lexer fourni par les profs (qu'on a, si je me souviens bien, dû modifier un peu...). C'est le code qui découpe en morceaux de même nature (entier, identificateur, virgule, registre, commentaire, fin de ligne, etc.) les octets du flux d'entrée. Il skippe tout ce qui est blanc non significatif (tabs, espaces, etc.). Ces morceaux de même nature sont des LEXical EleMents (lexem, "lexème" en français).
* l'analyse syntaxique (que nous avons dû écrire à la main) qui se base sur la grammaire et utilise le lexer. Il a une organisation hiérarchique du genre (de mémoire, modifié un poil pour une syntaxe M68k)
... ReadProgram(...) {
    EndOfFile = 0
    while (EndOfFile == 0) {
        ReadLine(...)
    }
}

... ReadLine(...) {
    Lexem l = ReadLexem(...) // appel du Lexer.
    if (LexemType(l) == Identifier) {
        // Le premier lexem de la ligne est un identificateur. Cette ligne risque de contenir un label, c'est à dire un ":" après. Ou alors c'est une instruction (par exemple).
        if (DecodeInstruction(l) == FALSE) {
            // Non, ce n'était pas une instruction
            Lexem l2 = ReadLexem(...)
            if (LexemType(l2) == Colon) { // Deux points
                // On vient de lire un label. Le stocker ça dans la table des symboles.

                // Ce qu'on fait maintenant *dans ce cas* dépend de la grammaire.
                // Si la grammaire dit qu'une ligne ne doit contenir qu'un label xor une instruction xor ...
                // (modulo l'introduction de commentaires un peu partout, si on les gère autrement que du whitespace),
                // et que maintenant qu'on a lu un label, ReadLexem(...) ne rend pas un lexem de type EOL,
                // on se plaint à l'utilisateur qu'il nous rentre des choses qu'on ne comprend pas.
            }
        }
        else {
            // Tiens, on vient de lire un mnémonique d'instruction (ex. "move"). Alors le prochain truc qu'on doit lire doit être un "."
            Lexem l2 = ReadLexem(...)
            if (LexemType(l2) == Dot) {
                // C'est bon, maintenant on doit lire un identificateur "b"/"w"/"l"
                Lexem l3 = ReadLexem(...)
                if (IsSizeQualifier(l3)) { 
                    // Maintenant on doit lire un registre, ou alors un identificateur

                    // Au coup suivant, si c'est une instruction qui a plusieurs opérandes, on doit lire une virgule. Et caetera (faut que j'aille bouffer :D).
            }
            else {
                 // On se plaint à l'utilisateur qu'il n'a pas mis de . après la mnémonique d'instruction.
            }
        }
}


* une analyse sémantique, par-dessus l'analyse syntaxique. Ca vérifiait par exemple, après avoir tout lu, si on ne faisait pas référence à des labels pas définis dans ce fichier (mais ça peut être étendu pour gérer des références externes, si tu introduis une directive assembleur avec cette sémantique).


Avantage des générateurs de parsers à partir d'une grammaire: facilité d'écriture pour la personne qui les utilise (il suffit d'avoir la grammaire du langage).
Défaut: on peut faire un peu plus efficace si on écrit le parser à la main.


[EDIT: quelques retours à la ligne supplémentaires permettent de ne pas casser la mise en page...]