@devalade/algolang 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +709 -192
- package/dist/index.js +1435 -385
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,268 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
|
+
// src/keywords.ts
|
|
5
|
+
var KEYWORDS = {
|
|
6
|
+
PROGRAMME: {
|
|
7
|
+
tokenType: "PROGRAMME" /* PROGRAM */,
|
|
8
|
+
kind: "control",
|
|
9
|
+
detail: "Mot-clé PROGRAMME",
|
|
10
|
+
documentation: "**PROGRAMME** : Début d'un programme AlgoLang."
|
|
11
|
+
},
|
|
12
|
+
DEBUT: {
|
|
13
|
+
tokenType: "DEBUT" /* BEGIN */,
|
|
14
|
+
kind: "control",
|
|
15
|
+
detail: "Mot-clé DEBUT",
|
|
16
|
+
documentation: "**DEBUT** : Début du bloc d'instructions principal."
|
|
17
|
+
},
|
|
18
|
+
FIN: {
|
|
19
|
+
tokenType: "FIN" /* END */,
|
|
20
|
+
kind: "control",
|
|
21
|
+
detail: "Mot-clé FIN",
|
|
22
|
+
documentation: "**FIN** : Fin du bloc d'instructions ou du programme."
|
|
23
|
+
},
|
|
24
|
+
VAR: {
|
|
25
|
+
tokenType: "VAR" /* VAR */,
|
|
26
|
+
kind: "declaration",
|
|
27
|
+
detail: "Mot-clé VAR",
|
|
28
|
+
documentation: "**VAR** : Section de déclaration des variables."
|
|
29
|
+
},
|
|
30
|
+
ENTIER: {
|
|
31
|
+
tokenType: "ENTIER" /* INTEGER */,
|
|
32
|
+
kind: "type",
|
|
33
|
+
detail: "Type ENTIER",
|
|
34
|
+
documentation: "**ENTIER** : Type de donnée pour les nombres entiers."
|
|
35
|
+
},
|
|
36
|
+
REEL: {
|
|
37
|
+
tokenType: "REEL" /* REAL */,
|
|
38
|
+
kind: "type",
|
|
39
|
+
detail: "Type REEL",
|
|
40
|
+
documentation: "**REEL** : Type de donnée pour les nombres à virgule."
|
|
41
|
+
},
|
|
42
|
+
BOOLEEN: {
|
|
43
|
+
tokenType: "BOOLEEN" /* BOOLEAN */,
|
|
44
|
+
kind: "type",
|
|
45
|
+
detail: "Type BOOLEEN",
|
|
46
|
+
documentation: "**BOOLEEN** : Type de donnée logique (VRAI/FAUX)."
|
|
47
|
+
},
|
|
48
|
+
CHAINE: {
|
|
49
|
+
tokenType: "CHAINE" /* STRING */,
|
|
50
|
+
kind: "type",
|
|
51
|
+
detail: "Type CHAINE",
|
|
52
|
+
documentation: "**CHAINE** : Type de donnée pour le texte."
|
|
53
|
+
},
|
|
54
|
+
SI: {
|
|
55
|
+
tokenType: "SI" /* IF */,
|
|
56
|
+
kind: "control",
|
|
57
|
+
detail: "Mot-clé SI",
|
|
58
|
+
documentation: "**SI** : Structure conditionnelle."
|
|
59
|
+
},
|
|
60
|
+
ALORS: {
|
|
61
|
+
tokenType: "ALORS" /* THEN */,
|
|
62
|
+
kind: "control",
|
|
63
|
+
detail: "Mot-clé ALORS",
|
|
64
|
+
documentation: "**ALORS** : Début du bloc exécuté si la condition est vraie."
|
|
65
|
+
},
|
|
66
|
+
SINON: {
|
|
67
|
+
tokenType: "SINON" /* ELSE */,
|
|
68
|
+
kind: "control",
|
|
69
|
+
detail: "Mot-clé SINON",
|
|
70
|
+
documentation: "**SINON** : Début du bloc exécuté si la condition est fausse."
|
|
71
|
+
},
|
|
72
|
+
FINSI: {
|
|
73
|
+
tokenType: "FINIF" /* ENDIF */,
|
|
74
|
+
kind: "control",
|
|
75
|
+
detail: "Mot-clé FINSI",
|
|
76
|
+
documentation: "**FINSI** : Fin d'une structure conditionnelle."
|
|
77
|
+
},
|
|
78
|
+
TANTQUE: {
|
|
79
|
+
tokenType: "TANTQUE" /* WHILE */,
|
|
80
|
+
kind: "control",
|
|
81
|
+
detail: "Mot-clé TANTQUE",
|
|
82
|
+
documentation: "**TANTQUE** : Boucle répétitive tant qu'une condition est vraie."
|
|
83
|
+
},
|
|
84
|
+
FAIRE: {
|
|
85
|
+
tokenType: "FAIRE" /* DO */,
|
|
86
|
+
kind: "control",
|
|
87
|
+
detail: "Mot-clé FAIRE",
|
|
88
|
+
documentation: "**FAIRE** : Début du corps d'une boucle."
|
|
89
|
+
},
|
|
90
|
+
FINTANTQUE: {
|
|
91
|
+
tokenType: "FINTANTQUE" /* ENDWHILE */,
|
|
92
|
+
kind: "control",
|
|
93
|
+
detail: "Mot-clé FINTANTQUE",
|
|
94
|
+
documentation: "**FINTANTQUE** : Fin d'une boucle TANTQUE."
|
|
95
|
+
},
|
|
96
|
+
POUR: {
|
|
97
|
+
tokenType: "POUR" /* FOR */,
|
|
98
|
+
kind: "control",
|
|
99
|
+
detail: "Mot-clé POUR",
|
|
100
|
+
documentation: "**POUR** : Boucle avec compteur."
|
|
101
|
+
},
|
|
102
|
+
ALLANT: {
|
|
103
|
+
tokenType: "ALLANT" /* ALLANT */,
|
|
104
|
+
kind: "control",
|
|
105
|
+
detail: "Mot-clé ALLANT",
|
|
106
|
+
documentation: "**ALLANT** : Utilisé dans une boucle POUR pour spécifier la plage."
|
|
107
|
+
},
|
|
108
|
+
DE: {
|
|
109
|
+
tokenType: "DE" /* DE */,
|
|
110
|
+
kind: "control",
|
|
111
|
+
detail: "Mot-clé DE",
|
|
112
|
+
documentation: "**DE** : Spécifie le début d'une plage dans une boucle POUR."
|
|
113
|
+
},
|
|
114
|
+
A: {
|
|
115
|
+
tokenType: "A" /* TO */,
|
|
116
|
+
kind: "control",
|
|
117
|
+
detail: "Mot-clé A",
|
|
118
|
+
documentation: "**A** : Spécifie la fin d'une plage dans une boucle POUR."
|
|
119
|
+
},
|
|
120
|
+
FINPOUR: {
|
|
121
|
+
tokenType: "FINPOUR" /* ENDFOR */,
|
|
122
|
+
kind: "control",
|
|
123
|
+
detail: "Mot-clé FINPOUR",
|
|
124
|
+
documentation: "**FINPOUR** : Fin d'une boucle POUR."
|
|
125
|
+
},
|
|
126
|
+
REPETER: {
|
|
127
|
+
tokenType: "REPETER" /* REPEAT */,
|
|
128
|
+
kind: "control",
|
|
129
|
+
detail: "Mot-clé REPETER",
|
|
130
|
+
documentation: "**REPETER** : Boucle exécutée au moins une fois."
|
|
131
|
+
},
|
|
132
|
+
"JUSQU'A": {
|
|
133
|
+
tokenType: "JUSQU'A" /* UNTIL */,
|
|
134
|
+
kind: "control",
|
|
135
|
+
detail: "Mot-clé JUSQU'A",
|
|
136
|
+
documentation: "**JUSQU'A** : Condition de fin d'une boucle REPETER."
|
|
137
|
+
},
|
|
138
|
+
LIRE: {
|
|
139
|
+
tokenType: "LIRE" /* READ */,
|
|
140
|
+
kind: "io",
|
|
141
|
+
detail: "Fonction LIRE",
|
|
142
|
+
documentation: "**LIRE(variable)** : Lit une valeur depuis l'entrée standard."
|
|
143
|
+
},
|
|
144
|
+
ECRIRE: {
|
|
145
|
+
tokenType: "ECRIRE" /* WRITE */,
|
|
146
|
+
kind: "io",
|
|
147
|
+
detail: "Fonction ECRIRE",
|
|
148
|
+
documentation: "**ECRIRE(...)** : Affiche des valeurs dans la console."
|
|
149
|
+
},
|
|
150
|
+
VRAI: {
|
|
151
|
+
tokenType: "VRAI" /* TRUE */,
|
|
152
|
+
kind: "literal",
|
|
153
|
+
detail: "Constante VRAI",
|
|
154
|
+
documentation: "**VRAI** : Valeur booléenne vraie."
|
|
155
|
+
},
|
|
156
|
+
FAUX: {
|
|
157
|
+
tokenType: "FAUX" /* FALSE */,
|
|
158
|
+
kind: "literal",
|
|
159
|
+
detail: "Constante FAUX",
|
|
160
|
+
documentation: "**FAUX** : Valeur booléenne fausse."
|
|
161
|
+
},
|
|
162
|
+
ET: {
|
|
163
|
+
tokenType: "ET" /* AND */,
|
|
164
|
+
kind: "operator",
|
|
165
|
+
detail: "Opérateur ET",
|
|
166
|
+
documentation: "**ET** : Opérateur logique ET (AND)."
|
|
167
|
+
},
|
|
168
|
+
OU: {
|
|
169
|
+
tokenType: "OU" /* OR */,
|
|
170
|
+
kind: "operator",
|
|
171
|
+
detail: "Opérateur OU",
|
|
172
|
+
documentation: "**OU** : Opérateur logique OU (OR)."
|
|
173
|
+
},
|
|
174
|
+
NON: {
|
|
175
|
+
tokenType: "NON" /* NOT */,
|
|
176
|
+
kind: "operator",
|
|
177
|
+
detail: "Opérateur NON",
|
|
178
|
+
documentation: "**NON** : Opérateur logique NON (NOT)."
|
|
179
|
+
},
|
|
180
|
+
TABLEAU: {
|
|
181
|
+
tokenType: "TABLEAU" /* ARRAY */,
|
|
182
|
+
kind: "declaration",
|
|
183
|
+
detail: "Type TABLEAU",
|
|
184
|
+
documentation: "**TABLEAU** : Déclare un tableau de n éléments.\nSyntaxe : `t: TABLEAU[10] DE ENTIER`"
|
|
185
|
+
},
|
|
186
|
+
FONCTION: {
|
|
187
|
+
tokenType: "FONCTION" /* FUNCTION */,
|
|
188
|
+
kind: "declaration",
|
|
189
|
+
detail: "Mot-clé FONCTION",
|
|
190
|
+
documentation: "**FONCTION** nom(params): TYPE : Déclare une fonction qui retourne une valeur.\nExemple : `FONCTION carre(n: ENTIER): ENTIER`"
|
|
191
|
+
},
|
|
192
|
+
PROCEDURE: {
|
|
193
|
+
tokenType: "PROCEDURE" /* PROCEDURE */,
|
|
194
|
+
kind: "declaration",
|
|
195
|
+
detail: "Mot-clé PROCEDURE",
|
|
196
|
+
documentation: "**PROCEDURE** nom(params) : Déclare une procédure (sans valeur de retour).\nExemple : `PROCEDURE afficher(s: CHAINE)`"
|
|
197
|
+
},
|
|
198
|
+
RETOURNER: {
|
|
199
|
+
tokenType: "RETOURNER" /* RETURN */,
|
|
200
|
+
kind: "declaration",
|
|
201
|
+
detail: "Mot-clé RETOURNER",
|
|
202
|
+
documentation: "**RETOURNER** expr : Retourne une valeur depuis une fonction."
|
|
203
|
+
},
|
|
204
|
+
abs: {
|
|
205
|
+
tokenType: null,
|
|
206
|
+
kind: "builtin-function",
|
|
207
|
+
detail: "Fonction abs(x)",
|
|
208
|
+
documentation: "**abs(x)** : Retourne la valeur absolue de x."
|
|
209
|
+
},
|
|
210
|
+
max: {
|
|
211
|
+
tokenType: null,
|
|
212
|
+
kind: "builtin-function",
|
|
213
|
+
detail: "Fonction max(a, b)",
|
|
214
|
+
documentation: "**max(a, b)** : Retourne le plus grand des deux nombres."
|
|
215
|
+
},
|
|
216
|
+
min: {
|
|
217
|
+
tokenType: null,
|
|
218
|
+
kind: "builtin-function",
|
|
219
|
+
detail: "Fonction min(a, b)",
|
|
220
|
+
documentation: "**min(a, b)** : Retourne le plus petit des deux nombres."
|
|
221
|
+
},
|
|
222
|
+
mod: {
|
|
223
|
+
tokenType: null,
|
|
224
|
+
kind: "builtin-function",
|
|
225
|
+
detail: "Fonction mod(a, b)",
|
|
226
|
+
documentation: "**mod(a, b)** : Retourne le reste de la division de a par b. Équivalent à `a % b`."
|
|
227
|
+
},
|
|
228
|
+
racine_carree: {
|
|
229
|
+
tokenType: null,
|
|
230
|
+
kind: "builtin-function",
|
|
231
|
+
detail: "Fonction racine_carree(x)",
|
|
232
|
+
documentation: "**racine_carree(x)** : Retourne la racine carrée de x."
|
|
233
|
+
},
|
|
234
|
+
taille: {
|
|
235
|
+
tokenType: null,
|
|
236
|
+
kind: "builtin-function",
|
|
237
|
+
detail: "Fonction taille(x)",
|
|
238
|
+
documentation: "**taille(x)** : Retourne la taille d'un tableau ou la longueur d'une chaîne."
|
|
239
|
+
},
|
|
240
|
+
sous_chaine: {
|
|
241
|
+
tokenType: null,
|
|
242
|
+
kind: "builtin-function",
|
|
243
|
+
detail: "Fonction sous_chaine(s, i, n)",
|
|
244
|
+
documentation: "**sous_chaine(s, i, n)** : Retourne n caractères de la chaîne s à partir de l'indice i."
|
|
245
|
+
},
|
|
246
|
+
concat: {
|
|
247
|
+
tokenType: null,
|
|
248
|
+
kind: "builtin-function",
|
|
249
|
+
detail: "Fonction concat(a, b)",
|
|
250
|
+
documentation: "**concat(a, b)** : Concatène deux chaînes de caractères."
|
|
251
|
+
},
|
|
252
|
+
entier_en_reel: {
|
|
253
|
+
tokenType: null,
|
|
254
|
+
kind: "builtin-function",
|
|
255
|
+
detail: "Fonction entier_en_reel(x)",
|
|
256
|
+
documentation: "**entier_en_reel(x)** : Convertit un entier en réel."
|
|
257
|
+
},
|
|
258
|
+
reel_en_entier: {
|
|
259
|
+
tokenType: null,
|
|
260
|
+
kind: "builtin-function",
|
|
261
|
+
detail: "Fonction reel_en_entier(x)",
|
|
262
|
+
documentation: "**reel_en_entier(x)** : Convertit un réel en entier (troncature)."
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
4
266
|
// src/lexer/lexer.ts
|
|
5
267
|
class Lexer {
|
|
6
268
|
source;
|
|
@@ -10,37 +272,7 @@ class Lexer {
|
|
|
10
272
|
keywords;
|
|
11
273
|
constructor(source) {
|
|
12
274
|
this.source = source;
|
|
13
|
-
this.keywords = new Map([
|
|
14
|
-
["PROGRAMME", "PROGRAMME" /* PROGRAM */],
|
|
15
|
-
["DEBUT", "DEBUT" /* BEGIN */],
|
|
16
|
-
["FIN", "FIN" /* END */],
|
|
17
|
-
["VAR", "VAR" /* VAR */],
|
|
18
|
-
["ENTIER", "ENTIER" /* INTEGER */],
|
|
19
|
-
["REEL", "REEL" /* REAL */],
|
|
20
|
-
["BOOLEEN", "BOOLEEN" /* BOOLEAN */],
|
|
21
|
-
["CHAINE", "CHAINE" /* STRING */],
|
|
22
|
-
["SI", "SI" /* IF */],
|
|
23
|
-
["ALORS", "ALORS" /* THEN */],
|
|
24
|
-
["SINON", "SINON" /* ELSE */],
|
|
25
|
-
["FINSI", "FINIF" /* ENDIF */],
|
|
26
|
-
["TANTQUE", "TANTQUE" /* WHILE */],
|
|
27
|
-
["FAIRE", "FAIRE" /* DO */],
|
|
28
|
-
["POUR", "POUR" /* FOR */],
|
|
29
|
-
["ALLANT", "ALLANT" /* ALLANT */],
|
|
30
|
-
["DE", "DE" /* DE */],
|
|
31
|
-
["A", "A" /* TO */],
|
|
32
|
-
["REPETER", "REPETER" /* REPEAT */],
|
|
33
|
-
["JUSQUA", "JUSQUA" /* UNTIL */],
|
|
34
|
-
["LIRE", "LIRE" /* READ */],
|
|
35
|
-
["ECRIRE", "ECRIRE" /* WRITE */],
|
|
36
|
-
["VRAI", "VRAI" /* TRUE */],
|
|
37
|
-
["FAUX", "FAUX" /* FALSE */],
|
|
38
|
-
["ET", "ET" /* AND */],
|
|
39
|
-
["OU", "OU" /* OR */],
|
|
40
|
-
["NON", "NON" /* NOT */],
|
|
41
|
-
["FINPOUR", "FINPOUR" /* ENDFOR */],
|
|
42
|
-
["FINTANTQUE", "FINTANTQUE" /* ENDWHILE */]
|
|
43
|
-
]);
|
|
275
|
+
this.keywords = new Map(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label, e]) => [label, e.tokenType]));
|
|
44
276
|
}
|
|
45
277
|
tokenize() {
|
|
46
278
|
const tokens = [];
|
|
@@ -113,6 +345,8 @@ class Lexer {
|
|
|
113
345
|
return this.createToken("MULTIPLY" /* MULTIPLY */, this.advance());
|
|
114
346
|
case "/":
|
|
115
347
|
return this.createToken("DIVIDE" /* DIVIDE */, this.advance());
|
|
348
|
+
case "%":
|
|
349
|
+
return this.createToken("MODULO" /* MODULO */, this.advance());
|
|
116
350
|
case "=":
|
|
117
351
|
return this.createToken("EQUAL" /* EQUAL */, this.advance());
|
|
118
352
|
case "<":
|
|
@@ -131,6 +365,10 @@ class Lexer {
|
|
|
131
365
|
return this.createToken("LEFT_PAREN" /* LEFT_PAREN */, this.advance());
|
|
132
366
|
case ")":
|
|
133
367
|
return this.createToken("RIGHT_PAREN" /* RIGHT_PAREN */, this.advance());
|
|
368
|
+
case "[":
|
|
369
|
+
return this.createToken("LEFT_BRACKET" /* LEFT_BRACKET */, this.advance());
|
|
370
|
+
case "]":
|
|
371
|
+
return this.createToken("RIGHT_BRACKET" /* RIGHT_BRACKET */, this.advance());
|
|
134
372
|
default:
|
|
135
373
|
throw new Error(`Caractère non reconnu '${char}' à la ligne ${this.line}, colonne ${this.column}`);
|
|
136
374
|
}
|
|
@@ -322,87 +560,108 @@ class Lexer {
|
|
|
322
560
|
` || char === "\r";
|
|
323
561
|
}
|
|
324
562
|
}
|
|
563
|
+
// src/semantic/semantic-analyzer.ts
|
|
564
|
+
class SemanticAnalyzer {
|
|
565
|
+
symbolTable = {
|
|
566
|
+
symbols: new Map,
|
|
567
|
+
children: [],
|
|
568
|
+
scopeName: "global"
|
|
569
|
+
};
|
|
570
|
+
errors = [];
|
|
571
|
+
analyze(ast) {
|
|
572
|
+
this.walk(ast);
|
|
573
|
+
return { symbolTable: this.symbolTable, errors: this.errors };
|
|
574
|
+
}
|
|
575
|
+
walk(node) {
|
|
576
|
+
switch (node.type) {
|
|
577
|
+
case "VAR_DECLARATION" /* VAR_DECLARATION */:
|
|
578
|
+
this.collectVarDeclaration(node);
|
|
579
|
+
break;
|
|
580
|
+
case "ARRAY_DECLARATION" /* ARRAY_DECLARATION */:
|
|
581
|
+
this.collectArrayDeclaration(node);
|
|
582
|
+
break;
|
|
583
|
+
default:
|
|
584
|
+
node.children?.forEach((child) => this.walk(child));
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
collectVarDeclaration(node) {
|
|
589
|
+
const algoType = node.value;
|
|
590
|
+
const line = node.token?.line ?? 0;
|
|
591
|
+
const column = node.token?.column ?? 0;
|
|
592
|
+
for (const child of node.children ?? []) {
|
|
593
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
594
|
+
continue;
|
|
595
|
+
const name = child.value;
|
|
596
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
597
|
+
this.errors.push({
|
|
598
|
+
type: "ERROR",
|
|
599
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
600
|
+
line,
|
|
601
|
+
column,
|
|
602
|
+
position: node.token?.position ?? 0,
|
|
603
|
+
code: "DUPLICATE_VARIABLE",
|
|
604
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
605
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
606
|
+
});
|
|
607
|
+
} else {
|
|
608
|
+
this.defineSymbol(name, algoType, line, column);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
collectArrayDeclaration(node) {
|
|
613
|
+
const elemType = node.value;
|
|
614
|
+
const sizeNode = node.children?.[0];
|
|
615
|
+
const size = sizeNode?.value;
|
|
616
|
+
const typeLabel = `TABLEAU[${size}] DE ${elemType}`;
|
|
617
|
+
const line = node.token?.line ?? 0;
|
|
618
|
+
const column = node.token?.column ?? 0;
|
|
619
|
+
for (const child of (node.children ?? []).slice(1)) {
|
|
620
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
621
|
+
continue;
|
|
622
|
+
const name = child.value;
|
|
623
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
624
|
+
this.errors.push({
|
|
625
|
+
type: "ERROR",
|
|
626
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
627
|
+
line,
|
|
628
|
+
column,
|
|
629
|
+
position: node.token?.position ?? 0,
|
|
630
|
+
code: "DUPLICATE_VARIABLE",
|
|
631
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
632
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
633
|
+
});
|
|
634
|
+
} else {
|
|
635
|
+
this.defineSymbol(name, typeLabel, line, column);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
defineSymbol(name, type, line, column) {
|
|
640
|
+
const info = {
|
|
641
|
+
name,
|
|
642
|
+
type,
|
|
643
|
+
scope: this.symbolTable.scopeName,
|
|
644
|
+
line,
|
|
645
|
+
column
|
|
646
|
+
};
|
|
647
|
+
this.symbolTable.symbols.set(name, info);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
325
651
|
// src/parser/parser.ts
|
|
326
652
|
class Parser {
|
|
327
653
|
tokens;
|
|
328
654
|
current = 0;
|
|
329
655
|
errors = [];
|
|
330
|
-
|
|
331
|
-
reservedKeywords = new Set([
|
|
332
|
-
"programme",
|
|
333
|
-
"debut",
|
|
334
|
-
"fin",
|
|
335
|
-
"var",
|
|
336
|
-
"entier",
|
|
337
|
-
"reel",
|
|
338
|
-
"booleen",
|
|
339
|
-
"chaine",
|
|
340
|
-
"si",
|
|
341
|
-
"alors",
|
|
342
|
-
"sinon",
|
|
343
|
-
"finsi",
|
|
344
|
-
"tantque",
|
|
345
|
-
"faire",
|
|
346
|
-
"fintantque",
|
|
347
|
-
"pour",
|
|
348
|
-
"a",
|
|
349
|
-
"finpour",
|
|
350
|
-
"repeter",
|
|
351
|
-
"jusqua",
|
|
352
|
-
"lire",
|
|
353
|
-
"ecrire",
|
|
354
|
-
"vrai",
|
|
355
|
-
"faux",
|
|
356
|
-
"et",
|
|
357
|
-
"ou",
|
|
358
|
-
"non",
|
|
359
|
-
"fonction",
|
|
360
|
-
"procedure",
|
|
361
|
-
"retourner"
|
|
362
|
-
]);
|
|
656
|
+
reservedKeywords = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label]) => label.toLowerCase()));
|
|
363
657
|
constructor(tokens) {
|
|
364
658
|
this.tokens = tokens;
|
|
365
|
-
this.symbolTable = {
|
|
366
|
-
symbols: new Map,
|
|
367
|
-
children: [],
|
|
368
|
-
scopeName: "global"
|
|
369
|
-
};
|
|
370
659
|
}
|
|
371
660
|
isReservedKeyword(identifier) {
|
|
372
661
|
return this.reservedKeywords.has(identifier.toLowerCase());
|
|
373
662
|
}
|
|
374
663
|
isKeywordToken(tokenType) {
|
|
375
|
-
return
|
|
376
|
-
"PROGRAMME" /* PROGRAM */,
|
|
377
|
-
"DEBUT" /* BEGIN */,
|
|
378
|
-
"FIN" /* END */,
|
|
379
|
-
"VAR" /* VAR */,
|
|
380
|
-
"ENTIER" /* INTEGER */,
|
|
381
|
-
"REEL" /* REAL */,
|
|
382
|
-
"BOOLEEN" /* BOOLEAN */,
|
|
383
|
-
"CHAINE" /* STRING */,
|
|
384
|
-
"SI" /* IF */,
|
|
385
|
-
"ALORS" /* THEN */,
|
|
386
|
-
"SINON" /* ELSE */,
|
|
387
|
-
"TANTQUE" /* WHILE */,
|
|
388
|
-
"FAIRE" /* DO */,
|
|
389
|
-
"POUR" /* FOR */,
|
|
390
|
-
"ALLANT" /* ALLANT */,
|
|
391
|
-
"DE" /* DE */,
|
|
392
|
-
"A" /* TO */,
|
|
393
|
-
"REPETER" /* REPEAT */,
|
|
394
|
-
"JUSQUA" /* UNTIL */,
|
|
395
|
-
"LIRE" /* READ */,
|
|
396
|
-
"ECRIRE" /* WRITE */,
|
|
397
|
-
"VRAI" /* TRUE */,
|
|
398
|
-
"FAUX" /* FALSE */,
|
|
399
|
-
"ET" /* AND */,
|
|
400
|
-
"OU" /* OR */,
|
|
401
|
-
"NON" /* NOT */,
|
|
402
|
-
"FINPOUR" /* ENDFOR */,
|
|
403
|
-
"FINIF" /* ENDIF */,
|
|
404
|
-
"FINTANTQUE" /* ENDWHILE */
|
|
405
|
-
].includes(tokenType);
|
|
664
|
+
return Object.values(KEYWORDS).some((e) => e.tokenType === tokenType);
|
|
406
665
|
}
|
|
407
666
|
createReservedKeywordError(identifier, token) {
|
|
408
667
|
this.errors.push({
|
|
@@ -417,30 +676,22 @@ class Parser {
|
|
|
417
676
|
});
|
|
418
677
|
}
|
|
419
678
|
parse() {
|
|
679
|
+
let ast;
|
|
420
680
|
try {
|
|
421
|
-
|
|
422
|
-
return {
|
|
423
|
-
ast: program,
|
|
424
|
-
errors: this.errors,
|
|
425
|
-
symbolTable: this.symbolTable
|
|
426
|
-
};
|
|
681
|
+
ast = this.parseProgram();
|
|
427
682
|
} catch (error) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
],
|
|
441
|
-
symbolTable: this.symbolTable
|
|
442
|
-
};
|
|
443
|
-
}
|
|
683
|
+
ast = { type: "PROGRAM" /* PROGRAM */, children: [] };
|
|
684
|
+
this.errors.push({
|
|
685
|
+
type: "ERROR",
|
|
686
|
+
message: error instanceof Error ? error.message : "Erreur de parsing inconnue",
|
|
687
|
+
line: 1,
|
|
688
|
+
column: 1,
|
|
689
|
+
position: 0,
|
|
690
|
+
code: "PARSE_ERROR"
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
const { symbolTable, errors: semanticErrors } = new SemanticAnalyzer().analyze(ast);
|
|
694
|
+
return { ast, errors: [...this.errors, ...semanticErrors], symbolTable };
|
|
444
695
|
}
|
|
445
696
|
parseProgram() {
|
|
446
697
|
const programToken = this.expect("PROGRAMME" /* PROGRAM */, 'Le programme doit commencer par le mot-clé "programme"');
|
|
@@ -456,17 +707,25 @@ class Parser {
|
|
|
456
707
|
}
|
|
457
708
|
parseBlock() {
|
|
458
709
|
const declarations = this.parseDeclarations();
|
|
710
|
+
const subprograms = [];
|
|
711
|
+
while (this.check(["FONCTION" /* FUNCTION */, "PROCEDURE" /* PROCEDURE */])) {
|
|
712
|
+
if (this.check("FONCTION" /* FUNCTION */)) {
|
|
713
|
+
subprograms.push(this.parseFunctionDeclaration());
|
|
714
|
+
} else {
|
|
715
|
+
subprograms.push(this.parseProcedureDeclaration());
|
|
716
|
+
}
|
|
717
|
+
}
|
|
459
718
|
const compoundStatement = this.parseCompoundStatement();
|
|
460
719
|
return {
|
|
461
720
|
type: "BLOCK" /* BLOCK */,
|
|
462
|
-
children: [declarations, compoundStatement]
|
|
721
|
+
children: [declarations, ...subprograms, compoundStatement]
|
|
463
722
|
};
|
|
464
723
|
}
|
|
465
724
|
parseDeclarations() {
|
|
466
725
|
const declarations = [];
|
|
467
726
|
while (this.check("VAR" /* VAR */)) {
|
|
468
727
|
this.advance();
|
|
469
|
-
while (!this.check("DEBUT" /* BEGIN */) && !this.isAtEnd()) {
|
|
728
|
+
while (!this.check("DEBUT" /* BEGIN */) && !this.check("FONCTION" /* FUNCTION */) && !this.check("PROCEDURE" /* PROCEDURE */) && !this.isAtEnd()) {
|
|
470
729
|
const declaration = this.parseVariableDeclaration();
|
|
471
730
|
declarations.push(declaration);
|
|
472
731
|
if (!this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
@@ -507,31 +766,27 @@ class Parser {
|
|
|
507
766
|
}
|
|
508
767
|
} while (true);
|
|
509
768
|
this.expect("COLON" /* COLON */, "Deux-points attendus après les identificateurs");
|
|
769
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
770
|
+
this.advance();
|
|
771
|
+
this.expect("LEFT_BRACKET" /* LEFT_BRACKET */, '"[" attendu après TABLEAU');
|
|
772
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille du tableau attendue");
|
|
773
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu après la taille du tableau');
|
|
774
|
+
this.expect("DE" /* DE */, '"DE" attendu après la taille du tableau');
|
|
775
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments du tableau attendu");
|
|
776
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
777
|
+
const size = parseInt(sizeToken.value);
|
|
778
|
+
return {
|
|
779
|
+
type: "ARRAY_DECLARATION" /* ARRAY_DECLARATION */,
|
|
780
|
+
value: elemType,
|
|
781
|
+
children: [
|
|
782
|
+
{ type: "LITERAL" /* LITERAL */, value: size },
|
|
783
|
+
...identifiers.map((name) => ({ type: "VARIABLE" /* VARIABLE */, value: name }))
|
|
784
|
+
],
|
|
785
|
+
token: elemTypeToken
|
|
786
|
+
};
|
|
787
|
+
}
|
|
510
788
|
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de variable attendu");
|
|
511
789
|
const type = this.tokenTypeToDataType(typeToken.type);
|
|
512
|
-
for (const identifier of identifiers) {
|
|
513
|
-
if (this.symbolTable.symbols.has(identifier)) {
|
|
514
|
-
this.errors.push({
|
|
515
|
-
type: "ERROR",
|
|
516
|
-
message: `La variable '${identifier}' est déjà déclarée`,
|
|
517
|
-
line: typeToken.line,
|
|
518
|
-
column: typeToken.column,
|
|
519
|
-
position: typeToken.position,
|
|
520
|
-
code: "DUPLICATE_VARIABLE",
|
|
521
|
-
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
522
|
-
suggestion: `Choisissez un autre nom pour la variable '${identifier}'`
|
|
523
|
-
});
|
|
524
|
-
} else {
|
|
525
|
-
const symbolInfo = {
|
|
526
|
-
name: identifier,
|
|
527
|
-
type,
|
|
528
|
-
scope: this.symbolTable.scopeName,
|
|
529
|
-
line: typeToken.line,
|
|
530
|
-
column: typeToken.column
|
|
531
|
-
};
|
|
532
|
-
this.symbolTable.symbols.set(identifier, symbolInfo);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
790
|
return {
|
|
536
791
|
type: "VAR_DECLARATION" /* VAR_DECLARATION */,
|
|
537
792
|
value: type,
|
|
@@ -548,9 +803,16 @@ class Parser {
|
|
|
548
803
|
while (!this.check("FIN" /* END */) && !this.isAtEnd()) {
|
|
549
804
|
const statement = this.parseStatement();
|
|
550
805
|
statements.push(statement);
|
|
806
|
+
const isBlockStatement = [
|
|
807
|
+
"IF_STATEMENT" /* IF_STATEMENT */,
|
|
808
|
+
"WHILE_STATEMENT" /* WHILE_STATEMENT */,
|
|
809
|
+
"FOR_STATEMENT" /* FOR_STATEMENT */,
|
|
810
|
+
"REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
811
|
+
"COMPOUND_STATEMENT" /* COMPOUND_STATEMENT */
|
|
812
|
+
].includes(statement.type);
|
|
551
813
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
552
814
|
this.advance();
|
|
553
|
-
} else if (!this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */)) {
|
|
815
|
+
} else if (!isBlockStatement && !this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */) && !this.check("FINPOUR" /* ENDFOR */)) {
|
|
554
816
|
this.errors.push({
|
|
555
817
|
type: "ERROR",
|
|
556
818
|
message: "Point-virgule attendu après l'instruction",
|
|
@@ -593,6 +855,16 @@ class Parser {
|
|
|
593
855
|
if (this.check("ECRIRE" /* WRITE */)) {
|
|
594
856
|
return this.parseWriteStatement();
|
|
595
857
|
}
|
|
858
|
+
if (this.check("RETOURNER" /* RETURN */)) {
|
|
859
|
+
return this.parseReturnStatement();
|
|
860
|
+
}
|
|
861
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_PAREN" /* LEFT_PAREN */) {
|
|
862
|
+
const nameToken = this.advance();
|
|
863
|
+
return this.parseFunctionCall(nameToken);
|
|
864
|
+
}
|
|
865
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_BRACKET" /* LEFT_BRACKET */) {
|
|
866
|
+
return this.parseArrayAssignment();
|
|
867
|
+
}
|
|
596
868
|
return this.parseAssignment();
|
|
597
869
|
}
|
|
598
870
|
parseIfStatement(isElseIf = false) {
|
|
@@ -712,14 +984,23 @@ class Parser {
|
|
|
712
984
|
parseReadStatement() {
|
|
713
985
|
const readToken = this.advance();
|
|
714
986
|
this.expect("LEFT_PAREN" /* LEFT_PAREN */, 'Parenthèse ouvrante attendue après "lire"');
|
|
715
|
-
let
|
|
987
|
+
let target;
|
|
716
988
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
717
|
-
variable = this.advance();
|
|
989
|
+
const variable = this.advance();
|
|
990
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
991
|
+
this.advance();
|
|
992
|
+
const index = this.parseExpression();
|
|
993
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
994
|
+
target = { type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: variable.value, children: [index], token: variable };
|
|
995
|
+
} else {
|
|
996
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
997
|
+
}
|
|
718
998
|
} else {
|
|
719
999
|
const currentToken = this.peek();
|
|
720
1000
|
if (this.isKeywordToken(currentToken.type)) {
|
|
721
|
-
variable = this.advance();
|
|
1001
|
+
const variable = this.advance();
|
|
722
1002
|
this.createReservedKeywordError(variable.value, variable);
|
|
1003
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
723
1004
|
} else {
|
|
724
1005
|
this.expect("IDENTIFIER" /* IDENTIFIER */, 'Variable attendue dans "lire"');
|
|
725
1006
|
throw new Error('Variable attendue dans "lire"');
|
|
@@ -728,9 +1009,7 @@ class Parser {
|
|
|
728
1009
|
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, 'Parenthèse fermante attendue dans "lire"');
|
|
729
1010
|
return {
|
|
730
1011
|
type: "READ_STATEMENT" /* READ_STATEMENT */,
|
|
731
|
-
children: [
|
|
732
|
-
{ type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable }
|
|
733
|
-
],
|
|
1012
|
+
children: [target],
|
|
734
1013
|
token: readToken
|
|
735
1014
|
};
|
|
736
1015
|
}
|
|
@@ -858,7 +1137,7 @@ class Parser {
|
|
|
858
1137
|
}
|
|
859
1138
|
parseMultiplicativeExpression() {
|
|
860
1139
|
let left = this.parseUnaryExpression();
|
|
861
|
-
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */])) {
|
|
1140
|
+
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */, "MODULO" /* MODULO */])) {
|
|
862
1141
|
const operator = this.advance();
|
|
863
1142
|
const right = this.parseUnaryExpression();
|
|
864
1143
|
left = {
|
|
@@ -907,7 +1186,7 @@ class Parser {
|
|
|
907
1186
|
const token2 = this.advance();
|
|
908
1187
|
return {
|
|
909
1188
|
type: "LITERAL" /* LITERAL */,
|
|
910
|
-
value: token2.value === "vrai",
|
|
1189
|
+
value: token2.value.toLowerCase() === "vrai",
|
|
911
1190
|
token: token2
|
|
912
1191
|
};
|
|
913
1192
|
}
|
|
@@ -921,6 +1200,20 @@ class Parser {
|
|
|
921
1200
|
}
|
|
922
1201
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
923
1202
|
const token2 = this.advance();
|
|
1203
|
+
if (this.check("LEFT_PAREN" /* LEFT_PAREN */)) {
|
|
1204
|
+
return this.parseFunctionCall(token2);
|
|
1205
|
+
}
|
|
1206
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
1207
|
+
this.advance();
|
|
1208
|
+
const index = this.parseExpression();
|
|
1209
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
1210
|
+
return {
|
|
1211
|
+
type: "ARRAY_ACCESS" /* ARRAY_ACCESS */,
|
|
1212
|
+
value: token2.value,
|
|
1213
|
+
children: [index],
|
|
1214
|
+
token: token2
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
924
1217
|
if (this.isReservedKeyword(token2.value)) {
|
|
925
1218
|
this.createReservedKeywordError(token2.value, token2);
|
|
926
1219
|
}
|
|
@@ -964,14 +1257,14 @@ class Parser {
|
|
|
964
1257
|
parseRepeatStatement() {
|
|
965
1258
|
const repeatToken = this.advance();
|
|
966
1259
|
const statements = [];
|
|
967
|
-
while (!this.check("
|
|
1260
|
+
while (!this.check("JUSQU'A" /* UNTIL */) && !this.isAtEnd()) {
|
|
968
1261
|
const statement = this.parseStatement();
|
|
969
1262
|
statements.push(statement);
|
|
970
1263
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
971
1264
|
this.advance();
|
|
972
1265
|
}
|
|
973
1266
|
}
|
|
974
|
-
this.expect("
|
|
1267
|
+
this.expect("JUSQU'A" /* UNTIL */, `"jusqu'a" attendu à la fin de la boucle repeter`);
|
|
975
1268
|
const condition = this.parseExpression();
|
|
976
1269
|
return {
|
|
977
1270
|
type: "REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
@@ -979,6 +1272,127 @@ class Parser {
|
|
|
979
1272
|
token: repeatToken
|
|
980
1273
|
};
|
|
981
1274
|
}
|
|
1275
|
+
parseFunctionCall(nameToken) {
|
|
1276
|
+
this.advance();
|
|
1277
|
+
const args = [];
|
|
1278
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
1279
|
+
args.push(this.parseExpression());
|
|
1280
|
+
while (this.check("COMMA" /* COMMA */)) {
|
|
1281
|
+
this.advance();
|
|
1282
|
+
args.push(this.parseExpression());
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")` attendu après les arguments');
|
|
1286
|
+
return {
|
|
1287
|
+
type: "FUNCTION_CALL" /* FUNCTION_CALL */,
|
|
1288
|
+
value: nameToken.value,
|
|
1289
|
+
children: args,
|
|
1290
|
+
token: nameToken
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
parseArrayAssignment() {
|
|
1294
|
+
const nameToken = this.advance();
|
|
1295
|
+
this.advance();
|
|
1296
|
+
const index = this.parseExpression();
|
|
1297
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
1298
|
+
this.expect("ASSIGN" /* ASSIGN */, `":=" attendu dans l'affectation`);
|
|
1299
|
+
const value = this.parseExpression();
|
|
1300
|
+
return {
|
|
1301
|
+
type: "ASSIGNMENT" /* ASSIGNMENT */,
|
|
1302
|
+
children: [
|
|
1303
|
+
{ type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: nameToken.value, children: [index], token: nameToken },
|
|
1304
|
+
value
|
|
1305
|
+
],
|
|
1306
|
+
token: nameToken
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
parseReturnStatement() {
|
|
1310
|
+
const token = this.advance();
|
|
1311
|
+
const expr = this.parseExpression();
|
|
1312
|
+
return {
|
|
1313
|
+
type: "RETURN_STATEMENT" /* RETURN_STATEMENT */,
|
|
1314
|
+
children: [expr],
|
|
1315
|
+
token
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
parseParameterList() {
|
|
1319
|
+
this.expect("LEFT_PAREN" /* LEFT_PAREN */, '"(" attendu dans la déclaration');
|
|
1320
|
+
const params = [];
|
|
1321
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
1322
|
+
params.push(this.parseParameter());
|
|
1323
|
+
while (this.check("SEMICOLON" /* SEMICOLON */) || this.check("COMMA" /* COMMA */)) {
|
|
1324
|
+
this.advance();
|
|
1325
|
+
if (this.check("RIGHT_PAREN" /* RIGHT_PAREN */))
|
|
1326
|
+
break;
|
|
1327
|
+
params.push(this.parseParameter());
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")" attendu après les paramètres');
|
|
1331
|
+
return { type: "PARAMETER_LIST" /* PARAMETER_LIST */, children: params };
|
|
1332
|
+
}
|
|
1333
|
+
parseParameter() {
|
|
1334
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de paramètre attendu");
|
|
1335
|
+
this.expect("COLON" /* COLON */, '":" attendu après le nom du paramètre');
|
|
1336
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
1337
|
+
this.advance();
|
|
1338
|
+
let size;
|
|
1339
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
1340
|
+
this.advance();
|
|
1341
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille attendue");
|
|
1342
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu');
|
|
1343
|
+
size = parseInt(sizeToken.value);
|
|
1344
|
+
}
|
|
1345
|
+
this.expect("DE" /* DE */, '"DE" attendu après TABLEAU');
|
|
1346
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments attendu");
|
|
1347
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
1348
|
+
const typeLabel = size !== undefined ? `TABLEAU[${size}] DE ${elemType}` : `TABLEAU DE ${elemType}`;
|
|
1349
|
+
return {
|
|
1350
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
1351
|
+
value: nameToken.value,
|
|
1352
|
+
token: nameToken,
|
|
1353
|
+
symbolInfo: { name: nameToken.value, type: typeLabel, scope: "param" }
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type du paramètre attendu");
|
|
1357
|
+
const paramType = this.tokenTypeToDataType(typeToken.type);
|
|
1358
|
+
return {
|
|
1359
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
1360
|
+
value: nameToken.value,
|
|
1361
|
+
token: nameToken,
|
|
1362
|
+
symbolInfo: { name: nameToken.value, type: paramType, scope: "param" }
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
parseFunctionDeclaration() {
|
|
1366
|
+
const token = this.advance();
|
|
1367
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de fonction attendu");
|
|
1368
|
+
const paramList = this.parseParameterList();
|
|
1369
|
+
this.expect("COLON" /* COLON */, '":" attendu pour le type de retour');
|
|
1370
|
+
const retTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de retour attendu");
|
|
1371
|
+
const retType = this.tokenTypeToDataType(retTypeToken.type);
|
|
1372
|
+
const body = this.parseCompoundStatement();
|
|
1373
|
+
return {
|
|
1374
|
+
type: "FUNCTION_DECLARATION" /* FUNCTION_DECLARATION */,
|
|
1375
|
+
value: nameToken.value,
|
|
1376
|
+
children: [
|
|
1377
|
+
paramList,
|
|
1378
|
+
{ type: "TYPE_SPECIFIER" /* TYPE_SPECIFIER */, value: retType },
|
|
1379
|
+
body
|
|
1380
|
+
],
|
|
1381
|
+
token
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
parseProcedureDeclaration() {
|
|
1385
|
+
const token = this.advance();
|
|
1386
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de procédure attendu");
|
|
1387
|
+
const paramList = this.parseParameterList();
|
|
1388
|
+
const body = this.parseCompoundStatement();
|
|
1389
|
+
return {
|
|
1390
|
+
type: "PROCEDURE_DECLARATION" /* PROCEDURE_DECLARATION */,
|
|
1391
|
+
value: nameToken.value,
|
|
1392
|
+
children: [paramList, body],
|
|
1393
|
+
token
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
982
1396
|
check(type) {
|
|
983
1397
|
if (this.isAtEnd())
|
|
984
1398
|
return false;
|
|
@@ -1127,6 +1541,8 @@ class CodeGenerator {
|
|
|
1127
1541
|
return this.generateBlock(node);
|
|
1128
1542
|
case "VAR_DECLARATION":
|
|
1129
1543
|
return this.generateVariableDeclaration(node);
|
|
1544
|
+
case "ARRAY_DECLARATION":
|
|
1545
|
+
return this.generateArrayDeclaration(node);
|
|
1130
1546
|
case "COMPOUND_STATEMENT":
|
|
1131
1547
|
return this.generateCompoundStatement(node);
|
|
1132
1548
|
case "ASSIGNMENT":
|
|
@@ -1143,6 +1559,12 @@ class CodeGenerator {
|
|
|
1143
1559
|
return this.generateReadStatement(node);
|
|
1144
1560
|
case "WRITE_STATEMENT":
|
|
1145
1561
|
return this.generateWriteStatement(node);
|
|
1562
|
+
case "FUNCTION_DECLARATION":
|
|
1563
|
+
return this.generateFunctionDeclaration(node);
|
|
1564
|
+
case "PROCEDURE_DECLARATION":
|
|
1565
|
+
return this.generateProcedureDeclaration(node);
|
|
1566
|
+
case "RETURN_STATEMENT":
|
|
1567
|
+
return this.generateReturnStatement(node);
|
|
1146
1568
|
case "BINARY_OP":
|
|
1147
1569
|
return this.generateBinaryOp(node);
|
|
1148
1570
|
case "UNARY_OP":
|
|
@@ -1151,6 +1573,10 @@ class CodeGenerator {
|
|
|
1151
1573
|
return this.generateLiteral(node);
|
|
1152
1574
|
case "VARIABLE":
|
|
1153
1575
|
return this.generateVariable(node);
|
|
1576
|
+
case "ARRAY_ACCESS":
|
|
1577
|
+
return this.generateArrayAccess(node);
|
|
1578
|
+
case "FUNCTION_CALL":
|
|
1579
|
+
return this.generateFunctionCall(node);
|
|
1154
1580
|
default:
|
|
1155
1581
|
this.errors.push({
|
|
1156
1582
|
type: "ERROR",
|
|
@@ -1165,14 +1591,25 @@ class CodeGenerator {
|
|
|
1165
1591
|
}
|
|
1166
1592
|
generateProgram(node) {
|
|
1167
1593
|
const lines = [];
|
|
1594
|
+
const block = node.children?.[0];
|
|
1168
1595
|
lines.push(`// Programme: ${node.value}`);
|
|
1596
|
+
if (block?.children) {
|
|
1597
|
+
for (const child of block.children) {
|
|
1598
|
+
if (child.type === "FUNCTION_DECLARATION" || child.type === "PROCEDURE_DECLARATION") {
|
|
1599
|
+
lines.push(this.generateNode(child));
|
|
1600
|
+
lines.push("");
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1169
1604
|
lines.push("async function main() {");
|
|
1170
1605
|
this.indentLevel++;
|
|
1171
|
-
if (
|
|
1172
|
-
for (const child of
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1606
|
+
if (block?.children) {
|
|
1607
|
+
for (const child of block.children) {
|
|
1608
|
+
if (child.type !== "FUNCTION_DECLARATION" && child.type !== "PROCEDURE_DECLARATION") {
|
|
1609
|
+
const childCode = this.generateNode(child);
|
|
1610
|
+
if (childCode) {
|
|
1611
|
+
lines.push(this.indent(childCode));
|
|
1612
|
+
}
|
|
1176
1613
|
}
|
|
1177
1614
|
}
|
|
1178
1615
|
}
|
|
@@ -1210,7 +1647,8 @@ class CodeGenerator {
|
|
|
1210
1647
|
for (const child of node.children) {
|
|
1211
1648
|
const childCode = this.generateNode(child);
|
|
1212
1649
|
if (childCode) {
|
|
1213
|
-
|
|
1650
|
+
const stmt = child.type === "FUNCTION_CALL" ? childCode.replace(/^\(|\)$/g, "") + ";" : childCode;
|
|
1651
|
+
lines.push(this.indent(stmt));
|
|
1214
1652
|
}
|
|
1215
1653
|
}
|
|
1216
1654
|
}
|
|
@@ -1221,15 +1659,107 @@ class CodeGenerator {
|
|
|
1221
1659
|
if (!node.children || node.children.length < 2) {
|
|
1222
1660
|
return "";
|
|
1223
1661
|
}
|
|
1224
|
-
const
|
|
1662
|
+
const target = node.children[0];
|
|
1225
1663
|
const expression = node.children[1];
|
|
1226
|
-
if (!
|
|
1664
|
+
if (!target || !expression) {
|
|
1227
1665
|
return "";
|
|
1228
1666
|
}
|
|
1229
|
-
const variableName = variable.value;
|
|
1230
1667
|
const expressionCode = this.generateNode(expression);
|
|
1668
|
+
if (target.type === "ARRAY_ACCESS") {
|
|
1669
|
+
const arrayName = target.value;
|
|
1670
|
+
const index = this.generateNode(target.children[0]);
|
|
1671
|
+
return `${arrayName}[${index}] = ${expressionCode};`;
|
|
1672
|
+
}
|
|
1673
|
+
const variableName = target.value;
|
|
1231
1674
|
return `${variableName} = ${expressionCode};`;
|
|
1232
1675
|
}
|
|
1676
|
+
generateArrayDeclaration(node) {
|
|
1677
|
+
if (!node.children || node.children.length < 2)
|
|
1678
|
+
return "";
|
|
1679
|
+
const size = node.children[0].value;
|
|
1680
|
+
const elemType = node.value;
|
|
1681
|
+
const defaultValue = elemType === "BOOLEEN" ? "false" : elemType === "CHAINE" ? '""' : "0";
|
|
1682
|
+
const names = node.children.slice(1).map((c) => c.value);
|
|
1683
|
+
return names.map((name) => `let ${name} = new Array(${size}).fill(${defaultValue});`).join(`
|
|
1684
|
+
`);
|
|
1685
|
+
}
|
|
1686
|
+
generateArrayAccess(node) {
|
|
1687
|
+
const name = node.value;
|
|
1688
|
+
const index = this.generateNode(node.children[0]);
|
|
1689
|
+
return `${name}[${index}]`;
|
|
1690
|
+
}
|
|
1691
|
+
generateFunctionCall(node) {
|
|
1692
|
+
const name = node.value;
|
|
1693
|
+
const args = (node.children ?? []).map((c) => {
|
|
1694
|
+
const code = this.generateNode(c);
|
|
1695
|
+
if (c.type === "VARIABLE") {
|
|
1696
|
+
const sym = this.symbolTable.symbols.get(c.value);
|
|
1697
|
+
if (sym?.type?.startsWith("TABLEAU"))
|
|
1698
|
+
return `${code}.slice()`;
|
|
1699
|
+
}
|
|
1700
|
+
return code;
|
|
1701
|
+
});
|
|
1702
|
+
switch (name.toLowerCase()) {
|
|
1703
|
+
case "abs":
|
|
1704
|
+
return `Math.abs(${args[0]})`;
|
|
1705
|
+
case "max":
|
|
1706
|
+
return `Math.max(${args.join(", ")})`;
|
|
1707
|
+
case "min":
|
|
1708
|
+
return `Math.min(${args.join(", ")})`;
|
|
1709
|
+
case "mod":
|
|
1710
|
+
return `(${args[0]} % ${args[1]})`;
|
|
1711
|
+
case "racine_carree":
|
|
1712
|
+
return `Math.sqrt(${args[0]})`;
|
|
1713
|
+
case "taille":
|
|
1714
|
+
return `${args[0]}.length`;
|
|
1715
|
+
case "sous_chaine":
|
|
1716
|
+
return `${args[0]}.substring(${args[1]}, ${args[1]} + ${args[2]})`;
|
|
1717
|
+
case "concat":
|
|
1718
|
+
return args.join(" + ");
|
|
1719
|
+
case "entier_en_reel":
|
|
1720
|
+
return `${args[0]}`;
|
|
1721
|
+
case "reel_en_entier":
|
|
1722
|
+
return `Math.trunc(${args[0]})`;
|
|
1723
|
+
default:
|
|
1724
|
+
return `(await ${name}(${args.join(", ")}))`;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
generateFunctionDeclaration(node) {
|
|
1728
|
+
const name = node.value;
|
|
1729
|
+
const [paramList, , body] = node.children ?? [];
|
|
1730
|
+
const params = this.generateParamList(paramList);
|
|
1731
|
+
const bodyCode = this.generateNode(body);
|
|
1732
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
1733
|
+
this.indentLevel++;
|
|
1734
|
+
lines.push(this.indent(bodyCode));
|
|
1735
|
+
this.indentLevel--;
|
|
1736
|
+
lines.push("}");
|
|
1737
|
+
return lines.join(`
|
|
1738
|
+
`);
|
|
1739
|
+
}
|
|
1740
|
+
generateProcedureDeclaration(node) {
|
|
1741
|
+
const name = node.value;
|
|
1742
|
+
const [paramList, body] = node.children ?? [];
|
|
1743
|
+
const params = this.generateParamList(paramList);
|
|
1744
|
+
const bodyCode = this.generateNode(body);
|
|
1745
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
1746
|
+
this.indentLevel++;
|
|
1747
|
+
lines.push(this.indent(bodyCode));
|
|
1748
|
+
this.indentLevel--;
|
|
1749
|
+
lines.push("}");
|
|
1750
|
+
return lines.join(`
|
|
1751
|
+
`);
|
|
1752
|
+
}
|
|
1753
|
+
generateParamList(paramList) {
|
|
1754
|
+
if (!paramList?.children)
|
|
1755
|
+
return "";
|
|
1756
|
+
return paramList.children.map((p) => p.value).join(", ");
|
|
1757
|
+
}
|
|
1758
|
+
generateReturnStatement(node) {
|
|
1759
|
+
if (!node.children?.[0])
|
|
1760
|
+
return "return;";
|
|
1761
|
+
return `return ${this.generateNode(node.children[0])};`;
|
|
1762
|
+
}
|
|
1233
1763
|
generateIfStatement(node) {
|
|
1234
1764
|
if (!node.children || node.children.length < 2) {
|
|
1235
1765
|
return "";
|
|
@@ -1331,34 +1861,20 @@ class CodeGenerator {
|
|
|
1331
1861
|
return code;
|
|
1332
1862
|
}
|
|
1333
1863
|
generateReadStatement(node) {
|
|
1334
|
-
if (!node.children || node.children.length === 0)
|
|
1864
|
+
if (!node.children || node.children.length === 0)
|
|
1335
1865
|
return "";
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
if (!variable) {
|
|
1866
|
+
const target = node.children[0];
|
|
1867
|
+
if (!target)
|
|
1339
1868
|
return "";
|
|
1869
|
+
const isArray = target.type === "ARRAY_ACCESS";
|
|
1870
|
+
const varName = isArray ? `${target.value}[${this.generateNode(target.children[0])}]` : target.value;
|
|
1871
|
+
const symbolName = target.value;
|
|
1872
|
+
const symbolInfo = this.symbolTable.symbols.get(symbolName);
|
|
1873
|
+
const typeHint = symbolInfo?.type ?? "";
|
|
1874
|
+
if (typeHint.startsWith("ENTIER") || typeHint === "REEL" || typeHint.startsWith("TABLEAU")) {
|
|
1875
|
+
return `${varName} = parseInt(await lire(""));`;
|
|
1340
1876
|
}
|
|
1341
|
-
|
|
1342
|
-
const symbolInfo = this.symbolTable.symbols.get(variableName);
|
|
1343
|
-
let conversion = "";
|
|
1344
|
-
if (symbolInfo) {
|
|
1345
|
-
switch (symbolInfo.type) {
|
|
1346
|
-
case "ENTIER":
|
|
1347
|
-
case "REEL":
|
|
1348
|
-
conversion = "parseInt(";
|
|
1349
|
-
break;
|
|
1350
|
-
case "BOOLEEN":
|
|
1351
|
-
conversion = "(";
|
|
1352
|
-
break;
|
|
1353
|
-
default:
|
|
1354
|
-
conversion = "";
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
if (conversion) {
|
|
1358
|
-
return `${variableName} = ${conversion}await lire(""));`;
|
|
1359
|
-
} else {
|
|
1360
|
-
return `${variableName} = await lire("");`;
|
|
1361
|
-
}
|
|
1877
|
+
return `${varName} = await lire("");`;
|
|
1362
1878
|
}
|
|
1363
1879
|
generateWriteStatement(node) {
|
|
1364
1880
|
if (!node.children || node.children.length === 0) {
|
|
@@ -1457,6 +1973,8 @@ class CodeGenerator {
|
|
|
1457
1973
|
return ">";
|
|
1458
1974
|
case ">=":
|
|
1459
1975
|
return ">=";
|
|
1976
|
+
case "%":
|
|
1977
|
+
return "%";
|
|
1460
1978
|
case "et":
|
|
1461
1979
|
return "&&";
|
|
1462
1980
|
case "ou":
|
|
@@ -1480,37 +1998,7 @@ class Lexer2 {
|
|
|
1480
1998
|
keywords;
|
|
1481
1999
|
constructor(source) {
|
|
1482
2000
|
this.source = source;
|
|
1483
|
-
this.keywords = new Map([
|
|
1484
|
-
["PROGRAMME", "PROGRAMME" /* PROGRAM */],
|
|
1485
|
-
["DEBUT", "DEBUT" /* BEGIN */],
|
|
1486
|
-
["FIN", "FIN" /* END */],
|
|
1487
|
-
["VAR", "VAR" /* VAR */],
|
|
1488
|
-
["ENTIER", "ENTIER" /* INTEGER */],
|
|
1489
|
-
["REEL", "REEL" /* REAL */],
|
|
1490
|
-
["BOOLEEN", "BOOLEEN" /* BOOLEAN */],
|
|
1491
|
-
["CHAINE", "CHAINE" /* STRING */],
|
|
1492
|
-
["SI", "SI" /* IF */],
|
|
1493
|
-
["ALORS", "ALORS" /* THEN */],
|
|
1494
|
-
["SINON", "SINON" /* ELSE */],
|
|
1495
|
-
["FINSI", "FINIF" /* ENDIF */],
|
|
1496
|
-
["TANTQUE", "TANTQUE" /* WHILE */],
|
|
1497
|
-
["FAIRE", "FAIRE" /* DO */],
|
|
1498
|
-
["POUR", "POUR" /* FOR */],
|
|
1499
|
-
["ALLANT", "ALLANT" /* ALLANT */],
|
|
1500
|
-
["DE", "DE" /* DE */],
|
|
1501
|
-
["A", "A" /* TO */],
|
|
1502
|
-
["REPETER", "REPETER" /* REPEAT */],
|
|
1503
|
-
["JUSQUA", "JUSQUA" /* UNTIL */],
|
|
1504
|
-
["LIRE", "LIRE" /* READ */],
|
|
1505
|
-
["ECRIRE", "ECRIRE" /* WRITE */],
|
|
1506
|
-
["VRAI", "VRAI" /* TRUE */],
|
|
1507
|
-
["FAUX", "FAUX" /* FALSE */],
|
|
1508
|
-
["ET", "ET" /* AND */],
|
|
1509
|
-
["OU", "OU" /* OR */],
|
|
1510
|
-
["NON", "NON" /* NOT */],
|
|
1511
|
-
["FINPOUR", "FINPOUR" /* ENDFOR */],
|
|
1512
|
-
["FINTANTQUE", "FINTANTQUE" /* ENDWHILE */]
|
|
1513
|
-
]);
|
|
2001
|
+
this.keywords = new Map(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label, e]) => [label, e.tokenType]));
|
|
1514
2002
|
}
|
|
1515
2003
|
tokenize() {
|
|
1516
2004
|
const tokens = [];
|
|
@@ -1583,6 +2071,8 @@ class Lexer2 {
|
|
|
1583
2071
|
return this.createToken("MULTIPLY" /* MULTIPLY */, this.advance());
|
|
1584
2072
|
case "/":
|
|
1585
2073
|
return this.createToken("DIVIDE" /* DIVIDE */, this.advance());
|
|
2074
|
+
case "%":
|
|
2075
|
+
return this.createToken("MODULO" /* MODULO */, this.advance());
|
|
1586
2076
|
case "=":
|
|
1587
2077
|
return this.createToken("EQUAL" /* EQUAL */, this.advance());
|
|
1588
2078
|
case "<":
|
|
@@ -1601,6 +2091,10 @@ class Lexer2 {
|
|
|
1601
2091
|
return this.createToken("LEFT_PAREN" /* LEFT_PAREN */, this.advance());
|
|
1602
2092
|
case ")":
|
|
1603
2093
|
return this.createToken("RIGHT_PAREN" /* RIGHT_PAREN */, this.advance());
|
|
2094
|
+
case "[":
|
|
2095
|
+
return this.createToken("LEFT_BRACKET" /* LEFT_BRACKET */, this.advance());
|
|
2096
|
+
case "]":
|
|
2097
|
+
return this.createToken("RIGHT_BRACKET" /* RIGHT_BRACKET */, this.advance());
|
|
1604
2098
|
default:
|
|
1605
2099
|
throw new Error(`Caractère non reconnu '${char}' à la ligne ${this.line}, colonne ${this.column}`);
|
|
1606
2100
|
}
|
|
@@ -1798,82 +2292,15 @@ class Parser2 {
|
|
|
1798
2292
|
tokens;
|
|
1799
2293
|
current = 0;
|
|
1800
2294
|
errors = [];
|
|
1801
|
-
|
|
1802
|
-
reservedKeywords = new Set([
|
|
1803
|
-
"programme",
|
|
1804
|
-
"debut",
|
|
1805
|
-
"fin",
|
|
1806
|
-
"var",
|
|
1807
|
-
"entier",
|
|
1808
|
-
"reel",
|
|
1809
|
-
"booleen",
|
|
1810
|
-
"chaine",
|
|
1811
|
-
"si",
|
|
1812
|
-
"alors",
|
|
1813
|
-
"sinon",
|
|
1814
|
-
"finsi",
|
|
1815
|
-
"tantque",
|
|
1816
|
-
"faire",
|
|
1817
|
-
"fintantque",
|
|
1818
|
-
"pour",
|
|
1819
|
-
"a",
|
|
1820
|
-
"finpour",
|
|
1821
|
-
"repeter",
|
|
1822
|
-
"jusqua",
|
|
1823
|
-
"lire",
|
|
1824
|
-
"ecrire",
|
|
1825
|
-
"vrai",
|
|
1826
|
-
"faux",
|
|
1827
|
-
"et",
|
|
1828
|
-
"ou",
|
|
1829
|
-
"non",
|
|
1830
|
-
"fonction",
|
|
1831
|
-
"procedure",
|
|
1832
|
-
"retourner"
|
|
1833
|
-
]);
|
|
2295
|
+
reservedKeywords = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label]) => label.toLowerCase()));
|
|
1834
2296
|
constructor(tokens) {
|
|
1835
2297
|
this.tokens = tokens;
|
|
1836
|
-
this.symbolTable = {
|
|
1837
|
-
symbols: new Map,
|
|
1838
|
-
children: [],
|
|
1839
|
-
scopeName: "global"
|
|
1840
|
-
};
|
|
1841
2298
|
}
|
|
1842
2299
|
isReservedKeyword(identifier) {
|
|
1843
2300
|
return this.reservedKeywords.has(identifier.toLowerCase());
|
|
1844
2301
|
}
|
|
1845
2302
|
isKeywordToken(tokenType) {
|
|
1846
|
-
return
|
|
1847
|
-
"PROGRAMME" /* PROGRAM */,
|
|
1848
|
-
"DEBUT" /* BEGIN */,
|
|
1849
|
-
"FIN" /* END */,
|
|
1850
|
-
"VAR" /* VAR */,
|
|
1851
|
-
"ENTIER" /* INTEGER */,
|
|
1852
|
-
"REEL" /* REAL */,
|
|
1853
|
-
"BOOLEEN" /* BOOLEAN */,
|
|
1854
|
-
"CHAINE" /* STRING */,
|
|
1855
|
-
"SI" /* IF */,
|
|
1856
|
-
"ALORS" /* THEN */,
|
|
1857
|
-
"SINON" /* ELSE */,
|
|
1858
|
-
"TANTQUE" /* WHILE */,
|
|
1859
|
-
"FAIRE" /* DO */,
|
|
1860
|
-
"POUR" /* FOR */,
|
|
1861
|
-
"ALLANT" /* ALLANT */,
|
|
1862
|
-
"DE" /* DE */,
|
|
1863
|
-
"A" /* TO */,
|
|
1864
|
-
"REPETER" /* REPEAT */,
|
|
1865
|
-
"JUSQUA" /* UNTIL */,
|
|
1866
|
-
"LIRE" /* READ */,
|
|
1867
|
-
"ECRIRE" /* WRITE */,
|
|
1868
|
-
"VRAI" /* TRUE */,
|
|
1869
|
-
"FAUX" /* FALSE */,
|
|
1870
|
-
"ET" /* AND */,
|
|
1871
|
-
"OU" /* OR */,
|
|
1872
|
-
"NON" /* NOT */,
|
|
1873
|
-
"FINPOUR" /* ENDFOR */,
|
|
1874
|
-
"FINIF" /* ENDIF */,
|
|
1875
|
-
"FINTANTQUE" /* ENDWHILE */
|
|
1876
|
-
].includes(tokenType);
|
|
2303
|
+
return Object.values(KEYWORDS).some((e) => e.tokenType === tokenType);
|
|
1877
2304
|
}
|
|
1878
2305
|
createReservedKeywordError(identifier, token) {
|
|
1879
2306
|
this.errors.push({
|
|
@@ -1888,30 +2315,22 @@ class Parser2 {
|
|
|
1888
2315
|
});
|
|
1889
2316
|
}
|
|
1890
2317
|
parse() {
|
|
2318
|
+
let ast;
|
|
1891
2319
|
try {
|
|
1892
|
-
|
|
1893
|
-
return {
|
|
1894
|
-
ast: program,
|
|
1895
|
-
errors: this.errors,
|
|
1896
|
-
symbolTable: this.symbolTable
|
|
1897
|
-
};
|
|
2320
|
+
ast = this.parseProgram();
|
|
1898
2321
|
} catch (error) {
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
],
|
|
1912
|
-
symbolTable: this.symbolTable
|
|
1913
|
-
};
|
|
1914
|
-
}
|
|
2322
|
+
ast = { type: "PROGRAM" /* PROGRAM */, children: [] };
|
|
2323
|
+
this.errors.push({
|
|
2324
|
+
type: "ERROR",
|
|
2325
|
+
message: error instanceof Error ? error.message : "Erreur de parsing inconnue",
|
|
2326
|
+
line: 1,
|
|
2327
|
+
column: 1,
|
|
2328
|
+
position: 0,
|
|
2329
|
+
code: "PARSE_ERROR"
|
|
2330
|
+
});
|
|
2331
|
+
}
|
|
2332
|
+
const { symbolTable, errors: semanticErrors } = new SemanticAnalyzer().analyze(ast);
|
|
2333
|
+
return { ast, errors: [...this.errors, ...semanticErrors], symbolTable };
|
|
1915
2334
|
}
|
|
1916
2335
|
parseProgram() {
|
|
1917
2336
|
const programToken = this.expect("PROGRAMME" /* PROGRAM */, 'Le programme doit commencer par le mot-clé "programme"');
|
|
@@ -1927,17 +2346,25 @@ class Parser2 {
|
|
|
1927
2346
|
}
|
|
1928
2347
|
parseBlock() {
|
|
1929
2348
|
const declarations = this.parseDeclarations();
|
|
2349
|
+
const subprograms = [];
|
|
2350
|
+
while (this.check(["FONCTION" /* FUNCTION */, "PROCEDURE" /* PROCEDURE */])) {
|
|
2351
|
+
if (this.check("FONCTION" /* FUNCTION */)) {
|
|
2352
|
+
subprograms.push(this.parseFunctionDeclaration());
|
|
2353
|
+
} else {
|
|
2354
|
+
subprograms.push(this.parseProcedureDeclaration());
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
1930
2357
|
const compoundStatement = this.parseCompoundStatement();
|
|
1931
2358
|
return {
|
|
1932
2359
|
type: "BLOCK" /* BLOCK */,
|
|
1933
|
-
children: [declarations, compoundStatement]
|
|
2360
|
+
children: [declarations, ...subprograms, compoundStatement]
|
|
1934
2361
|
};
|
|
1935
2362
|
}
|
|
1936
2363
|
parseDeclarations() {
|
|
1937
2364
|
const declarations = [];
|
|
1938
2365
|
while (this.check("VAR" /* VAR */)) {
|
|
1939
2366
|
this.advance();
|
|
1940
|
-
while (!this.check("DEBUT" /* BEGIN */) && !this.isAtEnd()) {
|
|
2367
|
+
while (!this.check("DEBUT" /* BEGIN */) && !this.check("FONCTION" /* FUNCTION */) && !this.check("PROCEDURE" /* PROCEDURE */) && !this.isAtEnd()) {
|
|
1941
2368
|
const declaration = this.parseVariableDeclaration();
|
|
1942
2369
|
declarations.push(declaration);
|
|
1943
2370
|
if (!this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
@@ -1978,31 +2405,27 @@ class Parser2 {
|
|
|
1978
2405
|
}
|
|
1979
2406
|
} while (true);
|
|
1980
2407
|
this.expect("COLON" /* COLON */, "Deux-points attendus après les identificateurs");
|
|
2408
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
2409
|
+
this.advance();
|
|
2410
|
+
this.expect("LEFT_BRACKET" /* LEFT_BRACKET */, '"[" attendu après TABLEAU');
|
|
2411
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille du tableau attendue");
|
|
2412
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu après la taille du tableau');
|
|
2413
|
+
this.expect("DE" /* DE */, '"DE" attendu après la taille du tableau');
|
|
2414
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments du tableau attendu");
|
|
2415
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
2416
|
+
const size = parseInt(sizeToken.value);
|
|
2417
|
+
return {
|
|
2418
|
+
type: "ARRAY_DECLARATION" /* ARRAY_DECLARATION */,
|
|
2419
|
+
value: elemType,
|
|
2420
|
+
children: [
|
|
2421
|
+
{ type: "LITERAL" /* LITERAL */, value: size },
|
|
2422
|
+
...identifiers.map((name) => ({ type: "VARIABLE" /* VARIABLE */, value: name }))
|
|
2423
|
+
],
|
|
2424
|
+
token: elemTypeToken
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
1981
2427
|
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de variable attendu");
|
|
1982
2428
|
const type = this.tokenTypeToDataType(typeToken.type);
|
|
1983
|
-
for (const identifier of identifiers) {
|
|
1984
|
-
if (this.symbolTable.symbols.has(identifier)) {
|
|
1985
|
-
this.errors.push({
|
|
1986
|
-
type: "ERROR",
|
|
1987
|
-
message: `La variable '${identifier}' est déjà déclarée`,
|
|
1988
|
-
line: typeToken.line,
|
|
1989
|
-
column: typeToken.column,
|
|
1990
|
-
position: typeToken.position,
|
|
1991
|
-
code: "DUPLICATE_VARIABLE",
|
|
1992
|
-
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
1993
|
-
suggestion: `Choisissez un autre nom pour la variable '${identifier}'`
|
|
1994
|
-
});
|
|
1995
|
-
} else {
|
|
1996
|
-
const symbolInfo = {
|
|
1997
|
-
name: identifier,
|
|
1998
|
-
type,
|
|
1999
|
-
scope: this.symbolTable.scopeName,
|
|
2000
|
-
line: typeToken.line,
|
|
2001
|
-
column: typeToken.column
|
|
2002
|
-
};
|
|
2003
|
-
this.symbolTable.symbols.set(identifier, symbolInfo);
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
2429
|
return {
|
|
2007
2430
|
type: "VAR_DECLARATION" /* VAR_DECLARATION */,
|
|
2008
2431
|
value: type,
|
|
@@ -2019,9 +2442,16 @@ class Parser2 {
|
|
|
2019
2442
|
while (!this.check("FIN" /* END */) && !this.isAtEnd()) {
|
|
2020
2443
|
const statement = this.parseStatement();
|
|
2021
2444
|
statements.push(statement);
|
|
2445
|
+
const isBlockStatement = [
|
|
2446
|
+
"IF_STATEMENT" /* IF_STATEMENT */,
|
|
2447
|
+
"WHILE_STATEMENT" /* WHILE_STATEMENT */,
|
|
2448
|
+
"FOR_STATEMENT" /* FOR_STATEMENT */,
|
|
2449
|
+
"REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
2450
|
+
"COMPOUND_STATEMENT" /* COMPOUND_STATEMENT */
|
|
2451
|
+
].includes(statement.type);
|
|
2022
2452
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
2023
2453
|
this.advance();
|
|
2024
|
-
} else if (!this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */)) {
|
|
2454
|
+
} else if (!isBlockStatement && !this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */) && !this.check("FINPOUR" /* ENDFOR */)) {
|
|
2025
2455
|
this.errors.push({
|
|
2026
2456
|
type: "ERROR",
|
|
2027
2457
|
message: "Point-virgule attendu après l'instruction",
|
|
@@ -2064,6 +2494,16 @@ class Parser2 {
|
|
|
2064
2494
|
if (this.check("ECRIRE" /* WRITE */)) {
|
|
2065
2495
|
return this.parseWriteStatement();
|
|
2066
2496
|
}
|
|
2497
|
+
if (this.check("RETOURNER" /* RETURN */)) {
|
|
2498
|
+
return this.parseReturnStatement();
|
|
2499
|
+
}
|
|
2500
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_PAREN" /* LEFT_PAREN */) {
|
|
2501
|
+
const nameToken = this.advance();
|
|
2502
|
+
return this.parseFunctionCall(nameToken);
|
|
2503
|
+
}
|
|
2504
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_BRACKET" /* LEFT_BRACKET */) {
|
|
2505
|
+
return this.parseArrayAssignment();
|
|
2506
|
+
}
|
|
2067
2507
|
return this.parseAssignment();
|
|
2068
2508
|
}
|
|
2069
2509
|
parseIfStatement(isElseIf = false) {
|
|
@@ -2183,14 +2623,23 @@ class Parser2 {
|
|
|
2183
2623
|
parseReadStatement() {
|
|
2184
2624
|
const readToken = this.advance();
|
|
2185
2625
|
this.expect("LEFT_PAREN" /* LEFT_PAREN */, 'Parenthèse ouvrante attendue après "lire"');
|
|
2186
|
-
let
|
|
2626
|
+
let target;
|
|
2187
2627
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
2188
|
-
variable = this.advance();
|
|
2628
|
+
const variable = this.advance();
|
|
2629
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
2630
|
+
this.advance();
|
|
2631
|
+
const index = this.parseExpression();
|
|
2632
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
2633
|
+
target = { type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: variable.value, children: [index], token: variable };
|
|
2634
|
+
} else {
|
|
2635
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
2636
|
+
}
|
|
2189
2637
|
} else {
|
|
2190
2638
|
const currentToken = this.peek();
|
|
2191
2639
|
if (this.isKeywordToken(currentToken.type)) {
|
|
2192
|
-
variable = this.advance();
|
|
2640
|
+
const variable = this.advance();
|
|
2193
2641
|
this.createReservedKeywordError(variable.value, variable);
|
|
2642
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
2194
2643
|
} else {
|
|
2195
2644
|
this.expect("IDENTIFIER" /* IDENTIFIER */, 'Variable attendue dans "lire"');
|
|
2196
2645
|
throw new Error('Variable attendue dans "lire"');
|
|
@@ -2199,9 +2648,7 @@ class Parser2 {
|
|
|
2199
2648
|
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, 'Parenthèse fermante attendue dans "lire"');
|
|
2200
2649
|
return {
|
|
2201
2650
|
type: "READ_STATEMENT" /* READ_STATEMENT */,
|
|
2202
|
-
children: [
|
|
2203
|
-
{ type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable }
|
|
2204
|
-
],
|
|
2651
|
+
children: [target],
|
|
2205
2652
|
token: readToken
|
|
2206
2653
|
};
|
|
2207
2654
|
}
|
|
@@ -2329,7 +2776,7 @@ class Parser2 {
|
|
|
2329
2776
|
}
|
|
2330
2777
|
parseMultiplicativeExpression() {
|
|
2331
2778
|
let left = this.parseUnaryExpression();
|
|
2332
|
-
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */])) {
|
|
2779
|
+
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */, "MODULO" /* MODULO */])) {
|
|
2333
2780
|
const operator = this.advance();
|
|
2334
2781
|
const right = this.parseUnaryExpression();
|
|
2335
2782
|
left = {
|
|
@@ -2378,7 +2825,7 @@ class Parser2 {
|
|
|
2378
2825
|
const token2 = this.advance();
|
|
2379
2826
|
return {
|
|
2380
2827
|
type: "LITERAL" /* LITERAL */,
|
|
2381
|
-
value: token2.value === "vrai",
|
|
2828
|
+
value: token2.value.toLowerCase() === "vrai",
|
|
2382
2829
|
token: token2
|
|
2383
2830
|
};
|
|
2384
2831
|
}
|
|
@@ -2392,6 +2839,20 @@ class Parser2 {
|
|
|
2392
2839
|
}
|
|
2393
2840
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
2394
2841
|
const token2 = this.advance();
|
|
2842
|
+
if (this.check("LEFT_PAREN" /* LEFT_PAREN */)) {
|
|
2843
|
+
return this.parseFunctionCall(token2);
|
|
2844
|
+
}
|
|
2845
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
2846
|
+
this.advance();
|
|
2847
|
+
const index = this.parseExpression();
|
|
2848
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
2849
|
+
return {
|
|
2850
|
+
type: "ARRAY_ACCESS" /* ARRAY_ACCESS */,
|
|
2851
|
+
value: token2.value,
|
|
2852
|
+
children: [index],
|
|
2853
|
+
token: token2
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2395
2856
|
if (this.isReservedKeyword(token2.value)) {
|
|
2396
2857
|
this.createReservedKeywordError(token2.value, token2);
|
|
2397
2858
|
}
|
|
@@ -2435,14 +2896,14 @@ class Parser2 {
|
|
|
2435
2896
|
parseRepeatStatement() {
|
|
2436
2897
|
const repeatToken = this.advance();
|
|
2437
2898
|
const statements = [];
|
|
2438
|
-
while (!this.check("
|
|
2899
|
+
while (!this.check("JUSQU'A" /* UNTIL */) && !this.isAtEnd()) {
|
|
2439
2900
|
const statement = this.parseStatement();
|
|
2440
2901
|
statements.push(statement);
|
|
2441
2902
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
2442
2903
|
this.advance();
|
|
2443
2904
|
}
|
|
2444
2905
|
}
|
|
2445
|
-
this.expect("
|
|
2906
|
+
this.expect("JUSQU'A" /* UNTIL */, `"jusqu'a" attendu à la fin de la boucle repeter`);
|
|
2446
2907
|
const condition = this.parseExpression();
|
|
2447
2908
|
return {
|
|
2448
2909
|
type: "REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
@@ -2450,6 +2911,127 @@ class Parser2 {
|
|
|
2450
2911
|
token: repeatToken
|
|
2451
2912
|
};
|
|
2452
2913
|
}
|
|
2914
|
+
parseFunctionCall(nameToken) {
|
|
2915
|
+
this.advance();
|
|
2916
|
+
const args = [];
|
|
2917
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
2918
|
+
args.push(this.parseExpression());
|
|
2919
|
+
while (this.check("COMMA" /* COMMA */)) {
|
|
2920
|
+
this.advance();
|
|
2921
|
+
args.push(this.parseExpression());
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")` attendu après les arguments');
|
|
2925
|
+
return {
|
|
2926
|
+
type: "FUNCTION_CALL" /* FUNCTION_CALL */,
|
|
2927
|
+
value: nameToken.value,
|
|
2928
|
+
children: args,
|
|
2929
|
+
token: nameToken
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
parseArrayAssignment() {
|
|
2933
|
+
const nameToken = this.advance();
|
|
2934
|
+
this.advance();
|
|
2935
|
+
const index = this.parseExpression();
|
|
2936
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
2937
|
+
this.expect("ASSIGN" /* ASSIGN */, `":=" attendu dans l'affectation`);
|
|
2938
|
+
const value = this.parseExpression();
|
|
2939
|
+
return {
|
|
2940
|
+
type: "ASSIGNMENT" /* ASSIGNMENT */,
|
|
2941
|
+
children: [
|
|
2942
|
+
{ type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: nameToken.value, children: [index], token: nameToken },
|
|
2943
|
+
value
|
|
2944
|
+
],
|
|
2945
|
+
token: nameToken
|
|
2946
|
+
};
|
|
2947
|
+
}
|
|
2948
|
+
parseReturnStatement() {
|
|
2949
|
+
const token = this.advance();
|
|
2950
|
+
const expr = this.parseExpression();
|
|
2951
|
+
return {
|
|
2952
|
+
type: "RETURN_STATEMENT" /* RETURN_STATEMENT */,
|
|
2953
|
+
children: [expr],
|
|
2954
|
+
token
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2957
|
+
parseParameterList() {
|
|
2958
|
+
this.expect("LEFT_PAREN" /* LEFT_PAREN */, '"(" attendu dans la déclaration');
|
|
2959
|
+
const params = [];
|
|
2960
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
2961
|
+
params.push(this.parseParameter());
|
|
2962
|
+
while (this.check("SEMICOLON" /* SEMICOLON */) || this.check("COMMA" /* COMMA */)) {
|
|
2963
|
+
this.advance();
|
|
2964
|
+
if (this.check("RIGHT_PAREN" /* RIGHT_PAREN */))
|
|
2965
|
+
break;
|
|
2966
|
+
params.push(this.parseParameter());
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")" attendu après les paramètres');
|
|
2970
|
+
return { type: "PARAMETER_LIST" /* PARAMETER_LIST */, children: params };
|
|
2971
|
+
}
|
|
2972
|
+
parseParameter() {
|
|
2973
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de paramètre attendu");
|
|
2974
|
+
this.expect("COLON" /* COLON */, '":" attendu après le nom du paramètre');
|
|
2975
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
2976
|
+
this.advance();
|
|
2977
|
+
let size;
|
|
2978
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
2979
|
+
this.advance();
|
|
2980
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille attendue");
|
|
2981
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu');
|
|
2982
|
+
size = parseInt(sizeToken.value);
|
|
2983
|
+
}
|
|
2984
|
+
this.expect("DE" /* DE */, '"DE" attendu après TABLEAU');
|
|
2985
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments attendu");
|
|
2986
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
2987
|
+
const typeLabel = size !== undefined ? `TABLEAU[${size}] DE ${elemType}` : `TABLEAU DE ${elemType}`;
|
|
2988
|
+
return {
|
|
2989
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
2990
|
+
value: nameToken.value,
|
|
2991
|
+
token: nameToken,
|
|
2992
|
+
symbolInfo: { name: nameToken.value, type: typeLabel, scope: "param" }
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type du paramètre attendu");
|
|
2996
|
+
const paramType = this.tokenTypeToDataType(typeToken.type);
|
|
2997
|
+
return {
|
|
2998
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
2999
|
+
value: nameToken.value,
|
|
3000
|
+
token: nameToken,
|
|
3001
|
+
symbolInfo: { name: nameToken.value, type: paramType, scope: "param" }
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
parseFunctionDeclaration() {
|
|
3005
|
+
const token = this.advance();
|
|
3006
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de fonction attendu");
|
|
3007
|
+
const paramList = this.parseParameterList();
|
|
3008
|
+
this.expect("COLON" /* COLON */, '":" attendu pour le type de retour');
|
|
3009
|
+
const retTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de retour attendu");
|
|
3010
|
+
const retType = this.tokenTypeToDataType(retTypeToken.type);
|
|
3011
|
+
const body = this.parseCompoundStatement();
|
|
3012
|
+
return {
|
|
3013
|
+
type: "FUNCTION_DECLARATION" /* FUNCTION_DECLARATION */,
|
|
3014
|
+
value: nameToken.value,
|
|
3015
|
+
children: [
|
|
3016
|
+
paramList,
|
|
3017
|
+
{ type: "TYPE_SPECIFIER" /* TYPE_SPECIFIER */, value: retType },
|
|
3018
|
+
body
|
|
3019
|
+
],
|
|
3020
|
+
token
|
|
3021
|
+
};
|
|
3022
|
+
}
|
|
3023
|
+
parseProcedureDeclaration() {
|
|
3024
|
+
const token = this.advance();
|
|
3025
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de procédure attendu");
|
|
3026
|
+
const paramList = this.parseParameterList();
|
|
3027
|
+
const body = this.parseCompoundStatement();
|
|
3028
|
+
return {
|
|
3029
|
+
type: "PROCEDURE_DECLARATION" /* PROCEDURE_DECLARATION */,
|
|
3030
|
+
value: nameToken.value,
|
|
3031
|
+
children: [paramList, body],
|
|
3032
|
+
token
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
2453
3035
|
check(type) {
|
|
2454
3036
|
if (this.isAtEnd())
|
|
2455
3037
|
return false;
|
|
@@ -2599,6 +3181,8 @@ class CodeGenerator2 {
|
|
|
2599
3181
|
return this.generateBlock(node);
|
|
2600
3182
|
case "VAR_DECLARATION":
|
|
2601
3183
|
return this.generateVariableDeclaration(node);
|
|
3184
|
+
case "ARRAY_DECLARATION":
|
|
3185
|
+
return this.generateArrayDeclaration(node);
|
|
2602
3186
|
case "COMPOUND_STATEMENT":
|
|
2603
3187
|
return this.generateCompoundStatement(node);
|
|
2604
3188
|
case "ASSIGNMENT":
|
|
@@ -2615,6 +3199,12 @@ class CodeGenerator2 {
|
|
|
2615
3199
|
return this.generateReadStatement(node);
|
|
2616
3200
|
case "WRITE_STATEMENT":
|
|
2617
3201
|
return this.generateWriteStatement(node);
|
|
3202
|
+
case "FUNCTION_DECLARATION":
|
|
3203
|
+
return this.generateFunctionDeclaration(node);
|
|
3204
|
+
case "PROCEDURE_DECLARATION":
|
|
3205
|
+
return this.generateProcedureDeclaration(node);
|
|
3206
|
+
case "RETURN_STATEMENT":
|
|
3207
|
+
return this.generateReturnStatement(node);
|
|
2618
3208
|
case "BINARY_OP":
|
|
2619
3209
|
return this.generateBinaryOp(node);
|
|
2620
3210
|
case "UNARY_OP":
|
|
@@ -2623,6 +3213,10 @@ class CodeGenerator2 {
|
|
|
2623
3213
|
return this.generateLiteral(node);
|
|
2624
3214
|
case "VARIABLE":
|
|
2625
3215
|
return this.generateVariable(node);
|
|
3216
|
+
case "ARRAY_ACCESS":
|
|
3217
|
+
return this.generateArrayAccess(node);
|
|
3218
|
+
case "FUNCTION_CALL":
|
|
3219
|
+
return this.generateFunctionCall(node);
|
|
2626
3220
|
default:
|
|
2627
3221
|
this.errors.push({
|
|
2628
3222
|
type: "ERROR",
|
|
@@ -2637,14 +3231,25 @@ class CodeGenerator2 {
|
|
|
2637
3231
|
}
|
|
2638
3232
|
generateProgram(node) {
|
|
2639
3233
|
const lines = [];
|
|
3234
|
+
const block = node.children?.[0];
|
|
2640
3235
|
lines.push(`// Programme: ${node.value}`);
|
|
3236
|
+
if (block?.children) {
|
|
3237
|
+
for (const child of block.children) {
|
|
3238
|
+
if (child.type === "FUNCTION_DECLARATION" || child.type === "PROCEDURE_DECLARATION") {
|
|
3239
|
+
lines.push(this.generateNode(child));
|
|
3240
|
+
lines.push("");
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
2641
3244
|
lines.push("async function main() {");
|
|
2642
3245
|
this.indentLevel++;
|
|
2643
|
-
if (
|
|
2644
|
-
for (const child of
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
3246
|
+
if (block?.children) {
|
|
3247
|
+
for (const child of block.children) {
|
|
3248
|
+
if (child.type !== "FUNCTION_DECLARATION" && child.type !== "PROCEDURE_DECLARATION") {
|
|
3249
|
+
const childCode = this.generateNode(child);
|
|
3250
|
+
if (childCode) {
|
|
3251
|
+
lines.push(this.indent(childCode));
|
|
3252
|
+
}
|
|
2648
3253
|
}
|
|
2649
3254
|
}
|
|
2650
3255
|
}
|
|
@@ -2682,7 +3287,8 @@ class CodeGenerator2 {
|
|
|
2682
3287
|
for (const child of node.children) {
|
|
2683
3288
|
const childCode = this.generateNode(child);
|
|
2684
3289
|
if (childCode) {
|
|
2685
|
-
|
|
3290
|
+
const stmt = child.type === "FUNCTION_CALL" ? childCode.replace(/^\(|\)$/g, "") + ";" : childCode;
|
|
3291
|
+
lines.push(this.indent(stmt));
|
|
2686
3292
|
}
|
|
2687
3293
|
}
|
|
2688
3294
|
}
|
|
@@ -2693,15 +3299,107 @@ class CodeGenerator2 {
|
|
|
2693
3299
|
if (!node.children || node.children.length < 2) {
|
|
2694
3300
|
return "";
|
|
2695
3301
|
}
|
|
2696
|
-
const
|
|
3302
|
+
const target = node.children[0];
|
|
2697
3303
|
const expression = node.children[1];
|
|
2698
|
-
if (!
|
|
3304
|
+
if (!target || !expression) {
|
|
2699
3305
|
return "";
|
|
2700
3306
|
}
|
|
2701
|
-
const variableName = variable.value;
|
|
2702
3307
|
const expressionCode = this.generateNode(expression);
|
|
3308
|
+
if (target.type === "ARRAY_ACCESS") {
|
|
3309
|
+
const arrayName = target.value;
|
|
3310
|
+
const index = this.generateNode(target.children[0]);
|
|
3311
|
+
return `${arrayName}[${index}] = ${expressionCode};`;
|
|
3312
|
+
}
|
|
3313
|
+
const variableName = target.value;
|
|
2703
3314
|
return `${variableName} = ${expressionCode};`;
|
|
2704
3315
|
}
|
|
3316
|
+
generateArrayDeclaration(node) {
|
|
3317
|
+
if (!node.children || node.children.length < 2)
|
|
3318
|
+
return "";
|
|
3319
|
+
const size = node.children[0].value;
|
|
3320
|
+
const elemType = node.value;
|
|
3321
|
+
const defaultValue = elemType === "BOOLEEN" ? "false" : elemType === "CHAINE" ? '""' : "0";
|
|
3322
|
+
const names = node.children.slice(1).map((c) => c.value);
|
|
3323
|
+
return names.map((name) => `let ${name} = new Array(${size}).fill(${defaultValue});`).join(`
|
|
3324
|
+
`);
|
|
3325
|
+
}
|
|
3326
|
+
generateArrayAccess(node) {
|
|
3327
|
+
const name = node.value;
|
|
3328
|
+
const index = this.generateNode(node.children[0]);
|
|
3329
|
+
return `${name}[${index}]`;
|
|
3330
|
+
}
|
|
3331
|
+
generateFunctionCall(node) {
|
|
3332
|
+
const name = node.value;
|
|
3333
|
+
const args = (node.children ?? []).map((c) => {
|
|
3334
|
+
const code = this.generateNode(c);
|
|
3335
|
+
if (c.type === "VARIABLE") {
|
|
3336
|
+
const sym = this.symbolTable.symbols.get(c.value);
|
|
3337
|
+
if (sym?.type?.startsWith("TABLEAU"))
|
|
3338
|
+
return `${code}.slice()`;
|
|
3339
|
+
}
|
|
3340
|
+
return code;
|
|
3341
|
+
});
|
|
3342
|
+
switch (name.toLowerCase()) {
|
|
3343
|
+
case "abs":
|
|
3344
|
+
return `Math.abs(${args[0]})`;
|
|
3345
|
+
case "max":
|
|
3346
|
+
return `Math.max(${args.join(", ")})`;
|
|
3347
|
+
case "min":
|
|
3348
|
+
return `Math.min(${args.join(", ")})`;
|
|
3349
|
+
case "mod":
|
|
3350
|
+
return `(${args[0]} % ${args[1]})`;
|
|
3351
|
+
case "racine_carree":
|
|
3352
|
+
return `Math.sqrt(${args[0]})`;
|
|
3353
|
+
case "taille":
|
|
3354
|
+
return `${args[0]}.length`;
|
|
3355
|
+
case "sous_chaine":
|
|
3356
|
+
return `${args[0]}.substring(${args[1]}, ${args[1]} + ${args[2]})`;
|
|
3357
|
+
case "concat":
|
|
3358
|
+
return args.join(" + ");
|
|
3359
|
+
case "entier_en_reel":
|
|
3360
|
+
return `${args[0]}`;
|
|
3361
|
+
case "reel_en_entier":
|
|
3362
|
+
return `Math.trunc(${args[0]})`;
|
|
3363
|
+
default:
|
|
3364
|
+
return `(await ${name}(${args.join(", ")}))`;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
generateFunctionDeclaration(node) {
|
|
3368
|
+
const name = node.value;
|
|
3369
|
+
const [paramList, , body] = node.children ?? [];
|
|
3370
|
+
const params = this.generateParamList(paramList);
|
|
3371
|
+
const bodyCode = this.generateNode(body);
|
|
3372
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
3373
|
+
this.indentLevel++;
|
|
3374
|
+
lines.push(this.indent(bodyCode));
|
|
3375
|
+
this.indentLevel--;
|
|
3376
|
+
lines.push("}");
|
|
3377
|
+
return lines.join(`
|
|
3378
|
+
`);
|
|
3379
|
+
}
|
|
3380
|
+
generateProcedureDeclaration(node) {
|
|
3381
|
+
const name = node.value;
|
|
3382
|
+
const [paramList, body] = node.children ?? [];
|
|
3383
|
+
const params = this.generateParamList(paramList);
|
|
3384
|
+
const bodyCode = this.generateNode(body);
|
|
3385
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
3386
|
+
this.indentLevel++;
|
|
3387
|
+
lines.push(this.indent(bodyCode));
|
|
3388
|
+
this.indentLevel--;
|
|
3389
|
+
lines.push("}");
|
|
3390
|
+
return lines.join(`
|
|
3391
|
+
`);
|
|
3392
|
+
}
|
|
3393
|
+
generateParamList(paramList) {
|
|
3394
|
+
if (!paramList?.children)
|
|
3395
|
+
return "";
|
|
3396
|
+
return paramList.children.map((p) => p.value).join(", ");
|
|
3397
|
+
}
|
|
3398
|
+
generateReturnStatement(node) {
|
|
3399
|
+
if (!node.children?.[0])
|
|
3400
|
+
return "return;";
|
|
3401
|
+
return `return ${this.generateNode(node.children[0])};`;
|
|
3402
|
+
}
|
|
2705
3403
|
generateIfStatement(node) {
|
|
2706
3404
|
if (!node.children || node.children.length < 2) {
|
|
2707
3405
|
return "";
|
|
@@ -2803,34 +3501,20 @@ class CodeGenerator2 {
|
|
|
2803
3501
|
return code;
|
|
2804
3502
|
}
|
|
2805
3503
|
generateReadStatement(node) {
|
|
2806
|
-
if (!node.children || node.children.length === 0)
|
|
3504
|
+
if (!node.children || node.children.length === 0)
|
|
2807
3505
|
return "";
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
if (!variable) {
|
|
3506
|
+
const target = node.children[0];
|
|
3507
|
+
if (!target)
|
|
2811
3508
|
return "";
|
|
3509
|
+
const isArray = target.type === "ARRAY_ACCESS";
|
|
3510
|
+
const varName = isArray ? `${target.value}[${this.generateNode(target.children[0])}]` : target.value;
|
|
3511
|
+
const symbolName = target.value;
|
|
3512
|
+
const symbolInfo = this.symbolTable.symbols.get(symbolName);
|
|
3513
|
+
const typeHint = symbolInfo?.type ?? "";
|
|
3514
|
+
if (typeHint.startsWith("ENTIER") || typeHint === "REEL" || typeHint.startsWith("TABLEAU")) {
|
|
3515
|
+
return `${varName} = parseInt(await lire(""));`;
|
|
2812
3516
|
}
|
|
2813
|
-
|
|
2814
|
-
const symbolInfo = this.symbolTable.symbols.get(variableName);
|
|
2815
|
-
let conversion = "";
|
|
2816
|
-
if (symbolInfo) {
|
|
2817
|
-
switch (symbolInfo.type) {
|
|
2818
|
-
case "ENTIER":
|
|
2819
|
-
case "REEL":
|
|
2820
|
-
conversion = "parseInt(";
|
|
2821
|
-
break;
|
|
2822
|
-
case "BOOLEEN":
|
|
2823
|
-
conversion = "(";
|
|
2824
|
-
break;
|
|
2825
|
-
default:
|
|
2826
|
-
conversion = "";
|
|
2827
|
-
}
|
|
2828
|
-
}
|
|
2829
|
-
if (conversion) {
|
|
2830
|
-
return `${variableName} = ${conversion}await lire(""));`;
|
|
2831
|
-
} else {
|
|
2832
|
-
return `${variableName} = await lire("");`;
|
|
2833
|
-
}
|
|
3517
|
+
return `${varName} = await lire("");`;
|
|
2834
3518
|
}
|
|
2835
3519
|
generateWriteStatement(node) {
|
|
2836
3520
|
if (!node.children || node.children.length === 0) {
|
|
@@ -2929,6 +3613,8 @@ class CodeGenerator2 {
|
|
|
2929
3613
|
return ">";
|
|
2930
3614
|
case ">=":
|
|
2931
3615
|
return ">=";
|
|
3616
|
+
case "%":
|
|
3617
|
+
return "%";
|
|
2932
3618
|
case "et":
|
|
2933
3619
|
return "&&";
|
|
2934
3620
|
case "ou":
|
|
@@ -3201,7 +3887,7 @@ var TokenType2;
|
|
|
3201
3887
|
TokenType3["DE"] = "DE";
|
|
3202
3888
|
TokenType3["TO"] = "A";
|
|
3203
3889
|
TokenType3["REPEAT"] = "REPETER";
|
|
3204
|
-
TokenType3["UNTIL"] = "
|
|
3890
|
+
TokenType3["UNTIL"] = "JUSQU'A";
|
|
3205
3891
|
TokenType3["READ"] = "LIRE";
|
|
3206
3892
|
TokenType3["WRITE"] = "ECRIRE";
|
|
3207
3893
|
TokenType3["TRUE"] = "VRAI";
|
|
@@ -3212,10 +3898,15 @@ var TokenType2;
|
|
|
3212
3898
|
TokenType3["ENDFOR"] = "FINPOUR";
|
|
3213
3899
|
TokenType3["ENDIF"] = "FINIF";
|
|
3214
3900
|
TokenType3["ENDWHILE"] = "FINTANTQUE";
|
|
3901
|
+
TokenType3["ARRAY"] = "TABLEAU";
|
|
3902
|
+
TokenType3["FUNCTION"] = "FONCTION";
|
|
3903
|
+
TokenType3["PROCEDURE"] = "PROCEDURE";
|
|
3904
|
+
TokenType3["RETURN"] = "RETOURNER";
|
|
3215
3905
|
TokenType3["PLUS"] = "PLUS";
|
|
3216
3906
|
TokenType3["MINUS"] = "MINUS";
|
|
3217
3907
|
TokenType3["MULTIPLY"] = "MULTIPLY";
|
|
3218
3908
|
TokenType3["DIVIDE"] = "DIVIDE";
|
|
3909
|
+
TokenType3["MODULO"] = "MODULO";
|
|
3219
3910
|
TokenType3["ASSIGN"] = "ASSIGN";
|
|
3220
3911
|
TokenType3["EQUAL"] = "EQUAL";
|
|
3221
3912
|
TokenType3["NOT_EQUAL"] = "NOT_EQUAL";
|
|
@@ -3229,6 +3920,8 @@ var TokenType2;
|
|
|
3229
3920
|
TokenType3["DOT"] = "DOT";
|
|
3230
3921
|
TokenType3["LEFT_PAREN"] = "LEFT_PAREN";
|
|
3231
3922
|
TokenType3["RIGHT_PAREN"] = "RIGHT_PAREN";
|
|
3923
|
+
TokenType3["LEFT_BRACKET"] = "LEFT_BRACKET";
|
|
3924
|
+
TokenType3["RIGHT_BRACKET"] = "RIGHT_BRACKET";
|
|
3232
3925
|
TokenType3["NUMBER"] = "NUMBER";
|
|
3233
3926
|
TokenType3["IDENTIFIER"] = "IDENTIFIER";
|
|
3234
3927
|
TokenType3["STRING_LITERAL"] = "STRING_LITERAL";
|
|
@@ -3253,6 +3946,13 @@ var NodeType2;
|
|
|
3253
3946
|
NodeType3["UNARY_OP"] = "UNARY_OP";
|
|
3254
3947
|
NodeType3["LITERAL"] = "LITERAL";
|
|
3255
3948
|
NodeType3["VARIABLE"] = "VARIABLE";
|
|
3949
|
+
NodeType3["FUNCTION_CALL"] = "FUNCTION_CALL";
|
|
3950
|
+
NodeType3["ARRAY_ACCESS"] = "ARRAY_ACCESS";
|
|
3951
|
+
NodeType3["ARRAY_DECLARATION"] = "ARRAY_DECLARATION";
|
|
3952
|
+
NodeType3["FUNCTION_DECLARATION"] = "FUNCTION_DECLARATION";
|
|
3953
|
+
NodeType3["PROCEDURE_DECLARATION"] = "PROCEDURE_DECLARATION";
|
|
3954
|
+
NodeType3["RETURN_STATEMENT"] = "RETURN_STATEMENT";
|
|
3955
|
+
NodeType3["PARAMETER"] = "PARAMETER";
|
|
3256
3956
|
NodeType3["BLOCK"] = "BLOCK";
|
|
3257
3957
|
NodeType3["PARAMETER_LIST"] = "PARAMETER_LIST";
|
|
3258
3958
|
})(NodeType2 ||= {});
|
|
@@ -3264,11 +3964,361 @@ var DataType2;
|
|
|
3264
3964
|
DataType3["STRING"] = "CHAINE";
|
|
3265
3965
|
DataType3["VOID"] = "VIDE";
|
|
3266
3966
|
})(DataType2 ||= {});
|
|
3967
|
+
// src/keywords.ts
|
|
3968
|
+
var KEYWORDS2 = {
|
|
3969
|
+
PROGRAMME: {
|
|
3970
|
+
tokenType: "PROGRAMME" /* PROGRAM */,
|
|
3971
|
+
kind: "control",
|
|
3972
|
+
detail: "Mot-clé PROGRAMME",
|
|
3973
|
+
documentation: "**PROGRAMME** : Début d'un programme AlgoLang."
|
|
3974
|
+
},
|
|
3975
|
+
DEBUT: {
|
|
3976
|
+
tokenType: "DEBUT" /* BEGIN */,
|
|
3977
|
+
kind: "control",
|
|
3978
|
+
detail: "Mot-clé DEBUT",
|
|
3979
|
+
documentation: "**DEBUT** : Début du bloc d'instructions principal."
|
|
3980
|
+
},
|
|
3981
|
+
FIN: {
|
|
3982
|
+
tokenType: "FIN" /* END */,
|
|
3983
|
+
kind: "control",
|
|
3984
|
+
detail: "Mot-clé FIN",
|
|
3985
|
+
documentation: "**FIN** : Fin du bloc d'instructions ou du programme."
|
|
3986
|
+
},
|
|
3987
|
+
VAR: {
|
|
3988
|
+
tokenType: "VAR" /* VAR */,
|
|
3989
|
+
kind: "declaration",
|
|
3990
|
+
detail: "Mot-clé VAR",
|
|
3991
|
+
documentation: "**VAR** : Section de déclaration des variables."
|
|
3992
|
+
},
|
|
3993
|
+
ENTIER: {
|
|
3994
|
+
tokenType: "ENTIER" /* INTEGER */,
|
|
3995
|
+
kind: "type",
|
|
3996
|
+
detail: "Type ENTIER",
|
|
3997
|
+
documentation: "**ENTIER** : Type de donnée pour les nombres entiers."
|
|
3998
|
+
},
|
|
3999
|
+
REEL: {
|
|
4000
|
+
tokenType: "REEL" /* REAL */,
|
|
4001
|
+
kind: "type",
|
|
4002
|
+
detail: "Type REEL",
|
|
4003
|
+
documentation: "**REEL** : Type de donnée pour les nombres à virgule."
|
|
4004
|
+
},
|
|
4005
|
+
BOOLEEN: {
|
|
4006
|
+
tokenType: "BOOLEEN" /* BOOLEAN */,
|
|
4007
|
+
kind: "type",
|
|
4008
|
+
detail: "Type BOOLEEN",
|
|
4009
|
+
documentation: "**BOOLEEN** : Type de donnée logique (VRAI/FAUX)."
|
|
4010
|
+
},
|
|
4011
|
+
CHAINE: {
|
|
4012
|
+
tokenType: "CHAINE" /* STRING */,
|
|
4013
|
+
kind: "type",
|
|
4014
|
+
detail: "Type CHAINE",
|
|
4015
|
+
documentation: "**CHAINE** : Type de donnée pour le texte."
|
|
4016
|
+
},
|
|
4017
|
+
SI: {
|
|
4018
|
+
tokenType: "SI" /* IF */,
|
|
4019
|
+
kind: "control",
|
|
4020
|
+
detail: "Mot-clé SI",
|
|
4021
|
+
documentation: "**SI** : Structure conditionnelle."
|
|
4022
|
+
},
|
|
4023
|
+
ALORS: {
|
|
4024
|
+
tokenType: "ALORS" /* THEN */,
|
|
4025
|
+
kind: "control",
|
|
4026
|
+
detail: "Mot-clé ALORS",
|
|
4027
|
+
documentation: "**ALORS** : Début du bloc exécuté si la condition est vraie."
|
|
4028
|
+
},
|
|
4029
|
+
SINON: {
|
|
4030
|
+
tokenType: "SINON" /* ELSE */,
|
|
4031
|
+
kind: "control",
|
|
4032
|
+
detail: "Mot-clé SINON",
|
|
4033
|
+
documentation: "**SINON** : Début du bloc exécuté si la condition est fausse."
|
|
4034
|
+
},
|
|
4035
|
+
FINSI: {
|
|
4036
|
+
tokenType: "FINIF" /* ENDIF */,
|
|
4037
|
+
kind: "control",
|
|
4038
|
+
detail: "Mot-clé FINSI",
|
|
4039
|
+
documentation: "**FINSI** : Fin d'une structure conditionnelle."
|
|
4040
|
+
},
|
|
4041
|
+
TANTQUE: {
|
|
4042
|
+
tokenType: "TANTQUE" /* WHILE */,
|
|
4043
|
+
kind: "control",
|
|
4044
|
+
detail: "Mot-clé TANTQUE",
|
|
4045
|
+
documentation: "**TANTQUE** : Boucle répétitive tant qu'une condition est vraie."
|
|
4046
|
+
},
|
|
4047
|
+
FAIRE: {
|
|
4048
|
+
tokenType: "FAIRE" /* DO */,
|
|
4049
|
+
kind: "control",
|
|
4050
|
+
detail: "Mot-clé FAIRE",
|
|
4051
|
+
documentation: "**FAIRE** : Début du corps d'une boucle."
|
|
4052
|
+
},
|
|
4053
|
+
FINTANTQUE: {
|
|
4054
|
+
tokenType: "FINTANTQUE" /* ENDWHILE */,
|
|
4055
|
+
kind: "control",
|
|
4056
|
+
detail: "Mot-clé FINTANTQUE",
|
|
4057
|
+
documentation: "**FINTANTQUE** : Fin d'une boucle TANTQUE."
|
|
4058
|
+
},
|
|
4059
|
+
POUR: {
|
|
4060
|
+
tokenType: "POUR" /* FOR */,
|
|
4061
|
+
kind: "control",
|
|
4062
|
+
detail: "Mot-clé POUR",
|
|
4063
|
+
documentation: "**POUR** : Boucle avec compteur."
|
|
4064
|
+
},
|
|
4065
|
+
ALLANT: {
|
|
4066
|
+
tokenType: "ALLANT" /* ALLANT */,
|
|
4067
|
+
kind: "control",
|
|
4068
|
+
detail: "Mot-clé ALLANT",
|
|
4069
|
+
documentation: "**ALLANT** : Utilisé dans une boucle POUR pour spécifier la plage."
|
|
4070
|
+
},
|
|
4071
|
+
DE: {
|
|
4072
|
+
tokenType: "DE" /* DE */,
|
|
4073
|
+
kind: "control",
|
|
4074
|
+
detail: "Mot-clé DE",
|
|
4075
|
+
documentation: "**DE** : Spécifie le début d'une plage dans une boucle POUR."
|
|
4076
|
+
},
|
|
4077
|
+
A: {
|
|
4078
|
+
tokenType: "A" /* TO */,
|
|
4079
|
+
kind: "control",
|
|
4080
|
+
detail: "Mot-clé A",
|
|
4081
|
+
documentation: "**A** : Spécifie la fin d'une plage dans une boucle POUR."
|
|
4082
|
+
},
|
|
4083
|
+
FINPOUR: {
|
|
4084
|
+
tokenType: "FINPOUR" /* ENDFOR */,
|
|
4085
|
+
kind: "control",
|
|
4086
|
+
detail: "Mot-clé FINPOUR",
|
|
4087
|
+
documentation: "**FINPOUR** : Fin d'une boucle POUR."
|
|
4088
|
+
},
|
|
4089
|
+
REPETER: {
|
|
4090
|
+
tokenType: "REPETER" /* REPEAT */,
|
|
4091
|
+
kind: "control",
|
|
4092
|
+
detail: "Mot-clé REPETER",
|
|
4093
|
+
documentation: "**REPETER** : Boucle exécutée au moins une fois."
|
|
4094
|
+
},
|
|
4095
|
+
"JUSQU'A": {
|
|
4096
|
+
tokenType: "JUSQU'A" /* UNTIL */,
|
|
4097
|
+
kind: "control",
|
|
4098
|
+
detail: "Mot-clé JUSQU'A",
|
|
4099
|
+
documentation: "**JUSQU'A** : Condition de fin d'une boucle REPETER."
|
|
4100
|
+
},
|
|
4101
|
+
LIRE: {
|
|
4102
|
+
tokenType: "LIRE" /* READ */,
|
|
4103
|
+
kind: "io",
|
|
4104
|
+
detail: "Fonction LIRE",
|
|
4105
|
+
documentation: "**LIRE(variable)** : Lit une valeur depuis l'entrée standard."
|
|
4106
|
+
},
|
|
4107
|
+
ECRIRE: {
|
|
4108
|
+
tokenType: "ECRIRE" /* WRITE */,
|
|
4109
|
+
kind: "io",
|
|
4110
|
+
detail: "Fonction ECRIRE",
|
|
4111
|
+
documentation: "**ECRIRE(...)** : Affiche des valeurs dans la console."
|
|
4112
|
+
},
|
|
4113
|
+
VRAI: {
|
|
4114
|
+
tokenType: "VRAI" /* TRUE */,
|
|
4115
|
+
kind: "literal",
|
|
4116
|
+
detail: "Constante VRAI",
|
|
4117
|
+
documentation: "**VRAI** : Valeur booléenne vraie."
|
|
4118
|
+
},
|
|
4119
|
+
FAUX: {
|
|
4120
|
+
tokenType: "FAUX" /* FALSE */,
|
|
4121
|
+
kind: "literal",
|
|
4122
|
+
detail: "Constante FAUX",
|
|
4123
|
+
documentation: "**FAUX** : Valeur booléenne fausse."
|
|
4124
|
+
},
|
|
4125
|
+
ET: {
|
|
4126
|
+
tokenType: "ET" /* AND */,
|
|
4127
|
+
kind: "operator",
|
|
4128
|
+
detail: "Opérateur ET",
|
|
4129
|
+
documentation: "**ET** : Opérateur logique ET (AND)."
|
|
4130
|
+
},
|
|
4131
|
+
OU: {
|
|
4132
|
+
tokenType: "OU" /* OR */,
|
|
4133
|
+
kind: "operator",
|
|
4134
|
+
detail: "Opérateur OU",
|
|
4135
|
+
documentation: "**OU** : Opérateur logique OU (OR)."
|
|
4136
|
+
},
|
|
4137
|
+
NON: {
|
|
4138
|
+
tokenType: "NON" /* NOT */,
|
|
4139
|
+
kind: "operator",
|
|
4140
|
+
detail: "Opérateur NON",
|
|
4141
|
+
documentation: "**NON** : Opérateur logique NON (NOT)."
|
|
4142
|
+
},
|
|
4143
|
+
TABLEAU: {
|
|
4144
|
+
tokenType: "TABLEAU" /* ARRAY */,
|
|
4145
|
+
kind: "declaration",
|
|
4146
|
+
detail: "Type TABLEAU",
|
|
4147
|
+
documentation: "**TABLEAU** : Déclare un tableau de n éléments.\nSyntaxe : `t: TABLEAU[10] DE ENTIER`"
|
|
4148
|
+
},
|
|
4149
|
+
FONCTION: {
|
|
4150
|
+
tokenType: "FONCTION" /* FUNCTION */,
|
|
4151
|
+
kind: "declaration",
|
|
4152
|
+
detail: "Mot-clé FONCTION",
|
|
4153
|
+
documentation: "**FONCTION** nom(params): TYPE : Déclare une fonction qui retourne une valeur.\nExemple : `FONCTION carre(n: ENTIER): ENTIER`"
|
|
4154
|
+
},
|
|
4155
|
+
PROCEDURE: {
|
|
4156
|
+
tokenType: "PROCEDURE" /* PROCEDURE */,
|
|
4157
|
+
kind: "declaration",
|
|
4158
|
+
detail: "Mot-clé PROCEDURE",
|
|
4159
|
+
documentation: "**PROCEDURE** nom(params) : Déclare une procédure (sans valeur de retour).\nExemple : `PROCEDURE afficher(s: CHAINE)`"
|
|
4160
|
+
},
|
|
4161
|
+
RETOURNER: {
|
|
4162
|
+
tokenType: "RETOURNER" /* RETURN */,
|
|
4163
|
+
kind: "declaration",
|
|
4164
|
+
detail: "Mot-clé RETOURNER",
|
|
4165
|
+
documentation: "**RETOURNER** expr : Retourne une valeur depuis une fonction."
|
|
4166
|
+
},
|
|
4167
|
+
abs: {
|
|
4168
|
+
tokenType: null,
|
|
4169
|
+
kind: "builtin-function",
|
|
4170
|
+
detail: "Fonction abs(x)",
|
|
4171
|
+
documentation: "**abs(x)** : Retourne la valeur absolue de x."
|
|
4172
|
+
},
|
|
4173
|
+
max: {
|
|
4174
|
+
tokenType: null,
|
|
4175
|
+
kind: "builtin-function",
|
|
4176
|
+
detail: "Fonction max(a, b)",
|
|
4177
|
+
documentation: "**max(a, b)** : Retourne le plus grand des deux nombres."
|
|
4178
|
+
},
|
|
4179
|
+
min: {
|
|
4180
|
+
tokenType: null,
|
|
4181
|
+
kind: "builtin-function",
|
|
4182
|
+
detail: "Fonction min(a, b)",
|
|
4183
|
+
documentation: "**min(a, b)** : Retourne le plus petit des deux nombres."
|
|
4184
|
+
},
|
|
4185
|
+
mod: {
|
|
4186
|
+
tokenType: null,
|
|
4187
|
+
kind: "builtin-function",
|
|
4188
|
+
detail: "Fonction mod(a, b)",
|
|
4189
|
+
documentation: "**mod(a, b)** : Retourne le reste de la division de a par b. Équivalent à `a % b`."
|
|
4190
|
+
},
|
|
4191
|
+
racine_carree: {
|
|
4192
|
+
tokenType: null,
|
|
4193
|
+
kind: "builtin-function",
|
|
4194
|
+
detail: "Fonction racine_carree(x)",
|
|
4195
|
+
documentation: "**racine_carree(x)** : Retourne la racine carrée de x."
|
|
4196
|
+
},
|
|
4197
|
+
taille: {
|
|
4198
|
+
tokenType: null,
|
|
4199
|
+
kind: "builtin-function",
|
|
4200
|
+
detail: "Fonction taille(x)",
|
|
4201
|
+
documentation: "**taille(x)** : Retourne la taille d'un tableau ou la longueur d'une chaîne."
|
|
4202
|
+
},
|
|
4203
|
+
sous_chaine: {
|
|
4204
|
+
tokenType: null,
|
|
4205
|
+
kind: "builtin-function",
|
|
4206
|
+
detail: "Fonction sous_chaine(s, i, n)",
|
|
4207
|
+
documentation: "**sous_chaine(s, i, n)** : Retourne n caractères de la chaîne s à partir de l'indice i."
|
|
4208
|
+
},
|
|
4209
|
+
concat: {
|
|
4210
|
+
tokenType: null,
|
|
4211
|
+
kind: "builtin-function",
|
|
4212
|
+
detail: "Fonction concat(a, b)",
|
|
4213
|
+
documentation: "**concat(a, b)** : Concatène deux chaînes de caractères."
|
|
4214
|
+
},
|
|
4215
|
+
entier_en_reel: {
|
|
4216
|
+
tokenType: null,
|
|
4217
|
+
kind: "builtin-function",
|
|
4218
|
+
detail: "Fonction entier_en_reel(x)",
|
|
4219
|
+
documentation: "**entier_en_reel(x)** : Convertit un entier en réel."
|
|
4220
|
+
},
|
|
4221
|
+
reel_en_entier: {
|
|
4222
|
+
tokenType: null,
|
|
4223
|
+
kind: "builtin-function",
|
|
4224
|
+
detail: "Fonction reel_en_entier(x)",
|
|
4225
|
+
documentation: "**reel_en_entier(x)** : Convertit un réel en entier (troncature)."
|
|
4226
|
+
}
|
|
4227
|
+
};
|
|
4228
|
+
// src/semantic/semantic-analyzer.ts
|
|
4229
|
+
class SemanticAnalyzer2 {
|
|
4230
|
+
symbolTable = {
|
|
4231
|
+
symbols: new Map,
|
|
4232
|
+
children: [],
|
|
4233
|
+
scopeName: "global"
|
|
4234
|
+
};
|
|
4235
|
+
errors = [];
|
|
4236
|
+
analyze(ast) {
|
|
4237
|
+
this.walk(ast);
|
|
4238
|
+
return { symbolTable: this.symbolTable, errors: this.errors };
|
|
4239
|
+
}
|
|
4240
|
+
walk(node) {
|
|
4241
|
+
switch (node.type) {
|
|
4242
|
+
case "VAR_DECLARATION" /* VAR_DECLARATION */:
|
|
4243
|
+
this.collectVarDeclaration(node);
|
|
4244
|
+
break;
|
|
4245
|
+
case "ARRAY_DECLARATION" /* ARRAY_DECLARATION */:
|
|
4246
|
+
this.collectArrayDeclaration(node);
|
|
4247
|
+
break;
|
|
4248
|
+
default:
|
|
4249
|
+
node.children?.forEach((child) => this.walk(child));
|
|
4250
|
+
break;
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
collectVarDeclaration(node) {
|
|
4254
|
+
const algoType = node.value;
|
|
4255
|
+
const line = node.token?.line ?? 0;
|
|
4256
|
+
const column = node.token?.column ?? 0;
|
|
4257
|
+
for (const child of node.children ?? []) {
|
|
4258
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
4259
|
+
continue;
|
|
4260
|
+
const name = child.value;
|
|
4261
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
4262
|
+
this.errors.push({
|
|
4263
|
+
type: "ERROR",
|
|
4264
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
4265
|
+
line,
|
|
4266
|
+
column,
|
|
4267
|
+
position: node.token?.position ?? 0,
|
|
4268
|
+
code: "DUPLICATE_VARIABLE",
|
|
4269
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
4270
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
4271
|
+
});
|
|
4272
|
+
} else {
|
|
4273
|
+
this.defineSymbol(name, algoType, line, column);
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
collectArrayDeclaration(node) {
|
|
4278
|
+
const elemType = node.value;
|
|
4279
|
+
const sizeNode = node.children?.[0];
|
|
4280
|
+
const size = sizeNode?.value;
|
|
4281
|
+
const typeLabel = `TABLEAU[${size}] DE ${elemType}`;
|
|
4282
|
+
const line = node.token?.line ?? 0;
|
|
4283
|
+
const column = node.token?.column ?? 0;
|
|
4284
|
+
for (const child of (node.children ?? []).slice(1)) {
|
|
4285
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
4286
|
+
continue;
|
|
4287
|
+
const name = child.value;
|
|
4288
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
4289
|
+
this.errors.push({
|
|
4290
|
+
type: "ERROR",
|
|
4291
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
4292
|
+
line,
|
|
4293
|
+
column,
|
|
4294
|
+
position: node.token?.position ?? 0,
|
|
4295
|
+
code: "DUPLICATE_VARIABLE",
|
|
4296
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
4297
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
4298
|
+
});
|
|
4299
|
+
} else {
|
|
4300
|
+
this.defineSymbol(name, typeLabel, line, column);
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
defineSymbol(name, type, line, column) {
|
|
4305
|
+
const info = {
|
|
4306
|
+
name,
|
|
4307
|
+
type,
|
|
4308
|
+
scope: this.symbolTable.scopeName,
|
|
4309
|
+
line,
|
|
4310
|
+
column
|
|
4311
|
+
};
|
|
4312
|
+
this.symbolTable.symbols.set(name, info);
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
3267
4315
|
export {
|
|
3268
4316
|
TokenType2 as TokenType,
|
|
4317
|
+
SemanticAnalyzer2 as SemanticAnalyzer,
|
|
3269
4318
|
Parser,
|
|
3270
4319
|
NodeType2 as NodeType,
|
|
3271
4320
|
Lexer,
|
|
4321
|
+
KEYWORDS2 as KEYWORDS,
|
|
3272
4322
|
DataType2 as DataType,
|
|
3273
4323
|
CodeGenerator,
|
|
3274
4324
|
AlgoLangCompiler
|