This shows you the differences between two versions of the page.
alf:laboratoare:04 [2021/03/25 12:29] alexandru.radovici |
alf:laboratoare:04 [2021/03/26 19:43] (current) diana.ghindaoanu |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== TP 4 - Introduction à ANTLR4. Lexer ====== | + | ====== TP 4 - Parser ====== |
+ | Le but de ce TP est d'introduire la notion de ''parser'' en ANTLR4. | ||
- | Ce TP est destiné à vous présenter le fonctionnement d'un **lexer**, son but étant de diviser un texte dans des parties identifiables, appelées jetons (**tokens**), comme dans l'exemple suivant. | + | Un **parser** représente la partie d'un compilateur qui reconnaît des jetons (//tokens//) ordonnés et spécifiés par un ensemble de règles d'analyse. Cet ensemble de règles décrit un "langage" source reçu par le parser sous la forme d'instructions séquentielles du programme source, des entrées de la ligne de commande, de balises ou d'autres. |
- | {{ :alf:laboratoare:lexer.png?600&nolink }} | + | L'analyseur généré fait correspondre les jetons d'entrée (tokens définis par le lexer) aux règles, émettant un **arbre de syntaxe abstraite (AST)**, dont on va discuter plus dans les TPs suivants. Cet arbre représente la sortie de l'analyseur. |
- | En informatique, on va réaliser cette division à l'aide de l'outil ANTLR, spécialement créé pour traiter les textes structurés. | + | {{ :alf:laboratoare:parser.png?800&nolink }} |
- | ===== ANTLR4 ===== | ||
- | ANTLR (ANother Tool for Language Recognition) est un générateur d'analyseurs pour lire, traiter, exécuter ou traduire du texte structuré ou des fichiers binaires. Il est largement utilisé pour créer des langages, des outils et des frameworks. À partir d'une description de langage formel, ANTLR génère un analyseur pour ce langage qui peut automatiquement créer des arbres d'analyse, dont on va discuter dans les cours et les TPs suivants. ANTLR génère également automatiquement des arborescences qu'on peut utiliser pour visiter les nœuds de ces arbres afin d'exécuter du code spécifique à l'application. | ||
+ | ===== Règles du parser ===== | ||
+ | La règle la plus simple est juste un nom de règle suivi d'une seule alternative terminée par un point-virgule, comme on l'a vu dans le TP précédent: | ||
+ | <code> | ||
+ | grammar Alf; | ||
+ | start:; | ||
+ | </code> | ||
- | ==== Configuration ==== | + | Lorsqu'une règle se compose de plusieurs aternatives, on utilise le séparateur ''|'' pour les différencier: |
+ | <code> | ||
+ | grammar Alf; | ||
- | L'outil nécessaire pour générer du code TypeScript à partir d'ANTLR4 est écrit en Java. Pour utiliser pleinement la cible ANTLR4 TypeScript (y compris la possibilité de régénérer le code à partir d'un fichier après les modifications), un environnement d'exécution Java (JRE) doit être installé sur la machine du développeur. Le code généré lui-même utilise plusieurs fonctionnalités nouvelles de TypeScript 2.0. | + | start: expression ';' ; |
- | === Installation globale === | + | expression: addition |
+ | | subtraction | ||
+ | | multiplication | ||
+ | | division | ||
+ | ; | ||
+ | </code> | ||
- | - Installer une version de [[https://www.java.com/en/|Java]] 1.6+ (on recommande 1.8+). Vous pouvez vérifier l'installation à l'aide de la commande suivante: {{ :alf:laboratoare:java-version.PNG?400&nolink }} | ||
- | - Télécharger le runtime complet d'antlr4 pour Java, ainsi que l'archive pour JavaScript depuis ce [[https://www.antlr.org/download/|lien]]. Le fichier ''.jar'' et le dossier decompressé pour JavaScript doivent etre sauvegardés dans un dossier spécial que vous devrez créer sur votre ordinateur, **C:\Javalib**. {{ :alf:laboratoare:runtime.JPG?550&nolink }} | ||
- | - Ajouter ''antlr-4.9-complete.jar'' à la variable d'environnement CLASSPATH (aussi pour les variables de systeme que pour celles de votre utilisateur). Vous pouvez lire plus sur les variables d'environnement [[https://support.shotgunsoftware.com/hc/en-us/articles/114094235653-Setting-global-environment-variables-on-Windows|ici]]. {{ :alf:laboratoare:set-system-var.PNG?450&nolink }} | ||
- | - Créer 2 fichiers [[https://www.computerhope.com/jargon/b/batchfil.htm|batch]]. | ||
- | - Le fichier ''antlr4.bat'' permet d'utiliser antlr4 dans la ligne de commande depuis n'importe quel fichier. Il doit contenir la commande **java org.antlr.v4.Tool %**. | ||
- | - Le fichier ''grun.bat'' est utilisé pour tester le fichier d'entrée et pour afficher l'arbre d'analyse sous des formes différentes. Il doit contenir la commande **java org.antlr.v4.runtime.misc.TestRig %***. {{ :alf:laboratoare:batch-files.PNG?550&nolink }} | ||
- | - Ajouter le dossier **C:\Javalib** a la variable d'environnement PATH. {{ :alf:laboratoare:set-javalib-path.PNG?500&nolink }} | ||
- | - Vérifier l'installation en tapant ''antlr4'' dans le Command Prompt. {{ :alf:laboratoare:check-antlr4.PNG?400&nolink }} | ||
- | <note important> | + | ==== Actions et attributs ==== |
- | Pour plus d'informations, vous pouvez consulter ce [[https://github.com/antlr/antlr4/blob/master/doc/getting-started.md|tutoriel]]. | + | Les **actions** représentent des blocs de texte écrits dans le langage de programmation cible et délimités par des accolades ''{}''. Elles sont déclenchées selon leur position dans la grammaire. |
- | </note> | + | |
- | === Installation pour TypeScript === | + | <code> |
- | - Créer un nouveau projet en Visual Studio Code et initialisez le fichier **package.json** avec la commande ''npm init''. | + | grammar Alf; |
- | - Installer ''antlr4ts'' en tant que dépendance d'exécution (runtime dependency) depuis le Terminal de VSCode. <code bash>npm install antlr4ts --save</code> | + | |
- | - Installer ''antlr4ts-cli'' en tant que dépendance de développement depuis le Terminal de VSCode.<code bash>npm install antlr4ts-cli --save-dev</code> | + | |
- | - Installer [[https://marketplace.visualstudio.com/items?itemName=mike-lischke.vscode-antlr4|l'extension d'ANTLR4]] depuis le navigateur web ou directement du MarketPlace de Visual Studio Code. {{ :alf:laboratoare:antlr4-extension.JPG?550&nolink }} | + | |
- | - Redemarrer Visual Studio Code | + | |
- | - Ajouter un nouveau //script// dans le fichier **package.json** avec le contenu <code javascript>"scripts": { | + | |
- | // ... | + | |
- | "antlr4ts": "antlr4ts -visitor path/to/Alf.g4" | + | |
- | }</code> {{ :alf:laboratoare:packagejson.JPG?400&nolink }} | + | |
- | - Configurer le fichier **tsconfig.json**. <code json>{ | + | |
- | "compilerOptions": { | + | |
- | "target": "es2016", | + | |
- | "module": "commonjs", | + | |
- | "strict": true, | + | |
- | "esModuleInterop": true, | + | |
- | "skipLibCheck": true, | + | |
- | "forceConsistentCasingInFileNames": true, | + | |
- | "lib": ["es2016"], | + | |
- | "moduleResolution": "node" | + | |
- | } | + | |
- | }</code> | + | |
- | - Télécharger les dépendences suivantes: <code bash>npm install --save-dev @types/node | + | |
- | npm install --save-dev typescript</code> | + | |
- | <note important> | + | declaration: type VARIABLE ';' { console.log('This is a declaration statement!'); } |
- | Vous pouvez lire plus de détails sur la configuration d'ANTLR4 pour TypeScript [[https://github.com/tunnelvisionlabs/antlr4ts|ici]]. | + | ; |
- | </note> | + | type : 'string' |
+ | | 'number' | ||
+ | | 'boolean' | ||
+ | ; | ||
+ | </code> | ||
- | ===== Lexer ===== | + | Dans la plupart des cas, les actions sont utilisées pour accéder aux attributs des tokens et aux références des regles qu'on a définies. Par exemple, dans la grammaire suivant, on va afficher pour chaque type d'expression la valeur numérique des opérants et le token pour l'opérateur: |
- | En informatique, //l'analyse lexicale//, le //lexing// ou la //tokenisation// est le processus de conversion d'une séquence de caractères (comme dans un programme informatique) en une séquence de jetons (chaînes avec une signification attribuée et donc identifiée). Un programme qui effectue une analyse lexicale peut être appelé un **lexer**. | + | <code> |
+ | grammar Alf; | ||
+ | start: expression | ||
+ | ; | ||
- | Un lexer est généralement combiné avec un //parser//, qui analysent ensemble la syntaxe des langages de programmation, des pages Web, etc. Dans ce chapitre, nous approfondirons uniquement les principes de construction d'un lexer, et nous discuterons des autres composants dans les TPs suivants. | + | expression: left=expression op=MUL right=expression {if($left.text && $op.text && $right.text) console.log($left.text + ' ' + $op.text + ' ' + $right.text + ' ');} |
+ | | left=expression op=DIV right=expression {if($left.text && $op.text && $right.text) console.log($left.text + ' ' + $op.text + ' ' + $right.text + ' ');} | ||
+ | | left=expression op=PLUS right=expression {if($left.text && $op.text && $right.text) console.log($left.text + ' ' + $op.text + ' ' + $right.text + ' ');} | ||
+ | | left=expression op=MINUS right=expression {if($left.text && $op.text && $right.text) console.log($left.text + ' ' + $op.text + ' ' + $right.text + ' ');} | ||
+ | | INT | ||
+ | ; | ||
- | Vous pouvez construire un lexer de deux façons: | + | WS : ([' ']+) -> skip; /* Skip whitespaces */ |
- | * Ecrire un programme qui utilise des expressions régulières et faire correspondre ces expressions régulières à aux jetons | + | NEWLINE : ([\r\n]+); |
- | * Utilisez un générateur de lexer qui reçoit les expressions régulières et écrit le lexer pour vous | + | INT : ([0-9]+) ; |
+ | MUL : ('*'); | ||
+ | DIV : ('/'); | ||
+ | PLUS : ('+'); | ||
+ | MINUS : ('-'); | ||
+ | </code> | ||
- | ==== Rappel - Expressions régulières ==== | + | Tous les jetons (tokens) incluent un ensemble d'**attributs** prédéfinis, read-only, qui décrivent des propriétés utiles pour les tokens et auxquels les actions peuvent accéder a travers l'instruction ''$label.attribute'', où //label// représente le nom du jeton/de la règle et //attribute// le nom de l'attribut. |
- | === Mathematique === | + | === Token Attributes === |
- | ^ Character ^ Description ^ Exemple ^ | + | |
- | | * | Zéro ou plusieurs fois | a*, (ab)* | | + | |
- | | + | Une ou plusieurs fois | a+, (ab)+ | | + | |
- | | ? | Zéro ou une fois | a?, (ab)? | | + | |
- | | %% ^ %% | début de string | %%^%%ab* | | + | |
- | | %%$%% | fin de string | b*a%%$%% | | + | |
- | | . | tout symbole | . | | + | |
- | | [ ] | Ensemble | [abc] | | + | |
- | | \s | Espace blanc | a\sb | | + | |
- | | [%%^%% ] | ensemble complémentaire | [%%^%%abc] | | + | |
- | | ( ) | groupe | (abc)+ | | + | |
- | | %% | %% | Ou | a %%|%% b, (ab) %%|%% (ba) | | + | |
+ | * **text**: string - Le texte correspondant au jeton; peut être traduit dans un appel de fonction ''getText()''. Exemple: **%%$%%ID.text**. | ||
+ | * **type**: number - Le type du jeton (nombre entier différent de 0), tel que INT; peut être traduit dans un appel de fonction ''getType()''. Exemple: **%%$%%ID.type**. | ||
+ | * **line**: number - Le numéro de la ligne ou le jeton peut etre trouvé, commençant par 1; peut être traduit dans un appel de la fonction ''getLine()''. Exemple: **%%$ID.line%%**. | ||
+ | * **pos**: number - La position ou le premier caractère du jeton se trouve dans une ligne, commençant par 0; peut être traduit dans un appel de la fonction ''togetCharPositionInLine()''. Example: **%%$%%ID.pos**. | ||
+ | * **index**: number: L'indice général d'un token dans le flux des jetons, commençant par 0; peut être traduit dans un appel de la fonction ''getTokenIndex()''. Example: **%%$%%ID.index**. | ||
+ | * **int**: number - La valeur entière du texte d'un jeton, supposant que le texte est une valeur numerique valide; peut être traduit dans un appel de la fonction ''Integer.valueOf(text-of-token)''. Example: **%%$%%INT.int**. | ||
- | === JavaScript === | ||
- | ^ Character ^ Description ^ Exemple ^ | ||
- | | {n} | n fois | a{3} | | ||
- | | {n,m} | minimum n, maximum m | a{3,7} | | ||
- | | \w | alphanumérique et _ | \w | | ||
- | | \t | TAB | a\ta* | | ||
- | | \n | fin de ligne | a\nb | | ||
- | | \r | retour chariot | a\rb | | ||
- | | a(?!b) | a seulement si non suivi par b | a(?!b) | | ||
- | | a(?=b) | a seulement si suivi par b | a(?=b) | | ||
- | | ( ) | group | a(ab)a | | ||
- | Pour déclarer des expressions régulières, on a 2 possibilités: | ||
- | <code javascript> | ||
- | // making a new RexExp object the standard way | + | ===== Génération du parser ===== |
- | let regex = new RegExp ("[0-9]+"); | + | Après avoir décrit une grammaire dans un fichier ''.g4'' avec des jetons et des règles, il faut exécuter la commande suivante: |
- | + | ||
- | // making a new RegEx object using a shortcut | + | |
- | let regex: RegExp = /[0-9]+/; | + | |
+ | <code bash> | ||
+ | npm run antlr4ts | ||
</code> | </code> | ||
+ | Cette commande va générer les fichiers ''.ts'' et ''.js'' correspondants, dont on va se servir pour diviser un texte en jetons et pour l'analyser a l'aide du parser. | ||
- | ==== Exemple de création d'un lexer ==== | ||
- | Le lexer général ressemblerait à | + | ===== Utilisation du parser ===== |
+ | Un exemple d'utilisation du parser dans un fichier ''index.ts'': | ||
<code javascript> | <code javascript> | ||
- | let number: RegExp = /^[0-9]+(\.?[0-9]*)/; | + | import { CharStreams, CodePointCharStream, CommonTokenStream, Token } from 'antlr4ts'; |
- | let whitespace: RegExp = /^\s+/; | + | import { AlfLexer } from './AlfLexer.js'; |
- | let identifier: RegExp = /^[A-Za-z_][A-Za-z0-9_]*/; | + | import { AlfParser } from './AlfParser.js'; |
+ | import { AlfListener } from './AlfListener.js'; | ||
+ | import { AlfVisitor } from './AlfVisitor.js'; | ||
+ | import { ParseTree } from 'antlr4ts/tree/ParseTree'; | ||
+ | import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor'; | ||
- | let str: string = "the text you want to lex is this one"; | + | let input: string = "1+2*4/5"; |
- | + | let inputStream: CodePointCharStream = CharStreams.fromString(input); | |
- | let error: boolean = false; | + | let lexer: AlfLexer = new AlfLexer(inputStream); |
- | + | let tokenStream: CommonTokenStream = new CommonTokenStream(lexer); | |
- | let tokens: string[] = []; | + | let parser: AlfParser = new AlfParser(tokenStream); |
- | + | ||
- | while (str.length > 0 && error === false) | + | |
- | { | + | |
- | // take each regular expression and try to match it | + | |
- | let match: RegExpExecArray|null = []; | + | |
- | // try to match the first regular expression | + | |
- | // match number | + | |
- | match = number.exec (str); | + | |
- | if (match.length > 0) // we matched something | + | |
- | { | + | |
- | // delete the matched part from the string | + | |
- | str = str.substring (0, match[0].length); | + | |
- | // add the token | + | |
- | tokens.push ({type: 'NUMBER', value: match[0]}); | + | |
- | // go to the next while iteration | + | |
- | continue; | + | |
- | } | + | |
- | // match whitespace | + | |
- | match = whitespace.exec (str); | + | |
- | if (match.length > 0) // we matched something | + | |
- | { | + | |
- | // delete the matched part from the string | + | |
- | str = str.substring (0, match[0].length); | + | |
- | // ignore whitespace, don;t add it to the tokens | + | |
- | // go to the next while iteration | + | |
- | continue; | + | |
- | } | + | |
- | // match idetifier | + | |
- | match = identifier.exec (str); | + | |
- | if (match.length > 0) // we matched something | + | |
- | { | + | |
- | // delete the matched part from the string | + | |
- | str = str.substring (0, match[0].length); | + | |
- | // add the token | + | |
- | tokens.push ({type: 'IDENTIFIER', value: match[0]}); | + | |
- | // go to the next while iteration | + | |
- | continue; | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | console.log (tokens); | + | |
+ | // Parse the input, where `start` is whatever entry point you defined | ||
+ | let tree = parser.start(); | ||
</code> | </code> | ||
- | Comme vous pouvez le voir, le code est assez long pour seulement trois expressions régulières. Les langages de programmation ont un contenu plus complexe, alors c'est pourquoi nous voulons utiliser un générateur de lexer, dans ce cas celui inclus en **ANTLR 4**. | + | ===== Visualisation de l'arbre d'analyse ===== |
- | ==== Lexer en ANTLR4 ==== | + | Dans ce TP on va ce concentrer sur la **visualisation de l'arbre** et sur sa génération pas à pas, pour comprendre comment le parser fonctionne. |
- | Ayant créé le projet TypeScript + ANTLR précédent, on va y ajouter un fichier **Alf.g4** avec le contenu suivant: | + | |
- | <code> | + | La structure de la grammaire peut etre visualisée d'une manière très intuitive à l'aide de [[https://marketplace.visualstudio.com/items?itemName=mike-lischke.vscode-antlr4|l'extension d'ANTLR4 pour VSCode]] qui a été installée dans le TP précédent. |
- | grammar Alf; | + | On aura besoin aussi d'un fichier de configuration, qui nous aidera aussi à la vérification des erreurs et au processus de debug. |
- | prog:; | + | Pour pouvoir configurer votre projet de sorte que vous puissiez visualiser la construction progressive de l'arbre d'analyse, vous devez suivre les étapes ci-dessous: |
+ | - Créez un nouveau projet en Visual Studio Code et assurez vous qu'il est //**le seul dossier ouvert**// | ||
+ | - Ajoutez le fichier qui contient la grammaire | ||
+ | - Créez un fichier avec l'extension ''.txt'' pour y ajouter les instructions que la grammaire doit analyser | ||
+ | - Créez un dossier spécial, appelé **.vscode** | ||
+ | - Dans le dossier ''.vscode'', créez un fichier de configuration **launch.json** avec la structure suivante: <code json> | ||
+ | { | ||
+ | "version": "0.2.0", | ||
+ | "configurations": [ | ||
+ | { | ||
+ | "name": "antlr4", | ||
+ | "type": "antlr-debug", | ||
+ | "request": "launch", | ||
+ | "input": "sample.txt", /* Le nom du fichier ou vous allez stocker les données de test */ | ||
+ | "grammar": "Alf.g4", /* Le nom du fichier de grammaire */ | ||
+ | "startRule": "start", /* Le nom de la première règle de la grammaire */ | ||
+ | "printParseTree": true, | ||
+ | "visualParseTree": true | ||
+ | } | ||
+ | ] | ||
+ | }</code> | ||
+ | - Entrez dans le fichier de grammaire et ajoutez un **Breakpoint** (il faut juste appuyer sur le point rouge derrière le numéro de la ligne) {{ :alf:laboratoare:breakpoint.jpg?500&nolink }} | ||
+ | - Dans la Palette de Commandes de la partie gauche, entrez sur la 4ème option (**Debugger**, indiqué par la flèche //rouge// dans l'image) et démarrez le Debugger en appuyant sur le bouton indiqué par la flèche verte de l'image suivante: {{ :alf:laboratoare:start-debugger.jpg?500&nolink }} | ||
+ | - Maintenant, la visualisation du ''Parse Tree'' devrait s'ouvrir à droite de la fenetre de VSCode. Pour voir la génération progressive de votre arbre d'analyse, vous devez appuyer sur le bouton **Step Into**, marqué par la fleche rouge dans l'image suivante: {{ :alf:laboratoare:step-into-tree.jpg?800&nolink }} | ||
+ | - Au fur et à mesure que vous appuyez sur **Step Into**, vous allez observer la **génération de l'arbre d'analyse** (flèche rouge), la **consommation des jetons** (flèche verte) et l'**appel des règles dans la pile** (flèche bleue) {{ :alf:laboratoare:parse-tree-generation.jpg?800&nolink }} | ||
+ | - A la fin de l'exécution, le **Parse Tree** aura la structure suivante: {{ :alf:laboratoare:parse-tree.jpg?500&nolink }} | ||
- | NEWLINE : ([\r\n]+); | ||
- | INT : ([0-9]+); | ||
- | </code> | ||
- | On peut observer la définition de 2 tokens différents (à l'aide des expressions régulières), //NEWLINE// et //INT// pour les nouvelles lignes, respectivement les numéros entiers. | ||
- | Après avoir créé ce fichier, on va exécuter la commande ''npm run antlr4ts'', qui va générer plusieurs fichiers //.ts//, ainsi que les fichiers ''.js'' nécessaires: | ||
- | * **Lexer**: les règles de lexer sont destinées à créer des objets token correspondants | ||
- | * **Parser**: les règles de parser sont responsables pour l'analyse du langage | ||
- | * **Listener**: fournit des méthodes appelées indépendamment par un objet walker fourni par ANTLR | ||
- | * **Visitor**: contient des méthodes qui doivent accompagner (walk) leurs enfants avec des appels de visite explicites | ||
- | L'étape suivante suppose la création d'un fichier **main.ts** où l'on doit écrire la structure du programme et gestionner le fichier d'entrée: | ||
- | <code javascript> | + | ===== Exercices ===== |
- | import { CharStreams, CodePointCharStream, CommonTokenStream } from 'antlr4ts'; | + | |
- | import { AlfLexer } from './AlfLexer'; | + | |
- | import { AlfParser, ProgContext } from './AlfParser'; | + | |
- | // Create the lexer and parser | + | - Suivez le tutoriel précédent (celui avec la déclaration d'une variable simple) pour la visualisation de l'arbre d'analyse. **(1p)** |
- | let input: string = "10\n20"; | + | - Ajoutez dans la grammaire les instructions nécessaires pour la déclaration complexe des variables, selon le langage suivant: **(2p)** <code>int _var1 = 2; |
- | let inputStream: CodePointCharStream = CharStreams.fromString(input); | + | float _var2 = 5.55; |
- | let lexer: AlfLexer = new AlfLexer(inputStream); | + | string _var3 = "alf"; </code> Suivez les étapes nécessaires pour visualiser l'arbre d'analyse et testez chaque règle à l'aide du fichier texte pour les données. |
- | let tokenStream: CommonTokenStream = new CommonTokenStream(lexer); | + | - Ajoutez à votre grammaire des jetons et des règles pour les expressions mathématiques. Votre grammaire doit reconnaître les opérations et les opérateurs d'addition, soustraction, multiplication et division (+, -, *, /, %). Suivez les étapes nécessaires pour visualiser l'arbre d'analyse et testez la fonctionnalité du programme pour l'expression ''2+5/10-7''. **(3p)** |
- | let parser: AlfParser = new AlfParser(tokenStream); | + | - Repétez l'exercice précédent en tenant compte du fait qu'une expression peut aussi contenir des variables. Comme mentionné dans le premier exercice, les variables sont de la forme ''_var1''. Vérifiez la correctitude de la grammaire en ajoutant l'expression ''15/_var1-5;'' au fichier texte pour les données.**(1p)** |
- | + | - Ajoutez à la grammaire les jetons et les règles nécessaires pour accépter des parantheses à l'intérieur des expressions. Suivez les étapes nécessaires pour la visualisation de l'arbre et testez le programme en ajoutant l'expression ''(2+5)/3'' au fichier texte pour les données. **(1p)** | |
- | // Parse the input, where `prog` is whatever entry point you defined | + | - Faites la grammaire accepter plus d'une expression, supposant que les expressions sont séparées par '';'', mais aussi par des lignes vides. Modifiez le contenu du fichier texte pour les données en ajoutant les expressions suivantes et visualisez l'arbre d'analyse: **(2p)** <code> |
- | let tree:ProgContext = parser.prog(); | + | string _var1 = "alf"; |
+ | (2+7)/5*3; | ||
</code> | </code> | ||
- | + | - **BONUS: ** Changez la grammaire pour que les variables puissent être déclarées en utilisant des expressions et visualisez l'arbre d'analyse pour l'instruction suivante: **(1.5p)** <code>int _var1 = 2 + 3 * 5;</code> | |
- | <note warning> | + | |
- | **ATTENTION!** Après chaque modification dans le fichier //main.js//, vous devez exécuter les commandes suivantes dans le Terminal de Visual Studio Code: | + | |
- | * ''npx tsc'' - pour exécuter le compilateur TypeScript | + | |
- | * ''node main.js'' - pour exécuter le fichier principal JavaScript généré | + | |
- | </note> | + | |
- | + | ||
- | + | ||
- | <note important> | + | |
- | Après chaque modification apportée à la grammaire (fichier ''Alf.g4''), vous devez exécuter la commande suivante pour enregistrer les changements: <code bash> | + | |
- | npm run antlr4ts | + | |
- | </code> | + | |
- | </note> | + | |
- | + | ||
- | ===== Exercices ===== | + | |
- | - Traitez le tableau ci-dessous a l'aide des **expressions régulières** de sorte que chaque livre devienne un objet de type Book, avec les suivantes propriétés: //titre// (string), //ISBN// (number), et //date de publication// (string). **(1p)** <code javascript> let books_array: string[] = [ | + | |
- | 'Engineering a Compiler,9780120884780,7th February 2011', | + | |
- | 'Modern Operating Systems 4,9780133591620,21st March 2014', | + | |
- | 'Computer Networks,9332518742,9th January 2010' | + | |
- | ];</code> | + | |
- | - Installez ANTLR4 globalement, ainsi que la version pour TypeScript. Attachez une capture d'écran avec l'exécution de la commande ''antlr4'' dans votre Command Prompt et une autre avec le résultat de la commande ''npm run antlr4ts'' dans le Terminal de Visual Studio Code. **(1p)** | + | |
- | - Créez le programme complet présenté dans le tutoriel de ce TP (fichiers de configuration, fichier Alf.g4, fichier main.ts) et assurez-vous que son exécution est réalisée sans d'erreurs. **(1p)** | + | |
- | - Affichez la liste des tokens: **(1p)** | + | |
- | - A l'aide des fonctions pour les strings, traitez le fichier ''Alf.tokens'' créé automatiquement par ANTLR et générez un dictionnaire qui ait comme clés l'indice de vos tokens et comme valeurs leur nom. **(0.5p)** <code bash>{ '1': 'NEWLINE', '2': 'INT' }</code> | + | |
- | - Parcourez et affichez la liste des tokens (pour chaque token, affichez son nom et sa valeur comme dans l'exemple. **Indice**: Pour pouvoir afficher la liste des tokens, vous devrez inspecter la classe //BufferedTokenStream// et choisir une méthode qui retourne cette liste. Vous aurez aussi besoin de la fonction **nextToken()** pour parcourir le buffer de données. **(0.5)** <code bash>[{ '10': 'INT', '\n': 'NEWLINE', '20': 'INT' }</code> | + | |
- | - Ajoutez a votre fichier ''Alf.g4'' des expressions régulières pour les jetons suivants: WORD - tout texte contenant uniquement des lettres et PONCTUATION - différents signes de ponctuation. Utilisez le lexer créé dans l'exercice précédent pour afficher la liste des tokens pour le texte: //Bonjour! Le TP d'ALF est de 8 a 10.// **(1p)** | + | |
- | - Utilisez votre lexer pour analyser les jetons du fichier [[https://github.com/alexandruradovici/alf2018/blob/master/TP/TP4/text_and_numbers.txt|text_and_numbers.txt]]. Pour chaque ligne de ce fichier affichez le texte du jeton, le type, et la ligne du fichier ou il a été trouvé. **(1p)** | + | |
- | - Ecrivez un lexer qui reconnaît un sous-ensemble du langage Python. Pour chaque point de cet exercice, utilisez comme exemple une chaîne quelconque correspondant au langage Python, pour voir si votre programme peut: **(3p)** | + | |
- | - Reconnaître les mots-clés suivants: for, if, while, else, def, break, class | + | |
- | - Reconnaître les noms des fonctions: ils commencent par une lettre ou _ et sont suivis de n'importe quelle lettre ou chiffre | + | |
- | - Reconnaître les nombres entiers | + | |
- | - Reconnaître les nombres à reels | + | |
- | - Reconnaître les variables | + | |
- | - Reconnaître les opérateurs: ( ) , ; : + - * / = % { } [ ] | + | |
- | - Utilisez le lexer de l'exercice précédent pour le fichier ''program.py''. Ecrivez chaque type de jeton, le texte qui lui correspond et la ligne où il a été trouvé. **(1p)** | + | |
- | - **Bonus**: Modifiez le lexer de l'exercice **7** pour qu'il reconnaisse aussi des chaînes de caractères (texte encapsulé entre ” ou '). **(1p)** | + | |