@devalade/algolang 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +709 -192
- package/dist/index.js +1435 -385
- package/package.json +7 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
#!/usr/bin/env bun
|
|
3
2
|
// @bun
|
|
4
3
|
import { createRequire } from "node:module";
|
|
5
4
|
var __create = Object.create;
|
|
@@ -2633,6 +2632,268 @@ var chalk = createChalk();
|
|
|
2633
2632
|
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
2634
2633
|
var source_default = chalk;
|
|
2635
2634
|
|
|
2635
|
+
// src/keywords.ts
|
|
2636
|
+
var KEYWORDS = {
|
|
2637
|
+
PROGRAMME: {
|
|
2638
|
+
tokenType: "PROGRAMME" /* PROGRAM */,
|
|
2639
|
+
kind: "control",
|
|
2640
|
+
detail: "Mot-clé PROGRAMME",
|
|
2641
|
+
documentation: "**PROGRAMME** : Début d'un programme AlgoLang."
|
|
2642
|
+
},
|
|
2643
|
+
DEBUT: {
|
|
2644
|
+
tokenType: "DEBUT" /* BEGIN */,
|
|
2645
|
+
kind: "control",
|
|
2646
|
+
detail: "Mot-clé DEBUT",
|
|
2647
|
+
documentation: "**DEBUT** : Début du bloc d'instructions principal."
|
|
2648
|
+
},
|
|
2649
|
+
FIN: {
|
|
2650
|
+
tokenType: "FIN" /* END */,
|
|
2651
|
+
kind: "control",
|
|
2652
|
+
detail: "Mot-clé FIN",
|
|
2653
|
+
documentation: "**FIN** : Fin du bloc d'instructions ou du programme."
|
|
2654
|
+
},
|
|
2655
|
+
VAR: {
|
|
2656
|
+
tokenType: "VAR" /* VAR */,
|
|
2657
|
+
kind: "declaration",
|
|
2658
|
+
detail: "Mot-clé VAR",
|
|
2659
|
+
documentation: "**VAR** : Section de déclaration des variables."
|
|
2660
|
+
},
|
|
2661
|
+
ENTIER: {
|
|
2662
|
+
tokenType: "ENTIER" /* INTEGER */,
|
|
2663
|
+
kind: "type",
|
|
2664
|
+
detail: "Type ENTIER",
|
|
2665
|
+
documentation: "**ENTIER** : Type de donnée pour les nombres entiers."
|
|
2666
|
+
},
|
|
2667
|
+
REEL: {
|
|
2668
|
+
tokenType: "REEL" /* REAL */,
|
|
2669
|
+
kind: "type",
|
|
2670
|
+
detail: "Type REEL",
|
|
2671
|
+
documentation: "**REEL** : Type de donnée pour les nombres à virgule."
|
|
2672
|
+
},
|
|
2673
|
+
BOOLEEN: {
|
|
2674
|
+
tokenType: "BOOLEEN" /* BOOLEAN */,
|
|
2675
|
+
kind: "type",
|
|
2676
|
+
detail: "Type BOOLEEN",
|
|
2677
|
+
documentation: "**BOOLEEN** : Type de donnée logique (VRAI/FAUX)."
|
|
2678
|
+
},
|
|
2679
|
+
CHAINE: {
|
|
2680
|
+
tokenType: "CHAINE" /* STRING */,
|
|
2681
|
+
kind: "type",
|
|
2682
|
+
detail: "Type CHAINE",
|
|
2683
|
+
documentation: "**CHAINE** : Type de donnée pour le texte."
|
|
2684
|
+
},
|
|
2685
|
+
SI: {
|
|
2686
|
+
tokenType: "SI" /* IF */,
|
|
2687
|
+
kind: "control",
|
|
2688
|
+
detail: "Mot-clé SI",
|
|
2689
|
+
documentation: "**SI** : Structure conditionnelle."
|
|
2690
|
+
},
|
|
2691
|
+
ALORS: {
|
|
2692
|
+
tokenType: "ALORS" /* THEN */,
|
|
2693
|
+
kind: "control",
|
|
2694
|
+
detail: "Mot-clé ALORS",
|
|
2695
|
+
documentation: "**ALORS** : Début du bloc exécuté si la condition est vraie."
|
|
2696
|
+
},
|
|
2697
|
+
SINON: {
|
|
2698
|
+
tokenType: "SINON" /* ELSE */,
|
|
2699
|
+
kind: "control",
|
|
2700
|
+
detail: "Mot-clé SINON",
|
|
2701
|
+
documentation: "**SINON** : Début du bloc exécuté si la condition est fausse."
|
|
2702
|
+
},
|
|
2703
|
+
FINSI: {
|
|
2704
|
+
tokenType: "FINIF" /* ENDIF */,
|
|
2705
|
+
kind: "control",
|
|
2706
|
+
detail: "Mot-clé FINSI",
|
|
2707
|
+
documentation: "**FINSI** : Fin d'une structure conditionnelle."
|
|
2708
|
+
},
|
|
2709
|
+
TANTQUE: {
|
|
2710
|
+
tokenType: "TANTQUE" /* WHILE */,
|
|
2711
|
+
kind: "control",
|
|
2712
|
+
detail: "Mot-clé TANTQUE",
|
|
2713
|
+
documentation: "**TANTQUE** : Boucle répétitive tant qu'une condition est vraie."
|
|
2714
|
+
},
|
|
2715
|
+
FAIRE: {
|
|
2716
|
+
tokenType: "FAIRE" /* DO */,
|
|
2717
|
+
kind: "control",
|
|
2718
|
+
detail: "Mot-clé FAIRE",
|
|
2719
|
+
documentation: "**FAIRE** : Début du corps d'une boucle."
|
|
2720
|
+
},
|
|
2721
|
+
FINTANTQUE: {
|
|
2722
|
+
tokenType: "FINTANTQUE" /* ENDWHILE */,
|
|
2723
|
+
kind: "control",
|
|
2724
|
+
detail: "Mot-clé FINTANTQUE",
|
|
2725
|
+
documentation: "**FINTANTQUE** : Fin d'une boucle TANTQUE."
|
|
2726
|
+
},
|
|
2727
|
+
POUR: {
|
|
2728
|
+
tokenType: "POUR" /* FOR */,
|
|
2729
|
+
kind: "control",
|
|
2730
|
+
detail: "Mot-clé POUR",
|
|
2731
|
+
documentation: "**POUR** : Boucle avec compteur."
|
|
2732
|
+
},
|
|
2733
|
+
ALLANT: {
|
|
2734
|
+
tokenType: "ALLANT" /* ALLANT */,
|
|
2735
|
+
kind: "control",
|
|
2736
|
+
detail: "Mot-clé ALLANT",
|
|
2737
|
+
documentation: "**ALLANT** : Utilisé dans une boucle POUR pour spécifier la plage."
|
|
2738
|
+
},
|
|
2739
|
+
DE: {
|
|
2740
|
+
tokenType: "DE" /* DE */,
|
|
2741
|
+
kind: "control",
|
|
2742
|
+
detail: "Mot-clé DE",
|
|
2743
|
+
documentation: "**DE** : Spécifie le début d'une plage dans une boucle POUR."
|
|
2744
|
+
},
|
|
2745
|
+
A: {
|
|
2746
|
+
tokenType: "A" /* TO */,
|
|
2747
|
+
kind: "control",
|
|
2748
|
+
detail: "Mot-clé A",
|
|
2749
|
+
documentation: "**A** : Spécifie la fin d'une plage dans une boucle POUR."
|
|
2750
|
+
},
|
|
2751
|
+
FINPOUR: {
|
|
2752
|
+
tokenType: "FINPOUR" /* ENDFOR */,
|
|
2753
|
+
kind: "control",
|
|
2754
|
+
detail: "Mot-clé FINPOUR",
|
|
2755
|
+
documentation: "**FINPOUR** : Fin d'une boucle POUR."
|
|
2756
|
+
},
|
|
2757
|
+
REPETER: {
|
|
2758
|
+
tokenType: "REPETER" /* REPEAT */,
|
|
2759
|
+
kind: "control",
|
|
2760
|
+
detail: "Mot-clé REPETER",
|
|
2761
|
+
documentation: "**REPETER** : Boucle exécutée au moins une fois."
|
|
2762
|
+
},
|
|
2763
|
+
"JUSQU'A": {
|
|
2764
|
+
tokenType: "JUSQU'A" /* UNTIL */,
|
|
2765
|
+
kind: "control",
|
|
2766
|
+
detail: "Mot-clé JUSQU'A",
|
|
2767
|
+
documentation: "**JUSQU'A** : Condition de fin d'une boucle REPETER."
|
|
2768
|
+
},
|
|
2769
|
+
LIRE: {
|
|
2770
|
+
tokenType: "LIRE" /* READ */,
|
|
2771
|
+
kind: "io",
|
|
2772
|
+
detail: "Fonction LIRE",
|
|
2773
|
+
documentation: "**LIRE(variable)** : Lit une valeur depuis l'entrée standard."
|
|
2774
|
+
},
|
|
2775
|
+
ECRIRE: {
|
|
2776
|
+
tokenType: "ECRIRE" /* WRITE */,
|
|
2777
|
+
kind: "io",
|
|
2778
|
+
detail: "Fonction ECRIRE",
|
|
2779
|
+
documentation: "**ECRIRE(...)** : Affiche des valeurs dans la console."
|
|
2780
|
+
},
|
|
2781
|
+
VRAI: {
|
|
2782
|
+
tokenType: "VRAI" /* TRUE */,
|
|
2783
|
+
kind: "literal",
|
|
2784
|
+
detail: "Constante VRAI",
|
|
2785
|
+
documentation: "**VRAI** : Valeur booléenne vraie."
|
|
2786
|
+
},
|
|
2787
|
+
FAUX: {
|
|
2788
|
+
tokenType: "FAUX" /* FALSE */,
|
|
2789
|
+
kind: "literal",
|
|
2790
|
+
detail: "Constante FAUX",
|
|
2791
|
+
documentation: "**FAUX** : Valeur booléenne fausse."
|
|
2792
|
+
},
|
|
2793
|
+
ET: {
|
|
2794
|
+
tokenType: "ET" /* AND */,
|
|
2795
|
+
kind: "operator",
|
|
2796
|
+
detail: "Opérateur ET",
|
|
2797
|
+
documentation: "**ET** : Opérateur logique ET (AND)."
|
|
2798
|
+
},
|
|
2799
|
+
OU: {
|
|
2800
|
+
tokenType: "OU" /* OR */,
|
|
2801
|
+
kind: "operator",
|
|
2802
|
+
detail: "Opérateur OU",
|
|
2803
|
+
documentation: "**OU** : Opérateur logique OU (OR)."
|
|
2804
|
+
},
|
|
2805
|
+
NON: {
|
|
2806
|
+
tokenType: "NON" /* NOT */,
|
|
2807
|
+
kind: "operator",
|
|
2808
|
+
detail: "Opérateur NON",
|
|
2809
|
+
documentation: "**NON** : Opérateur logique NON (NOT)."
|
|
2810
|
+
},
|
|
2811
|
+
TABLEAU: {
|
|
2812
|
+
tokenType: "TABLEAU" /* ARRAY */,
|
|
2813
|
+
kind: "declaration",
|
|
2814
|
+
detail: "Type TABLEAU",
|
|
2815
|
+
documentation: "**TABLEAU** : Déclare un tableau de n éléments.\nSyntaxe : `t: TABLEAU[10] DE ENTIER`"
|
|
2816
|
+
},
|
|
2817
|
+
FONCTION: {
|
|
2818
|
+
tokenType: "FONCTION" /* FUNCTION */,
|
|
2819
|
+
kind: "declaration",
|
|
2820
|
+
detail: "Mot-clé FONCTION",
|
|
2821
|
+
documentation: "**FONCTION** nom(params): TYPE : Déclare une fonction qui retourne une valeur.\nExemple : `FONCTION carre(n: ENTIER): ENTIER`"
|
|
2822
|
+
},
|
|
2823
|
+
PROCEDURE: {
|
|
2824
|
+
tokenType: "PROCEDURE" /* PROCEDURE */,
|
|
2825
|
+
kind: "declaration",
|
|
2826
|
+
detail: "Mot-clé PROCEDURE",
|
|
2827
|
+
documentation: "**PROCEDURE** nom(params) : Déclare une procédure (sans valeur de retour).\nExemple : `PROCEDURE afficher(s: CHAINE)`"
|
|
2828
|
+
},
|
|
2829
|
+
RETOURNER: {
|
|
2830
|
+
tokenType: "RETOURNER" /* RETURN */,
|
|
2831
|
+
kind: "declaration",
|
|
2832
|
+
detail: "Mot-clé RETOURNER",
|
|
2833
|
+
documentation: "**RETOURNER** expr : Retourne une valeur depuis une fonction."
|
|
2834
|
+
},
|
|
2835
|
+
abs: {
|
|
2836
|
+
tokenType: null,
|
|
2837
|
+
kind: "builtin-function",
|
|
2838
|
+
detail: "Fonction abs(x)",
|
|
2839
|
+
documentation: "**abs(x)** : Retourne la valeur absolue de x."
|
|
2840
|
+
},
|
|
2841
|
+
max: {
|
|
2842
|
+
tokenType: null,
|
|
2843
|
+
kind: "builtin-function",
|
|
2844
|
+
detail: "Fonction max(a, b)",
|
|
2845
|
+
documentation: "**max(a, b)** : Retourne le plus grand des deux nombres."
|
|
2846
|
+
},
|
|
2847
|
+
min: {
|
|
2848
|
+
tokenType: null,
|
|
2849
|
+
kind: "builtin-function",
|
|
2850
|
+
detail: "Fonction min(a, b)",
|
|
2851
|
+
documentation: "**min(a, b)** : Retourne le plus petit des deux nombres."
|
|
2852
|
+
},
|
|
2853
|
+
mod: {
|
|
2854
|
+
tokenType: null,
|
|
2855
|
+
kind: "builtin-function",
|
|
2856
|
+
detail: "Fonction mod(a, b)",
|
|
2857
|
+
documentation: "**mod(a, b)** : Retourne le reste de la division de a par b. Équivalent à `a % b`."
|
|
2858
|
+
},
|
|
2859
|
+
racine_carree: {
|
|
2860
|
+
tokenType: null,
|
|
2861
|
+
kind: "builtin-function",
|
|
2862
|
+
detail: "Fonction racine_carree(x)",
|
|
2863
|
+
documentation: "**racine_carree(x)** : Retourne la racine carrée de x."
|
|
2864
|
+
},
|
|
2865
|
+
taille: {
|
|
2866
|
+
tokenType: null,
|
|
2867
|
+
kind: "builtin-function",
|
|
2868
|
+
detail: "Fonction taille(x)",
|
|
2869
|
+
documentation: "**taille(x)** : Retourne la taille d'un tableau ou la longueur d'une chaîne."
|
|
2870
|
+
},
|
|
2871
|
+
sous_chaine: {
|
|
2872
|
+
tokenType: null,
|
|
2873
|
+
kind: "builtin-function",
|
|
2874
|
+
detail: "Fonction sous_chaine(s, i, n)",
|
|
2875
|
+
documentation: "**sous_chaine(s, i, n)** : Retourne n caractères de la chaîne s à partir de l'indice i."
|
|
2876
|
+
},
|
|
2877
|
+
concat: {
|
|
2878
|
+
tokenType: null,
|
|
2879
|
+
kind: "builtin-function",
|
|
2880
|
+
detail: "Fonction concat(a, b)",
|
|
2881
|
+
documentation: "**concat(a, b)** : Concatène deux chaînes de caractères."
|
|
2882
|
+
},
|
|
2883
|
+
entier_en_reel: {
|
|
2884
|
+
tokenType: null,
|
|
2885
|
+
kind: "builtin-function",
|
|
2886
|
+
detail: "Fonction entier_en_reel(x)",
|
|
2887
|
+
documentation: "**entier_en_reel(x)** : Convertit un entier en réel."
|
|
2888
|
+
},
|
|
2889
|
+
reel_en_entier: {
|
|
2890
|
+
tokenType: null,
|
|
2891
|
+
kind: "builtin-function",
|
|
2892
|
+
detail: "Fonction reel_en_entier(x)",
|
|
2893
|
+
documentation: "**reel_en_entier(x)** : Convertit un réel en entier (troncature)."
|
|
2894
|
+
}
|
|
2895
|
+
};
|
|
2896
|
+
|
|
2636
2897
|
// src/lexer/lexer.ts
|
|
2637
2898
|
class Lexer {
|
|
2638
2899
|
source;
|
|
@@ -2642,37 +2903,7 @@ class Lexer {
|
|
|
2642
2903
|
keywords;
|
|
2643
2904
|
constructor(source) {
|
|
2644
2905
|
this.source = source;
|
|
2645
|
-
this.keywords = new Map([
|
|
2646
|
-
["PROGRAMME", "PROGRAMME" /* PROGRAM */],
|
|
2647
|
-
["DEBUT", "DEBUT" /* BEGIN */],
|
|
2648
|
-
["FIN", "FIN" /* END */],
|
|
2649
|
-
["VAR", "VAR" /* VAR */],
|
|
2650
|
-
["ENTIER", "ENTIER" /* INTEGER */],
|
|
2651
|
-
["REEL", "REEL" /* REAL */],
|
|
2652
|
-
["BOOLEEN", "BOOLEEN" /* BOOLEAN */],
|
|
2653
|
-
["CHAINE", "CHAINE" /* STRING */],
|
|
2654
|
-
["SI", "SI" /* IF */],
|
|
2655
|
-
["ALORS", "ALORS" /* THEN */],
|
|
2656
|
-
["SINON", "SINON" /* ELSE */],
|
|
2657
|
-
["FINSI", "FINIF" /* ENDIF */],
|
|
2658
|
-
["TANTQUE", "TANTQUE" /* WHILE */],
|
|
2659
|
-
["FAIRE", "FAIRE" /* DO */],
|
|
2660
|
-
["POUR", "POUR" /* FOR */],
|
|
2661
|
-
["ALLANT", "ALLANT" /* ALLANT */],
|
|
2662
|
-
["DE", "DE" /* DE */],
|
|
2663
|
-
["A", "A" /* TO */],
|
|
2664
|
-
["REPETER", "REPETER" /* REPEAT */],
|
|
2665
|
-
["JUSQUA", "JUSQUA" /* UNTIL */],
|
|
2666
|
-
["LIRE", "LIRE" /* READ */],
|
|
2667
|
-
["ECRIRE", "ECRIRE" /* WRITE */],
|
|
2668
|
-
["VRAI", "VRAI" /* TRUE */],
|
|
2669
|
-
["FAUX", "FAUX" /* FALSE */],
|
|
2670
|
-
["ET", "ET" /* AND */],
|
|
2671
|
-
["OU", "OU" /* OR */],
|
|
2672
|
-
["NON", "NON" /* NOT */],
|
|
2673
|
-
["FINPOUR", "FINPOUR" /* ENDFOR */],
|
|
2674
|
-
["FINTANTQUE", "FINTANTQUE" /* ENDWHILE */]
|
|
2675
|
-
]);
|
|
2906
|
+
this.keywords = new Map(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label, e]) => [label, e.tokenType]));
|
|
2676
2907
|
}
|
|
2677
2908
|
tokenize() {
|
|
2678
2909
|
const tokens = [];
|
|
@@ -2745,6 +2976,8 @@ class Lexer {
|
|
|
2745
2976
|
return this.createToken("MULTIPLY" /* MULTIPLY */, this.advance());
|
|
2746
2977
|
case "/":
|
|
2747
2978
|
return this.createToken("DIVIDE" /* DIVIDE */, this.advance());
|
|
2979
|
+
case "%":
|
|
2980
|
+
return this.createToken("MODULO" /* MODULO */, this.advance());
|
|
2748
2981
|
case "=":
|
|
2749
2982
|
return this.createToken("EQUAL" /* EQUAL */, this.advance());
|
|
2750
2983
|
case "<":
|
|
@@ -2763,6 +2996,10 @@ class Lexer {
|
|
|
2763
2996
|
return this.createToken("LEFT_PAREN" /* LEFT_PAREN */, this.advance());
|
|
2764
2997
|
case ")":
|
|
2765
2998
|
return this.createToken("RIGHT_PAREN" /* RIGHT_PAREN */, this.advance());
|
|
2999
|
+
case "[":
|
|
3000
|
+
return this.createToken("LEFT_BRACKET" /* LEFT_BRACKET */, this.advance());
|
|
3001
|
+
case "]":
|
|
3002
|
+
return this.createToken("RIGHT_BRACKET" /* RIGHT_BRACKET */, this.advance());
|
|
2766
3003
|
default:
|
|
2767
3004
|
throw new Error(`Caractère non reconnu '${char}' à la ligne ${this.line}, colonne ${this.column}`);
|
|
2768
3005
|
}
|
|
@@ -2955,87 +3192,108 @@ class Lexer {
|
|
|
2955
3192
|
}
|
|
2956
3193
|
}
|
|
2957
3194
|
|
|
3195
|
+
// src/semantic/semantic-analyzer.ts
|
|
3196
|
+
class SemanticAnalyzer {
|
|
3197
|
+
symbolTable = {
|
|
3198
|
+
symbols: new Map,
|
|
3199
|
+
children: [],
|
|
3200
|
+
scopeName: "global"
|
|
3201
|
+
};
|
|
3202
|
+
errors = [];
|
|
3203
|
+
analyze(ast) {
|
|
3204
|
+
this.walk(ast);
|
|
3205
|
+
return { symbolTable: this.symbolTable, errors: this.errors };
|
|
3206
|
+
}
|
|
3207
|
+
walk(node) {
|
|
3208
|
+
switch (node.type) {
|
|
3209
|
+
case "VAR_DECLARATION" /* VAR_DECLARATION */:
|
|
3210
|
+
this.collectVarDeclaration(node);
|
|
3211
|
+
break;
|
|
3212
|
+
case "ARRAY_DECLARATION" /* ARRAY_DECLARATION */:
|
|
3213
|
+
this.collectArrayDeclaration(node);
|
|
3214
|
+
break;
|
|
3215
|
+
default:
|
|
3216
|
+
node.children?.forEach((child) => this.walk(child));
|
|
3217
|
+
break;
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
collectVarDeclaration(node) {
|
|
3221
|
+
const algoType = node.value;
|
|
3222
|
+
const line = node.token?.line ?? 0;
|
|
3223
|
+
const column = node.token?.column ?? 0;
|
|
3224
|
+
for (const child of node.children ?? []) {
|
|
3225
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
3226
|
+
continue;
|
|
3227
|
+
const name = child.value;
|
|
3228
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
3229
|
+
this.errors.push({
|
|
3230
|
+
type: "ERROR",
|
|
3231
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
3232
|
+
line,
|
|
3233
|
+
column,
|
|
3234
|
+
position: node.token?.position ?? 0,
|
|
3235
|
+
code: "DUPLICATE_VARIABLE",
|
|
3236
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
3237
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
3238
|
+
});
|
|
3239
|
+
} else {
|
|
3240
|
+
this.defineSymbol(name, algoType, line, column);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
collectArrayDeclaration(node) {
|
|
3245
|
+
const elemType = node.value;
|
|
3246
|
+
const sizeNode = node.children?.[0];
|
|
3247
|
+
const size = sizeNode?.value;
|
|
3248
|
+
const typeLabel = `TABLEAU[${size}] DE ${elemType}`;
|
|
3249
|
+
const line = node.token?.line ?? 0;
|
|
3250
|
+
const column = node.token?.column ?? 0;
|
|
3251
|
+
for (const child of (node.children ?? []).slice(1)) {
|
|
3252
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
3253
|
+
continue;
|
|
3254
|
+
const name = child.value;
|
|
3255
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
3256
|
+
this.errors.push({
|
|
3257
|
+
type: "ERROR",
|
|
3258
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
3259
|
+
line,
|
|
3260
|
+
column,
|
|
3261
|
+
position: node.token?.position ?? 0,
|
|
3262
|
+
code: "DUPLICATE_VARIABLE",
|
|
3263
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
3264
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
3265
|
+
});
|
|
3266
|
+
} else {
|
|
3267
|
+
this.defineSymbol(name, typeLabel, line, column);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
defineSymbol(name, type, line, column) {
|
|
3272
|
+
const info = {
|
|
3273
|
+
name,
|
|
3274
|
+
type,
|
|
3275
|
+
scope: this.symbolTable.scopeName,
|
|
3276
|
+
line,
|
|
3277
|
+
column
|
|
3278
|
+
};
|
|
3279
|
+
this.symbolTable.symbols.set(name, info);
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
|
|
2958
3283
|
// src/parser/parser.ts
|
|
2959
3284
|
class Parser {
|
|
2960
3285
|
tokens;
|
|
2961
3286
|
current = 0;
|
|
2962
3287
|
errors = [];
|
|
2963
|
-
|
|
2964
|
-
reservedKeywords = new Set([
|
|
2965
|
-
"programme",
|
|
2966
|
-
"debut",
|
|
2967
|
-
"fin",
|
|
2968
|
-
"var",
|
|
2969
|
-
"entier",
|
|
2970
|
-
"reel",
|
|
2971
|
-
"booleen",
|
|
2972
|
-
"chaine",
|
|
2973
|
-
"si",
|
|
2974
|
-
"alors",
|
|
2975
|
-
"sinon",
|
|
2976
|
-
"finsi",
|
|
2977
|
-
"tantque",
|
|
2978
|
-
"faire",
|
|
2979
|
-
"fintantque",
|
|
2980
|
-
"pour",
|
|
2981
|
-
"a",
|
|
2982
|
-
"finpour",
|
|
2983
|
-
"repeter",
|
|
2984
|
-
"jusqua",
|
|
2985
|
-
"lire",
|
|
2986
|
-
"ecrire",
|
|
2987
|
-
"vrai",
|
|
2988
|
-
"faux",
|
|
2989
|
-
"et",
|
|
2990
|
-
"ou",
|
|
2991
|
-
"non",
|
|
2992
|
-
"fonction",
|
|
2993
|
-
"procedure",
|
|
2994
|
-
"retourner"
|
|
2995
|
-
]);
|
|
3288
|
+
reservedKeywords = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label]) => label.toLowerCase()));
|
|
2996
3289
|
constructor(tokens) {
|
|
2997
3290
|
this.tokens = tokens;
|
|
2998
|
-
this.symbolTable = {
|
|
2999
|
-
symbols: new Map,
|
|
3000
|
-
children: [],
|
|
3001
|
-
scopeName: "global"
|
|
3002
|
-
};
|
|
3003
3291
|
}
|
|
3004
3292
|
isReservedKeyword(identifier) {
|
|
3005
3293
|
return this.reservedKeywords.has(identifier.toLowerCase());
|
|
3006
3294
|
}
|
|
3007
3295
|
isKeywordToken(tokenType) {
|
|
3008
|
-
return
|
|
3009
|
-
"PROGRAMME" /* PROGRAM */,
|
|
3010
|
-
"DEBUT" /* BEGIN */,
|
|
3011
|
-
"FIN" /* END */,
|
|
3012
|
-
"VAR" /* VAR */,
|
|
3013
|
-
"ENTIER" /* INTEGER */,
|
|
3014
|
-
"REEL" /* REAL */,
|
|
3015
|
-
"BOOLEEN" /* BOOLEAN */,
|
|
3016
|
-
"CHAINE" /* STRING */,
|
|
3017
|
-
"SI" /* IF */,
|
|
3018
|
-
"ALORS" /* THEN */,
|
|
3019
|
-
"SINON" /* ELSE */,
|
|
3020
|
-
"TANTQUE" /* WHILE */,
|
|
3021
|
-
"FAIRE" /* DO */,
|
|
3022
|
-
"POUR" /* FOR */,
|
|
3023
|
-
"ALLANT" /* ALLANT */,
|
|
3024
|
-
"DE" /* DE */,
|
|
3025
|
-
"A" /* TO */,
|
|
3026
|
-
"REPETER" /* REPEAT */,
|
|
3027
|
-
"JUSQUA" /* UNTIL */,
|
|
3028
|
-
"LIRE" /* READ */,
|
|
3029
|
-
"ECRIRE" /* WRITE */,
|
|
3030
|
-
"VRAI" /* TRUE */,
|
|
3031
|
-
"FAUX" /* FALSE */,
|
|
3032
|
-
"ET" /* AND */,
|
|
3033
|
-
"OU" /* OR */,
|
|
3034
|
-
"NON" /* NOT */,
|
|
3035
|
-
"FINPOUR" /* ENDFOR */,
|
|
3036
|
-
"FINIF" /* ENDIF */,
|
|
3037
|
-
"FINTANTQUE" /* ENDWHILE */
|
|
3038
|
-
].includes(tokenType);
|
|
3296
|
+
return Object.values(KEYWORDS).some((e) => e.tokenType === tokenType);
|
|
3039
3297
|
}
|
|
3040
3298
|
createReservedKeywordError(identifier, token) {
|
|
3041
3299
|
this.errors.push({
|
|
@@ -3050,30 +3308,22 @@ class Parser {
|
|
|
3050
3308
|
});
|
|
3051
3309
|
}
|
|
3052
3310
|
parse() {
|
|
3311
|
+
let ast;
|
|
3053
3312
|
try {
|
|
3054
|
-
|
|
3055
|
-
return {
|
|
3056
|
-
ast: program2,
|
|
3057
|
-
errors: this.errors,
|
|
3058
|
-
symbolTable: this.symbolTable
|
|
3059
|
-
};
|
|
3313
|
+
ast = this.parseProgram();
|
|
3060
3314
|
} catch (error) {
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
position: 0,
|
|
3071
|
-
code: "PARSE_ERROR"
|
|
3072
|
-
}
|
|
3073
|
-
],
|
|
3074
|
-
symbolTable: this.symbolTable
|
|
3075
|
-
};
|
|
3315
|
+
ast = { type: "PROGRAM" /* PROGRAM */, children: [] };
|
|
3316
|
+
this.errors.push({
|
|
3317
|
+
type: "ERROR",
|
|
3318
|
+
message: error instanceof Error ? error.message : "Erreur de parsing inconnue",
|
|
3319
|
+
line: 1,
|
|
3320
|
+
column: 1,
|
|
3321
|
+
position: 0,
|
|
3322
|
+
code: "PARSE_ERROR"
|
|
3323
|
+
});
|
|
3076
3324
|
}
|
|
3325
|
+
const { symbolTable, errors: semanticErrors } = new SemanticAnalyzer().analyze(ast);
|
|
3326
|
+
return { ast, errors: [...this.errors, ...semanticErrors], symbolTable };
|
|
3077
3327
|
}
|
|
3078
3328
|
parseProgram() {
|
|
3079
3329
|
const programToken = this.expect("PROGRAMME" /* PROGRAM */, 'Le programme doit commencer par le mot-clé "programme"');
|
|
@@ -3089,17 +3339,25 @@ class Parser {
|
|
|
3089
3339
|
}
|
|
3090
3340
|
parseBlock() {
|
|
3091
3341
|
const declarations = this.parseDeclarations();
|
|
3342
|
+
const subprograms = [];
|
|
3343
|
+
while (this.check(["FONCTION" /* FUNCTION */, "PROCEDURE" /* PROCEDURE */])) {
|
|
3344
|
+
if (this.check("FONCTION" /* FUNCTION */)) {
|
|
3345
|
+
subprograms.push(this.parseFunctionDeclaration());
|
|
3346
|
+
} else {
|
|
3347
|
+
subprograms.push(this.parseProcedureDeclaration());
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
3092
3350
|
const compoundStatement = this.parseCompoundStatement();
|
|
3093
3351
|
return {
|
|
3094
3352
|
type: "BLOCK" /* BLOCK */,
|
|
3095
|
-
children: [declarations, compoundStatement]
|
|
3353
|
+
children: [declarations, ...subprograms, compoundStatement]
|
|
3096
3354
|
};
|
|
3097
3355
|
}
|
|
3098
3356
|
parseDeclarations() {
|
|
3099
3357
|
const declarations = [];
|
|
3100
3358
|
while (this.check("VAR" /* VAR */)) {
|
|
3101
3359
|
this.advance();
|
|
3102
|
-
while (!this.check("DEBUT" /* BEGIN */) && !this.isAtEnd()) {
|
|
3360
|
+
while (!this.check("DEBUT" /* BEGIN */) && !this.check("FONCTION" /* FUNCTION */) && !this.check("PROCEDURE" /* PROCEDURE */) && !this.isAtEnd()) {
|
|
3103
3361
|
const declaration = this.parseVariableDeclaration();
|
|
3104
3362
|
declarations.push(declaration);
|
|
3105
3363
|
if (!this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
@@ -3140,31 +3398,27 @@ class Parser {
|
|
|
3140
3398
|
}
|
|
3141
3399
|
} while (true);
|
|
3142
3400
|
this.expect("COLON" /* COLON */, "Deux-points attendus après les identificateurs");
|
|
3401
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
3402
|
+
this.advance();
|
|
3403
|
+
this.expect("LEFT_BRACKET" /* LEFT_BRACKET */, '"[" attendu après TABLEAU');
|
|
3404
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille du tableau attendue");
|
|
3405
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu après la taille du tableau');
|
|
3406
|
+
this.expect("DE" /* DE */, '"DE" attendu après la taille du tableau');
|
|
3407
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments du tableau attendu");
|
|
3408
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
3409
|
+
const size = parseInt(sizeToken.value);
|
|
3410
|
+
return {
|
|
3411
|
+
type: "ARRAY_DECLARATION" /* ARRAY_DECLARATION */,
|
|
3412
|
+
value: elemType,
|
|
3413
|
+
children: [
|
|
3414
|
+
{ type: "LITERAL" /* LITERAL */, value: size },
|
|
3415
|
+
...identifiers.map((name) => ({ type: "VARIABLE" /* VARIABLE */, value: name }))
|
|
3416
|
+
],
|
|
3417
|
+
token: elemTypeToken
|
|
3418
|
+
};
|
|
3419
|
+
}
|
|
3143
3420
|
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de variable attendu");
|
|
3144
3421
|
const type = this.tokenTypeToDataType(typeToken.type);
|
|
3145
|
-
for (const identifier of identifiers) {
|
|
3146
|
-
if (this.symbolTable.symbols.has(identifier)) {
|
|
3147
|
-
this.errors.push({
|
|
3148
|
-
type: "ERROR",
|
|
3149
|
-
message: `La variable '${identifier}' est déjà déclarée`,
|
|
3150
|
-
line: typeToken.line,
|
|
3151
|
-
column: typeToken.column,
|
|
3152
|
-
position: typeToken.position,
|
|
3153
|
-
code: "DUPLICATE_VARIABLE",
|
|
3154
|
-
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
3155
|
-
suggestion: `Choisissez un autre nom pour la variable '${identifier}'`
|
|
3156
|
-
});
|
|
3157
|
-
} else {
|
|
3158
|
-
const symbolInfo = {
|
|
3159
|
-
name: identifier,
|
|
3160
|
-
type,
|
|
3161
|
-
scope: this.symbolTable.scopeName,
|
|
3162
|
-
line: typeToken.line,
|
|
3163
|
-
column: typeToken.column
|
|
3164
|
-
};
|
|
3165
|
-
this.symbolTable.symbols.set(identifier, symbolInfo);
|
|
3166
|
-
}
|
|
3167
|
-
}
|
|
3168
3422
|
return {
|
|
3169
3423
|
type: "VAR_DECLARATION" /* VAR_DECLARATION */,
|
|
3170
3424
|
value: type,
|
|
@@ -3181,9 +3435,16 @@ class Parser {
|
|
|
3181
3435
|
while (!this.check("FIN" /* END */) && !this.isAtEnd()) {
|
|
3182
3436
|
const statement = this.parseStatement();
|
|
3183
3437
|
statements.push(statement);
|
|
3438
|
+
const isBlockStatement = [
|
|
3439
|
+
"IF_STATEMENT" /* IF_STATEMENT */,
|
|
3440
|
+
"WHILE_STATEMENT" /* WHILE_STATEMENT */,
|
|
3441
|
+
"FOR_STATEMENT" /* FOR_STATEMENT */,
|
|
3442
|
+
"REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
3443
|
+
"COMPOUND_STATEMENT" /* COMPOUND_STATEMENT */
|
|
3444
|
+
].includes(statement.type);
|
|
3184
3445
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
3185
3446
|
this.advance();
|
|
3186
|
-
} else if (!this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */)) {
|
|
3447
|
+
} else if (!isBlockStatement && !this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */) && !this.check("FINPOUR" /* ENDFOR */)) {
|
|
3187
3448
|
this.errors.push({
|
|
3188
3449
|
type: "ERROR",
|
|
3189
3450
|
message: "Point-virgule attendu après l'instruction",
|
|
@@ -3226,6 +3487,16 @@ class Parser {
|
|
|
3226
3487
|
if (this.check("ECRIRE" /* WRITE */)) {
|
|
3227
3488
|
return this.parseWriteStatement();
|
|
3228
3489
|
}
|
|
3490
|
+
if (this.check("RETOURNER" /* RETURN */)) {
|
|
3491
|
+
return this.parseReturnStatement();
|
|
3492
|
+
}
|
|
3493
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_PAREN" /* LEFT_PAREN */) {
|
|
3494
|
+
const nameToken = this.advance();
|
|
3495
|
+
return this.parseFunctionCall(nameToken);
|
|
3496
|
+
}
|
|
3497
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_BRACKET" /* LEFT_BRACKET */) {
|
|
3498
|
+
return this.parseArrayAssignment();
|
|
3499
|
+
}
|
|
3229
3500
|
return this.parseAssignment();
|
|
3230
3501
|
}
|
|
3231
3502
|
parseIfStatement(isElseIf = false) {
|
|
@@ -3345,14 +3616,23 @@ class Parser {
|
|
|
3345
3616
|
parseReadStatement() {
|
|
3346
3617
|
const readToken = this.advance();
|
|
3347
3618
|
this.expect("LEFT_PAREN" /* LEFT_PAREN */, 'Parenthèse ouvrante attendue après "lire"');
|
|
3348
|
-
let
|
|
3619
|
+
let target;
|
|
3349
3620
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
3350
|
-
variable = this.advance();
|
|
3621
|
+
const variable = this.advance();
|
|
3622
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
3623
|
+
this.advance();
|
|
3624
|
+
const index = this.parseExpression();
|
|
3625
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
3626
|
+
target = { type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: variable.value, children: [index], token: variable };
|
|
3627
|
+
} else {
|
|
3628
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
3629
|
+
}
|
|
3351
3630
|
} else {
|
|
3352
3631
|
const currentToken = this.peek();
|
|
3353
3632
|
if (this.isKeywordToken(currentToken.type)) {
|
|
3354
|
-
variable = this.advance();
|
|
3633
|
+
const variable = this.advance();
|
|
3355
3634
|
this.createReservedKeywordError(variable.value, variable);
|
|
3635
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
3356
3636
|
} else {
|
|
3357
3637
|
this.expect("IDENTIFIER" /* IDENTIFIER */, 'Variable attendue dans "lire"');
|
|
3358
3638
|
throw new Error('Variable attendue dans "lire"');
|
|
@@ -3361,9 +3641,7 @@ class Parser {
|
|
|
3361
3641
|
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, 'Parenthèse fermante attendue dans "lire"');
|
|
3362
3642
|
return {
|
|
3363
3643
|
type: "READ_STATEMENT" /* READ_STATEMENT */,
|
|
3364
|
-
children: [
|
|
3365
|
-
{ type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable }
|
|
3366
|
-
],
|
|
3644
|
+
children: [target],
|
|
3367
3645
|
token: readToken
|
|
3368
3646
|
};
|
|
3369
3647
|
}
|
|
@@ -3491,7 +3769,7 @@ class Parser {
|
|
|
3491
3769
|
}
|
|
3492
3770
|
parseMultiplicativeExpression() {
|
|
3493
3771
|
let left = this.parseUnaryExpression();
|
|
3494
|
-
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */])) {
|
|
3772
|
+
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */, "MODULO" /* MODULO */])) {
|
|
3495
3773
|
const operator = this.advance();
|
|
3496
3774
|
const right = this.parseUnaryExpression();
|
|
3497
3775
|
left = {
|
|
@@ -3540,7 +3818,7 @@ class Parser {
|
|
|
3540
3818
|
const token2 = this.advance();
|
|
3541
3819
|
return {
|
|
3542
3820
|
type: "LITERAL" /* LITERAL */,
|
|
3543
|
-
value: token2.value === "vrai",
|
|
3821
|
+
value: token2.value.toLowerCase() === "vrai",
|
|
3544
3822
|
token: token2
|
|
3545
3823
|
};
|
|
3546
3824
|
}
|
|
@@ -3554,6 +3832,20 @@ class Parser {
|
|
|
3554
3832
|
}
|
|
3555
3833
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
3556
3834
|
const token2 = this.advance();
|
|
3835
|
+
if (this.check("LEFT_PAREN" /* LEFT_PAREN */)) {
|
|
3836
|
+
return this.parseFunctionCall(token2);
|
|
3837
|
+
}
|
|
3838
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
3839
|
+
this.advance();
|
|
3840
|
+
const index = this.parseExpression();
|
|
3841
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
3842
|
+
return {
|
|
3843
|
+
type: "ARRAY_ACCESS" /* ARRAY_ACCESS */,
|
|
3844
|
+
value: token2.value,
|
|
3845
|
+
children: [index],
|
|
3846
|
+
token: token2
|
|
3847
|
+
};
|
|
3848
|
+
}
|
|
3557
3849
|
if (this.isReservedKeyword(token2.value)) {
|
|
3558
3850
|
this.createReservedKeywordError(token2.value, token2);
|
|
3559
3851
|
}
|
|
@@ -3597,14 +3889,14 @@ class Parser {
|
|
|
3597
3889
|
parseRepeatStatement() {
|
|
3598
3890
|
const repeatToken = this.advance();
|
|
3599
3891
|
const statements = [];
|
|
3600
|
-
while (!this.check("
|
|
3892
|
+
while (!this.check("JUSQU'A" /* UNTIL */) && !this.isAtEnd()) {
|
|
3601
3893
|
const statement = this.parseStatement();
|
|
3602
3894
|
statements.push(statement);
|
|
3603
3895
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
3604
3896
|
this.advance();
|
|
3605
3897
|
}
|
|
3606
3898
|
}
|
|
3607
|
-
this.expect("
|
|
3899
|
+
this.expect("JUSQU'A" /* UNTIL */, `"jusqu'a" attendu à la fin de la boucle repeter`);
|
|
3608
3900
|
const condition = this.parseExpression();
|
|
3609
3901
|
return {
|
|
3610
3902
|
type: "REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
@@ -3612,6 +3904,127 @@ class Parser {
|
|
|
3612
3904
|
token: repeatToken
|
|
3613
3905
|
};
|
|
3614
3906
|
}
|
|
3907
|
+
parseFunctionCall(nameToken) {
|
|
3908
|
+
this.advance();
|
|
3909
|
+
const args = [];
|
|
3910
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
3911
|
+
args.push(this.parseExpression());
|
|
3912
|
+
while (this.check("COMMA" /* COMMA */)) {
|
|
3913
|
+
this.advance();
|
|
3914
|
+
args.push(this.parseExpression());
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")` attendu après les arguments');
|
|
3918
|
+
return {
|
|
3919
|
+
type: "FUNCTION_CALL" /* FUNCTION_CALL */,
|
|
3920
|
+
value: nameToken.value,
|
|
3921
|
+
children: args,
|
|
3922
|
+
token: nameToken
|
|
3923
|
+
};
|
|
3924
|
+
}
|
|
3925
|
+
parseArrayAssignment() {
|
|
3926
|
+
const nameToken = this.advance();
|
|
3927
|
+
this.advance();
|
|
3928
|
+
const index = this.parseExpression();
|
|
3929
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
3930
|
+
this.expect("ASSIGN" /* ASSIGN */, `":=" attendu dans l'affectation`);
|
|
3931
|
+
const value = this.parseExpression();
|
|
3932
|
+
return {
|
|
3933
|
+
type: "ASSIGNMENT" /* ASSIGNMENT */,
|
|
3934
|
+
children: [
|
|
3935
|
+
{ type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: nameToken.value, children: [index], token: nameToken },
|
|
3936
|
+
value
|
|
3937
|
+
],
|
|
3938
|
+
token: nameToken
|
|
3939
|
+
};
|
|
3940
|
+
}
|
|
3941
|
+
parseReturnStatement() {
|
|
3942
|
+
const token = this.advance();
|
|
3943
|
+
const expr = this.parseExpression();
|
|
3944
|
+
return {
|
|
3945
|
+
type: "RETURN_STATEMENT" /* RETURN_STATEMENT */,
|
|
3946
|
+
children: [expr],
|
|
3947
|
+
token
|
|
3948
|
+
};
|
|
3949
|
+
}
|
|
3950
|
+
parseParameterList() {
|
|
3951
|
+
this.expect("LEFT_PAREN" /* LEFT_PAREN */, '"(" attendu dans la déclaration');
|
|
3952
|
+
const params = [];
|
|
3953
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
3954
|
+
params.push(this.parseParameter());
|
|
3955
|
+
while (this.check("SEMICOLON" /* SEMICOLON */) || this.check("COMMA" /* COMMA */)) {
|
|
3956
|
+
this.advance();
|
|
3957
|
+
if (this.check("RIGHT_PAREN" /* RIGHT_PAREN */))
|
|
3958
|
+
break;
|
|
3959
|
+
params.push(this.parseParameter());
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")" attendu après les paramètres');
|
|
3963
|
+
return { type: "PARAMETER_LIST" /* PARAMETER_LIST */, children: params };
|
|
3964
|
+
}
|
|
3965
|
+
parseParameter() {
|
|
3966
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de paramètre attendu");
|
|
3967
|
+
this.expect("COLON" /* COLON */, '":" attendu après le nom du paramètre');
|
|
3968
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
3969
|
+
this.advance();
|
|
3970
|
+
let size;
|
|
3971
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
3972
|
+
this.advance();
|
|
3973
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille attendue");
|
|
3974
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu');
|
|
3975
|
+
size = parseInt(sizeToken.value);
|
|
3976
|
+
}
|
|
3977
|
+
this.expect("DE" /* DE */, '"DE" attendu après TABLEAU');
|
|
3978
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments attendu");
|
|
3979
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
3980
|
+
const typeLabel = size !== undefined ? `TABLEAU[${size}] DE ${elemType}` : `TABLEAU DE ${elemType}`;
|
|
3981
|
+
return {
|
|
3982
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
3983
|
+
value: nameToken.value,
|
|
3984
|
+
token: nameToken,
|
|
3985
|
+
symbolInfo: { name: nameToken.value, type: typeLabel, scope: "param" }
|
|
3986
|
+
};
|
|
3987
|
+
}
|
|
3988
|
+
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type du paramètre attendu");
|
|
3989
|
+
const paramType = this.tokenTypeToDataType(typeToken.type);
|
|
3990
|
+
return {
|
|
3991
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
3992
|
+
value: nameToken.value,
|
|
3993
|
+
token: nameToken,
|
|
3994
|
+
symbolInfo: { name: nameToken.value, type: paramType, scope: "param" }
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3997
|
+
parseFunctionDeclaration() {
|
|
3998
|
+
const token = this.advance();
|
|
3999
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de fonction attendu");
|
|
4000
|
+
const paramList = this.parseParameterList();
|
|
4001
|
+
this.expect("COLON" /* COLON */, '":" attendu pour le type de retour');
|
|
4002
|
+
const retTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de retour attendu");
|
|
4003
|
+
const retType = this.tokenTypeToDataType(retTypeToken.type);
|
|
4004
|
+
const body = this.parseCompoundStatement();
|
|
4005
|
+
return {
|
|
4006
|
+
type: "FUNCTION_DECLARATION" /* FUNCTION_DECLARATION */,
|
|
4007
|
+
value: nameToken.value,
|
|
4008
|
+
children: [
|
|
4009
|
+
paramList,
|
|
4010
|
+
{ type: "TYPE_SPECIFIER" /* TYPE_SPECIFIER */, value: retType },
|
|
4011
|
+
body
|
|
4012
|
+
],
|
|
4013
|
+
token
|
|
4014
|
+
};
|
|
4015
|
+
}
|
|
4016
|
+
parseProcedureDeclaration() {
|
|
4017
|
+
const token = this.advance();
|
|
4018
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de procédure attendu");
|
|
4019
|
+
const paramList = this.parseParameterList();
|
|
4020
|
+
const body = this.parseCompoundStatement();
|
|
4021
|
+
return {
|
|
4022
|
+
type: "PROCEDURE_DECLARATION" /* PROCEDURE_DECLARATION */,
|
|
4023
|
+
value: nameToken.value,
|
|
4024
|
+
children: [paramList, body],
|
|
4025
|
+
token
|
|
4026
|
+
};
|
|
4027
|
+
}
|
|
3615
4028
|
check(type) {
|
|
3616
4029
|
if (this.isAtEnd())
|
|
3617
4030
|
return false;
|
|
@@ -3761,6 +4174,8 @@ class CodeGenerator {
|
|
|
3761
4174
|
return this.generateBlock(node);
|
|
3762
4175
|
case "VAR_DECLARATION":
|
|
3763
4176
|
return this.generateVariableDeclaration(node);
|
|
4177
|
+
case "ARRAY_DECLARATION":
|
|
4178
|
+
return this.generateArrayDeclaration(node);
|
|
3764
4179
|
case "COMPOUND_STATEMENT":
|
|
3765
4180
|
return this.generateCompoundStatement(node);
|
|
3766
4181
|
case "ASSIGNMENT":
|
|
@@ -3777,6 +4192,12 @@ class CodeGenerator {
|
|
|
3777
4192
|
return this.generateReadStatement(node);
|
|
3778
4193
|
case "WRITE_STATEMENT":
|
|
3779
4194
|
return this.generateWriteStatement(node);
|
|
4195
|
+
case "FUNCTION_DECLARATION":
|
|
4196
|
+
return this.generateFunctionDeclaration(node);
|
|
4197
|
+
case "PROCEDURE_DECLARATION":
|
|
4198
|
+
return this.generateProcedureDeclaration(node);
|
|
4199
|
+
case "RETURN_STATEMENT":
|
|
4200
|
+
return this.generateReturnStatement(node);
|
|
3780
4201
|
case "BINARY_OP":
|
|
3781
4202
|
return this.generateBinaryOp(node);
|
|
3782
4203
|
case "UNARY_OP":
|
|
@@ -3785,6 +4206,10 @@ class CodeGenerator {
|
|
|
3785
4206
|
return this.generateLiteral(node);
|
|
3786
4207
|
case "VARIABLE":
|
|
3787
4208
|
return this.generateVariable(node);
|
|
4209
|
+
case "ARRAY_ACCESS":
|
|
4210
|
+
return this.generateArrayAccess(node);
|
|
4211
|
+
case "FUNCTION_CALL":
|
|
4212
|
+
return this.generateFunctionCall(node);
|
|
3788
4213
|
default:
|
|
3789
4214
|
this.errors.push({
|
|
3790
4215
|
type: "ERROR",
|
|
@@ -3799,14 +4224,25 @@ class CodeGenerator {
|
|
|
3799
4224
|
}
|
|
3800
4225
|
generateProgram(node) {
|
|
3801
4226
|
const lines = [];
|
|
4227
|
+
const block = node.children?.[0];
|
|
3802
4228
|
lines.push(`// Programme: ${node.value}`);
|
|
4229
|
+
if (block?.children) {
|
|
4230
|
+
for (const child of block.children) {
|
|
4231
|
+
if (child.type === "FUNCTION_DECLARATION" || child.type === "PROCEDURE_DECLARATION") {
|
|
4232
|
+
lines.push(this.generateNode(child));
|
|
4233
|
+
lines.push("");
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
3803
4237
|
lines.push("async function main() {");
|
|
3804
4238
|
this.indentLevel++;
|
|
3805
|
-
if (
|
|
3806
|
-
for (const child of
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
4239
|
+
if (block?.children) {
|
|
4240
|
+
for (const child of block.children) {
|
|
4241
|
+
if (child.type !== "FUNCTION_DECLARATION" && child.type !== "PROCEDURE_DECLARATION") {
|
|
4242
|
+
const childCode = this.generateNode(child);
|
|
4243
|
+
if (childCode) {
|
|
4244
|
+
lines.push(this.indent(childCode));
|
|
4245
|
+
}
|
|
3810
4246
|
}
|
|
3811
4247
|
}
|
|
3812
4248
|
}
|
|
@@ -3844,7 +4280,8 @@ class CodeGenerator {
|
|
|
3844
4280
|
for (const child of node.children) {
|
|
3845
4281
|
const childCode = this.generateNode(child);
|
|
3846
4282
|
if (childCode) {
|
|
3847
|
-
|
|
4283
|
+
const stmt = child.type === "FUNCTION_CALL" ? childCode.replace(/^\(|\)$/g, "") + ";" : childCode;
|
|
4284
|
+
lines.push(this.indent(stmt));
|
|
3848
4285
|
}
|
|
3849
4286
|
}
|
|
3850
4287
|
}
|
|
@@ -3855,15 +4292,107 @@ class CodeGenerator {
|
|
|
3855
4292
|
if (!node.children || node.children.length < 2) {
|
|
3856
4293
|
return "";
|
|
3857
4294
|
}
|
|
3858
|
-
const
|
|
4295
|
+
const target = node.children[0];
|
|
3859
4296
|
const expression = node.children[1];
|
|
3860
|
-
if (!
|
|
4297
|
+
if (!target || !expression) {
|
|
3861
4298
|
return "";
|
|
3862
4299
|
}
|
|
3863
|
-
const variableName = variable.value;
|
|
3864
4300
|
const expressionCode = this.generateNode(expression);
|
|
4301
|
+
if (target.type === "ARRAY_ACCESS") {
|
|
4302
|
+
const arrayName = target.value;
|
|
4303
|
+
const index = this.generateNode(target.children[0]);
|
|
4304
|
+
return `${arrayName}[${index}] = ${expressionCode};`;
|
|
4305
|
+
}
|
|
4306
|
+
const variableName = target.value;
|
|
3865
4307
|
return `${variableName} = ${expressionCode};`;
|
|
3866
4308
|
}
|
|
4309
|
+
generateArrayDeclaration(node) {
|
|
4310
|
+
if (!node.children || node.children.length < 2)
|
|
4311
|
+
return "";
|
|
4312
|
+
const size = node.children[0].value;
|
|
4313
|
+
const elemType = node.value;
|
|
4314
|
+
const defaultValue = elemType === "BOOLEEN" ? "false" : elemType === "CHAINE" ? '""' : "0";
|
|
4315
|
+
const names = node.children.slice(1).map((c) => c.value);
|
|
4316
|
+
return names.map((name) => `let ${name} = new Array(${size}).fill(${defaultValue});`).join(`
|
|
4317
|
+
`);
|
|
4318
|
+
}
|
|
4319
|
+
generateArrayAccess(node) {
|
|
4320
|
+
const name = node.value;
|
|
4321
|
+
const index = this.generateNode(node.children[0]);
|
|
4322
|
+
return `${name}[${index}]`;
|
|
4323
|
+
}
|
|
4324
|
+
generateFunctionCall(node) {
|
|
4325
|
+
const name = node.value;
|
|
4326
|
+
const args = (node.children ?? []).map((c) => {
|
|
4327
|
+
const code = this.generateNode(c);
|
|
4328
|
+
if (c.type === "VARIABLE") {
|
|
4329
|
+
const sym = this.symbolTable.symbols.get(c.value);
|
|
4330
|
+
if (sym?.type?.startsWith("TABLEAU"))
|
|
4331
|
+
return `${code}.slice()`;
|
|
4332
|
+
}
|
|
4333
|
+
return code;
|
|
4334
|
+
});
|
|
4335
|
+
switch (name.toLowerCase()) {
|
|
4336
|
+
case "abs":
|
|
4337
|
+
return `Math.abs(${args[0]})`;
|
|
4338
|
+
case "max":
|
|
4339
|
+
return `Math.max(${args.join(", ")})`;
|
|
4340
|
+
case "min":
|
|
4341
|
+
return `Math.min(${args.join(", ")})`;
|
|
4342
|
+
case "mod":
|
|
4343
|
+
return `(${args[0]} % ${args[1]})`;
|
|
4344
|
+
case "racine_carree":
|
|
4345
|
+
return `Math.sqrt(${args[0]})`;
|
|
4346
|
+
case "taille":
|
|
4347
|
+
return `${args[0]}.length`;
|
|
4348
|
+
case "sous_chaine":
|
|
4349
|
+
return `${args[0]}.substring(${args[1]}, ${args[1]} + ${args[2]})`;
|
|
4350
|
+
case "concat":
|
|
4351
|
+
return args.join(" + ");
|
|
4352
|
+
case "entier_en_reel":
|
|
4353
|
+
return `${args[0]}`;
|
|
4354
|
+
case "reel_en_entier":
|
|
4355
|
+
return `Math.trunc(${args[0]})`;
|
|
4356
|
+
default:
|
|
4357
|
+
return `(await ${name}(${args.join(", ")}))`;
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
generateFunctionDeclaration(node) {
|
|
4361
|
+
const name = node.value;
|
|
4362
|
+
const [paramList, , body] = node.children ?? [];
|
|
4363
|
+
const params = this.generateParamList(paramList);
|
|
4364
|
+
const bodyCode = this.generateNode(body);
|
|
4365
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
4366
|
+
this.indentLevel++;
|
|
4367
|
+
lines.push(this.indent(bodyCode));
|
|
4368
|
+
this.indentLevel--;
|
|
4369
|
+
lines.push("}");
|
|
4370
|
+
return lines.join(`
|
|
4371
|
+
`);
|
|
4372
|
+
}
|
|
4373
|
+
generateProcedureDeclaration(node) {
|
|
4374
|
+
const name = node.value;
|
|
4375
|
+
const [paramList, body] = node.children ?? [];
|
|
4376
|
+
const params = this.generateParamList(paramList);
|
|
4377
|
+
const bodyCode = this.generateNode(body);
|
|
4378
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
4379
|
+
this.indentLevel++;
|
|
4380
|
+
lines.push(this.indent(bodyCode));
|
|
4381
|
+
this.indentLevel--;
|
|
4382
|
+
lines.push("}");
|
|
4383
|
+
return lines.join(`
|
|
4384
|
+
`);
|
|
4385
|
+
}
|
|
4386
|
+
generateParamList(paramList) {
|
|
4387
|
+
if (!paramList?.children)
|
|
4388
|
+
return "";
|
|
4389
|
+
return paramList.children.map((p) => p.value).join(", ");
|
|
4390
|
+
}
|
|
4391
|
+
generateReturnStatement(node) {
|
|
4392
|
+
if (!node.children?.[0])
|
|
4393
|
+
return "return;";
|
|
4394
|
+
return `return ${this.generateNode(node.children[0])};`;
|
|
4395
|
+
}
|
|
3867
4396
|
generateIfStatement(node) {
|
|
3868
4397
|
if (!node.children || node.children.length < 2) {
|
|
3869
4398
|
return "";
|
|
@@ -3965,34 +4494,20 @@ class CodeGenerator {
|
|
|
3965
4494
|
return code;
|
|
3966
4495
|
}
|
|
3967
4496
|
generateReadStatement(node) {
|
|
3968
|
-
if (!node.children || node.children.length === 0)
|
|
4497
|
+
if (!node.children || node.children.length === 0)
|
|
3969
4498
|
return "";
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
if (!variable) {
|
|
4499
|
+
const target = node.children[0];
|
|
4500
|
+
if (!target)
|
|
3973
4501
|
return "";
|
|
4502
|
+
const isArray = target.type === "ARRAY_ACCESS";
|
|
4503
|
+
const varName = isArray ? `${target.value}[${this.generateNode(target.children[0])}]` : target.value;
|
|
4504
|
+
const symbolName = target.value;
|
|
4505
|
+
const symbolInfo = this.symbolTable.symbols.get(symbolName);
|
|
4506
|
+
const typeHint = symbolInfo?.type ?? "";
|
|
4507
|
+
if (typeHint.startsWith("ENTIER") || typeHint === "REEL" || typeHint.startsWith("TABLEAU")) {
|
|
4508
|
+
return `${varName} = parseInt(await lire(""));`;
|
|
3974
4509
|
}
|
|
3975
|
-
|
|
3976
|
-
const symbolInfo = this.symbolTable.symbols.get(variableName);
|
|
3977
|
-
let conversion = "";
|
|
3978
|
-
if (symbolInfo) {
|
|
3979
|
-
switch (symbolInfo.type) {
|
|
3980
|
-
case "ENTIER":
|
|
3981
|
-
case "REEL":
|
|
3982
|
-
conversion = "parseInt(";
|
|
3983
|
-
break;
|
|
3984
|
-
case "BOOLEEN":
|
|
3985
|
-
conversion = "(";
|
|
3986
|
-
break;
|
|
3987
|
-
default:
|
|
3988
|
-
conversion = "";
|
|
3989
|
-
}
|
|
3990
|
-
}
|
|
3991
|
-
if (conversion) {
|
|
3992
|
-
return `${variableName} = ${conversion}await lire(""));`;
|
|
3993
|
-
} else {
|
|
3994
|
-
return `${variableName} = await lire("");`;
|
|
3995
|
-
}
|
|
4510
|
+
return `${varName} = await lire("");`;
|
|
3996
4511
|
}
|
|
3997
4512
|
generateWriteStatement(node) {
|
|
3998
4513
|
if (!node.children || node.children.length === 0) {
|
|
@@ -4091,6 +4606,8 @@ class CodeGenerator {
|
|
|
4091
4606
|
return ">";
|
|
4092
4607
|
case ">=":
|
|
4093
4608
|
return ">=";
|
|
4609
|
+
case "%":
|
|
4610
|
+
return "%";
|
|
4094
4611
|
case "et":
|
|
4095
4612
|
return "&&";
|
|
4096
4613
|
case "ou":
|