@devalade/algolang 1.0.1 → 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 -191
- package/dist/index.js +1435 -385
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2632,6 +2632,268 @@ var chalk = createChalk();
|
|
|
2632
2632
|
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
2633
2633
|
var source_default = chalk;
|
|
2634
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
|
+
|
|
2635
2897
|
// src/lexer/lexer.ts
|
|
2636
2898
|
class Lexer {
|
|
2637
2899
|
source;
|
|
@@ -2641,37 +2903,7 @@ class Lexer {
|
|
|
2641
2903
|
keywords;
|
|
2642
2904
|
constructor(source) {
|
|
2643
2905
|
this.source = source;
|
|
2644
|
-
this.keywords = new Map([
|
|
2645
|
-
["PROGRAMME", "PROGRAMME" /* PROGRAM */],
|
|
2646
|
-
["DEBUT", "DEBUT" /* BEGIN */],
|
|
2647
|
-
["FIN", "FIN" /* END */],
|
|
2648
|
-
["VAR", "VAR" /* VAR */],
|
|
2649
|
-
["ENTIER", "ENTIER" /* INTEGER */],
|
|
2650
|
-
["REEL", "REEL" /* REAL */],
|
|
2651
|
-
["BOOLEEN", "BOOLEEN" /* BOOLEAN */],
|
|
2652
|
-
["CHAINE", "CHAINE" /* STRING */],
|
|
2653
|
-
["SI", "SI" /* IF */],
|
|
2654
|
-
["ALORS", "ALORS" /* THEN */],
|
|
2655
|
-
["SINON", "SINON" /* ELSE */],
|
|
2656
|
-
["FINSI", "FINIF" /* ENDIF */],
|
|
2657
|
-
["TANTQUE", "TANTQUE" /* WHILE */],
|
|
2658
|
-
["FAIRE", "FAIRE" /* DO */],
|
|
2659
|
-
["POUR", "POUR" /* FOR */],
|
|
2660
|
-
["ALLANT", "ALLANT" /* ALLANT */],
|
|
2661
|
-
["DE", "DE" /* DE */],
|
|
2662
|
-
["A", "A" /* TO */],
|
|
2663
|
-
["REPETER", "REPETER" /* REPEAT */],
|
|
2664
|
-
["JUSQUA", "JUSQUA" /* UNTIL */],
|
|
2665
|
-
["LIRE", "LIRE" /* READ */],
|
|
2666
|
-
["ECRIRE", "ECRIRE" /* WRITE */],
|
|
2667
|
-
["VRAI", "VRAI" /* TRUE */],
|
|
2668
|
-
["FAUX", "FAUX" /* FALSE */],
|
|
2669
|
-
["ET", "ET" /* AND */],
|
|
2670
|
-
["OU", "OU" /* OR */],
|
|
2671
|
-
["NON", "NON" /* NOT */],
|
|
2672
|
-
["FINPOUR", "FINPOUR" /* ENDFOR */],
|
|
2673
|
-
["FINTANTQUE", "FINTANTQUE" /* ENDWHILE */]
|
|
2674
|
-
]);
|
|
2906
|
+
this.keywords = new Map(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label, e]) => [label, e.tokenType]));
|
|
2675
2907
|
}
|
|
2676
2908
|
tokenize() {
|
|
2677
2909
|
const tokens = [];
|
|
@@ -2744,6 +2976,8 @@ class Lexer {
|
|
|
2744
2976
|
return this.createToken("MULTIPLY" /* MULTIPLY */, this.advance());
|
|
2745
2977
|
case "/":
|
|
2746
2978
|
return this.createToken("DIVIDE" /* DIVIDE */, this.advance());
|
|
2979
|
+
case "%":
|
|
2980
|
+
return this.createToken("MODULO" /* MODULO */, this.advance());
|
|
2747
2981
|
case "=":
|
|
2748
2982
|
return this.createToken("EQUAL" /* EQUAL */, this.advance());
|
|
2749
2983
|
case "<":
|
|
@@ -2762,6 +2996,10 @@ class Lexer {
|
|
|
2762
2996
|
return this.createToken("LEFT_PAREN" /* LEFT_PAREN */, this.advance());
|
|
2763
2997
|
case ")":
|
|
2764
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());
|
|
2765
3003
|
default:
|
|
2766
3004
|
throw new Error(`Caractère non reconnu '${char}' à la ligne ${this.line}, colonne ${this.column}`);
|
|
2767
3005
|
}
|
|
@@ -2954,87 +3192,108 @@ class Lexer {
|
|
|
2954
3192
|
}
|
|
2955
3193
|
}
|
|
2956
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
|
+
|
|
2957
3283
|
// src/parser/parser.ts
|
|
2958
3284
|
class Parser {
|
|
2959
3285
|
tokens;
|
|
2960
3286
|
current = 0;
|
|
2961
3287
|
errors = [];
|
|
2962
|
-
|
|
2963
|
-
reservedKeywords = new Set([
|
|
2964
|
-
"programme",
|
|
2965
|
-
"debut",
|
|
2966
|
-
"fin",
|
|
2967
|
-
"var",
|
|
2968
|
-
"entier",
|
|
2969
|
-
"reel",
|
|
2970
|
-
"booleen",
|
|
2971
|
-
"chaine",
|
|
2972
|
-
"si",
|
|
2973
|
-
"alors",
|
|
2974
|
-
"sinon",
|
|
2975
|
-
"finsi",
|
|
2976
|
-
"tantque",
|
|
2977
|
-
"faire",
|
|
2978
|
-
"fintantque",
|
|
2979
|
-
"pour",
|
|
2980
|
-
"a",
|
|
2981
|
-
"finpour",
|
|
2982
|
-
"repeter",
|
|
2983
|
-
"jusqua",
|
|
2984
|
-
"lire",
|
|
2985
|
-
"ecrire",
|
|
2986
|
-
"vrai",
|
|
2987
|
-
"faux",
|
|
2988
|
-
"et",
|
|
2989
|
-
"ou",
|
|
2990
|
-
"non",
|
|
2991
|
-
"fonction",
|
|
2992
|
-
"procedure",
|
|
2993
|
-
"retourner"
|
|
2994
|
-
]);
|
|
3288
|
+
reservedKeywords = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label]) => label.toLowerCase()));
|
|
2995
3289
|
constructor(tokens) {
|
|
2996
3290
|
this.tokens = tokens;
|
|
2997
|
-
this.symbolTable = {
|
|
2998
|
-
symbols: new Map,
|
|
2999
|
-
children: [],
|
|
3000
|
-
scopeName: "global"
|
|
3001
|
-
};
|
|
3002
3291
|
}
|
|
3003
3292
|
isReservedKeyword(identifier) {
|
|
3004
3293
|
return this.reservedKeywords.has(identifier.toLowerCase());
|
|
3005
3294
|
}
|
|
3006
3295
|
isKeywordToken(tokenType) {
|
|
3007
|
-
return
|
|
3008
|
-
"PROGRAMME" /* PROGRAM */,
|
|
3009
|
-
"DEBUT" /* BEGIN */,
|
|
3010
|
-
"FIN" /* END */,
|
|
3011
|
-
"VAR" /* VAR */,
|
|
3012
|
-
"ENTIER" /* INTEGER */,
|
|
3013
|
-
"REEL" /* REAL */,
|
|
3014
|
-
"BOOLEEN" /* BOOLEAN */,
|
|
3015
|
-
"CHAINE" /* STRING */,
|
|
3016
|
-
"SI" /* IF */,
|
|
3017
|
-
"ALORS" /* THEN */,
|
|
3018
|
-
"SINON" /* ELSE */,
|
|
3019
|
-
"TANTQUE" /* WHILE */,
|
|
3020
|
-
"FAIRE" /* DO */,
|
|
3021
|
-
"POUR" /* FOR */,
|
|
3022
|
-
"ALLANT" /* ALLANT */,
|
|
3023
|
-
"DE" /* DE */,
|
|
3024
|
-
"A" /* TO */,
|
|
3025
|
-
"REPETER" /* REPEAT */,
|
|
3026
|
-
"JUSQUA" /* UNTIL */,
|
|
3027
|
-
"LIRE" /* READ */,
|
|
3028
|
-
"ECRIRE" /* WRITE */,
|
|
3029
|
-
"VRAI" /* TRUE */,
|
|
3030
|
-
"FAUX" /* FALSE */,
|
|
3031
|
-
"ET" /* AND */,
|
|
3032
|
-
"OU" /* OR */,
|
|
3033
|
-
"NON" /* NOT */,
|
|
3034
|
-
"FINPOUR" /* ENDFOR */,
|
|
3035
|
-
"FINIF" /* ENDIF */,
|
|
3036
|
-
"FINTANTQUE" /* ENDWHILE */
|
|
3037
|
-
].includes(tokenType);
|
|
3296
|
+
return Object.values(KEYWORDS).some((e) => e.tokenType === tokenType);
|
|
3038
3297
|
}
|
|
3039
3298
|
createReservedKeywordError(identifier, token) {
|
|
3040
3299
|
this.errors.push({
|
|
@@ -3049,30 +3308,22 @@ class Parser {
|
|
|
3049
3308
|
});
|
|
3050
3309
|
}
|
|
3051
3310
|
parse() {
|
|
3311
|
+
let ast;
|
|
3052
3312
|
try {
|
|
3053
|
-
|
|
3054
|
-
return {
|
|
3055
|
-
ast: program2,
|
|
3056
|
-
errors: this.errors,
|
|
3057
|
-
symbolTable: this.symbolTable
|
|
3058
|
-
};
|
|
3313
|
+
ast = this.parseProgram();
|
|
3059
3314
|
} catch (error) {
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
position: 0,
|
|
3070
|
-
code: "PARSE_ERROR"
|
|
3071
|
-
}
|
|
3072
|
-
],
|
|
3073
|
-
symbolTable: this.symbolTable
|
|
3074
|
-
};
|
|
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
|
+
});
|
|
3075
3324
|
}
|
|
3325
|
+
const { symbolTable, errors: semanticErrors } = new SemanticAnalyzer().analyze(ast);
|
|
3326
|
+
return { ast, errors: [...this.errors, ...semanticErrors], symbolTable };
|
|
3076
3327
|
}
|
|
3077
3328
|
parseProgram() {
|
|
3078
3329
|
const programToken = this.expect("PROGRAMME" /* PROGRAM */, 'Le programme doit commencer par le mot-clé "programme"');
|
|
@@ -3088,17 +3339,25 @@ class Parser {
|
|
|
3088
3339
|
}
|
|
3089
3340
|
parseBlock() {
|
|
3090
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
|
+
}
|
|
3091
3350
|
const compoundStatement = this.parseCompoundStatement();
|
|
3092
3351
|
return {
|
|
3093
3352
|
type: "BLOCK" /* BLOCK */,
|
|
3094
|
-
children: [declarations, compoundStatement]
|
|
3353
|
+
children: [declarations, ...subprograms, compoundStatement]
|
|
3095
3354
|
};
|
|
3096
3355
|
}
|
|
3097
3356
|
parseDeclarations() {
|
|
3098
3357
|
const declarations = [];
|
|
3099
3358
|
while (this.check("VAR" /* VAR */)) {
|
|
3100
3359
|
this.advance();
|
|
3101
|
-
while (!this.check("DEBUT" /* BEGIN */) && !this.isAtEnd()) {
|
|
3360
|
+
while (!this.check("DEBUT" /* BEGIN */) && !this.check("FONCTION" /* FUNCTION */) && !this.check("PROCEDURE" /* PROCEDURE */) && !this.isAtEnd()) {
|
|
3102
3361
|
const declaration = this.parseVariableDeclaration();
|
|
3103
3362
|
declarations.push(declaration);
|
|
3104
3363
|
if (!this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
@@ -3139,31 +3398,27 @@ class Parser {
|
|
|
3139
3398
|
}
|
|
3140
3399
|
} while (true);
|
|
3141
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
|
+
}
|
|
3142
3420
|
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de variable attendu");
|
|
3143
3421
|
const type = this.tokenTypeToDataType(typeToken.type);
|
|
3144
|
-
for (const identifier of identifiers) {
|
|
3145
|
-
if (this.symbolTable.symbols.has(identifier)) {
|
|
3146
|
-
this.errors.push({
|
|
3147
|
-
type: "ERROR",
|
|
3148
|
-
message: `La variable '${identifier}' est déjà déclarée`,
|
|
3149
|
-
line: typeToken.line,
|
|
3150
|
-
column: typeToken.column,
|
|
3151
|
-
position: typeToken.position,
|
|
3152
|
-
code: "DUPLICATE_VARIABLE",
|
|
3153
|
-
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
3154
|
-
suggestion: `Choisissez un autre nom pour la variable '${identifier}'`
|
|
3155
|
-
});
|
|
3156
|
-
} else {
|
|
3157
|
-
const symbolInfo = {
|
|
3158
|
-
name: identifier,
|
|
3159
|
-
type,
|
|
3160
|
-
scope: this.symbolTable.scopeName,
|
|
3161
|
-
line: typeToken.line,
|
|
3162
|
-
column: typeToken.column
|
|
3163
|
-
};
|
|
3164
|
-
this.symbolTable.symbols.set(identifier, symbolInfo);
|
|
3165
|
-
}
|
|
3166
|
-
}
|
|
3167
3422
|
return {
|
|
3168
3423
|
type: "VAR_DECLARATION" /* VAR_DECLARATION */,
|
|
3169
3424
|
value: type,
|
|
@@ -3180,9 +3435,16 @@ class Parser {
|
|
|
3180
3435
|
while (!this.check("FIN" /* END */) && !this.isAtEnd()) {
|
|
3181
3436
|
const statement = this.parseStatement();
|
|
3182
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);
|
|
3183
3445
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
3184
3446
|
this.advance();
|
|
3185
|
-
} 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 */)) {
|
|
3186
3448
|
this.errors.push({
|
|
3187
3449
|
type: "ERROR",
|
|
3188
3450
|
message: "Point-virgule attendu après l'instruction",
|
|
@@ -3225,6 +3487,16 @@ class Parser {
|
|
|
3225
3487
|
if (this.check("ECRIRE" /* WRITE */)) {
|
|
3226
3488
|
return this.parseWriteStatement();
|
|
3227
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
|
+
}
|
|
3228
3500
|
return this.parseAssignment();
|
|
3229
3501
|
}
|
|
3230
3502
|
parseIfStatement(isElseIf = false) {
|
|
@@ -3344,14 +3616,23 @@ class Parser {
|
|
|
3344
3616
|
parseReadStatement() {
|
|
3345
3617
|
const readToken = this.advance();
|
|
3346
3618
|
this.expect("LEFT_PAREN" /* LEFT_PAREN */, 'Parenthèse ouvrante attendue après "lire"');
|
|
3347
|
-
let
|
|
3619
|
+
let target;
|
|
3348
3620
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
3349
|
-
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
|
+
}
|
|
3350
3630
|
} else {
|
|
3351
3631
|
const currentToken = this.peek();
|
|
3352
3632
|
if (this.isKeywordToken(currentToken.type)) {
|
|
3353
|
-
variable = this.advance();
|
|
3633
|
+
const variable = this.advance();
|
|
3354
3634
|
this.createReservedKeywordError(variable.value, variable);
|
|
3635
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
3355
3636
|
} else {
|
|
3356
3637
|
this.expect("IDENTIFIER" /* IDENTIFIER */, 'Variable attendue dans "lire"');
|
|
3357
3638
|
throw new Error('Variable attendue dans "lire"');
|
|
@@ -3360,9 +3641,7 @@ class Parser {
|
|
|
3360
3641
|
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, 'Parenthèse fermante attendue dans "lire"');
|
|
3361
3642
|
return {
|
|
3362
3643
|
type: "READ_STATEMENT" /* READ_STATEMENT */,
|
|
3363
|
-
children: [
|
|
3364
|
-
{ type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable }
|
|
3365
|
-
],
|
|
3644
|
+
children: [target],
|
|
3366
3645
|
token: readToken
|
|
3367
3646
|
};
|
|
3368
3647
|
}
|
|
@@ -3490,7 +3769,7 @@ class Parser {
|
|
|
3490
3769
|
}
|
|
3491
3770
|
parseMultiplicativeExpression() {
|
|
3492
3771
|
let left = this.parseUnaryExpression();
|
|
3493
|
-
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */])) {
|
|
3772
|
+
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */, "MODULO" /* MODULO */])) {
|
|
3494
3773
|
const operator = this.advance();
|
|
3495
3774
|
const right = this.parseUnaryExpression();
|
|
3496
3775
|
left = {
|
|
@@ -3539,7 +3818,7 @@ class Parser {
|
|
|
3539
3818
|
const token2 = this.advance();
|
|
3540
3819
|
return {
|
|
3541
3820
|
type: "LITERAL" /* LITERAL */,
|
|
3542
|
-
value: token2.value === "vrai",
|
|
3821
|
+
value: token2.value.toLowerCase() === "vrai",
|
|
3543
3822
|
token: token2
|
|
3544
3823
|
};
|
|
3545
3824
|
}
|
|
@@ -3553,6 +3832,20 @@ class Parser {
|
|
|
3553
3832
|
}
|
|
3554
3833
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
3555
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
|
+
}
|
|
3556
3849
|
if (this.isReservedKeyword(token2.value)) {
|
|
3557
3850
|
this.createReservedKeywordError(token2.value, token2);
|
|
3558
3851
|
}
|
|
@@ -3596,14 +3889,14 @@ class Parser {
|
|
|
3596
3889
|
parseRepeatStatement() {
|
|
3597
3890
|
const repeatToken = this.advance();
|
|
3598
3891
|
const statements = [];
|
|
3599
|
-
while (!this.check("
|
|
3892
|
+
while (!this.check("JUSQU'A" /* UNTIL */) && !this.isAtEnd()) {
|
|
3600
3893
|
const statement = this.parseStatement();
|
|
3601
3894
|
statements.push(statement);
|
|
3602
3895
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
3603
3896
|
this.advance();
|
|
3604
3897
|
}
|
|
3605
3898
|
}
|
|
3606
|
-
this.expect("
|
|
3899
|
+
this.expect("JUSQU'A" /* UNTIL */, `"jusqu'a" attendu à la fin de la boucle repeter`);
|
|
3607
3900
|
const condition = this.parseExpression();
|
|
3608
3901
|
return {
|
|
3609
3902
|
type: "REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
@@ -3611,6 +3904,127 @@ class Parser {
|
|
|
3611
3904
|
token: repeatToken
|
|
3612
3905
|
};
|
|
3613
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
|
+
}
|
|
3614
4028
|
check(type) {
|
|
3615
4029
|
if (this.isAtEnd())
|
|
3616
4030
|
return false;
|
|
@@ -3760,6 +4174,8 @@ class CodeGenerator {
|
|
|
3760
4174
|
return this.generateBlock(node);
|
|
3761
4175
|
case "VAR_DECLARATION":
|
|
3762
4176
|
return this.generateVariableDeclaration(node);
|
|
4177
|
+
case "ARRAY_DECLARATION":
|
|
4178
|
+
return this.generateArrayDeclaration(node);
|
|
3763
4179
|
case "COMPOUND_STATEMENT":
|
|
3764
4180
|
return this.generateCompoundStatement(node);
|
|
3765
4181
|
case "ASSIGNMENT":
|
|
@@ -3776,6 +4192,12 @@ class CodeGenerator {
|
|
|
3776
4192
|
return this.generateReadStatement(node);
|
|
3777
4193
|
case "WRITE_STATEMENT":
|
|
3778
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);
|
|
3779
4201
|
case "BINARY_OP":
|
|
3780
4202
|
return this.generateBinaryOp(node);
|
|
3781
4203
|
case "UNARY_OP":
|
|
@@ -3784,6 +4206,10 @@ class CodeGenerator {
|
|
|
3784
4206
|
return this.generateLiteral(node);
|
|
3785
4207
|
case "VARIABLE":
|
|
3786
4208
|
return this.generateVariable(node);
|
|
4209
|
+
case "ARRAY_ACCESS":
|
|
4210
|
+
return this.generateArrayAccess(node);
|
|
4211
|
+
case "FUNCTION_CALL":
|
|
4212
|
+
return this.generateFunctionCall(node);
|
|
3787
4213
|
default:
|
|
3788
4214
|
this.errors.push({
|
|
3789
4215
|
type: "ERROR",
|
|
@@ -3798,14 +4224,25 @@ class CodeGenerator {
|
|
|
3798
4224
|
}
|
|
3799
4225
|
generateProgram(node) {
|
|
3800
4226
|
const lines = [];
|
|
4227
|
+
const block = node.children?.[0];
|
|
3801
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
|
+
}
|
|
3802
4237
|
lines.push("async function main() {");
|
|
3803
4238
|
this.indentLevel++;
|
|
3804
|
-
if (
|
|
3805
|
-
for (const child of
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
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
|
+
}
|
|
3809
4246
|
}
|
|
3810
4247
|
}
|
|
3811
4248
|
}
|
|
@@ -3843,7 +4280,8 @@ class CodeGenerator {
|
|
|
3843
4280
|
for (const child of node.children) {
|
|
3844
4281
|
const childCode = this.generateNode(child);
|
|
3845
4282
|
if (childCode) {
|
|
3846
|
-
|
|
4283
|
+
const stmt = child.type === "FUNCTION_CALL" ? childCode.replace(/^\(|\)$/g, "") + ";" : childCode;
|
|
4284
|
+
lines.push(this.indent(stmt));
|
|
3847
4285
|
}
|
|
3848
4286
|
}
|
|
3849
4287
|
}
|
|
@@ -3854,15 +4292,107 @@ class CodeGenerator {
|
|
|
3854
4292
|
if (!node.children || node.children.length < 2) {
|
|
3855
4293
|
return "";
|
|
3856
4294
|
}
|
|
3857
|
-
const
|
|
4295
|
+
const target = node.children[0];
|
|
3858
4296
|
const expression = node.children[1];
|
|
3859
|
-
if (!
|
|
4297
|
+
if (!target || !expression) {
|
|
3860
4298
|
return "";
|
|
3861
4299
|
}
|
|
3862
|
-
const variableName = variable.value;
|
|
3863
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;
|
|
3864
4307
|
return `${variableName} = ${expressionCode};`;
|
|
3865
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
|
+
}
|
|
3866
4396
|
generateIfStatement(node) {
|
|
3867
4397
|
if (!node.children || node.children.length < 2) {
|
|
3868
4398
|
return "";
|
|
@@ -3964,34 +4494,20 @@ class CodeGenerator {
|
|
|
3964
4494
|
return code;
|
|
3965
4495
|
}
|
|
3966
4496
|
generateReadStatement(node) {
|
|
3967
|
-
if (!node.children || node.children.length === 0)
|
|
4497
|
+
if (!node.children || node.children.length === 0)
|
|
3968
4498
|
return "";
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
if (!variable) {
|
|
4499
|
+
const target = node.children[0];
|
|
4500
|
+
if (!target)
|
|
3972
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(""));`;
|
|
3973
4509
|
}
|
|
3974
|
-
|
|
3975
|
-
const symbolInfo = this.symbolTable.symbols.get(variableName);
|
|
3976
|
-
let conversion = "";
|
|
3977
|
-
if (symbolInfo) {
|
|
3978
|
-
switch (symbolInfo.type) {
|
|
3979
|
-
case "ENTIER":
|
|
3980
|
-
case "REEL":
|
|
3981
|
-
conversion = "parseInt(";
|
|
3982
|
-
break;
|
|
3983
|
-
case "BOOLEEN":
|
|
3984
|
-
conversion = "(";
|
|
3985
|
-
break;
|
|
3986
|
-
default:
|
|
3987
|
-
conversion = "";
|
|
3988
|
-
}
|
|
3989
|
-
}
|
|
3990
|
-
if (conversion) {
|
|
3991
|
-
return `${variableName} = ${conversion}await lire(""));`;
|
|
3992
|
-
} else {
|
|
3993
|
-
return `${variableName} = await lire("");`;
|
|
3994
|
-
}
|
|
4510
|
+
return `${varName} = await lire("");`;
|
|
3995
4511
|
}
|
|
3996
4512
|
generateWriteStatement(node) {
|
|
3997
4513
|
if (!node.children || node.children.length === 0) {
|
|
@@ -4090,6 +4606,8 @@ class CodeGenerator {
|
|
|
4090
4606
|
return ">";
|
|
4091
4607
|
case ">=":
|
|
4092
4608
|
return ">=";
|
|
4609
|
+
case "%":
|
|
4610
|
+
return "%";
|
|
4093
4611
|
case "et":
|
|
4094
4612
|
return "&&";
|
|
4095
4613
|
case "ou":
|