@devalade/algolang 1.0.1 → 2.0.1
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 +719 -191
- package/dist/index.js +1456 -385
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,269 @@
|
|
|
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
|
+
var BUILTIN_NAMES = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType === null).map(([label]) => label));
|
|
266
|
+
|
|
4
267
|
// src/lexer/lexer.ts
|
|
5
268
|
class Lexer {
|
|
6
269
|
source;
|
|
@@ -10,37 +273,7 @@ class Lexer {
|
|
|
10
273
|
keywords;
|
|
11
274
|
constructor(source) {
|
|
12
275
|
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
|
-
]);
|
|
276
|
+
this.keywords = new Map(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label, e]) => [label, e.tokenType]));
|
|
44
277
|
}
|
|
45
278
|
tokenize() {
|
|
46
279
|
const tokens = [];
|
|
@@ -113,6 +346,8 @@ class Lexer {
|
|
|
113
346
|
return this.createToken("MULTIPLY" /* MULTIPLY */, this.advance());
|
|
114
347
|
case "/":
|
|
115
348
|
return this.createToken("DIVIDE" /* DIVIDE */, this.advance());
|
|
349
|
+
case "%":
|
|
350
|
+
return this.createToken("MODULO" /* MODULO */, this.advance());
|
|
116
351
|
case "=":
|
|
117
352
|
return this.createToken("EQUAL" /* EQUAL */, this.advance());
|
|
118
353
|
case "<":
|
|
@@ -131,6 +366,10 @@ class Lexer {
|
|
|
131
366
|
return this.createToken("LEFT_PAREN" /* LEFT_PAREN */, this.advance());
|
|
132
367
|
case ")":
|
|
133
368
|
return this.createToken("RIGHT_PAREN" /* RIGHT_PAREN */, this.advance());
|
|
369
|
+
case "[":
|
|
370
|
+
return this.createToken("LEFT_BRACKET" /* LEFT_BRACKET */, this.advance());
|
|
371
|
+
case "]":
|
|
372
|
+
return this.createToken("RIGHT_BRACKET" /* RIGHT_BRACKET */, this.advance());
|
|
134
373
|
default:
|
|
135
374
|
throw new Error(`Caractère non reconnu '${char}' à la ligne ${this.line}, colonne ${this.column}`);
|
|
136
375
|
}
|
|
@@ -322,87 +561,117 @@ class Lexer {
|
|
|
322
561
|
` || char === "\r";
|
|
323
562
|
}
|
|
324
563
|
}
|
|
564
|
+
// src/semantic/semantic-analyzer.ts
|
|
565
|
+
class SemanticAnalyzer {
|
|
566
|
+
symbolTable = {
|
|
567
|
+
symbols: new Map,
|
|
568
|
+
children: [],
|
|
569
|
+
scopeName: "global"
|
|
570
|
+
};
|
|
571
|
+
errors = [];
|
|
572
|
+
analyze(ast) {
|
|
573
|
+
this.walk(ast);
|
|
574
|
+
return { symbolTable: this.symbolTable, errors: this.errors };
|
|
575
|
+
}
|
|
576
|
+
walk(node) {
|
|
577
|
+
switch (node.type) {
|
|
578
|
+
case "VAR_DECLARATION" /* VAR_DECLARATION */:
|
|
579
|
+
this.collectVarDeclaration(node);
|
|
580
|
+
break;
|
|
581
|
+
case "ARRAY_DECLARATION" /* ARRAY_DECLARATION */:
|
|
582
|
+
this.collectArrayDeclaration(node);
|
|
583
|
+
break;
|
|
584
|
+
default:
|
|
585
|
+
node.children?.forEach((child) => this.walk(child));
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
collectVarDeclaration(node) {
|
|
590
|
+
const algoType = node.value;
|
|
591
|
+
const line = node.token?.line ?? 0;
|
|
592
|
+
const column = node.token?.column ?? 0;
|
|
593
|
+
const position = node.token?.position ?? 0;
|
|
594
|
+
for (const child of node.children ?? []) {
|
|
595
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
596
|
+
continue;
|
|
597
|
+
const name = child.value;
|
|
598
|
+
if (this.validateName(name, line, column, position)) {
|
|
599
|
+
this.defineSymbol(name, algoType, line, column);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
collectArrayDeclaration(node) {
|
|
604
|
+
const elemType = node.value;
|
|
605
|
+
const sizeNode = node.children?.[0];
|
|
606
|
+
const size = sizeNode?.value;
|
|
607
|
+
const typeLabel = `TABLEAU[${size}] DE ${elemType}`;
|
|
608
|
+
const line = node.token?.line ?? 0;
|
|
609
|
+
const column = node.token?.column ?? 0;
|
|
610
|
+
const position = node.token?.position ?? 0;
|
|
611
|
+
for (const child of (node.children ?? []).slice(1)) {
|
|
612
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
613
|
+
continue;
|
|
614
|
+
const name = child.value;
|
|
615
|
+
if (this.validateName(name, line, column, position)) {
|
|
616
|
+
this.defineSymbol(name, typeLabel, line, column);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
validateName(name, line, column, position) {
|
|
621
|
+
if (BUILTIN_NAMES.has(name)) {
|
|
622
|
+
this.errors.push({
|
|
623
|
+
type: "ERROR",
|
|
624
|
+
message: `Le nom '${name}' est une fonction intégrée et ne peut pas être utilisé comme nom de variable`,
|
|
625
|
+
line,
|
|
626
|
+
column,
|
|
627
|
+
position,
|
|
628
|
+
code: "BUILTIN_SHADOWING",
|
|
629
|
+
explanation: `'${name}' est une fonction intégrée d'AlgoLang`,
|
|
630
|
+
suggestion: `Choisissez un autre nom, par exemple '${name}Valeur' ou 'mon${name.charAt(0).toUpperCase()}${name.slice(1)}'`
|
|
631
|
+
});
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
635
|
+
this.errors.push({
|
|
636
|
+
type: "ERROR",
|
|
637
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
638
|
+
line,
|
|
639
|
+
column,
|
|
640
|
+
position,
|
|
641
|
+
code: "DUPLICATE_VARIABLE",
|
|
642
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
643
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
644
|
+
});
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
defineSymbol(name, type, line, column) {
|
|
650
|
+
const info = {
|
|
651
|
+
name,
|
|
652
|
+
type,
|
|
653
|
+
scope: this.symbolTable.scopeName,
|
|
654
|
+
line,
|
|
655
|
+
column
|
|
656
|
+
};
|
|
657
|
+
this.symbolTable.symbols.set(name, info);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
325
661
|
// src/parser/parser.ts
|
|
326
662
|
class Parser {
|
|
327
663
|
tokens;
|
|
328
664
|
current = 0;
|
|
329
665
|
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
|
-
]);
|
|
666
|
+
reservedKeywords = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label]) => label.toLowerCase()));
|
|
363
667
|
constructor(tokens) {
|
|
364
668
|
this.tokens = tokens;
|
|
365
|
-
this.symbolTable = {
|
|
366
|
-
symbols: new Map,
|
|
367
|
-
children: [],
|
|
368
|
-
scopeName: "global"
|
|
369
|
-
};
|
|
370
669
|
}
|
|
371
670
|
isReservedKeyword(identifier) {
|
|
372
671
|
return this.reservedKeywords.has(identifier.toLowerCase());
|
|
373
672
|
}
|
|
374
673
|
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);
|
|
674
|
+
return Object.values(KEYWORDS).some((e) => e.tokenType === tokenType);
|
|
406
675
|
}
|
|
407
676
|
createReservedKeywordError(identifier, token) {
|
|
408
677
|
this.errors.push({
|
|
@@ -417,30 +686,22 @@ class Parser {
|
|
|
417
686
|
});
|
|
418
687
|
}
|
|
419
688
|
parse() {
|
|
689
|
+
let ast;
|
|
420
690
|
try {
|
|
421
|
-
|
|
422
|
-
return {
|
|
423
|
-
ast: program,
|
|
424
|
-
errors: this.errors,
|
|
425
|
-
symbolTable: this.symbolTable
|
|
426
|
-
};
|
|
691
|
+
ast = this.parseProgram();
|
|
427
692
|
} catch (error) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
],
|
|
441
|
-
symbolTable: this.symbolTable
|
|
442
|
-
};
|
|
443
|
-
}
|
|
693
|
+
ast = { type: "PROGRAM" /* PROGRAM */, children: [] };
|
|
694
|
+
this.errors.push({
|
|
695
|
+
type: "ERROR",
|
|
696
|
+
message: error instanceof Error ? error.message : "Erreur de parsing inconnue",
|
|
697
|
+
line: 1,
|
|
698
|
+
column: 1,
|
|
699
|
+
position: 0,
|
|
700
|
+
code: "PARSE_ERROR"
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
const { symbolTable, errors: semanticErrors } = new SemanticAnalyzer().analyze(ast);
|
|
704
|
+
return { ast, errors: [...this.errors, ...semanticErrors], symbolTable };
|
|
444
705
|
}
|
|
445
706
|
parseProgram() {
|
|
446
707
|
const programToken = this.expect("PROGRAMME" /* PROGRAM */, 'Le programme doit commencer par le mot-clé "programme"');
|
|
@@ -456,17 +717,25 @@ class Parser {
|
|
|
456
717
|
}
|
|
457
718
|
parseBlock() {
|
|
458
719
|
const declarations = this.parseDeclarations();
|
|
720
|
+
const subprograms = [];
|
|
721
|
+
while (this.check(["FONCTION" /* FUNCTION */, "PROCEDURE" /* PROCEDURE */])) {
|
|
722
|
+
if (this.check("FONCTION" /* FUNCTION */)) {
|
|
723
|
+
subprograms.push(this.parseFunctionDeclaration());
|
|
724
|
+
} else {
|
|
725
|
+
subprograms.push(this.parseProcedureDeclaration());
|
|
726
|
+
}
|
|
727
|
+
}
|
|
459
728
|
const compoundStatement = this.parseCompoundStatement();
|
|
460
729
|
return {
|
|
461
730
|
type: "BLOCK" /* BLOCK */,
|
|
462
|
-
children: [declarations, compoundStatement]
|
|
731
|
+
children: [declarations, ...subprograms, compoundStatement]
|
|
463
732
|
};
|
|
464
733
|
}
|
|
465
734
|
parseDeclarations() {
|
|
466
735
|
const declarations = [];
|
|
467
736
|
while (this.check("VAR" /* VAR */)) {
|
|
468
737
|
this.advance();
|
|
469
|
-
while (!this.check("DEBUT" /* BEGIN */) && !this.isAtEnd()) {
|
|
738
|
+
while (!this.check("DEBUT" /* BEGIN */) && !this.check("FONCTION" /* FUNCTION */) && !this.check("PROCEDURE" /* PROCEDURE */) && !this.isAtEnd()) {
|
|
470
739
|
const declaration = this.parseVariableDeclaration();
|
|
471
740
|
declarations.push(declaration);
|
|
472
741
|
if (!this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
@@ -507,31 +776,27 @@ class Parser {
|
|
|
507
776
|
}
|
|
508
777
|
} while (true);
|
|
509
778
|
this.expect("COLON" /* COLON */, "Deux-points attendus après les identificateurs");
|
|
779
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
780
|
+
this.advance();
|
|
781
|
+
this.expect("LEFT_BRACKET" /* LEFT_BRACKET */, '"[" attendu après TABLEAU');
|
|
782
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille du tableau attendue");
|
|
783
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu après la taille du tableau');
|
|
784
|
+
this.expect("DE" /* DE */, '"DE" attendu après la taille du tableau');
|
|
785
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments du tableau attendu");
|
|
786
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
787
|
+
const size = parseInt(sizeToken.value);
|
|
788
|
+
return {
|
|
789
|
+
type: "ARRAY_DECLARATION" /* ARRAY_DECLARATION */,
|
|
790
|
+
value: elemType,
|
|
791
|
+
children: [
|
|
792
|
+
{ type: "LITERAL" /* LITERAL */, value: size },
|
|
793
|
+
...identifiers.map((name) => ({ type: "VARIABLE" /* VARIABLE */, value: name }))
|
|
794
|
+
],
|
|
795
|
+
token: elemTypeToken
|
|
796
|
+
};
|
|
797
|
+
}
|
|
510
798
|
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de variable attendu");
|
|
511
799
|
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
800
|
return {
|
|
536
801
|
type: "VAR_DECLARATION" /* VAR_DECLARATION */,
|
|
537
802
|
value: type,
|
|
@@ -548,9 +813,16 @@ class Parser {
|
|
|
548
813
|
while (!this.check("FIN" /* END */) && !this.isAtEnd()) {
|
|
549
814
|
const statement = this.parseStatement();
|
|
550
815
|
statements.push(statement);
|
|
816
|
+
const isBlockStatement = [
|
|
817
|
+
"IF_STATEMENT" /* IF_STATEMENT */,
|
|
818
|
+
"WHILE_STATEMENT" /* WHILE_STATEMENT */,
|
|
819
|
+
"FOR_STATEMENT" /* FOR_STATEMENT */,
|
|
820
|
+
"REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
821
|
+
"COMPOUND_STATEMENT" /* COMPOUND_STATEMENT */
|
|
822
|
+
].includes(statement.type);
|
|
551
823
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
552
824
|
this.advance();
|
|
553
|
-
} else if (!this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */)) {
|
|
825
|
+
} else if (!isBlockStatement && !this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */) && !this.check("FINPOUR" /* ENDFOR */)) {
|
|
554
826
|
this.errors.push({
|
|
555
827
|
type: "ERROR",
|
|
556
828
|
message: "Point-virgule attendu après l'instruction",
|
|
@@ -593,6 +865,16 @@ class Parser {
|
|
|
593
865
|
if (this.check("ECRIRE" /* WRITE */)) {
|
|
594
866
|
return this.parseWriteStatement();
|
|
595
867
|
}
|
|
868
|
+
if (this.check("RETOURNER" /* RETURN */)) {
|
|
869
|
+
return this.parseReturnStatement();
|
|
870
|
+
}
|
|
871
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_PAREN" /* LEFT_PAREN */) {
|
|
872
|
+
const nameToken = this.advance();
|
|
873
|
+
return this.parseFunctionCall(nameToken);
|
|
874
|
+
}
|
|
875
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_BRACKET" /* LEFT_BRACKET */) {
|
|
876
|
+
return this.parseArrayAssignment();
|
|
877
|
+
}
|
|
596
878
|
return this.parseAssignment();
|
|
597
879
|
}
|
|
598
880
|
parseIfStatement(isElseIf = false) {
|
|
@@ -712,14 +994,23 @@ class Parser {
|
|
|
712
994
|
parseReadStatement() {
|
|
713
995
|
const readToken = this.advance();
|
|
714
996
|
this.expect("LEFT_PAREN" /* LEFT_PAREN */, 'Parenthèse ouvrante attendue après "lire"');
|
|
715
|
-
let
|
|
997
|
+
let target;
|
|
716
998
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
717
|
-
variable = this.advance();
|
|
999
|
+
const variable = this.advance();
|
|
1000
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
1001
|
+
this.advance();
|
|
1002
|
+
const index = this.parseExpression();
|
|
1003
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
1004
|
+
target = { type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: variable.value, children: [index], token: variable };
|
|
1005
|
+
} else {
|
|
1006
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
1007
|
+
}
|
|
718
1008
|
} else {
|
|
719
1009
|
const currentToken = this.peek();
|
|
720
1010
|
if (this.isKeywordToken(currentToken.type)) {
|
|
721
|
-
variable = this.advance();
|
|
1011
|
+
const variable = this.advance();
|
|
722
1012
|
this.createReservedKeywordError(variable.value, variable);
|
|
1013
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
723
1014
|
} else {
|
|
724
1015
|
this.expect("IDENTIFIER" /* IDENTIFIER */, 'Variable attendue dans "lire"');
|
|
725
1016
|
throw new Error('Variable attendue dans "lire"');
|
|
@@ -728,9 +1019,7 @@ class Parser {
|
|
|
728
1019
|
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, 'Parenthèse fermante attendue dans "lire"');
|
|
729
1020
|
return {
|
|
730
1021
|
type: "READ_STATEMENT" /* READ_STATEMENT */,
|
|
731
|
-
children: [
|
|
732
|
-
{ type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable }
|
|
733
|
-
],
|
|
1022
|
+
children: [target],
|
|
734
1023
|
token: readToken
|
|
735
1024
|
};
|
|
736
1025
|
}
|
|
@@ -858,7 +1147,7 @@ class Parser {
|
|
|
858
1147
|
}
|
|
859
1148
|
parseMultiplicativeExpression() {
|
|
860
1149
|
let left = this.parseUnaryExpression();
|
|
861
|
-
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */])) {
|
|
1150
|
+
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */, "MODULO" /* MODULO */])) {
|
|
862
1151
|
const operator = this.advance();
|
|
863
1152
|
const right = this.parseUnaryExpression();
|
|
864
1153
|
left = {
|
|
@@ -907,7 +1196,7 @@ class Parser {
|
|
|
907
1196
|
const token2 = this.advance();
|
|
908
1197
|
return {
|
|
909
1198
|
type: "LITERAL" /* LITERAL */,
|
|
910
|
-
value: token2.value === "vrai",
|
|
1199
|
+
value: token2.value.toLowerCase() === "vrai",
|
|
911
1200
|
token: token2
|
|
912
1201
|
};
|
|
913
1202
|
}
|
|
@@ -921,6 +1210,20 @@ class Parser {
|
|
|
921
1210
|
}
|
|
922
1211
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
923
1212
|
const token2 = this.advance();
|
|
1213
|
+
if (this.check("LEFT_PAREN" /* LEFT_PAREN */)) {
|
|
1214
|
+
return this.parseFunctionCall(token2);
|
|
1215
|
+
}
|
|
1216
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
1217
|
+
this.advance();
|
|
1218
|
+
const index = this.parseExpression();
|
|
1219
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
1220
|
+
return {
|
|
1221
|
+
type: "ARRAY_ACCESS" /* ARRAY_ACCESS */,
|
|
1222
|
+
value: token2.value,
|
|
1223
|
+
children: [index],
|
|
1224
|
+
token: token2
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
924
1227
|
if (this.isReservedKeyword(token2.value)) {
|
|
925
1228
|
this.createReservedKeywordError(token2.value, token2);
|
|
926
1229
|
}
|
|
@@ -964,14 +1267,14 @@ class Parser {
|
|
|
964
1267
|
parseRepeatStatement() {
|
|
965
1268
|
const repeatToken = this.advance();
|
|
966
1269
|
const statements = [];
|
|
967
|
-
while (!this.check("
|
|
1270
|
+
while (!this.check("JUSQU'A" /* UNTIL */) && !this.isAtEnd()) {
|
|
968
1271
|
const statement = this.parseStatement();
|
|
969
1272
|
statements.push(statement);
|
|
970
1273
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
971
1274
|
this.advance();
|
|
972
1275
|
}
|
|
973
1276
|
}
|
|
974
|
-
this.expect("
|
|
1277
|
+
this.expect("JUSQU'A" /* UNTIL */, `"jusqu'a" attendu à la fin de la boucle repeter`);
|
|
975
1278
|
const condition = this.parseExpression();
|
|
976
1279
|
return {
|
|
977
1280
|
type: "REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
@@ -979,6 +1282,127 @@ class Parser {
|
|
|
979
1282
|
token: repeatToken
|
|
980
1283
|
};
|
|
981
1284
|
}
|
|
1285
|
+
parseFunctionCall(nameToken) {
|
|
1286
|
+
this.advance();
|
|
1287
|
+
const args = [];
|
|
1288
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
1289
|
+
args.push(this.parseExpression());
|
|
1290
|
+
while (this.check("COMMA" /* COMMA */)) {
|
|
1291
|
+
this.advance();
|
|
1292
|
+
args.push(this.parseExpression());
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")` attendu après les arguments');
|
|
1296
|
+
return {
|
|
1297
|
+
type: "FUNCTION_CALL" /* FUNCTION_CALL */,
|
|
1298
|
+
value: nameToken.value,
|
|
1299
|
+
children: args,
|
|
1300
|
+
token: nameToken
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
parseArrayAssignment() {
|
|
1304
|
+
const nameToken = this.advance();
|
|
1305
|
+
this.advance();
|
|
1306
|
+
const index = this.parseExpression();
|
|
1307
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
1308
|
+
this.expect("ASSIGN" /* ASSIGN */, `":=" attendu dans l'affectation`);
|
|
1309
|
+
const value = this.parseExpression();
|
|
1310
|
+
return {
|
|
1311
|
+
type: "ASSIGNMENT" /* ASSIGNMENT */,
|
|
1312
|
+
children: [
|
|
1313
|
+
{ type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: nameToken.value, children: [index], token: nameToken },
|
|
1314
|
+
value
|
|
1315
|
+
],
|
|
1316
|
+
token: nameToken
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
parseReturnStatement() {
|
|
1320
|
+
const token = this.advance();
|
|
1321
|
+
const expr = this.parseExpression();
|
|
1322
|
+
return {
|
|
1323
|
+
type: "RETURN_STATEMENT" /* RETURN_STATEMENT */,
|
|
1324
|
+
children: [expr],
|
|
1325
|
+
token
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
parseParameterList() {
|
|
1329
|
+
this.expect("LEFT_PAREN" /* LEFT_PAREN */, '"(" attendu dans la déclaration');
|
|
1330
|
+
const params = [];
|
|
1331
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
1332
|
+
params.push(this.parseParameter());
|
|
1333
|
+
while (this.check("SEMICOLON" /* SEMICOLON */) || this.check("COMMA" /* COMMA */)) {
|
|
1334
|
+
this.advance();
|
|
1335
|
+
if (this.check("RIGHT_PAREN" /* RIGHT_PAREN */))
|
|
1336
|
+
break;
|
|
1337
|
+
params.push(this.parseParameter());
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")" attendu après les paramètres');
|
|
1341
|
+
return { type: "PARAMETER_LIST" /* PARAMETER_LIST */, children: params };
|
|
1342
|
+
}
|
|
1343
|
+
parseParameter() {
|
|
1344
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de paramètre attendu");
|
|
1345
|
+
this.expect("COLON" /* COLON */, '":" attendu après le nom du paramètre');
|
|
1346
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
1347
|
+
this.advance();
|
|
1348
|
+
let size;
|
|
1349
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
1350
|
+
this.advance();
|
|
1351
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille attendue");
|
|
1352
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu');
|
|
1353
|
+
size = parseInt(sizeToken.value);
|
|
1354
|
+
}
|
|
1355
|
+
this.expect("DE" /* DE */, '"DE" attendu après TABLEAU');
|
|
1356
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments attendu");
|
|
1357
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
1358
|
+
const typeLabel = size !== undefined ? `TABLEAU[${size}] DE ${elemType}` : `TABLEAU DE ${elemType}`;
|
|
1359
|
+
return {
|
|
1360
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
1361
|
+
value: nameToken.value,
|
|
1362
|
+
token: nameToken,
|
|
1363
|
+
symbolInfo: { name: nameToken.value, type: typeLabel, scope: "param" }
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type du paramètre attendu");
|
|
1367
|
+
const paramType = this.tokenTypeToDataType(typeToken.type);
|
|
1368
|
+
return {
|
|
1369
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
1370
|
+
value: nameToken.value,
|
|
1371
|
+
token: nameToken,
|
|
1372
|
+
symbolInfo: { name: nameToken.value, type: paramType, scope: "param" }
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
parseFunctionDeclaration() {
|
|
1376
|
+
const token = this.advance();
|
|
1377
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de fonction attendu");
|
|
1378
|
+
const paramList = this.parseParameterList();
|
|
1379
|
+
this.expect("COLON" /* COLON */, '":" attendu pour le type de retour');
|
|
1380
|
+
const retTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de retour attendu");
|
|
1381
|
+
const retType = this.tokenTypeToDataType(retTypeToken.type);
|
|
1382
|
+
const body = this.parseCompoundStatement();
|
|
1383
|
+
return {
|
|
1384
|
+
type: "FUNCTION_DECLARATION" /* FUNCTION_DECLARATION */,
|
|
1385
|
+
value: nameToken.value,
|
|
1386
|
+
children: [
|
|
1387
|
+
paramList,
|
|
1388
|
+
{ type: "TYPE_SPECIFIER" /* TYPE_SPECIFIER */, value: retType },
|
|
1389
|
+
body
|
|
1390
|
+
],
|
|
1391
|
+
token
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
parseProcedureDeclaration() {
|
|
1395
|
+
const token = this.advance();
|
|
1396
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de procédure attendu");
|
|
1397
|
+
const paramList = this.parseParameterList();
|
|
1398
|
+
const body = this.parseCompoundStatement();
|
|
1399
|
+
return {
|
|
1400
|
+
type: "PROCEDURE_DECLARATION" /* PROCEDURE_DECLARATION */,
|
|
1401
|
+
value: nameToken.value,
|
|
1402
|
+
children: [paramList, body],
|
|
1403
|
+
token
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
982
1406
|
check(type) {
|
|
983
1407
|
if (this.isAtEnd())
|
|
984
1408
|
return false;
|
|
@@ -1127,6 +1551,8 @@ class CodeGenerator {
|
|
|
1127
1551
|
return this.generateBlock(node);
|
|
1128
1552
|
case "VAR_DECLARATION":
|
|
1129
1553
|
return this.generateVariableDeclaration(node);
|
|
1554
|
+
case "ARRAY_DECLARATION":
|
|
1555
|
+
return this.generateArrayDeclaration(node);
|
|
1130
1556
|
case "COMPOUND_STATEMENT":
|
|
1131
1557
|
return this.generateCompoundStatement(node);
|
|
1132
1558
|
case "ASSIGNMENT":
|
|
@@ -1143,6 +1569,12 @@ class CodeGenerator {
|
|
|
1143
1569
|
return this.generateReadStatement(node);
|
|
1144
1570
|
case "WRITE_STATEMENT":
|
|
1145
1571
|
return this.generateWriteStatement(node);
|
|
1572
|
+
case "FUNCTION_DECLARATION":
|
|
1573
|
+
return this.generateFunctionDeclaration(node);
|
|
1574
|
+
case "PROCEDURE_DECLARATION":
|
|
1575
|
+
return this.generateProcedureDeclaration(node);
|
|
1576
|
+
case "RETURN_STATEMENT":
|
|
1577
|
+
return this.generateReturnStatement(node);
|
|
1146
1578
|
case "BINARY_OP":
|
|
1147
1579
|
return this.generateBinaryOp(node);
|
|
1148
1580
|
case "UNARY_OP":
|
|
@@ -1151,6 +1583,10 @@ class CodeGenerator {
|
|
|
1151
1583
|
return this.generateLiteral(node);
|
|
1152
1584
|
case "VARIABLE":
|
|
1153
1585
|
return this.generateVariable(node);
|
|
1586
|
+
case "ARRAY_ACCESS":
|
|
1587
|
+
return this.generateArrayAccess(node);
|
|
1588
|
+
case "FUNCTION_CALL":
|
|
1589
|
+
return this.generateFunctionCall(node);
|
|
1154
1590
|
default:
|
|
1155
1591
|
this.errors.push({
|
|
1156
1592
|
type: "ERROR",
|
|
@@ -1165,14 +1601,25 @@ class CodeGenerator {
|
|
|
1165
1601
|
}
|
|
1166
1602
|
generateProgram(node) {
|
|
1167
1603
|
const lines = [];
|
|
1604
|
+
const block = node.children?.[0];
|
|
1168
1605
|
lines.push(`// Programme: ${node.value}`);
|
|
1606
|
+
if (block?.children) {
|
|
1607
|
+
for (const child of block.children) {
|
|
1608
|
+
if (child.type === "FUNCTION_DECLARATION" || child.type === "PROCEDURE_DECLARATION") {
|
|
1609
|
+
lines.push(this.generateNode(child));
|
|
1610
|
+
lines.push("");
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1169
1614
|
lines.push("async function main() {");
|
|
1170
1615
|
this.indentLevel++;
|
|
1171
|
-
if (
|
|
1172
|
-
for (const child of
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1616
|
+
if (block?.children) {
|
|
1617
|
+
for (const child of block.children) {
|
|
1618
|
+
if (child.type !== "FUNCTION_DECLARATION" && child.type !== "PROCEDURE_DECLARATION") {
|
|
1619
|
+
const childCode = this.generateNode(child);
|
|
1620
|
+
if (childCode) {
|
|
1621
|
+
lines.push(this.indent(childCode));
|
|
1622
|
+
}
|
|
1176
1623
|
}
|
|
1177
1624
|
}
|
|
1178
1625
|
}
|
|
@@ -1210,7 +1657,8 @@ class CodeGenerator {
|
|
|
1210
1657
|
for (const child of node.children) {
|
|
1211
1658
|
const childCode = this.generateNode(child);
|
|
1212
1659
|
if (childCode) {
|
|
1213
|
-
|
|
1660
|
+
const stmt = child.type === "FUNCTION_CALL" ? childCode.replace(/^\(|\)$/g, "") + ";" : childCode;
|
|
1661
|
+
lines.push(this.indent(stmt));
|
|
1214
1662
|
}
|
|
1215
1663
|
}
|
|
1216
1664
|
}
|
|
@@ -1221,15 +1669,107 @@ class CodeGenerator {
|
|
|
1221
1669
|
if (!node.children || node.children.length < 2) {
|
|
1222
1670
|
return "";
|
|
1223
1671
|
}
|
|
1224
|
-
const
|
|
1672
|
+
const target = node.children[0];
|
|
1225
1673
|
const expression = node.children[1];
|
|
1226
|
-
if (!
|
|
1674
|
+
if (!target || !expression) {
|
|
1227
1675
|
return "";
|
|
1228
1676
|
}
|
|
1229
|
-
const variableName = variable.value;
|
|
1230
1677
|
const expressionCode = this.generateNode(expression);
|
|
1678
|
+
if (target.type === "ARRAY_ACCESS") {
|
|
1679
|
+
const arrayName = target.value;
|
|
1680
|
+
const index = this.generateNode(target.children[0]);
|
|
1681
|
+
return `${arrayName}[${index}] = ${expressionCode};`;
|
|
1682
|
+
}
|
|
1683
|
+
const variableName = target.value;
|
|
1231
1684
|
return `${variableName} = ${expressionCode};`;
|
|
1232
1685
|
}
|
|
1686
|
+
generateArrayDeclaration(node) {
|
|
1687
|
+
if (!node.children || node.children.length < 2)
|
|
1688
|
+
return "";
|
|
1689
|
+
const size = node.children[0].value;
|
|
1690
|
+
const elemType = node.value;
|
|
1691
|
+
const defaultValue = elemType === "BOOLEEN" ? "false" : elemType === "CHAINE" ? '""' : "0";
|
|
1692
|
+
const names = node.children.slice(1).map((c) => c.value);
|
|
1693
|
+
return names.map((name) => `let ${name} = new Array(${size}).fill(${defaultValue});`).join(`
|
|
1694
|
+
`);
|
|
1695
|
+
}
|
|
1696
|
+
generateArrayAccess(node) {
|
|
1697
|
+
const name = node.value;
|
|
1698
|
+
const index = this.generateNode(node.children[0]);
|
|
1699
|
+
return `${name}[${index}]`;
|
|
1700
|
+
}
|
|
1701
|
+
generateFunctionCall(node) {
|
|
1702
|
+
const name = node.value;
|
|
1703
|
+
const args = (node.children ?? []).map((c) => {
|
|
1704
|
+
const code = this.generateNode(c);
|
|
1705
|
+
if (c.type === "VARIABLE") {
|
|
1706
|
+
const sym = this.symbolTable.symbols.get(c.value);
|
|
1707
|
+
if (sym?.type?.startsWith("TABLEAU"))
|
|
1708
|
+
return `${code}.slice()`;
|
|
1709
|
+
}
|
|
1710
|
+
return code;
|
|
1711
|
+
});
|
|
1712
|
+
switch (name.toLowerCase()) {
|
|
1713
|
+
case "abs":
|
|
1714
|
+
return `Math.abs(${args[0]})`;
|
|
1715
|
+
case "max":
|
|
1716
|
+
return `Math.max(${args.join(", ")})`;
|
|
1717
|
+
case "min":
|
|
1718
|
+
return `Math.min(${args.join(", ")})`;
|
|
1719
|
+
case "mod":
|
|
1720
|
+
return `(${args[0]} % ${args[1]})`;
|
|
1721
|
+
case "racine_carree":
|
|
1722
|
+
return `Math.sqrt(${args[0]})`;
|
|
1723
|
+
case "taille":
|
|
1724
|
+
return `${args[0]}.length`;
|
|
1725
|
+
case "sous_chaine":
|
|
1726
|
+
return `${args[0]}.substring(${args[1]}, ${args[1]} + ${args[2]})`;
|
|
1727
|
+
case "concat":
|
|
1728
|
+
return args.join(" + ");
|
|
1729
|
+
case "entier_en_reel":
|
|
1730
|
+
return `${args[0]}`;
|
|
1731
|
+
case "reel_en_entier":
|
|
1732
|
+
return `Math.trunc(${args[0]})`;
|
|
1733
|
+
default:
|
|
1734
|
+
return `(await ${name}(${args.join(", ")}))`;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
generateFunctionDeclaration(node) {
|
|
1738
|
+
const name = node.value;
|
|
1739
|
+
const [paramList, , body] = node.children ?? [];
|
|
1740
|
+
const params = this.generateParamList(paramList);
|
|
1741
|
+
const bodyCode = this.generateNode(body);
|
|
1742
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
1743
|
+
this.indentLevel++;
|
|
1744
|
+
lines.push(this.indent(bodyCode));
|
|
1745
|
+
this.indentLevel--;
|
|
1746
|
+
lines.push("}");
|
|
1747
|
+
return lines.join(`
|
|
1748
|
+
`);
|
|
1749
|
+
}
|
|
1750
|
+
generateProcedureDeclaration(node) {
|
|
1751
|
+
const name = node.value;
|
|
1752
|
+
const [paramList, body] = node.children ?? [];
|
|
1753
|
+
const params = this.generateParamList(paramList);
|
|
1754
|
+
const bodyCode = this.generateNode(body);
|
|
1755
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
1756
|
+
this.indentLevel++;
|
|
1757
|
+
lines.push(this.indent(bodyCode));
|
|
1758
|
+
this.indentLevel--;
|
|
1759
|
+
lines.push("}");
|
|
1760
|
+
return lines.join(`
|
|
1761
|
+
`);
|
|
1762
|
+
}
|
|
1763
|
+
generateParamList(paramList) {
|
|
1764
|
+
if (!paramList?.children)
|
|
1765
|
+
return "";
|
|
1766
|
+
return paramList.children.map((p) => p.value).join(", ");
|
|
1767
|
+
}
|
|
1768
|
+
generateReturnStatement(node) {
|
|
1769
|
+
if (!node.children?.[0])
|
|
1770
|
+
return "return;";
|
|
1771
|
+
return `return ${this.generateNode(node.children[0])};`;
|
|
1772
|
+
}
|
|
1233
1773
|
generateIfStatement(node) {
|
|
1234
1774
|
if (!node.children || node.children.length < 2) {
|
|
1235
1775
|
return "";
|
|
@@ -1331,34 +1871,20 @@ class CodeGenerator {
|
|
|
1331
1871
|
return code;
|
|
1332
1872
|
}
|
|
1333
1873
|
generateReadStatement(node) {
|
|
1334
|
-
if (!node.children || node.children.length === 0)
|
|
1874
|
+
if (!node.children || node.children.length === 0)
|
|
1335
1875
|
return "";
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
if (!variable) {
|
|
1876
|
+
const target = node.children[0];
|
|
1877
|
+
if (!target)
|
|
1339
1878
|
return "";
|
|
1879
|
+
const isArray = target.type === "ARRAY_ACCESS";
|
|
1880
|
+
const varName = isArray ? `${target.value}[${this.generateNode(target.children[0])}]` : target.value;
|
|
1881
|
+
const symbolName = target.value;
|
|
1882
|
+
const symbolInfo = this.symbolTable.symbols.get(symbolName);
|
|
1883
|
+
const typeHint = symbolInfo?.type ?? "";
|
|
1884
|
+
if (typeHint.startsWith("ENTIER") || typeHint === "REEL" || typeHint.startsWith("TABLEAU")) {
|
|
1885
|
+
return `${varName} = parseInt(await lire(""));`;
|
|
1340
1886
|
}
|
|
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
|
-
}
|
|
1887
|
+
return `${varName} = await lire("");`;
|
|
1362
1888
|
}
|
|
1363
1889
|
generateWriteStatement(node) {
|
|
1364
1890
|
if (!node.children || node.children.length === 0) {
|
|
@@ -1457,6 +1983,8 @@ class CodeGenerator {
|
|
|
1457
1983
|
return ">";
|
|
1458
1984
|
case ">=":
|
|
1459
1985
|
return ">=";
|
|
1986
|
+
case "%":
|
|
1987
|
+
return "%";
|
|
1460
1988
|
case "et":
|
|
1461
1989
|
return "&&";
|
|
1462
1990
|
case "ou":
|
|
@@ -1480,37 +2008,7 @@ class Lexer2 {
|
|
|
1480
2008
|
keywords;
|
|
1481
2009
|
constructor(source) {
|
|
1482
2010
|
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
|
-
]);
|
|
2011
|
+
this.keywords = new Map(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label, e]) => [label, e.tokenType]));
|
|
1514
2012
|
}
|
|
1515
2013
|
tokenize() {
|
|
1516
2014
|
const tokens = [];
|
|
@@ -1583,6 +2081,8 @@ class Lexer2 {
|
|
|
1583
2081
|
return this.createToken("MULTIPLY" /* MULTIPLY */, this.advance());
|
|
1584
2082
|
case "/":
|
|
1585
2083
|
return this.createToken("DIVIDE" /* DIVIDE */, this.advance());
|
|
2084
|
+
case "%":
|
|
2085
|
+
return this.createToken("MODULO" /* MODULO */, this.advance());
|
|
1586
2086
|
case "=":
|
|
1587
2087
|
return this.createToken("EQUAL" /* EQUAL */, this.advance());
|
|
1588
2088
|
case "<":
|
|
@@ -1601,6 +2101,10 @@ class Lexer2 {
|
|
|
1601
2101
|
return this.createToken("LEFT_PAREN" /* LEFT_PAREN */, this.advance());
|
|
1602
2102
|
case ")":
|
|
1603
2103
|
return this.createToken("RIGHT_PAREN" /* RIGHT_PAREN */, this.advance());
|
|
2104
|
+
case "[":
|
|
2105
|
+
return this.createToken("LEFT_BRACKET" /* LEFT_BRACKET */, this.advance());
|
|
2106
|
+
case "]":
|
|
2107
|
+
return this.createToken("RIGHT_BRACKET" /* RIGHT_BRACKET */, this.advance());
|
|
1604
2108
|
default:
|
|
1605
2109
|
throw new Error(`Caractère non reconnu '${char}' à la ligne ${this.line}, colonne ${this.column}`);
|
|
1606
2110
|
}
|
|
@@ -1798,82 +2302,15 @@ class Parser2 {
|
|
|
1798
2302
|
tokens;
|
|
1799
2303
|
current = 0;
|
|
1800
2304
|
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
|
-
]);
|
|
2305
|
+
reservedKeywords = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label]) => label.toLowerCase()));
|
|
1834
2306
|
constructor(tokens) {
|
|
1835
2307
|
this.tokens = tokens;
|
|
1836
|
-
this.symbolTable = {
|
|
1837
|
-
symbols: new Map,
|
|
1838
|
-
children: [],
|
|
1839
|
-
scopeName: "global"
|
|
1840
|
-
};
|
|
1841
2308
|
}
|
|
1842
2309
|
isReservedKeyword(identifier) {
|
|
1843
2310
|
return this.reservedKeywords.has(identifier.toLowerCase());
|
|
1844
2311
|
}
|
|
1845
2312
|
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);
|
|
2313
|
+
return Object.values(KEYWORDS).some((e) => e.tokenType === tokenType);
|
|
1877
2314
|
}
|
|
1878
2315
|
createReservedKeywordError(identifier, token) {
|
|
1879
2316
|
this.errors.push({
|
|
@@ -1888,30 +2325,22 @@ class Parser2 {
|
|
|
1888
2325
|
});
|
|
1889
2326
|
}
|
|
1890
2327
|
parse() {
|
|
2328
|
+
let ast;
|
|
1891
2329
|
try {
|
|
1892
|
-
|
|
1893
|
-
return {
|
|
1894
|
-
ast: program,
|
|
1895
|
-
errors: this.errors,
|
|
1896
|
-
symbolTable: this.symbolTable
|
|
1897
|
-
};
|
|
2330
|
+
ast = this.parseProgram();
|
|
1898
2331
|
} catch (error) {
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
],
|
|
1912
|
-
symbolTable: this.symbolTable
|
|
1913
|
-
};
|
|
1914
|
-
}
|
|
2332
|
+
ast = { type: "PROGRAM" /* PROGRAM */, children: [] };
|
|
2333
|
+
this.errors.push({
|
|
2334
|
+
type: "ERROR",
|
|
2335
|
+
message: error instanceof Error ? error.message : "Erreur de parsing inconnue",
|
|
2336
|
+
line: 1,
|
|
2337
|
+
column: 1,
|
|
2338
|
+
position: 0,
|
|
2339
|
+
code: "PARSE_ERROR"
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
const { symbolTable, errors: semanticErrors } = new SemanticAnalyzer().analyze(ast);
|
|
2343
|
+
return { ast, errors: [...this.errors, ...semanticErrors], symbolTable };
|
|
1915
2344
|
}
|
|
1916
2345
|
parseProgram() {
|
|
1917
2346
|
const programToken = this.expect("PROGRAMME" /* PROGRAM */, 'Le programme doit commencer par le mot-clé "programme"');
|
|
@@ -1927,17 +2356,25 @@ class Parser2 {
|
|
|
1927
2356
|
}
|
|
1928
2357
|
parseBlock() {
|
|
1929
2358
|
const declarations = this.parseDeclarations();
|
|
2359
|
+
const subprograms = [];
|
|
2360
|
+
while (this.check(["FONCTION" /* FUNCTION */, "PROCEDURE" /* PROCEDURE */])) {
|
|
2361
|
+
if (this.check("FONCTION" /* FUNCTION */)) {
|
|
2362
|
+
subprograms.push(this.parseFunctionDeclaration());
|
|
2363
|
+
} else {
|
|
2364
|
+
subprograms.push(this.parseProcedureDeclaration());
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
1930
2367
|
const compoundStatement = this.parseCompoundStatement();
|
|
1931
2368
|
return {
|
|
1932
2369
|
type: "BLOCK" /* BLOCK */,
|
|
1933
|
-
children: [declarations, compoundStatement]
|
|
2370
|
+
children: [declarations, ...subprograms, compoundStatement]
|
|
1934
2371
|
};
|
|
1935
2372
|
}
|
|
1936
2373
|
parseDeclarations() {
|
|
1937
2374
|
const declarations = [];
|
|
1938
2375
|
while (this.check("VAR" /* VAR */)) {
|
|
1939
2376
|
this.advance();
|
|
1940
|
-
while (!this.check("DEBUT" /* BEGIN */) && !this.isAtEnd()) {
|
|
2377
|
+
while (!this.check("DEBUT" /* BEGIN */) && !this.check("FONCTION" /* FUNCTION */) && !this.check("PROCEDURE" /* PROCEDURE */) && !this.isAtEnd()) {
|
|
1941
2378
|
const declaration = this.parseVariableDeclaration();
|
|
1942
2379
|
declarations.push(declaration);
|
|
1943
2380
|
if (!this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
@@ -1978,31 +2415,27 @@ class Parser2 {
|
|
|
1978
2415
|
}
|
|
1979
2416
|
} while (true);
|
|
1980
2417
|
this.expect("COLON" /* COLON */, "Deux-points attendus après les identificateurs");
|
|
2418
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
2419
|
+
this.advance();
|
|
2420
|
+
this.expect("LEFT_BRACKET" /* LEFT_BRACKET */, '"[" attendu après TABLEAU');
|
|
2421
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille du tableau attendue");
|
|
2422
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu après la taille du tableau');
|
|
2423
|
+
this.expect("DE" /* DE */, '"DE" attendu après la taille du tableau');
|
|
2424
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments du tableau attendu");
|
|
2425
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
2426
|
+
const size = parseInt(sizeToken.value);
|
|
2427
|
+
return {
|
|
2428
|
+
type: "ARRAY_DECLARATION" /* ARRAY_DECLARATION */,
|
|
2429
|
+
value: elemType,
|
|
2430
|
+
children: [
|
|
2431
|
+
{ type: "LITERAL" /* LITERAL */, value: size },
|
|
2432
|
+
...identifiers.map((name) => ({ type: "VARIABLE" /* VARIABLE */, value: name }))
|
|
2433
|
+
],
|
|
2434
|
+
token: elemTypeToken
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
1981
2437
|
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de variable attendu");
|
|
1982
2438
|
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
2439
|
return {
|
|
2007
2440
|
type: "VAR_DECLARATION" /* VAR_DECLARATION */,
|
|
2008
2441
|
value: type,
|
|
@@ -2019,9 +2452,16 @@ class Parser2 {
|
|
|
2019
2452
|
while (!this.check("FIN" /* END */) && !this.isAtEnd()) {
|
|
2020
2453
|
const statement = this.parseStatement();
|
|
2021
2454
|
statements.push(statement);
|
|
2455
|
+
const isBlockStatement = [
|
|
2456
|
+
"IF_STATEMENT" /* IF_STATEMENT */,
|
|
2457
|
+
"WHILE_STATEMENT" /* WHILE_STATEMENT */,
|
|
2458
|
+
"FOR_STATEMENT" /* FOR_STATEMENT */,
|
|
2459
|
+
"REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
2460
|
+
"COMPOUND_STATEMENT" /* COMPOUND_STATEMENT */
|
|
2461
|
+
].includes(statement.type);
|
|
2022
2462
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
2023
2463
|
this.advance();
|
|
2024
|
-
} else if (!this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */)) {
|
|
2464
|
+
} else if (!isBlockStatement && !this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */) && !this.check("FINPOUR" /* ENDFOR */)) {
|
|
2025
2465
|
this.errors.push({
|
|
2026
2466
|
type: "ERROR",
|
|
2027
2467
|
message: "Point-virgule attendu après l'instruction",
|
|
@@ -2064,6 +2504,16 @@ class Parser2 {
|
|
|
2064
2504
|
if (this.check("ECRIRE" /* WRITE */)) {
|
|
2065
2505
|
return this.parseWriteStatement();
|
|
2066
2506
|
}
|
|
2507
|
+
if (this.check("RETOURNER" /* RETURN */)) {
|
|
2508
|
+
return this.parseReturnStatement();
|
|
2509
|
+
}
|
|
2510
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_PAREN" /* LEFT_PAREN */) {
|
|
2511
|
+
const nameToken = this.advance();
|
|
2512
|
+
return this.parseFunctionCall(nameToken);
|
|
2513
|
+
}
|
|
2514
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_BRACKET" /* LEFT_BRACKET */) {
|
|
2515
|
+
return this.parseArrayAssignment();
|
|
2516
|
+
}
|
|
2067
2517
|
return this.parseAssignment();
|
|
2068
2518
|
}
|
|
2069
2519
|
parseIfStatement(isElseIf = false) {
|
|
@@ -2183,14 +2633,23 @@ class Parser2 {
|
|
|
2183
2633
|
parseReadStatement() {
|
|
2184
2634
|
const readToken = this.advance();
|
|
2185
2635
|
this.expect("LEFT_PAREN" /* LEFT_PAREN */, 'Parenthèse ouvrante attendue après "lire"');
|
|
2186
|
-
let
|
|
2636
|
+
let target;
|
|
2187
2637
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
2188
|
-
variable = this.advance();
|
|
2638
|
+
const variable = this.advance();
|
|
2639
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
2640
|
+
this.advance();
|
|
2641
|
+
const index = this.parseExpression();
|
|
2642
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
2643
|
+
target = { type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: variable.value, children: [index], token: variable };
|
|
2644
|
+
} else {
|
|
2645
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
2646
|
+
}
|
|
2189
2647
|
} else {
|
|
2190
2648
|
const currentToken = this.peek();
|
|
2191
2649
|
if (this.isKeywordToken(currentToken.type)) {
|
|
2192
|
-
variable = this.advance();
|
|
2650
|
+
const variable = this.advance();
|
|
2193
2651
|
this.createReservedKeywordError(variable.value, variable);
|
|
2652
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
2194
2653
|
} else {
|
|
2195
2654
|
this.expect("IDENTIFIER" /* IDENTIFIER */, 'Variable attendue dans "lire"');
|
|
2196
2655
|
throw new Error('Variable attendue dans "lire"');
|
|
@@ -2199,9 +2658,7 @@ class Parser2 {
|
|
|
2199
2658
|
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, 'Parenthèse fermante attendue dans "lire"');
|
|
2200
2659
|
return {
|
|
2201
2660
|
type: "READ_STATEMENT" /* READ_STATEMENT */,
|
|
2202
|
-
children: [
|
|
2203
|
-
{ type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable }
|
|
2204
|
-
],
|
|
2661
|
+
children: [target],
|
|
2205
2662
|
token: readToken
|
|
2206
2663
|
};
|
|
2207
2664
|
}
|
|
@@ -2329,7 +2786,7 @@ class Parser2 {
|
|
|
2329
2786
|
}
|
|
2330
2787
|
parseMultiplicativeExpression() {
|
|
2331
2788
|
let left = this.parseUnaryExpression();
|
|
2332
|
-
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */])) {
|
|
2789
|
+
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */, "MODULO" /* MODULO */])) {
|
|
2333
2790
|
const operator = this.advance();
|
|
2334
2791
|
const right = this.parseUnaryExpression();
|
|
2335
2792
|
left = {
|
|
@@ -2378,7 +2835,7 @@ class Parser2 {
|
|
|
2378
2835
|
const token2 = this.advance();
|
|
2379
2836
|
return {
|
|
2380
2837
|
type: "LITERAL" /* LITERAL */,
|
|
2381
|
-
value: token2.value === "vrai",
|
|
2838
|
+
value: token2.value.toLowerCase() === "vrai",
|
|
2382
2839
|
token: token2
|
|
2383
2840
|
};
|
|
2384
2841
|
}
|
|
@@ -2392,6 +2849,20 @@ class Parser2 {
|
|
|
2392
2849
|
}
|
|
2393
2850
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
2394
2851
|
const token2 = this.advance();
|
|
2852
|
+
if (this.check("LEFT_PAREN" /* LEFT_PAREN */)) {
|
|
2853
|
+
return this.parseFunctionCall(token2);
|
|
2854
|
+
}
|
|
2855
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
2856
|
+
this.advance();
|
|
2857
|
+
const index = this.parseExpression();
|
|
2858
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
2859
|
+
return {
|
|
2860
|
+
type: "ARRAY_ACCESS" /* ARRAY_ACCESS */,
|
|
2861
|
+
value: token2.value,
|
|
2862
|
+
children: [index],
|
|
2863
|
+
token: token2
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2395
2866
|
if (this.isReservedKeyword(token2.value)) {
|
|
2396
2867
|
this.createReservedKeywordError(token2.value, token2);
|
|
2397
2868
|
}
|
|
@@ -2435,14 +2906,14 @@ class Parser2 {
|
|
|
2435
2906
|
parseRepeatStatement() {
|
|
2436
2907
|
const repeatToken = this.advance();
|
|
2437
2908
|
const statements = [];
|
|
2438
|
-
while (!this.check("
|
|
2909
|
+
while (!this.check("JUSQU'A" /* UNTIL */) && !this.isAtEnd()) {
|
|
2439
2910
|
const statement = this.parseStatement();
|
|
2440
2911
|
statements.push(statement);
|
|
2441
2912
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
2442
2913
|
this.advance();
|
|
2443
2914
|
}
|
|
2444
2915
|
}
|
|
2445
|
-
this.expect("
|
|
2916
|
+
this.expect("JUSQU'A" /* UNTIL */, `"jusqu'a" attendu à la fin de la boucle repeter`);
|
|
2446
2917
|
const condition = this.parseExpression();
|
|
2447
2918
|
return {
|
|
2448
2919
|
type: "REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
@@ -2450,6 +2921,127 @@ class Parser2 {
|
|
|
2450
2921
|
token: repeatToken
|
|
2451
2922
|
};
|
|
2452
2923
|
}
|
|
2924
|
+
parseFunctionCall(nameToken) {
|
|
2925
|
+
this.advance();
|
|
2926
|
+
const args = [];
|
|
2927
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
2928
|
+
args.push(this.parseExpression());
|
|
2929
|
+
while (this.check("COMMA" /* COMMA */)) {
|
|
2930
|
+
this.advance();
|
|
2931
|
+
args.push(this.parseExpression());
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")` attendu après les arguments');
|
|
2935
|
+
return {
|
|
2936
|
+
type: "FUNCTION_CALL" /* FUNCTION_CALL */,
|
|
2937
|
+
value: nameToken.value,
|
|
2938
|
+
children: args,
|
|
2939
|
+
token: nameToken
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
parseArrayAssignment() {
|
|
2943
|
+
const nameToken = this.advance();
|
|
2944
|
+
this.advance();
|
|
2945
|
+
const index = this.parseExpression();
|
|
2946
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
2947
|
+
this.expect("ASSIGN" /* ASSIGN */, `":=" attendu dans l'affectation`);
|
|
2948
|
+
const value = this.parseExpression();
|
|
2949
|
+
return {
|
|
2950
|
+
type: "ASSIGNMENT" /* ASSIGNMENT */,
|
|
2951
|
+
children: [
|
|
2952
|
+
{ type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: nameToken.value, children: [index], token: nameToken },
|
|
2953
|
+
value
|
|
2954
|
+
],
|
|
2955
|
+
token: nameToken
|
|
2956
|
+
};
|
|
2957
|
+
}
|
|
2958
|
+
parseReturnStatement() {
|
|
2959
|
+
const token = this.advance();
|
|
2960
|
+
const expr = this.parseExpression();
|
|
2961
|
+
return {
|
|
2962
|
+
type: "RETURN_STATEMENT" /* RETURN_STATEMENT */,
|
|
2963
|
+
children: [expr],
|
|
2964
|
+
token
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
parseParameterList() {
|
|
2968
|
+
this.expect("LEFT_PAREN" /* LEFT_PAREN */, '"(" attendu dans la déclaration');
|
|
2969
|
+
const params = [];
|
|
2970
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
2971
|
+
params.push(this.parseParameter());
|
|
2972
|
+
while (this.check("SEMICOLON" /* SEMICOLON */) || this.check("COMMA" /* COMMA */)) {
|
|
2973
|
+
this.advance();
|
|
2974
|
+
if (this.check("RIGHT_PAREN" /* RIGHT_PAREN */))
|
|
2975
|
+
break;
|
|
2976
|
+
params.push(this.parseParameter());
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")" attendu après les paramètres');
|
|
2980
|
+
return { type: "PARAMETER_LIST" /* PARAMETER_LIST */, children: params };
|
|
2981
|
+
}
|
|
2982
|
+
parseParameter() {
|
|
2983
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de paramètre attendu");
|
|
2984
|
+
this.expect("COLON" /* COLON */, '":" attendu après le nom du paramètre');
|
|
2985
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
2986
|
+
this.advance();
|
|
2987
|
+
let size;
|
|
2988
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
2989
|
+
this.advance();
|
|
2990
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille attendue");
|
|
2991
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu');
|
|
2992
|
+
size = parseInt(sizeToken.value);
|
|
2993
|
+
}
|
|
2994
|
+
this.expect("DE" /* DE */, '"DE" attendu après TABLEAU');
|
|
2995
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments attendu");
|
|
2996
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
2997
|
+
const typeLabel = size !== undefined ? `TABLEAU[${size}] DE ${elemType}` : `TABLEAU DE ${elemType}`;
|
|
2998
|
+
return {
|
|
2999
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
3000
|
+
value: nameToken.value,
|
|
3001
|
+
token: nameToken,
|
|
3002
|
+
symbolInfo: { name: nameToken.value, type: typeLabel, scope: "param" }
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type du paramètre attendu");
|
|
3006
|
+
const paramType = this.tokenTypeToDataType(typeToken.type);
|
|
3007
|
+
return {
|
|
3008
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
3009
|
+
value: nameToken.value,
|
|
3010
|
+
token: nameToken,
|
|
3011
|
+
symbolInfo: { name: nameToken.value, type: paramType, scope: "param" }
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
parseFunctionDeclaration() {
|
|
3015
|
+
const token = this.advance();
|
|
3016
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de fonction attendu");
|
|
3017
|
+
const paramList = this.parseParameterList();
|
|
3018
|
+
this.expect("COLON" /* COLON */, '":" attendu pour le type de retour');
|
|
3019
|
+
const retTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de retour attendu");
|
|
3020
|
+
const retType = this.tokenTypeToDataType(retTypeToken.type);
|
|
3021
|
+
const body = this.parseCompoundStatement();
|
|
3022
|
+
return {
|
|
3023
|
+
type: "FUNCTION_DECLARATION" /* FUNCTION_DECLARATION */,
|
|
3024
|
+
value: nameToken.value,
|
|
3025
|
+
children: [
|
|
3026
|
+
paramList,
|
|
3027
|
+
{ type: "TYPE_SPECIFIER" /* TYPE_SPECIFIER */, value: retType },
|
|
3028
|
+
body
|
|
3029
|
+
],
|
|
3030
|
+
token
|
|
3031
|
+
};
|
|
3032
|
+
}
|
|
3033
|
+
parseProcedureDeclaration() {
|
|
3034
|
+
const token = this.advance();
|
|
3035
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de procédure attendu");
|
|
3036
|
+
const paramList = this.parseParameterList();
|
|
3037
|
+
const body = this.parseCompoundStatement();
|
|
3038
|
+
return {
|
|
3039
|
+
type: "PROCEDURE_DECLARATION" /* PROCEDURE_DECLARATION */,
|
|
3040
|
+
value: nameToken.value,
|
|
3041
|
+
children: [paramList, body],
|
|
3042
|
+
token
|
|
3043
|
+
};
|
|
3044
|
+
}
|
|
2453
3045
|
check(type) {
|
|
2454
3046
|
if (this.isAtEnd())
|
|
2455
3047
|
return false;
|
|
@@ -2599,6 +3191,8 @@ class CodeGenerator2 {
|
|
|
2599
3191
|
return this.generateBlock(node);
|
|
2600
3192
|
case "VAR_DECLARATION":
|
|
2601
3193
|
return this.generateVariableDeclaration(node);
|
|
3194
|
+
case "ARRAY_DECLARATION":
|
|
3195
|
+
return this.generateArrayDeclaration(node);
|
|
2602
3196
|
case "COMPOUND_STATEMENT":
|
|
2603
3197
|
return this.generateCompoundStatement(node);
|
|
2604
3198
|
case "ASSIGNMENT":
|
|
@@ -2615,6 +3209,12 @@ class CodeGenerator2 {
|
|
|
2615
3209
|
return this.generateReadStatement(node);
|
|
2616
3210
|
case "WRITE_STATEMENT":
|
|
2617
3211
|
return this.generateWriteStatement(node);
|
|
3212
|
+
case "FUNCTION_DECLARATION":
|
|
3213
|
+
return this.generateFunctionDeclaration(node);
|
|
3214
|
+
case "PROCEDURE_DECLARATION":
|
|
3215
|
+
return this.generateProcedureDeclaration(node);
|
|
3216
|
+
case "RETURN_STATEMENT":
|
|
3217
|
+
return this.generateReturnStatement(node);
|
|
2618
3218
|
case "BINARY_OP":
|
|
2619
3219
|
return this.generateBinaryOp(node);
|
|
2620
3220
|
case "UNARY_OP":
|
|
@@ -2623,6 +3223,10 @@ class CodeGenerator2 {
|
|
|
2623
3223
|
return this.generateLiteral(node);
|
|
2624
3224
|
case "VARIABLE":
|
|
2625
3225
|
return this.generateVariable(node);
|
|
3226
|
+
case "ARRAY_ACCESS":
|
|
3227
|
+
return this.generateArrayAccess(node);
|
|
3228
|
+
case "FUNCTION_CALL":
|
|
3229
|
+
return this.generateFunctionCall(node);
|
|
2626
3230
|
default:
|
|
2627
3231
|
this.errors.push({
|
|
2628
3232
|
type: "ERROR",
|
|
@@ -2637,14 +3241,25 @@ class CodeGenerator2 {
|
|
|
2637
3241
|
}
|
|
2638
3242
|
generateProgram(node) {
|
|
2639
3243
|
const lines = [];
|
|
3244
|
+
const block = node.children?.[0];
|
|
2640
3245
|
lines.push(`// Programme: ${node.value}`);
|
|
3246
|
+
if (block?.children) {
|
|
3247
|
+
for (const child of block.children) {
|
|
3248
|
+
if (child.type === "FUNCTION_DECLARATION" || child.type === "PROCEDURE_DECLARATION") {
|
|
3249
|
+
lines.push(this.generateNode(child));
|
|
3250
|
+
lines.push("");
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
2641
3254
|
lines.push("async function main() {");
|
|
2642
3255
|
this.indentLevel++;
|
|
2643
|
-
if (
|
|
2644
|
-
for (const child of
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
3256
|
+
if (block?.children) {
|
|
3257
|
+
for (const child of block.children) {
|
|
3258
|
+
if (child.type !== "FUNCTION_DECLARATION" && child.type !== "PROCEDURE_DECLARATION") {
|
|
3259
|
+
const childCode = this.generateNode(child);
|
|
3260
|
+
if (childCode) {
|
|
3261
|
+
lines.push(this.indent(childCode));
|
|
3262
|
+
}
|
|
2648
3263
|
}
|
|
2649
3264
|
}
|
|
2650
3265
|
}
|
|
@@ -2682,7 +3297,8 @@ class CodeGenerator2 {
|
|
|
2682
3297
|
for (const child of node.children) {
|
|
2683
3298
|
const childCode = this.generateNode(child);
|
|
2684
3299
|
if (childCode) {
|
|
2685
|
-
|
|
3300
|
+
const stmt = child.type === "FUNCTION_CALL" ? childCode.replace(/^\(|\)$/g, "") + ";" : childCode;
|
|
3301
|
+
lines.push(this.indent(stmt));
|
|
2686
3302
|
}
|
|
2687
3303
|
}
|
|
2688
3304
|
}
|
|
@@ -2693,15 +3309,107 @@ class CodeGenerator2 {
|
|
|
2693
3309
|
if (!node.children || node.children.length < 2) {
|
|
2694
3310
|
return "";
|
|
2695
3311
|
}
|
|
2696
|
-
const
|
|
3312
|
+
const target = node.children[0];
|
|
2697
3313
|
const expression = node.children[1];
|
|
2698
|
-
if (!
|
|
3314
|
+
if (!target || !expression) {
|
|
2699
3315
|
return "";
|
|
2700
3316
|
}
|
|
2701
|
-
const variableName = variable.value;
|
|
2702
3317
|
const expressionCode = this.generateNode(expression);
|
|
3318
|
+
if (target.type === "ARRAY_ACCESS") {
|
|
3319
|
+
const arrayName = target.value;
|
|
3320
|
+
const index = this.generateNode(target.children[0]);
|
|
3321
|
+
return `${arrayName}[${index}] = ${expressionCode};`;
|
|
3322
|
+
}
|
|
3323
|
+
const variableName = target.value;
|
|
2703
3324
|
return `${variableName} = ${expressionCode};`;
|
|
2704
3325
|
}
|
|
3326
|
+
generateArrayDeclaration(node) {
|
|
3327
|
+
if (!node.children || node.children.length < 2)
|
|
3328
|
+
return "";
|
|
3329
|
+
const size = node.children[0].value;
|
|
3330
|
+
const elemType = node.value;
|
|
3331
|
+
const defaultValue = elemType === "BOOLEEN" ? "false" : elemType === "CHAINE" ? '""' : "0";
|
|
3332
|
+
const names = node.children.slice(1).map((c) => c.value);
|
|
3333
|
+
return names.map((name) => `let ${name} = new Array(${size}).fill(${defaultValue});`).join(`
|
|
3334
|
+
`);
|
|
3335
|
+
}
|
|
3336
|
+
generateArrayAccess(node) {
|
|
3337
|
+
const name = node.value;
|
|
3338
|
+
const index = this.generateNode(node.children[0]);
|
|
3339
|
+
return `${name}[${index}]`;
|
|
3340
|
+
}
|
|
3341
|
+
generateFunctionCall(node) {
|
|
3342
|
+
const name = node.value;
|
|
3343
|
+
const args = (node.children ?? []).map((c) => {
|
|
3344
|
+
const code = this.generateNode(c);
|
|
3345
|
+
if (c.type === "VARIABLE") {
|
|
3346
|
+
const sym = this.symbolTable.symbols.get(c.value);
|
|
3347
|
+
if (sym?.type?.startsWith("TABLEAU"))
|
|
3348
|
+
return `${code}.slice()`;
|
|
3349
|
+
}
|
|
3350
|
+
return code;
|
|
3351
|
+
});
|
|
3352
|
+
switch (name.toLowerCase()) {
|
|
3353
|
+
case "abs":
|
|
3354
|
+
return `Math.abs(${args[0]})`;
|
|
3355
|
+
case "max":
|
|
3356
|
+
return `Math.max(${args.join(", ")})`;
|
|
3357
|
+
case "min":
|
|
3358
|
+
return `Math.min(${args.join(", ")})`;
|
|
3359
|
+
case "mod":
|
|
3360
|
+
return `(${args[0]} % ${args[1]})`;
|
|
3361
|
+
case "racine_carree":
|
|
3362
|
+
return `Math.sqrt(${args[0]})`;
|
|
3363
|
+
case "taille":
|
|
3364
|
+
return `${args[0]}.length`;
|
|
3365
|
+
case "sous_chaine":
|
|
3366
|
+
return `${args[0]}.substring(${args[1]}, ${args[1]} + ${args[2]})`;
|
|
3367
|
+
case "concat":
|
|
3368
|
+
return args.join(" + ");
|
|
3369
|
+
case "entier_en_reel":
|
|
3370
|
+
return `${args[0]}`;
|
|
3371
|
+
case "reel_en_entier":
|
|
3372
|
+
return `Math.trunc(${args[0]})`;
|
|
3373
|
+
default:
|
|
3374
|
+
return `(await ${name}(${args.join(", ")}))`;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
generateFunctionDeclaration(node) {
|
|
3378
|
+
const name = node.value;
|
|
3379
|
+
const [paramList, , body] = node.children ?? [];
|
|
3380
|
+
const params = this.generateParamList(paramList);
|
|
3381
|
+
const bodyCode = this.generateNode(body);
|
|
3382
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
3383
|
+
this.indentLevel++;
|
|
3384
|
+
lines.push(this.indent(bodyCode));
|
|
3385
|
+
this.indentLevel--;
|
|
3386
|
+
lines.push("}");
|
|
3387
|
+
return lines.join(`
|
|
3388
|
+
`);
|
|
3389
|
+
}
|
|
3390
|
+
generateProcedureDeclaration(node) {
|
|
3391
|
+
const name = node.value;
|
|
3392
|
+
const [paramList, body] = node.children ?? [];
|
|
3393
|
+
const params = this.generateParamList(paramList);
|
|
3394
|
+
const bodyCode = this.generateNode(body);
|
|
3395
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
3396
|
+
this.indentLevel++;
|
|
3397
|
+
lines.push(this.indent(bodyCode));
|
|
3398
|
+
this.indentLevel--;
|
|
3399
|
+
lines.push("}");
|
|
3400
|
+
return lines.join(`
|
|
3401
|
+
`);
|
|
3402
|
+
}
|
|
3403
|
+
generateParamList(paramList) {
|
|
3404
|
+
if (!paramList?.children)
|
|
3405
|
+
return "";
|
|
3406
|
+
return paramList.children.map((p) => p.value).join(", ");
|
|
3407
|
+
}
|
|
3408
|
+
generateReturnStatement(node) {
|
|
3409
|
+
if (!node.children?.[0])
|
|
3410
|
+
return "return;";
|
|
3411
|
+
return `return ${this.generateNode(node.children[0])};`;
|
|
3412
|
+
}
|
|
2705
3413
|
generateIfStatement(node) {
|
|
2706
3414
|
if (!node.children || node.children.length < 2) {
|
|
2707
3415
|
return "";
|
|
@@ -2803,34 +3511,20 @@ class CodeGenerator2 {
|
|
|
2803
3511
|
return code;
|
|
2804
3512
|
}
|
|
2805
3513
|
generateReadStatement(node) {
|
|
2806
|
-
if (!node.children || node.children.length === 0)
|
|
3514
|
+
if (!node.children || node.children.length === 0)
|
|
2807
3515
|
return "";
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
if (!variable) {
|
|
3516
|
+
const target = node.children[0];
|
|
3517
|
+
if (!target)
|
|
2811
3518
|
return "";
|
|
3519
|
+
const isArray = target.type === "ARRAY_ACCESS";
|
|
3520
|
+
const varName = isArray ? `${target.value}[${this.generateNode(target.children[0])}]` : target.value;
|
|
3521
|
+
const symbolName = target.value;
|
|
3522
|
+
const symbolInfo = this.symbolTable.symbols.get(symbolName);
|
|
3523
|
+
const typeHint = symbolInfo?.type ?? "";
|
|
3524
|
+
if (typeHint.startsWith("ENTIER") || typeHint === "REEL" || typeHint.startsWith("TABLEAU")) {
|
|
3525
|
+
return `${varName} = parseInt(await lire(""));`;
|
|
2812
3526
|
}
|
|
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
|
-
}
|
|
3527
|
+
return `${varName} = await lire("");`;
|
|
2834
3528
|
}
|
|
2835
3529
|
generateWriteStatement(node) {
|
|
2836
3530
|
if (!node.children || node.children.length === 0) {
|
|
@@ -2929,6 +3623,8 @@ class CodeGenerator2 {
|
|
|
2929
3623
|
return ">";
|
|
2930
3624
|
case ">=":
|
|
2931
3625
|
return ">=";
|
|
3626
|
+
case "%":
|
|
3627
|
+
return "%";
|
|
2932
3628
|
case "et":
|
|
2933
3629
|
return "&&";
|
|
2934
3630
|
case "ou":
|
|
@@ -3201,7 +3897,7 @@ var TokenType2;
|
|
|
3201
3897
|
TokenType3["DE"] = "DE";
|
|
3202
3898
|
TokenType3["TO"] = "A";
|
|
3203
3899
|
TokenType3["REPEAT"] = "REPETER";
|
|
3204
|
-
TokenType3["UNTIL"] = "
|
|
3900
|
+
TokenType3["UNTIL"] = "JUSQU'A";
|
|
3205
3901
|
TokenType3["READ"] = "LIRE";
|
|
3206
3902
|
TokenType3["WRITE"] = "ECRIRE";
|
|
3207
3903
|
TokenType3["TRUE"] = "VRAI";
|
|
@@ -3212,10 +3908,15 @@ var TokenType2;
|
|
|
3212
3908
|
TokenType3["ENDFOR"] = "FINPOUR";
|
|
3213
3909
|
TokenType3["ENDIF"] = "FINIF";
|
|
3214
3910
|
TokenType3["ENDWHILE"] = "FINTANTQUE";
|
|
3911
|
+
TokenType3["ARRAY"] = "TABLEAU";
|
|
3912
|
+
TokenType3["FUNCTION"] = "FONCTION";
|
|
3913
|
+
TokenType3["PROCEDURE"] = "PROCEDURE";
|
|
3914
|
+
TokenType3["RETURN"] = "RETOURNER";
|
|
3215
3915
|
TokenType3["PLUS"] = "PLUS";
|
|
3216
3916
|
TokenType3["MINUS"] = "MINUS";
|
|
3217
3917
|
TokenType3["MULTIPLY"] = "MULTIPLY";
|
|
3218
3918
|
TokenType3["DIVIDE"] = "DIVIDE";
|
|
3919
|
+
TokenType3["MODULO"] = "MODULO";
|
|
3219
3920
|
TokenType3["ASSIGN"] = "ASSIGN";
|
|
3220
3921
|
TokenType3["EQUAL"] = "EQUAL";
|
|
3221
3922
|
TokenType3["NOT_EQUAL"] = "NOT_EQUAL";
|
|
@@ -3229,6 +3930,8 @@ var TokenType2;
|
|
|
3229
3930
|
TokenType3["DOT"] = "DOT";
|
|
3230
3931
|
TokenType3["LEFT_PAREN"] = "LEFT_PAREN";
|
|
3231
3932
|
TokenType3["RIGHT_PAREN"] = "RIGHT_PAREN";
|
|
3933
|
+
TokenType3["LEFT_BRACKET"] = "LEFT_BRACKET";
|
|
3934
|
+
TokenType3["RIGHT_BRACKET"] = "RIGHT_BRACKET";
|
|
3232
3935
|
TokenType3["NUMBER"] = "NUMBER";
|
|
3233
3936
|
TokenType3["IDENTIFIER"] = "IDENTIFIER";
|
|
3234
3937
|
TokenType3["STRING_LITERAL"] = "STRING_LITERAL";
|
|
@@ -3253,6 +3956,13 @@ var NodeType2;
|
|
|
3253
3956
|
NodeType3["UNARY_OP"] = "UNARY_OP";
|
|
3254
3957
|
NodeType3["LITERAL"] = "LITERAL";
|
|
3255
3958
|
NodeType3["VARIABLE"] = "VARIABLE";
|
|
3959
|
+
NodeType3["FUNCTION_CALL"] = "FUNCTION_CALL";
|
|
3960
|
+
NodeType3["ARRAY_ACCESS"] = "ARRAY_ACCESS";
|
|
3961
|
+
NodeType3["ARRAY_DECLARATION"] = "ARRAY_DECLARATION";
|
|
3962
|
+
NodeType3["FUNCTION_DECLARATION"] = "FUNCTION_DECLARATION";
|
|
3963
|
+
NodeType3["PROCEDURE_DECLARATION"] = "PROCEDURE_DECLARATION";
|
|
3964
|
+
NodeType3["RETURN_STATEMENT"] = "RETURN_STATEMENT";
|
|
3965
|
+
NodeType3["PARAMETER"] = "PARAMETER";
|
|
3256
3966
|
NodeType3["BLOCK"] = "BLOCK";
|
|
3257
3967
|
NodeType3["PARAMETER_LIST"] = "PARAMETER_LIST";
|
|
3258
3968
|
})(NodeType2 ||= {});
|
|
@@ -3264,12 +3974,373 @@ var DataType2;
|
|
|
3264
3974
|
DataType3["STRING"] = "CHAINE";
|
|
3265
3975
|
DataType3["VOID"] = "VIDE";
|
|
3266
3976
|
})(DataType2 ||= {});
|
|
3977
|
+
// src/keywords.ts
|
|
3978
|
+
var KEYWORDS2 = {
|
|
3979
|
+
PROGRAMME: {
|
|
3980
|
+
tokenType: "PROGRAMME" /* PROGRAM */,
|
|
3981
|
+
kind: "control",
|
|
3982
|
+
detail: "Mot-clé PROGRAMME",
|
|
3983
|
+
documentation: "**PROGRAMME** : Début d'un programme AlgoLang."
|
|
3984
|
+
},
|
|
3985
|
+
DEBUT: {
|
|
3986
|
+
tokenType: "DEBUT" /* BEGIN */,
|
|
3987
|
+
kind: "control",
|
|
3988
|
+
detail: "Mot-clé DEBUT",
|
|
3989
|
+
documentation: "**DEBUT** : Début du bloc d'instructions principal."
|
|
3990
|
+
},
|
|
3991
|
+
FIN: {
|
|
3992
|
+
tokenType: "FIN" /* END */,
|
|
3993
|
+
kind: "control",
|
|
3994
|
+
detail: "Mot-clé FIN",
|
|
3995
|
+
documentation: "**FIN** : Fin du bloc d'instructions ou du programme."
|
|
3996
|
+
},
|
|
3997
|
+
VAR: {
|
|
3998
|
+
tokenType: "VAR" /* VAR */,
|
|
3999
|
+
kind: "declaration",
|
|
4000
|
+
detail: "Mot-clé VAR",
|
|
4001
|
+
documentation: "**VAR** : Section de déclaration des variables."
|
|
4002
|
+
},
|
|
4003
|
+
ENTIER: {
|
|
4004
|
+
tokenType: "ENTIER" /* INTEGER */,
|
|
4005
|
+
kind: "type",
|
|
4006
|
+
detail: "Type ENTIER",
|
|
4007
|
+
documentation: "**ENTIER** : Type de donnée pour les nombres entiers."
|
|
4008
|
+
},
|
|
4009
|
+
REEL: {
|
|
4010
|
+
tokenType: "REEL" /* REAL */,
|
|
4011
|
+
kind: "type",
|
|
4012
|
+
detail: "Type REEL",
|
|
4013
|
+
documentation: "**REEL** : Type de donnée pour les nombres à virgule."
|
|
4014
|
+
},
|
|
4015
|
+
BOOLEEN: {
|
|
4016
|
+
tokenType: "BOOLEEN" /* BOOLEAN */,
|
|
4017
|
+
kind: "type",
|
|
4018
|
+
detail: "Type BOOLEEN",
|
|
4019
|
+
documentation: "**BOOLEEN** : Type de donnée logique (VRAI/FAUX)."
|
|
4020
|
+
},
|
|
4021
|
+
CHAINE: {
|
|
4022
|
+
tokenType: "CHAINE" /* STRING */,
|
|
4023
|
+
kind: "type",
|
|
4024
|
+
detail: "Type CHAINE",
|
|
4025
|
+
documentation: "**CHAINE** : Type de donnée pour le texte."
|
|
4026
|
+
},
|
|
4027
|
+
SI: {
|
|
4028
|
+
tokenType: "SI" /* IF */,
|
|
4029
|
+
kind: "control",
|
|
4030
|
+
detail: "Mot-clé SI",
|
|
4031
|
+
documentation: "**SI** : Structure conditionnelle."
|
|
4032
|
+
},
|
|
4033
|
+
ALORS: {
|
|
4034
|
+
tokenType: "ALORS" /* THEN */,
|
|
4035
|
+
kind: "control",
|
|
4036
|
+
detail: "Mot-clé ALORS",
|
|
4037
|
+
documentation: "**ALORS** : Début du bloc exécuté si la condition est vraie."
|
|
4038
|
+
},
|
|
4039
|
+
SINON: {
|
|
4040
|
+
tokenType: "SINON" /* ELSE */,
|
|
4041
|
+
kind: "control",
|
|
4042
|
+
detail: "Mot-clé SINON",
|
|
4043
|
+
documentation: "**SINON** : Début du bloc exécuté si la condition est fausse."
|
|
4044
|
+
},
|
|
4045
|
+
FINSI: {
|
|
4046
|
+
tokenType: "FINIF" /* ENDIF */,
|
|
4047
|
+
kind: "control",
|
|
4048
|
+
detail: "Mot-clé FINSI",
|
|
4049
|
+
documentation: "**FINSI** : Fin d'une structure conditionnelle."
|
|
4050
|
+
},
|
|
4051
|
+
TANTQUE: {
|
|
4052
|
+
tokenType: "TANTQUE" /* WHILE */,
|
|
4053
|
+
kind: "control",
|
|
4054
|
+
detail: "Mot-clé TANTQUE",
|
|
4055
|
+
documentation: "**TANTQUE** : Boucle répétitive tant qu'une condition est vraie."
|
|
4056
|
+
},
|
|
4057
|
+
FAIRE: {
|
|
4058
|
+
tokenType: "FAIRE" /* DO */,
|
|
4059
|
+
kind: "control",
|
|
4060
|
+
detail: "Mot-clé FAIRE",
|
|
4061
|
+
documentation: "**FAIRE** : Début du corps d'une boucle."
|
|
4062
|
+
},
|
|
4063
|
+
FINTANTQUE: {
|
|
4064
|
+
tokenType: "FINTANTQUE" /* ENDWHILE */,
|
|
4065
|
+
kind: "control",
|
|
4066
|
+
detail: "Mot-clé FINTANTQUE",
|
|
4067
|
+
documentation: "**FINTANTQUE** : Fin d'une boucle TANTQUE."
|
|
4068
|
+
},
|
|
4069
|
+
POUR: {
|
|
4070
|
+
tokenType: "POUR" /* FOR */,
|
|
4071
|
+
kind: "control",
|
|
4072
|
+
detail: "Mot-clé POUR",
|
|
4073
|
+
documentation: "**POUR** : Boucle avec compteur."
|
|
4074
|
+
},
|
|
4075
|
+
ALLANT: {
|
|
4076
|
+
tokenType: "ALLANT" /* ALLANT */,
|
|
4077
|
+
kind: "control",
|
|
4078
|
+
detail: "Mot-clé ALLANT",
|
|
4079
|
+
documentation: "**ALLANT** : Utilisé dans une boucle POUR pour spécifier la plage."
|
|
4080
|
+
},
|
|
4081
|
+
DE: {
|
|
4082
|
+
tokenType: "DE" /* DE */,
|
|
4083
|
+
kind: "control",
|
|
4084
|
+
detail: "Mot-clé DE",
|
|
4085
|
+
documentation: "**DE** : Spécifie le début d'une plage dans une boucle POUR."
|
|
4086
|
+
},
|
|
4087
|
+
A: {
|
|
4088
|
+
tokenType: "A" /* TO */,
|
|
4089
|
+
kind: "control",
|
|
4090
|
+
detail: "Mot-clé A",
|
|
4091
|
+
documentation: "**A** : Spécifie la fin d'une plage dans une boucle POUR."
|
|
4092
|
+
},
|
|
4093
|
+
FINPOUR: {
|
|
4094
|
+
tokenType: "FINPOUR" /* ENDFOR */,
|
|
4095
|
+
kind: "control",
|
|
4096
|
+
detail: "Mot-clé FINPOUR",
|
|
4097
|
+
documentation: "**FINPOUR** : Fin d'une boucle POUR."
|
|
4098
|
+
},
|
|
4099
|
+
REPETER: {
|
|
4100
|
+
tokenType: "REPETER" /* REPEAT */,
|
|
4101
|
+
kind: "control",
|
|
4102
|
+
detail: "Mot-clé REPETER",
|
|
4103
|
+
documentation: "**REPETER** : Boucle exécutée au moins une fois."
|
|
4104
|
+
},
|
|
4105
|
+
"JUSQU'A": {
|
|
4106
|
+
tokenType: "JUSQU'A" /* UNTIL */,
|
|
4107
|
+
kind: "control",
|
|
4108
|
+
detail: "Mot-clé JUSQU'A",
|
|
4109
|
+
documentation: "**JUSQU'A** : Condition de fin d'une boucle REPETER."
|
|
4110
|
+
},
|
|
4111
|
+
LIRE: {
|
|
4112
|
+
tokenType: "LIRE" /* READ */,
|
|
4113
|
+
kind: "io",
|
|
4114
|
+
detail: "Fonction LIRE",
|
|
4115
|
+
documentation: "**LIRE(variable)** : Lit une valeur depuis l'entrée standard."
|
|
4116
|
+
},
|
|
4117
|
+
ECRIRE: {
|
|
4118
|
+
tokenType: "ECRIRE" /* WRITE */,
|
|
4119
|
+
kind: "io",
|
|
4120
|
+
detail: "Fonction ECRIRE",
|
|
4121
|
+
documentation: "**ECRIRE(...)** : Affiche des valeurs dans la console."
|
|
4122
|
+
},
|
|
4123
|
+
VRAI: {
|
|
4124
|
+
tokenType: "VRAI" /* TRUE */,
|
|
4125
|
+
kind: "literal",
|
|
4126
|
+
detail: "Constante VRAI",
|
|
4127
|
+
documentation: "**VRAI** : Valeur booléenne vraie."
|
|
4128
|
+
},
|
|
4129
|
+
FAUX: {
|
|
4130
|
+
tokenType: "FAUX" /* FALSE */,
|
|
4131
|
+
kind: "literal",
|
|
4132
|
+
detail: "Constante FAUX",
|
|
4133
|
+
documentation: "**FAUX** : Valeur booléenne fausse."
|
|
4134
|
+
},
|
|
4135
|
+
ET: {
|
|
4136
|
+
tokenType: "ET" /* AND */,
|
|
4137
|
+
kind: "operator",
|
|
4138
|
+
detail: "Opérateur ET",
|
|
4139
|
+
documentation: "**ET** : Opérateur logique ET (AND)."
|
|
4140
|
+
},
|
|
4141
|
+
OU: {
|
|
4142
|
+
tokenType: "OU" /* OR */,
|
|
4143
|
+
kind: "operator",
|
|
4144
|
+
detail: "Opérateur OU",
|
|
4145
|
+
documentation: "**OU** : Opérateur logique OU (OR)."
|
|
4146
|
+
},
|
|
4147
|
+
NON: {
|
|
4148
|
+
tokenType: "NON" /* NOT */,
|
|
4149
|
+
kind: "operator",
|
|
4150
|
+
detail: "Opérateur NON",
|
|
4151
|
+
documentation: "**NON** : Opérateur logique NON (NOT)."
|
|
4152
|
+
},
|
|
4153
|
+
TABLEAU: {
|
|
4154
|
+
tokenType: "TABLEAU" /* ARRAY */,
|
|
4155
|
+
kind: "declaration",
|
|
4156
|
+
detail: "Type TABLEAU",
|
|
4157
|
+
documentation: "**TABLEAU** : Déclare un tableau de n éléments.\nSyntaxe : `t: TABLEAU[10] DE ENTIER`"
|
|
4158
|
+
},
|
|
4159
|
+
FONCTION: {
|
|
4160
|
+
tokenType: "FONCTION" /* FUNCTION */,
|
|
4161
|
+
kind: "declaration",
|
|
4162
|
+
detail: "Mot-clé FONCTION",
|
|
4163
|
+
documentation: "**FONCTION** nom(params): TYPE : Déclare une fonction qui retourne une valeur.\nExemple : `FONCTION carre(n: ENTIER): ENTIER`"
|
|
4164
|
+
},
|
|
4165
|
+
PROCEDURE: {
|
|
4166
|
+
tokenType: "PROCEDURE" /* PROCEDURE */,
|
|
4167
|
+
kind: "declaration",
|
|
4168
|
+
detail: "Mot-clé PROCEDURE",
|
|
4169
|
+
documentation: "**PROCEDURE** nom(params) : Déclare une procédure (sans valeur de retour).\nExemple : `PROCEDURE afficher(s: CHAINE)`"
|
|
4170
|
+
},
|
|
4171
|
+
RETOURNER: {
|
|
4172
|
+
tokenType: "RETOURNER" /* RETURN */,
|
|
4173
|
+
kind: "declaration",
|
|
4174
|
+
detail: "Mot-clé RETOURNER",
|
|
4175
|
+
documentation: "**RETOURNER** expr : Retourne une valeur depuis une fonction."
|
|
4176
|
+
},
|
|
4177
|
+
abs: {
|
|
4178
|
+
tokenType: null,
|
|
4179
|
+
kind: "builtin-function",
|
|
4180
|
+
detail: "Fonction abs(x)",
|
|
4181
|
+
documentation: "**abs(x)** : Retourne la valeur absolue de x."
|
|
4182
|
+
},
|
|
4183
|
+
max: {
|
|
4184
|
+
tokenType: null,
|
|
4185
|
+
kind: "builtin-function",
|
|
4186
|
+
detail: "Fonction max(a, b)",
|
|
4187
|
+
documentation: "**max(a, b)** : Retourne le plus grand des deux nombres."
|
|
4188
|
+
},
|
|
4189
|
+
min: {
|
|
4190
|
+
tokenType: null,
|
|
4191
|
+
kind: "builtin-function",
|
|
4192
|
+
detail: "Fonction min(a, b)",
|
|
4193
|
+
documentation: "**min(a, b)** : Retourne le plus petit des deux nombres."
|
|
4194
|
+
},
|
|
4195
|
+
mod: {
|
|
4196
|
+
tokenType: null,
|
|
4197
|
+
kind: "builtin-function",
|
|
4198
|
+
detail: "Fonction mod(a, b)",
|
|
4199
|
+
documentation: "**mod(a, b)** : Retourne le reste de la division de a par b. Équivalent à `a % b`."
|
|
4200
|
+
},
|
|
4201
|
+
racine_carree: {
|
|
4202
|
+
tokenType: null,
|
|
4203
|
+
kind: "builtin-function",
|
|
4204
|
+
detail: "Fonction racine_carree(x)",
|
|
4205
|
+
documentation: "**racine_carree(x)** : Retourne la racine carrée de x."
|
|
4206
|
+
},
|
|
4207
|
+
taille: {
|
|
4208
|
+
tokenType: null,
|
|
4209
|
+
kind: "builtin-function",
|
|
4210
|
+
detail: "Fonction taille(x)",
|
|
4211
|
+
documentation: "**taille(x)** : Retourne la taille d'un tableau ou la longueur d'une chaîne."
|
|
4212
|
+
},
|
|
4213
|
+
sous_chaine: {
|
|
4214
|
+
tokenType: null,
|
|
4215
|
+
kind: "builtin-function",
|
|
4216
|
+
detail: "Fonction sous_chaine(s, i, n)",
|
|
4217
|
+
documentation: "**sous_chaine(s, i, n)** : Retourne n caractères de la chaîne s à partir de l'indice i."
|
|
4218
|
+
},
|
|
4219
|
+
concat: {
|
|
4220
|
+
tokenType: null,
|
|
4221
|
+
kind: "builtin-function",
|
|
4222
|
+
detail: "Fonction concat(a, b)",
|
|
4223
|
+
documentation: "**concat(a, b)** : Concatène deux chaînes de caractères."
|
|
4224
|
+
},
|
|
4225
|
+
entier_en_reel: {
|
|
4226
|
+
tokenType: null,
|
|
4227
|
+
kind: "builtin-function",
|
|
4228
|
+
detail: "Fonction entier_en_reel(x)",
|
|
4229
|
+
documentation: "**entier_en_reel(x)** : Convertit un entier en réel."
|
|
4230
|
+
},
|
|
4231
|
+
reel_en_entier: {
|
|
4232
|
+
tokenType: null,
|
|
4233
|
+
kind: "builtin-function",
|
|
4234
|
+
detail: "Fonction reel_en_entier(x)",
|
|
4235
|
+
documentation: "**reel_en_entier(x)** : Convertit un réel en entier (troncature)."
|
|
4236
|
+
}
|
|
4237
|
+
};
|
|
4238
|
+
var BUILTIN_NAMES2 = new Set(Object.entries(KEYWORDS2).filter(([, e]) => e.tokenType === null).map(([label]) => label));
|
|
4239
|
+
// src/semantic/semantic-analyzer.ts
|
|
4240
|
+
class SemanticAnalyzer2 {
|
|
4241
|
+
symbolTable = {
|
|
4242
|
+
symbols: new Map,
|
|
4243
|
+
children: [],
|
|
4244
|
+
scopeName: "global"
|
|
4245
|
+
};
|
|
4246
|
+
errors = [];
|
|
4247
|
+
analyze(ast) {
|
|
4248
|
+
this.walk(ast);
|
|
4249
|
+
return { symbolTable: this.symbolTable, errors: this.errors };
|
|
4250
|
+
}
|
|
4251
|
+
walk(node) {
|
|
4252
|
+
switch (node.type) {
|
|
4253
|
+
case "VAR_DECLARATION" /* VAR_DECLARATION */:
|
|
4254
|
+
this.collectVarDeclaration(node);
|
|
4255
|
+
break;
|
|
4256
|
+
case "ARRAY_DECLARATION" /* ARRAY_DECLARATION */:
|
|
4257
|
+
this.collectArrayDeclaration(node);
|
|
4258
|
+
break;
|
|
4259
|
+
default:
|
|
4260
|
+
node.children?.forEach((child) => this.walk(child));
|
|
4261
|
+
break;
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
collectVarDeclaration(node) {
|
|
4265
|
+
const algoType = node.value;
|
|
4266
|
+
const line = node.token?.line ?? 0;
|
|
4267
|
+
const column = node.token?.column ?? 0;
|
|
4268
|
+
const position = node.token?.position ?? 0;
|
|
4269
|
+
for (const child of node.children ?? []) {
|
|
4270
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
4271
|
+
continue;
|
|
4272
|
+
const name = child.value;
|
|
4273
|
+
if (this.validateName(name, line, column, position)) {
|
|
4274
|
+
this.defineSymbol(name, algoType, line, column);
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
collectArrayDeclaration(node) {
|
|
4279
|
+
const elemType = node.value;
|
|
4280
|
+
const sizeNode = node.children?.[0];
|
|
4281
|
+
const size = sizeNode?.value;
|
|
4282
|
+
const typeLabel = `TABLEAU[${size}] DE ${elemType}`;
|
|
4283
|
+
const line = node.token?.line ?? 0;
|
|
4284
|
+
const column = node.token?.column ?? 0;
|
|
4285
|
+
const position = node.token?.position ?? 0;
|
|
4286
|
+
for (const child of (node.children ?? []).slice(1)) {
|
|
4287
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
4288
|
+
continue;
|
|
4289
|
+
const name = child.value;
|
|
4290
|
+
if (this.validateName(name, line, column, position)) {
|
|
4291
|
+
this.defineSymbol(name, typeLabel, line, column);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
validateName(name, line, column, position) {
|
|
4296
|
+
if (BUILTIN_NAMES.has(name)) {
|
|
4297
|
+
this.errors.push({
|
|
4298
|
+
type: "ERROR",
|
|
4299
|
+
message: `Le nom '${name}' est une fonction intégrée et ne peut pas être utilisé comme nom de variable`,
|
|
4300
|
+
line,
|
|
4301
|
+
column,
|
|
4302
|
+
position,
|
|
4303
|
+
code: "BUILTIN_SHADOWING",
|
|
4304
|
+
explanation: `'${name}' est une fonction intégrée d'AlgoLang`,
|
|
4305
|
+
suggestion: `Choisissez un autre nom, par exemple '${name}Valeur' ou 'mon${name.charAt(0).toUpperCase()}${name.slice(1)}'`
|
|
4306
|
+
});
|
|
4307
|
+
return false;
|
|
4308
|
+
}
|
|
4309
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
4310
|
+
this.errors.push({
|
|
4311
|
+
type: "ERROR",
|
|
4312
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
4313
|
+
line,
|
|
4314
|
+
column,
|
|
4315
|
+
position,
|
|
4316
|
+
code: "DUPLICATE_VARIABLE",
|
|
4317
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
4318
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
4319
|
+
});
|
|
4320
|
+
return false;
|
|
4321
|
+
}
|
|
4322
|
+
return true;
|
|
4323
|
+
}
|
|
4324
|
+
defineSymbol(name, type, line, column) {
|
|
4325
|
+
const info = {
|
|
4326
|
+
name,
|
|
4327
|
+
type,
|
|
4328
|
+
scope: this.symbolTable.scopeName,
|
|
4329
|
+
line,
|
|
4330
|
+
column
|
|
4331
|
+
};
|
|
4332
|
+
this.symbolTable.symbols.set(name, info);
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
3267
4335
|
export {
|
|
3268
4336
|
TokenType2 as TokenType,
|
|
4337
|
+
SemanticAnalyzer2 as SemanticAnalyzer,
|
|
3269
4338
|
Parser,
|
|
3270
4339
|
NodeType2 as NodeType,
|
|
3271
4340
|
Lexer,
|
|
4341
|
+
KEYWORDS2 as KEYWORDS,
|
|
3272
4342
|
DataType2 as DataType,
|
|
3273
4343
|
CodeGenerator,
|
|
4344
|
+
BUILTIN_NAMES2 as BUILTIN_NAMES,
|
|
3274
4345
|
AlgoLangCompiler
|
|
3275
4346
|
};
|