This shows you the differences between two versions of the page.
alf:laboratoare:07 [2020/03/29 21:49] diana.ghindaoanu [Exercises] |
alf:laboratoare:07 [2021/04/17 19:29] (current) diana.ghindaoanu [Exercises] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== TP 7 - AST ====== | + | ====== TP 7 - Analyse Sémantique ====== |
- | ===== AST ===== | + | ===== Table des symboles ===== |
- | L'AST est un arbre représentant le format source. Il est indépendant de la forme de la grammaire. L'AST peut être une transformation du parse tree ou peut être généré directement par l'analyseur. | + | La **table de symboles** représente un structure de données créée et traitée par le compilateur afin de stocker des informations sur l'apparition de certains éléments tels que le nom des variables, le nom des fonctions, les objets, les classes, les interfaces et d'autres similaires. Essentiellement, la table de symbole est utilisée pour acccomplir la fonction d'**analyse** du compilateur. |
- | Dans l'AST, chaque règle de langue a comme corespondant un noeud. | + | Selon le langage utilisé, le tableau de symbole peut servir aux suivants objectifs: |
- | Pour ce laboratoire, vous pouvez définir le format de nœud AST comme vous le souhaitez. Un point de départ est le suivant: | + | * stocker les noms de toutes les entités sous une forme structurée, a un seul endroit |
+ | * vérifier si une variable a été déclarée avant l'utilisation | ||
+ | * implémenter la vérification des types, en vérifiant si les attributions et les expressions du code source sont correctes de point de vue sémantique | ||
+ | * déterminer la portée (//scope//) d'un nom (résolution de la portée) | ||
+ | |||
+ | |||
+ | Pour stocker les variables dans la table de symboles, on va utiliser un dictionnaire: | ||
<code javascript> | <code javascript> | ||
- | { | + | interface VariableSymbolTable { |
- | type:"node_type", | + | type: string, |
- | ... | + | value: any |
} | } | ||
- | </code> | ||
- | ==== Exemple d'AST ==== | + | let symbol_table: { [variable: string]: VariableSymbolTable } = {}; |
- | Pour le code suivant | + | |
- | <code> | + | function addVariableToSymbolTable(variable: string, type: string) { |
- | 2+3 | + | symbol_table[variable] = { |
- | var a of int | + | type: type, |
- | function fid (a of int) | + | value: undefined |
- | 5+6 | + | }; |
- | endoffunction | + | } |
</code> | </code> | ||
- | Nous pouvons décrire l'AST suivant | + | Du TP passé on a appris comment créer et structurer l'AST. Le but est de retourner les 2 structures de données dans un meme objet: |
<code javascript> | <code javascript> | ||
- | { | + | let ast = visitor.visit(tree).toJSON(); |
- | type: 'program', | + | let symbol_tree = { |
- | elements: | + | ast, |
- | [ | + | symbol_table |
- | { | + | |
- | type:'expr', | + | |
- | op: '+', | + | |
- | left: { | + | |
- | type: 'int', | + | |
- | value: 2 | + | |
- | }, | + | |
- | right: { | + | |
- | type: 'int', | + | |
- | value: 3 | + | |
- | } | + | |
- | }, | + | |
- | { | + | |
- | type: 'var', | + | |
- | variables: [ | + | |
- | { | + | |
- | id: 'a', | + | |
- | type: 'int' | + | |
- | } | + | |
- | ] | + | |
- | }, | + | |
- | { | + | |
- | type: 'function_definition', | + | |
- | id: 'fid', | + | |
- | parameters:[ | + | |
- | { | + | |
- | id:'a', | + | |
- | type:'int' | + | |
- | } | + | |
- | ] | + | |
- | } | + | |
- | ] | + | |
} | } | ||
+ | |||
+ | console.log(JSON.stringify(symbol_tree, null, 4)); | ||
</code> | </code> | ||
- | ===== Grammaire pour le langage ===== | ||
- | <code jison alf.l> | + | Pour générer le **parse tree** et la **table de symboles** on va utiliser la grammaire suivante: |
- | Newline \r?\n | + | |
- | Ws [ \t] | + | |
- | Number [0-9]+("."[0-9]+)? | + | |
- | String_value \"[^\"]*\" | + | |
- | Identifier [A-Za-z][A-Za-z0-9]* | + | |
+ | <code g4 Alf.g4> | ||
+ | grammar Alf; | ||
- | %% | + | start : (statement SEMICOLON NEWLINE*)* #multilineProg |
+ | | statement SEMICOLON #singlelineProg | ||
+ | ; | ||
+ | statement : declaration #declarationRule | ||
+ | | expression #expressionRule | ||
+ | | list_declaration #listRule | ||
+ | | function_declaration #functionRule | ||
+ | | attribution #attributionRule | ||
+ | ; | ||
- | "var" { return 'VAR'; } | + | declaration : type VARIABLE EQ expression #variableDeclaration |
- | "of" { return 'OF'; } | + | | type VARIABLE EQ function_call #variableFunctionCall |
- | "int" { return 'INT'; } | + | ; |
- | "float" { return 'FLOAT'; } | + | |
- | "string" { return 'STRING'; } | + | type : INT #typeInt |
- | "function" { return 'FUNCTION'; } | + | | FLOAT #typeFloat |
- | "endfunction" { return 'END_FUNCTION'; } | + | | STRING #typeString |
- | "=" { return '='; } | + | ; |
- | "-" { return '-'; } | + | |
- | "+" { return '+'; } | + | value : INT_NUMBER #valueInt |
- | "*" { return '*'; } | + | | FLOAT_NUMBER #valueFloat |
- | "/" { return '/'; } | + | | STRING_TEXT #valueString |
- | "(" { return 'LP'; } | + | | VARIABLE #valueVariable |
- | ")" { return 'RP'; } | + | ; |
- | ',' { return ','; } | + | |
- | {Ws} { /*skip whitespace*/ } | + | expression : left=expression op=MUL right=expression #expressionMultiply |
- | {Newline} { return 'NEWLINE'; } | + | | left=expression op=DIV right=expression #expressionDivision |
- | {Number} { return 'NUMBER'; } | + | | left=expression op=REM right=expression #expressionRem |
- | {String_value} { return 'STRING_VALUE'; } | + | | left=expression op=ADD right=expression #expressionAddition |
- | {Identifier} { return 'IDENTIFIER'; } | + | | left=expression op=SUB right=expression #expressionSubtraction |
+ | | LP expression RP #expressionParanthesis | ||
+ | | value #expressionValue | ||
+ | ; | ||
+ | |||
+ | attribution : VARIABLE EQ expression #variableAttribution | ||
+ | ; | ||
+ | |||
+ | list_declaration : LIST VARIABLE EQ LSP values RSP #listDeclaration | ||
+ | ; | ||
+ | |||
+ | values : (value COMMA)* #listValues | ||
+ | ; | ||
+ | |||
+ | function_declaration: FUNCTION VARIABLE LP (parameter COMMA*)* RP LB (statement SEMICOLON)* return_function RB #functionContent | ||
+ | ; | ||
+ | |||
+ | parameter : declaration #functionParameter | ||
+ | ; | ||
+ | |||
+ | return_function : RETURN statement SEMICOLON #returnStatement | ||
+ | | RETURN SEMICOLON #emptyReturn | ||
+ | ; | ||
+ | |||
+ | function_call : VARIABLE LP (value COMMA*)* RP #functionCall | ||
+ | ; | ||
+ | |||
+ | |||
+ | WS : (' ') -> skip; | ||
+ | NEWLINE : ([\r\n]+) -> skip; | ||
+ | FUNCTION : 'function'; | ||
+ | VARIABLE : ('_'[a-zA-Z0-9]+); | ||
+ | ADD : '+'; | ||
+ | SUB : '-'; | ||
+ | MUL : '*'; | ||
+ | DIV : '/'; | ||
+ | REM : '%'; | ||
+ | INT : 'int'; | ||
+ | FLOAT : 'float'; | ||
+ | STRING : 'string'; | ||
+ | LIST : 'list'; | ||
+ | LP : '('; | ||
+ | RP : ')'; | ||
+ | EQ : '='; | ||
+ | SEMICOLON : ';'; | ||
+ | LSP : '['; | ||
+ | RSP : ']'; | ||
+ | COMMA : ','; | ||
+ | LB : '{'; | ||
+ | RB : '}'; | ||
+ | RETURN : 'return'; | ||
+ | INT_NUMBER : ([0-9]+); | ||
+ | FLOAT_NUMBER : ([0-9]+'.'[0-9]+); | ||
+ | STRING_TEXT : ('"'~["]+'"'|'\''~[']+'\''); | ||
</code> | </code> | ||
- | <code jison alf.y> | + | Pour pouvoir visiter tous les noeuds et générer les 2 structures de données, on va utiliser comme support les méthodes suivantes: |
- | %left '+' '-' | + | |
- | // * and / have higher priority | + | <code javascript index.ts> |
- | %left '*' '/' | + | import { CharStreams, CodePointCharStream, CommonTokenStream, Token } from 'antlr4ts'; |
- | + | import { AlfLexer } from './AlfLexer.js'; | |
- | %{ | + | import { AlfParser, ExpressionAdditionContext, ExpressionDivisionContext, ExpressionMultiplyContext, ExpressionParanthesisContext, ExpressionRemContext, ExpressionSubtractionContext, MultilineProgContext, SinglelineProgContext, TypeFloatContext, TypeIntContext, TypeStringContext, ValueFloatContext, ValueIntContext, ValueStringContext, VariableDeclarationContext, ExpressionValueContext, ListDeclarationContext, ListValuesContext, ValueVariableContext, FunctionContentContext, FunctionParameterContext, VariableAttributionContext, FunctionCallContext, VariableFunctionCallContext } from './AlfParser.js'; |
- | // function for grammar rule | + | import { AlfListener } from './AlfListener.js'; |
- | function rule (rule_name, items) | + | import { AlfVisitor } from './AlfVisitor.js'; |
- | { | + | import * as fs from 'fs'; |
- | return { | + | import { ParseTree } from 'antlr4ts/tree/ParseTree'; |
- | rule: rule_name, | + | import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor'; |
- | items: items | + | |
- | }; | + | let input: string = fs.readFileSync('./ex1.txt').toString(); |
+ | let inputStream: CodePointCharStream = CharStreams.fromString(input); | ||
+ | let lexer: AlfLexer = new AlfLexer(inputStream); | ||
+ | let tokenStream: CommonTokenStream = new CommonTokenStream(lexer); | ||
+ | let parser: AlfParser = new AlfParser(tokenStream); | ||
+ | |||
+ | let tree = parser.start(); | ||
+ | |||
+ | abstract class ASTNode { | ||
+ | constructor() { }; | ||
+ | public toJSON(): any { | ||
+ | return { | ||
+ | ...this, id: this.constructor.name | ||
+ | }; | ||
+ | } | ||
} | } | ||
- | + | ||
- | // function for token | + | class StatementsNode extends ASTNode { |
- | function token (token_name, value) | + | constructor(public readonly statements: ASTNode[], public readonly line: number) { |
- | { | + | super(); |
- | return { | + | } |
- | token: token_name, | + | |
- | value: value | + | } |
- | }; | + | class DeclarationNode extends ASTNode { |
+ | constructor(public readonly variable_type: string, public readonly variable: string, public readonly op: string, public readonly value: Expression | ValueNode | FunctionCallNode, public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
} | } | ||
- | |||
- | |||
- | %} | ||
- | |||
- | %% | ||
- | |||
- | start: expressions { | ||
- | $$ = rule ('start', [$1]); | ||
- | return $$; | ||
- | // AST | ||
- | // return { | ||
- | // type: 'program', | ||
- | // elements: $1 | ||
- | // } | ||
- | }; | ||
- | |||
- | expressions: statement NEWLINE expressions { | ||
- | $$ = rule ('statement', [$1, token ('NEWLINE', $2), $3]); | ||
- | // AST | ||
- | // $3.splice (0, 0, $1); // add the expression to the array produced by expressions ($3) | ||
- | // $$ = $3; | ||
- | } | ||
- | | statement NEWLINE { | ||
- | $$ = rule ('statement', [$1, token ('NEWLINE', $2)]); | ||
- | // AST | ||
- | // $$ = [$1]; // an array with one element | ||
- | } | ||
- | | statement { | ||
- | $$ = rule ('statement', [$1]); | ||
- | // AST | ||
- | // $$ = [$1]; // an array with one element | ||
- | }; | ||
- | |||
- | statement: expr { | ||
- | $$ = rule ('statement', [$1]); | ||
- | } | ||
- | | variable { | ||
- | $$ = rule ('statement', [$1]); | ||
- | } | ||
- | | assign { | ||
- | $$ = rule ('statement', [$1]); | ||
- | } | ||
- | | function { | ||
- | $$ = rule ('function', [$1]); | ||
- | } | ||
- | | function_run { | ||
- | $$ = rule ('function_run', [$1]); | ||
- | }; | ||
- | |||
- | |||
- | expr: 'LP' expr 'RP' { | ||
- | $$ = rule ('expr', [token ('(', $1), $2, token (')', $3)]); | ||
- | } | ||
- | | expr '+' expr { | ||
- | $$ = rule ('expr', [$1, token ('+', $2), $3]); | ||
- | } | ||
- | | expr '-' expr { | ||
- | $$ = rule ('expr', [$1, token ('-', $2), $3]); | ||
- | } | ||
- | | expr '*' expr { | ||
- | $$ = rule ('expr', [$1, token ('*', $2), $3]); | ||
- | } | ||
- | | expr '/' expr { | ||
- | $$ = rule ('expr', [$1, token ('/', $2), $3]); | ||
- | } | ||
- | | NUMBER { | ||
- | // $1 is string so we store its float value | ||
- | $$ = token ('NUMBER', parseFloat ($1)); | ||
- | } | ||
- | | IDENTIFIER { | ||
- | // store the variable | ||
- | $$ = token ('IDENTIFIER', $1); | ||
- | } | ||
- | | STRING_VALUE { | ||
- | // store the variable | ||
- | // get the value of the string without the quotes | ||
- | $$ = token ('STRING_VALUE', $1.substring (1, $1.length-2)); | ||
- | }; | ||
- | |||
- | variable: VAR variables | ||
- | { | ||
- | $$ = rule ('variable', [token ('VAR', $1), $2]); | ||
- | }; | ||
- | |||
- | variables: IDENTIFIER ',' variables | ||
- | { | ||
- | $$ = rule ('variables', | ||
- | [ | ||
- | token ('IDENTIFIER', $1), | ||
- | token (',', ','), | ||
- | $3 | ||
- | ] | ||
- | ); | ||
- | |||
- | } | ||
- | | IDENTIFIER | ||
- | { | ||
- | $$ = token ('IDENTIFIER', $1); | ||
- | |||
- | } | ||
- | | IDENTIFIER OF type ',' variables | ||
- | { | ||
- | $$ = rule ('variables', | ||
- | [ | ||
- | token ('IDENTIFIER', $1), | ||
- | token ('OF', 'of'), | ||
- | $3, | ||
- | token (',', ','), | ||
- | $5 | ||
- | ] | ||
- | ); | ||
- | |||
- | } | ||
- | | IDENTIFIER OF type | ||
- | { | ||
- | $$ = rule ('variables', | ||
- | [ | ||
- | token ('IDENTIFIER', $1), | ||
- | token ('OF', 'of'), | ||
- | $3 | ||
- | ] | ||
- | ); | ||
- | |||
- | }; | ||
- | |||
- | type: INT | ||
- | { | ||
- | $$ = token ('INT', 'int'); | ||
- | } | ||
- | | FLOAT | ||
- | { | ||
- | $$ = token ('FLOAT', 'float'); | ||
- | } | ||
- | | STRING | ||
- | { | ||
- | $$ = token ('STRING', 'string'); | ||
- | }; | ||
- | |||
- | assign: IDENTIFIER '=' expr | ||
- | { | ||
- | $$ = rule ('assign', | ||
- | [ | ||
- | token ('IDENTIFIER', $1), | ||
- | token ('=', '='), | ||
- | $3 | ||
- | ] | ||
- | ); | ||
- | |||
- | }; | ||
- | |||
- | |||
- | |||
- | function: FUNCTION IDENTIFIER 'LP' parameters 'RP' NEWLINE expressions END_FUNCTION {$$ = rule ('function', [token('FUNCTION', $1), token('IDENTIFIER', $2), token('(', $3), $4, token(')', $5), token('NEWLINE', $6), $7, token('END_FUNCTION', $8)]);}; | ||
- | |||
- | parameters: IDENTIFIER OF type ',' parameters | ||
- | { | ||
- | $$ = rule ('parameters', [$1, token ('OF', 'of'), $3, token (',', $4), $5]); | ||
- | } | ||
- | | IDENTIFIER OF type | ||
- | { | ||
- | $$ = rule ('parameters', [$1, token ('OF', 'of'), $3]); | ||
- | } | ||
- | | { | ||
- | $$ = token ('EMPTY', ''); | ||
- | }; | ||
- | |||
- | function_run: IDENTIFIER 'LP' parameters_run 'RP' { $$ = rule ('function_run', [token ('IDENTIFIER', $1), token ('(', $2), $3, token (')', $4)]); | ||
- | }; | ||
- | |||
- | parameters_run: expr ',' parameters_run | ||
- | { | ||
- | $$ = rule ('parameters_run', [$1, token (',', $2), $3]); | ||
- | } | ||
- | | expr | ||
- | { | ||
- | $$ = rule ('parameters_run', [$1]); | ||
- | } | ||
- | | { | ||
- | $$ = token ('EMPTY', ''); | ||
- | }; | ||
- | </code> | + | class ValueNode extends ASTNode { |
+ | constructor(public readonly value: number | string, public readonly line: number, public readonly type: string) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class TypeNode extends ASTNode { | ||
+ | constructor(public readonly type_name: string, public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class Expression extends ASTNode { | ||
+ | constructor(public readonly op: string, public readonly left: Expression, public readonly right: Expression, public readonly line: number, public readonly type: string) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class ListNode extends ASTNode { | ||
+ | constructor(public readonly type: string, public readonly name: string, public readonly values: ListValuesNode, public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class ListValuesNode extends ASTNode { | ||
+ | constructor(public readonly values: ValueNode[], public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class AttributionNode extends ASTNode { | ||
+ | constructor(public readonly variable: string, public readonly value: Expression, public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class FunctionNode extends ASTNode { | ||
+ | constructor(public readonly function_name: string, public readonly parameters: ParameterNode[], public readonly instructions: StatementsNode, public readonly return_node: ReturnNode, public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class ParameterNode extends ASTNode { | ||
+ | constructor(public readonly type: string, public readonly value: string | number, public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class ReturnNode extends ASTNode { | ||
+ | constructor(public readonly statement: ASTNode, public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
+ | class FunctionCallNode extends ASTNode { | ||
+ | constructor(public readonly function_name: string, public readonly parameters: ValueNode[], public readonly line: number) { | ||
+ | super(); | ||
+ | } | ||
+ | } | ||
- | Le javascript | + | /* TODO 1: Declare Symbol Table type and initialize object */ |
- | <code javascript main.js> | + | let symbol_table = {}; |
- | "use strict"; | + | function addVariableToSymbolTable(variable: string, type: string) { |
- | + | /* symbol_table[variable] = { | |
- | // import fs for reading | + | type: type, |
- | var fs = require ('fs'); | + | value: undefined |
- | + | }; */ | |
- | // import the generated Parser | + | } |
- | var parser = require ('./program.js').parser; | + | |
- | + | ||
- | var str = fs.readFileSync (process.argv[2], 'UTF-8'); | + | |
- | // add a text to the parser | + | /* TODO 2: Check if a variable was already defined in the Symbol Table */ |
- | try | + | function isVariableDefined(variable: string) { |
- | { | + | |
- | // run the parser using a string, why are the values printed upside down? | + | |
- | var info = parser.parse (str); | + | |
- | console.log ('AST'); | + | |
- | console.log (JSON.stringify (info, null, 4)); | + | |
} | } | ||
- | catch (e) | + | |
- | { | + | /** TODO 3: Check if the types of the value nodes of an expression's operands are matching |
- | // display the error message and data | + | * The only operation allowed between strings is addition |
- | // console.log ('You have an error at line '+(e.hash.line+1)+' at position '+e.hash.loc.first_column); | + | */ |
- | // console.log (e.message); | + | function checkTypes(left: ValueNode, right: ValueNode, op: string) { |
- | console.log (e); | + | |
} | } | ||
- | </code> | ||
- | Exemple de program | + | /** TODO 4: Get expression final type |
+ | * If both operants have the same type, the expression will receive their type | ||
+ | * If one of the operands is string in the addition expression, the expression will receive the type 'string' | ||
+ | * If one of the operands is float, the expression will receive the type 'float' | ||
+ | * Add a new parameter to the Expression class, type, which will be the expression final type | ||
+ | */ | ||
- | <code> | + | function getType(left: ValueNode, right: ValueNode, op: string) { |
- | var a of int | + | |
- | a=10 | + | } |
- | function f () | + | |
- | var s of int | + | |
- | endfunction | + | |
- | </code> | + | /** TODO 5: Add the function parameters to the Symbol Table |
+ | * Each entry will have the function name as key and an object with the parameters list as value | ||
+ | * The function will also store the parameters as variables in the symbol table. This action is not necessary | ||
+ | */ | ||
+ | function addFunctionToSymbolTable(function_name: string, parameters: ParameterNode[]) { | ||
+ | | ||
+ | } | ||
+ | class MyAlfVisitor extends AbstractParseTreeVisitor<ASTNode> implements AlfVisitor<ASTNode> { | ||
+ | defaultResult() { | ||
+ | return new StatementsNode([], 0); | ||
+ | } | ||
+ | visitMultilineProg(ctx: MultilineProgContext): StatementsNode { | ||
+ | let statements = []; | ||
+ | for (let i = 0; i < ctx.statement().length; i++) | ||
+ | statements[i] = this.visit(ctx.statement(i)); | ||
+ | if (statements) { | ||
+ | return new StatementsNode(statements, 1); | ||
+ | } else { | ||
+ | throw new Error(); | ||
+ | } | ||
+ | } | ||
+ | visitSinglelineProg(ctx: SinglelineProgContext): ASTNode { | ||
+ | return new StatementsNode([this.visit(ctx.statement())], 1); | ||
+ | } | ||
+ | visitVariableDeclaration(ctx: VariableDeclarationContext): DeclarationNode { | ||
+ | /* TODO 1 & 2 */ | ||
+ | return new DeclarationNode( | ||
+ | (this.visit(ctx.type()) as TypeNode).type_name, | ||
+ | ctx.VARIABLE().text, | ||
+ | ctx.EQ().text, | ||
+ | this.visit(ctx.expression()) as Expression, | ||
+ | ctx.VARIABLE().symbol.line | ||
+ | ); | ||
+ | } | ||
+ | visitValueInt(ctx: ValueIntContext): ValueNode { | ||
+ | return new ValueNode( | ||
+ | parseInt(ctx.INT_NUMBER().text), | ||
+ | ctx.INT_NUMBER().symbol.line, | ||
+ | 'int' | ||
+ | ); | ||
+ | } | ||
+ | visitValueVariable(ctx: ValueVariableContext): ValueNode { | ||
+ | /* TODO 1 & 2 */ | ||
+ | return new ValueNode( | ||
+ | ctx.VARIABLE().text, | ||
+ | ctx.VARIABLE().symbol.line, | ||
+ | 'variable' | ||
+ | ); | ||
+ | } | ||
+ | visitValueFloat(ctx: ValueFloatContext): ValueNode { | ||
+ | return new ValueNode( | ||
+ | parseFloat(ctx.FLOAT_NUMBER().text), | ||
+ | ctx.FLOAT_NUMBER().symbol.line, | ||
+ | 'float' | ||
+ | ); | ||
+ | } | ||
+ | visitValueString(ctx: ValueStringContext): ValueNode { | ||
+ | return new ValueNode( | ||
+ | ctx.STRING_TEXT().text, | ||
+ | ctx.STRING_TEXT().symbol.line, | ||
+ | 'string' | ||
+ | ); | ||
+ | } | ||
+ | visitTypeInt(ctx: TypeIntContext): TypeNode { | ||
+ | return new TypeNode( | ||
+ | ctx.INT().text, | ||
+ | ctx.INT().symbol.line | ||
+ | ) | ||
+ | } | ||
+ | visitTypeString(ctx: TypeStringContext): TypeNode { | ||
+ | return new TypeNode( | ||
+ | ctx.STRING().text, | ||
+ | ctx.STRING().symbol.line | ||
+ | ) | ||
+ | } | ||
+ | visitTypeFloat(ctx: TypeFloatContext): TypeNode { | ||
+ | return new TypeNode( | ||
+ | ctx.FLOAT().text, | ||
+ | ctx.FLOAT().symbol.line | ||
+ | ) | ||
+ | } | ||
+ | visitExpressionMultiply(ctx: ExpressionMultiplyContext): Expression { | ||
+ | const left = this.visit(ctx.expression(0)); | ||
+ | const right = this.visit(ctx.expression(1)); | ||
+ | const op = ctx._op; | ||
- | ===== Exercises ===== | + | /** TODO 3: Check the type for each operand Value Node |
- | <note> | + | * If the types are not matching, throw ERROR: The types are not corresponding |
- | L'exemple inclus dans la structure du tp génère le parse tree pour le langage du fichier //script.alf//. | + | */ |
- | Utilisez la même source pour tous les exercices, écrivez des fonctions pour toutes les actions et ajoutez-les dans le meme fichier .y. Écrivez les erreurs sur l'écran. | + | if (op.text) { |
- | </note> | + | /* TODO 4: Add expression final type */ |
- | - Téléchargez/clonez [[https://github.com/UPB-FILS/alf/tree/master/TP/TP7|la structure]] du tp. Ajoutez des numéros de ligne au parse tree (**Indice**: modifiez la fonction rule() de sorte qu'elle accèpte comme paramètre yylineno pour chaque règle) (**2p**) | + | let type = ''; |
- | - Modifiez la grammaire de l'exemple pour générer un AST pour les expressions. Suivez les explications marquées par TODO 2. Pour chaque règle correspondant a expression/expression_string, on doit attribuer a la variable ''$$'' un objet avec les propriétés suivantes: //type// (nom de la règle), //op// (valeur de l'opérateur), //left// (valeur du premier opérande), //right// (valeur du deuxième opérande). Pour chaque feuille, ''$$'' recevra un objet contenant le //type// et la //valeur// du noeud. Au moment de la génération de l'AST, il faut commenter l'appel des fonctions ''rule'' et ''token''. L'AST pour les expressions doit etre similaire a celui présenté dans la section **Exemple d'AST**. Pour tester la correctitude de votre grammaire, parsez le contenu du fichier **ex2.txt**. (**3p**) | + | return new Expression(op.text, left as Expression, right as Expression, ctx._op.line, type); |
- | - Modifiez la grammaire de l'exemple afin de générer un AST pour tout le programme. Suivez les indications marquées par TODO 3. Parsez le contenu du fichier **ex3.txt**. (**5p**) | + | } else throw new Error(); |
- | - **Bonus** Vérifiez que toutes les variables utilisées dans les expressions sont définies avant. Parsez le contenu du fichier **ex4.txt** pour vérifier votre grammaire. (**1p**) | + | } |
+ | visitExpressionDivision(ctx: ExpressionDivisionContext): Expression { | ||
+ | const left = this.visit(ctx.expression(0)); | ||
+ | const right = this.visit(ctx.expression(1)); | ||
+ | const op = ctx._op; | ||
+ | /** TODO 3: Check the type for each operand Value Node | ||
+ | * If the types are not matching, throw ERROR: The types are not corresponding | ||
+ | */ | ||
- | <hidden> | + | if (op.text) { |
- | ==== Solutions ==== | + | /* TODO 4: Add expression final type */ |
+ | let type = ''; | ||
+ | return new Expression(op.text, left as Expression, right as Expression, ctx._op.line, type); | ||
+ | } else throw new Error(); | ||
+ | } | ||
+ | visitExpressionRem(ctx: ExpressionRemContext): Expression { | ||
+ | const left = this.visit(ctx.expression(0)); | ||
+ | const right = this.visit(ctx.expression(1)); | ||
+ | const op = ctx._op; | ||
- | [[https://github.com/UPB-FILS/alf/tree/master/TP/TP7|Solutions]] | + | /** TODO 3: Check the type for each operand Value Node |
- | </hidden> | + | * If the types are not matching, throw ERROR: The types are not corresponding |
+ | */ | ||
+ | if (op.text) { | ||
+ | /* TODO 4: Add expression final type */ | ||
+ | let type = ''; | ||
+ | return new Expression(op.text, left as Expression, right as Expression, ctx._op.line, type); | ||
+ | } else throw new Error(); | ||
+ | } | ||
+ | visitExpressionAddition(ctx: ExpressionAdditionContext): Expression { | ||
+ | const left = this.visit(ctx.expression(0)); | ||
+ | const right = this.visit(ctx.expression(1)); | ||
+ | const op = ctx._op; | ||
+ | /** TODO 3: Check the type for each operand Value Node | ||
+ | * If the types are not matching, throw ERROR: The types are not corresponding | ||
+ | */ | ||
+ | if (op.text) { | ||
+ | /* TODO 4: Add expression final type */ | ||
+ | let type = ''; | ||
+ | return new Expression(op.text, left as Expression, right as Expression, ctx._op.line, type); | ||
+ | } else throw new Error(); | ||
+ | } | ||
+ | visitExpressionSubtraction(ctx: ExpressionSubtractionContext): Expression { | ||
+ | const left = this.visit(ctx.expression(0)); | ||
+ | const right = this.visit(ctx.expression(1)); | ||
+ | const op = ctx._op; | ||
+ | /** TODO 3: Check the type for each operand Value Node | ||
+ | * If the types are not matching, throw ERROR: The types are not corresponding | ||
+ | */ | ||
+ | |||
+ | if (op.text) { | ||
+ | /* TODO 4: Add expression final type */ | ||
+ | let type = ''; | ||
+ | return new Expression(op.text, left as Expression, right as Expression, ctx._op.line, type); | ||
+ | } else throw new Error(); | ||
+ | } | ||
+ | visitExpressionParanthesis(ctx: ExpressionParanthesisContext) { | ||
+ | return this.visit(ctx.expression()); | ||
+ | } | ||
+ | visitExpressionValue(ctx: ExpressionValueContext): ValueNode { | ||
+ | let value = this.visit(ctx.value()); | ||
+ | if (value !== undefined) { | ||
+ | return new ValueNode((this.visit(ctx.value()) as ValueNode).value, ctx.value()._start.line, (this.visit(ctx.value()) as ValueNode).type); | ||
+ | } else throw new Error(); | ||
+ | } | ||
+ | visitListDeclaration(ctx: ListDeclarationContext): ListNode { | ||
+ | return new ListNode( | ||
+ | 'list', | ||
+ | ctx.VARIABLE().text, | ||
+ | this.visit(ctx.values()) as ListValuesNode, | ||
+ | ctx.values().start.line | ||
+ | ); | ||
+ | } | ||
+ | visitListValues(ctx: ListValuesContext): ListValuesNode { | ||
+ | let values: ValueNode[] = []; | ||
+ | for (let i = 0; i < ctx.value().length; i++) { | ||
+ | let node = (this.visit(ctx.value(i)) as ValueNode); | ||
+ | values[i] = new ValueNode(node.value, node.line, node.type); | ||
+ | } | ||
+ | if (values) { | ||
+ | return new ListValuesNode(values, values[0].line); | ||
+ | } else { | ||
+ | throw new Error(); | ||
+ | } | ||
+ | } | ||
+ | visitVariableAttribution(ctx: VariableAttributionContext): AttributionNode { | ||
+ | /* TODO 1 & 2*/ | ||
+ | | ||
+ | /* TODO 5: Set the type of the variable in the Symbol Tables as being the final type of the expression */ | ||
+ | return new AttributionNode( | ||
+ | ctx.VARIABLE().text, | ||
+ | this.visit(ctx.expression()) as Expression, | ||
+ | ctx.VARIABLE().symbol.line | ||
+ | ); | ||
+ | } | ||
+ | visitFunctionContent(ctx: FunctionContentContext): FunctionNode { | ||
+ | let parameters = []; | ||
+ | for (let i = 0; i < ctx.parameter().length; i++) { | ||
+ | let node = (this.visit(ctx.parameter(i)) as ParameterNode); | ||
+ | parameters[i] = new ParameterNode(node.type, node.value, node.line); | ||
+ | } | ||
+ | let instructions = []; | ||
+ | for (let i = 0; i < ctx.statement().length; i++) { | ||
+ | instructions[i] = this.visit(ctx.statement(i)); | ||
+ | } | ||
+ | /** TODO 5 */ | ||
+ | |||
+ | return new FunctionNode( | ||
+ | ctx.VARIABLE().text, | ||
+ | parameters, | ||
+ | new StatementsNode(instructions, ctx.FUNCTION().symbol.line), | ||
+ | new ReturnNode(this.visit(ctx.return_function()), ctx.return_function().start.line), | ||
+ | ctx.FUNCTION().symbol.line | ||
+ | ); | ||
+ | } | ||
+ | visitVariableFunctionCall(ctx: VariableFunctionCallContext): DeclarationNode { | ||
+ | /* TODO 1 & 2 */ | ||
+ | return new DeclarationNode( | ||
+ | (this.visit(ctx.type()) as TypeNode).type_name, | ||
+ | ctx.VARIABLE().text, | ||
+ | ctx.EQ().text, | ||
+ | this.visit(ctx.function_call()) as FunctionCallNode, | ||
+ | ctx.VARIABLE().symbol.line | ||
+ | ); | ||
+ | } | ||
+ | visitFunctionCall(ctx: FunctionCallContext): FunctionCallNode { | ||
+ | let parameters = []; | ||
+ | for (let i = 0; i < ctx.value().length; i++) { | ||
+ | let node = (this.visit(ctx.value(i)) as ValueNode); | ||
+ | parameters[i] = new ValueNode(node.value, node.line, node.type); | ||
+ | } | ||
+ | return new FunctionCallNode( | ||
+ | ctx.VARIABLE().text, | ||
+ | parameters, | ||
+ | ctx.VARIABLE().symbol.line | ||
+ | ); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | const visitor = new MyAlfVisitor(); | ||
+ | |||
+ | let ast = visitor.visit(tree).toJSON(); | ||
+ | |||
+ | let symbol_tree = { | ||
+ | ast, | ||
+ | symbol_table | ||
+ | } | ||
+ | |||
+ | console.log(JSON.stringify(symbol_tree, null, 4)); | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ===== Exercises ===== | ||
+ | - Téléchargez [[https://github.com/UPB-FILS-ALF/TP/tree/main/TP7|la structure]] du TP. En suivant les lignes marquées par TODO 1, ajoutez les variables et leur type au tableau des symboles. Testez avec le fichier ''ex1.txt'' (**2.5p**). | ||
+ | - Si une variable est définie plusieurs fois, retournez sur l'écran une erreur et arretez le programme. Suivez les lignes marquées par TODO 2 et puis testez avec le ficher ''ex3.txt'' (**2p**) | ||
+ | - Pour chaque type d'expression, vérifiez le type de chaque élément (int, float ou string). Si l'opérateur est une variable, vérifiez son type en utilisant le tableau des symboles. Retournez une erreur et arretez l'exécution du programme si vous ne pouvez pas calculer la valeur de l'expression (par exemple, toute opération entre les string et les nombres, sauf la somme). (**Hint**: Vous pouvez définir une fonction supplémentaire qui fait toutes les validations et seulement l'appeler pour chaque regle). Suivez les lignes avec TODO 3 et testez avec ''ex3.txt''. A la fin, modifiez le contenu du fichier ''ex3.txt'' pour tester votre programme pour l'expression **123 - "alf";**. (**3p**). | ||
+ | - Pour les variables qui n'ont pas recu un type au moment de la déclaration, déterminez le type lorsque la variable obtient une valeur (regle assign). Vous devrez déterminer le type de chaque expression en tenant compte du type de chaque opérande. (**Hint**: Si l'un des opérandes est nombre réel/string, le résultat sera réel/string. Vous pouvez implémenter une fonction qui fait les vérifications et l'appeler pour déterminer le type de chaque expression). Testez avec le fichier ''ex4.txt''. Suivez les lignes avec TODO 4. (**2.5p**). | ||
+ | - **BONUS:** Ajoutez la définition de fonction dans la table des symboles, de facon similaire a l'ajout d'une variable. Chaque parametre doit etre ajouté au tableau des symboles avec le type //parameter//. L'entrée de la table de symboles recevra la fonction et la liste des paramètres de la fonction. Testez avec ''ex5.txt''. Suivez les lignes marquées par TODO 5. (**2p**) | ||