Differences

This shows you the differences between two versions of the page.

Link to this comparison view

alf:laboratoare:05_fr_java [2023/04/03 16:40]
alexandra.negoita02 [TP 5 - Parser du langage. Introduction à l'AST]
alf:laboratoare:05_fr_java [2023/04/04 09:19] (current)
alexandra.negoita02 [Exercices]
Line 1: Line 1:
 ====== TP 5 - Parser du langage. Introduction à l'AST ====== ====== TP 5 - Parser du langage. Introduction à l'AST ======
 +<note warning>
 +Vous devez **accepter** l'​assignment d'ici est travailler avec ce **repository**:​ [[https://​classroom.github.com/​a/​g8KTvWH-|Lab 5]]
 +</​note>​
 +
  
 Le principal role du **parser** est de  reconnaître les ordres contextuels décrits par les jetons spécifiés par un ensemble de règles d'​analyse. ​ Le principal role du **parser** est de  reconnaître les ordres contextuels décrits par les jetons spécifiés par un ensemble de règles d'​analyse. ​
Line 30: Line 34:
  
 <​code>​ <​code>​
-start       : declaration ​                 #declarationStatement+grammar Alf; 
 + 
 +start       : declaration ​                  ​#declarationRule
             ;             ;
  
-declaration : type VARIABLE EQ value       ​#​variableDeclaration+declaration : type VARIABLE EQ value        #​variableDeclaration
             ;             ;
  
Line 44: Line 50:
             | FLOAT_NUMBER ​                 #valueFloat             | FLOAT_NUMBER ​                 #valueFloat
             | STRING_TEXT ​                  #​valueString             | STRING_TEXT ​                  #​valueString
 +            | VARIABLE ​                     #​valueVariable
             ;             ;
 +
 +WS          :   ​('​ '​) ​      -> skip;
 +NEWLINE ​    : ​  ​([\r\n]+) ​  -> skip;
 +VARIABLE ​   :   ​('​_'​[a-zA-Z0-9]+);​
 +INT         : ​  '​int';​
 +FLOAT       : ​  '​float';​
 +STRING ​     :   '​string';​
 +EQ          :   '​=';​
 +SEMICOLON ​  : ​  ';';​
 +INT_NUMBER ​ :   ​([0-9]+);​
 +FLOAT_NUMBER: ​  ​([0-9]+'​.'​[0-9]+);​
 +STRING_TEXT :   ​('"'​~["​]+'"'​|'​\''​~['​]+'​\''​);​
 </​code>​ </​code>​
 Si on va étiquetter les alternatives,​ dans le fichier ''​AlfParser.ts''​ on obtiendra des classes spécifiques pour chaque type de sous-regle et dans le fichier ''​AlfVisitor.ts''​ les interfaces exportées incluront des fonctions dépendant du contexte pour chaque étiquette définie: Si on va étiquetter les alternatives,​ dans le fichier ''​AlfParser.ts''​ on obtiendra des classes spécifiques pour chaque type de sous-regle et dans le fichier ''​AlfVisitor.ts''​ les interfaces exportées incluront des fonctions dépendant du contexte pour chaque étiquette définie:
  
-<​code ​javascript+<​code ​java
-export ​interface AlfVisitor<​Result> extends ParseTreeVisitor<​Result> { +public ​interface AlfVisitor<​T> extends ParseTreeVisitor<​T> { 
- visitDeclarationStatement?: ​(ctx: DeclarationStatementContext=> Result+ T visitDeclarationRule(AlfParser.DeclarationRuleContext ​ctx); 
-        ​visitVariable?: ​(ctx: VariableContext=> Result+ T visitVariableDeclaration(AlfParser.VariableDeclarationContext ​ctx); 
-        visitTypeInt?: (ctx: TypeIntContext=> Result+ visitTypeInt(AlfParser.TypeIntContext ​ctx); 
-        visitTypeFloat?: (ctx: TypeFloatContext=> Result+ visitTypeFloat(AlfParser.TypeFloatContext ​ctx); 
-        visitTypeString?: (ctx: TypeStringContext) ​=> Result+ visitTypeString(AlfParser.TypeStringContext ​ctx); 
-        visitValueInt?: (ctx: ValueIntContext=> Result+ visitValueInt(AlfParser.ValueIntContext ​ctx); 
-        visitValueFloat?: (ctx: ValueFloatContext=> Result+ visitValueFloat(AlfParser.ValueFloatContext ​ctx); 
-        visitValueString?: (ctx: ValueStringContext=> Result;+ visitValueString(AlfParser.ValueStringContext ctx); 
 + T visitValueVariable(AlfParser.ValueVariableContext ​ctx);
 } }
 </​code>​ </​code>​
Line 73: Line 93:
 <code javascript>​ <code javascript>​
  
 +// On fait une classe abstraite ASTNode qui sera hérité par toutes les noeuds ​
 +// ASTNode.java
 abstract class ASTNode { abstract class ASTNode {
-    ​constructor(){};+    ​ASTNode(){}
 } }
-class StatementNode ​extends ASTNode { + 
-    ​constructor(public readonly statement: ​ASTNode) {+// Puis, tous les autres noeuds qui sont nommees dans notre grammaire 
 +// DeclarationNode.java 
 +public ​class DeclarationNode ​extends ASTNode{ 
 +    ​String id = "​declaration";​ 
 +    String variableType;​ 
 +    String variable; 
 +    ASTNode value; 
 +    DeclarationNode(String variableType,​ String variable, ​ASTNode ​value) {
         super();         super();
-    } +        this.variableType = variableType;​ 
-    ​toJSON() { +        ​this.variable = variable; 
-        ​/* TODO: Return here an object having the id "​statement"​ and the statement a list of instructions */+        ​this.value = value;
     }     }
 } }
-class DeclarationNode ​extends ASTNode { + 
-    ​constructor(public readonly variable_type:​ string, public readonly variable: string, public readonly op: string, public readonly ​value: string|number) {+// TypeNode.java 
 +public ​class TypeNode ​extends ASTNode{ 
 +    ​String id = "value"; 
 +    String typeName; 
 +    TypeNode(String typeName) {
         super();         super();
-    } +        this.typeName = typeName;
-    toJSON() { +
- +
-        /* TODO: Return here an object having the id "​declaration"​ and the following properties: variable_type,​ variable and value */ +
-        ​+
     }     }
 } }
  
-class ValueNode extends ASTNode { +// ValueNode.java 
-    ​constructor(public ​readonly ​value: number|string) {+public ​class ValueNode extends ASTNode{ 
 +    ​String id = "​value";​ 
 +    ​public ​Object value; 
 +    ValueNode(Object ​value) {
         super();         super();
-    } +        this.value value;
-    toJSON() { +
-        /*TODO: Return here an object having the id "value" and the following properties: ​value */ +
-         +
- +
-    } +
-+
-class TypeNode extends ASTNode { +
-    constructor(public readonly type_name: string) { +
-        super(); +
-    } +
-    toJSON() { +
-        /* TODO: Return here an object having the id "​type"​ and the following properties: type */+
     }     }
 } }
 +
 </​code>​ </​code>​
  
 ===== Visiteur de l'​arbre ===== ===== Visiteur de l'​arbre =====
  
-Pour //visiter// les composants et générer l'​arbre,​ il faut surcharger les méthodes du visiteur de la façon suivante:+Pour //visiter// les composants et générer l'​arbre,​ il faut surcharger les méthodes du visiteur ​de base de la façon suivante: ​on fait notre classe visiteur, //​MyAlfVisitor//,​ qui va implementer l'​interface du visiteur genree par la grammaire //​AlfVisitor//​. 
 +L'​action de visiter un noeud représente parcourir l'​arbre d'​analyse créé par le parser, la création d'un objet du type de règle mentionné dans l'AST et sa transmission à la notre classe visiteur.
  
-<​code ​typescript+<​code ​java
-class MyAlfVisitor extends AbstractParseTreeVisitor<​ASTNode>​ implements AlfVisitor<​ASTNode>​ { +public ​class MyAlfVisitor extends AbstractParseTreeVisitor<​ASTNode>​ implements AlfVisitor<​ASTNode>​ { 
-    ​defaultResult() { +    ​@Override 
-        return ​new StatementNode({} as ASTNode);+    public ASTNode visitDeclarationRule(AlfParser.DeclarationRuleContext ctx) { 
 +        return (DeclarationNode) this.visit(ctx.declaration());
     }     }
-    ​visitVariableDeclaration(ctx: VariableDeclarationContext): DeclarationNode ​{+ 
 +    @Override 
 +    public ASTNode ​visitVariableDeclaration(AlfParser.VariableDeclarationContext ​ctx) {
         return new DeclarationNode(         return new DeclarationNode(
-            ​(this.visit(ctx.type()) ​as TypeNode).type_name+                ​((TypeNode) ​this.visit(ctx.type())).typeName
-            ctx.VARIABLE().text, +                ctx.VARIABLE().getText(), 
-            ctx.EQ().text+                ​String.valueOf(((ValueNode) ​this.visit(ctx.value())).value)
-            (this.visit(ctx.value()) ​as ValueNode).value+
         );         );
     }     }
-    visitValueInt(ctx: ValueIntContext): ValueNode ​+ 
-        return new ValueNode+    @Override 
-            ​parseInt(ctx.INT_NUMBER().text)+    public ASTNode visitTypeInt(AlfParser.TypeIntContext ​ctx) { 
 +        return new TypeNode
 +                ctx.INT().getText()
         );         );
     }     }
-     ​visitTypeString(ctx: TypeStringContext): TypeNode ​{+ 
 +    @Override 
 +    public ASTNode visitTypeFloat(AlfParser.TypeFloatContext ​ctx) {
         return new TypeNode(         return new TypeNode(
-            ​ctx.STRING().text +                ​ctx.FLOAT().getText() 
-        )+        );
     }     }
-</​code>​ 
  
 +    @Override
 +    public ASTNode visitTypeString(AlfParser.TypeStringContext ctx) {
 +        return new TypeNode(
 +                ctx.STRING().getText()
 +        );
 +    }
  
-Pour voir le résultat de l'​arbre généré, on va l'​afficher dans un fichier:+    @Override 
 +    public ASTNode visitValueInt(AlfParser.ValueIntContext ctx) { 
 +        return new ValueNode( 
 +                Integer.parseInt(ctx.INT_NUMBER().getText()) 
 +        ); 
 +    }
  
-<code javascript>​ +    @Override 
-const visitor = new MyAlfVisitor(); +    ​public ASTNode visitValueFloat(AlfParser.ValueFloatContext ctx{ 
-fs.writeFileSync('./​output.json',​ JSON.stringify(visitor.visit(tree).toJSON(), null, 4), '​utf8'​); +        ​return new ValueNode( 
-</​code>​+                Float.parseFloat(ctx.FLOAT_NUMBER().getText()) 
 +        ​); 
 +    }
  
 +    @Override
 +    public ASTNode visitValueString(AlfParser.ValueStringContext ctx) {
 +        return new ValueNode(
 +                ctx.STRING_TEXT().getText()
 +        );
 +    }
  
-<note important>​ +    @Override 
-Après chaque modification apportée à la grammaire ​(fichier ''​Alf.g4''​), vous devez exécuter la commande suivante pour enregistrer les changements:​ <code bash> +    ​public ASTNode visitValueVariable(AlfParser.ValueVariableContext ctx{ 
-npm run antlr4ts+        ​return new ValueNode( 
 +                ctx.VARIABLE().getText() 
 +        ); 
 +    } 
 +}
 </​code>​ </​code>​
  
-Après chaque modification apportée au fichier ''index.ts''​, vous devez exécuter les commandes suivantes ​pour voir le résultat correct: <​code ​bash+ 
-npx tsc +Pour voir le résultat de l'arbre généré, on utilise le format de notation [[https://​www.w3schools.com/​js/​js_json_intro.asp|JSON]]. Pour utiliser JSON pour afficher des classes Java, on doit telecharger et importer le paquet [[https://​jar-download.com/​artifacts/​com.google.code.gson/​gson/​2.8.2/​source-code|GSON]],​ et de l'ajouter dans le projet Intellij avec ''File > Project Structure > Modules''​. La page de modules doit ressembler à ça: 
-node index.js+{{ :​alf:​laboratoare:​gson_module.png?​550&​nolink }} 
 + 
 +Et le code pour afficher ​le contenu JSON dans un fichier ''​output.json''​: 
 +<​code ​java
 +// Une methode toJSON qui utilise les fonctions du paquet GSON pour interpreter un noeud AST:  
 +static String toJSON(ASTNode visitor) { 
 +        GsonBuilder builder = new GsonBuilder();​ 
 +        builder.setPrettyPrinting();​ 
 +        Gson gson = builder.create();​ 
 +        return gson.toJson(visitor);​ 
 +    } 
 +     
 +// Appeller dans Main.java:​ 
 +writeToFile("​files\\output.json",​ toJSON(visitor.visit(tree)));​
 </​code>​ </​code>​
 +Pour un fichier ''​sample.txt''​ contenant le texte ''​int _var = 5;''​
 +{{ :​alf:​laboratoare:​json_output.png?​550&​nolink }}
 +
 +
 +<note important>​
 +Après chaque modification apportée à la grammaire (fichier ''​alf.g4''​),​ vous devez generer de nouveau les fichiers de ANTLR, avec clic-droit sur le fichier de grammaire > ''​Generate ANTLR Recognizer''​
 </​note>​ </​note>​
  
 ===== Exercices ===== ===== Exercices =====
  
-  - Créez un nouveau projet ANLTR4. ​Téléchargez la structure du TP depuis le [[https://​github.com/​UPB-FILS-ALF/​TP/​tree/​main/​TP5|github repository]], inspectez la structure de la grammaire ​et complétez les fonctions **toJSON()** selon les indications marquées par TODO. **(2p)** +  - Téléchargez la structure du TP depuis le github repository ​donnétéléchargez et importez l'​archive GSON comme dit dans le laboratoire,​ et inspectez la structure de la grammaire. **(1p)** 
-  - Ajoutez au fichier de grammaire les règles pour accepter plusieurs instructions. Les instructions peuvent etre séparées par '';''​ et une ou plusieurs lignes vides. Ajoutez les classes et les méthodes nécessaires pour pouvoir visiter les noeuds. Dans l'AST, chaque instruction doit etre ajoutée dans la liste //​statements//​ du noeud principal. Testez le programme pour les instructions suivantes: **(3p)** <code bash>​float _var1 = 7.5;+  - Ajoutez au fichier de grammaire les règles pour accepter plusieurs instructions. Les instructions peuvent etre séparées par '';''​ et une ou plusieurs lignes vides. Ajoutez les classes et les méthodes nécessaires pour pouvoir visiter les noeuds. Dans l'AST, chaque instruction doit être ajoutée dans la liste //​statements//​ du noeud principal. Testez le programme pour les instructions suivantes: **(3p)** <code bash>​float _var1 = 7.5;
 string _var2 = '​alf';</​code>​ string _var2 = '​alf';</​code>​
-   - Ajoutez à la grammaire décrite dans l'​exemple les règles nécessaires pour l'​utilisation des expressions (plusieurs expressions) du laboratoire précédent. Votre grammaire doit accepter ​maintenent ​aussi les déclarations des variables, que les expressions. Ajoutez les classes et les méthodes nécessaires pour visiter les nouveaux noeuds et générer ​l'​arbre. Le noued correspondant aux expressions doit avoir les propriétés suivantes:, //id: "​expression"//,​ //left// (le neoud correspondant a l'​opérande de gauche), //right// (le neoud correspondant a l'​opérande de droite), //op// (la valeur de l'​opérateur) Testez la correctitude de votre grammaire en lisant les instructions suivantes depuis un fichier texte: **(3p)** <code bash> int _var1 = 1;+   - Ajoutez à la grammaire décrite dans l'​exemple les règles nécessaires pour l'​utilisation des expressions (plusieurs expressions) du laboratoire précédent. Votre grammaire doit accepter ​maintenant ​aussi les déclarations des variables, que les expressions. Ajoutez les classes et les méthodes nécessaires pour visiter les nouveaux noeuds et générez ​l'​arbre. Le noued correspondant aux expressions doit avoir les propriétés suivantes:, //id: "​expression"//,​ //left// (le neoud correspondant a l'​opérande de gauche), //right// (le neoud correspondant a l'​opérande de droite), //op// (la valeur de l'​opérateur)Testez la correctitude de votre grammaire en lisant les instructions suivantes depuis un fichier texte: **(3p)** <code bash>int _var1 = 1;
 5*(2+4)/​7;</​code>​ 5*(2+4)/​7;</​code>​
-   - Ajoutez des règles et modifiez les méthodes du **Visitor** pour que les variables puissent prendre des valeurs qui sont des expressions. Testez pour l'​instruction suivante: **2p** <code bash>​float _var1 = 5*(2+4)/​7;</​code>​ +   - Ajoutez des règles et modifiez les méthodes du **Visitor** pour que les variables puissent prendre des valeurs qui sont des expressions. Testez pour l'​instruction suivante: **(3p)** <code bash>​float _var1 = 5*(2+4)/​7;</​code>​
-   - **BONUS**: Affichez le contenu de l'​arbre d'​analyse dans un fichier ayant le format ''​JSON''​. **(1p)**+
  
  
  
alf/laboratoare/05_fr_java.1680529214.txt.gz · Last modified: 2023/04/03 16:40 by alexandra.negoita02
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0