@devalade/algolang 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +719 -191
- package/dist/index.js +1456 -385
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2632,6 +2632,269 @@ 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
|
+
var BUILTIN_NAMES = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType === null).map(([label]) => label));
|
|
2897
|
+
|
|
2635
2898
|
// src/lexer/lexer.ts
|
|
2636
2899
|
class Lexer {
|
|
2637
2900
|
source;
|
|
@@ -2641,37 +2904,7 @@ class Lexer {
|
|
|
2641
2904
|
keywords;
|
|
2642
2905
|
constructor(source) {
|
|
2643
2906
|
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
|
-
]);
|
|
2907
|
+
this.keywords = new Map(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label, e]) => [label, e.tokenType]));
|
|
2675
2908
|
}
|
|
2676
2909
|
tokenize() {
|
|
2677
2910
|
const tokens = [];
|
|
@@ -2744,6 +2977,8 @@ class Lexer {
|
|
|
2744
2977
|
return this.createToken("MULTIPLY" /* MULTIPLY */, this.advance());
|
|
2745
2978
|
case "/":
|
|
2746
2979
|
return this.createToken("DIVIDE" /* DIVIDE */, this.advance());
|
|
2980
|
+
case "%":
|
|
2981
|
+
return this.createToken("MODULO" /* MODULO */, this.advance());
|
|
2747
2982
|
case "=":
|
|
2748
2983
|
return this.createToken("EQUAL" /* EQUAL */, this.advance());
|
|
2749
2984
|
case "<":
|
|
@@ -2762,6 +2997,10 @@ class Lexer {
|
|
|
2762
2997
|
return this.createToken("LEFT_PAREN" /* LEFT_PAREN */, this.advance());
|
|
2763
2998
|
case ")":
|
|
2764
2999
|
return this.createToken("RIGHT_PAREN" /* RIGHT_PAREN */, this.advance());
|
|
3000
|
+
case "[":
|
|
3001
|
+
return this.createToken("LEFT_BRACKET" /* LEFT_BRACKET */, this.advance());
|
|
3002
|
+
case "]":
|
|
3003
|
+
return this.createToken("RIGHT_BRACKET" /* RIGHT_BRACKET */, this.advance());
|
|
2765
3004
|
default:
|
|
2766
3005
|
throw new Error(`Caractère non reconnu '${char}' à la ligne ${this.line}, colonne ${this.column}`);
|
|
2767
3006
|
}
|
|
@@ -2954,87 +3193,117 @@ class Lexer {
|
|
|
2954
3193
|
}
|
|
2955
3194
|
}
|
|
2956
3195
|
|
|
3196
|
+
// src/semantic/semantic-analyzer.ts
|
|
3197
|
+
class SemanticAnalyzer {
|
|
3198
|
+
symbolTable = {
|
|
3199
|
+
symbols: new Map,
|
|
3200
|
+
children: [],
|
|
3201
|
+
scopeName: "global"
|
|
3202
|
+
};
|
|
3203
|
+
errors = [];
|
|
3204
|
+
analyze(ast) {
|
|
3205
|
+
this.walk(ast);
|
|
3206
|
+
return { symbolTable: this.symbolTable, errors: this.errors };
|
|
3207
|
+
}
|
|
3208
|
+
walk(node) {
|
|
3209
|
+
switch (node.type) {
|
|
3210
|
+
case "VAR_DECLARATION" /* VAR_DECLARATION */:
|
|
3211
|
+
this.collectVarDeclaration(node);
|
|
3212
|
+
break;
|
|
3213
|
+
case "ARRAY_DECLARATION" /* ARRAY_DECLARATION */:
|
|
3214
|
+
this.collectArrayDeclaration(node);
|
|
3215
|
+
break;
|
|
3216
|
+
default:
|
|
3217
|
+
node.children?.forEach((child) => this.walk(child));
|
|
3218
|
+
break;
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
collectVarDeclaration(node) {
|
|
3222
|
+
const algoType = node.value;
|
|
3223
|
+
const line = node.token?.line ?? 0;
|
|
3224
|
+
const column = node.token?.column ?? 0;
|
|
3225
|
+
const position = node.token?.position ?? 0;
|
|
3226
|
+
for (const child of node.children ?? []) {
|
|
3227
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
3228
|
+
continue;
|
|
3229
|
+
const name = child.value;
|
|
3230
|
+
if (this.validateName(name, line, column, position)) {
|
|
3231
|
+
this.defineSymbol(name, algoType, line, column);
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
collectArrayDeclaration(node) {
|
|
3236
|
+
const elemType = node.value;
|
|
3237
|
+
const sizeNode = node.children?.[0];
|
|
3238
|
+
const size = sizeNode?.value;
|
|
3239
|
+
const typeLabel = `TABLEAU[${size}] DE ${elemType}`;
|
|
3240
|
+
const line = node.token?.line ?? 0;
|
|
3241
|
+
const column = node.token?.column ?? 0;
|
|
3242
|
+
const position = node.token?.position ?? 0;
|
|
3243
|
+
for (const child of (node.children ?? []).slice(1)) {
|
|
3244
|
+
if (child.type !== "VARIABLE" /* VARIABLE */)
|
|
3245
|
+
continue;
|
|
3246
|
+
const name = child.value;
|
|
3247
|
+
if (this.validateName(name, line, column, position)) {
|
|
3248
|
+
this.defineSymbol(name, typeLabel, line, column);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
validateName(name, line, column, position) {
|
|
3253
|
+
if (BUILTIN_NAMES.has(name)) {
|
|
3254
|
+
this.errors.push({
|
|
3255
|
+
type: "ERROR",
|
|
3256
|
+
message: `Le nom '${name}' est une fonction intégrée et ne peut pas être utilisé comme nom de variable`,
|
|
3257
|
+
line,
|
|
3258
|
+
column,
|
|
3259
|
+
position,
|
|
3260
|
+
code: "BUILTIN_SHADOWING",
|
|
3261
|
+
explanation: `'${name}' est une fonction intégrée d'AlgoLang`,
|
|
3262
|
+
suggestion: `Choisissez un autre nom, par exemple '${name}Valeur' ou 'mon${name.charAt(0).toUpperCase()}${name.slice(1)}'`
|
|
3263
|
+
});
|
|
3264
|
+
return false;
|
|
3265
|
+
}
|
|
3266
|
+
if (this.symbolTable.symbols.has(name)) {
|
|
3267
|
+
this.errors.push({
|
|
3268
|
+
type: "ERROR",
|
|
3269
|
+
message: `La variable '${name}' est déjà déclarée`,
|
|
3270
|
+
line,
|
|
3271
|
+
column,
|
|
3272
|
+
position,
|
|
3273
|
+
code: "DUPLICATE_VARIABLE",
|
|
3274
|
+
explanation: "Chaque variable doit avoir un nom unique dans son scope",
|
|
3275
|
+
suggestion: `Choisissez un autre nom pour la variable '${name}'`
|
|
3276
|
+
});
|
|
3277
|
+
return false;
|
|
3278
|
+
}
|
|
3279
|
+
return true;
|
|
3280
|
+
}
|
|
3281
|
+
defineSymbol(name, type, line, column) {
|
|
3282
|
+
const info = {
|
|
3283
|
+
name,
|
|
3284
|
+
type,
|
|
3285
|
+
scope: this.symbolTable.scopeName,
|
|
3286
|
+
line,
|
|
3287
|
+
column
|
|
3288
|
+
};
|
|
3289
|
+
this.symbolTable.symbols.set(name, info);
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
|
|
2957
3293
|
// src/parser/parser.ts
|
|
2958
3294
|
class Parser {
|
|
2959
3295
|
tokens;
|
|
2960
3296
|
current = 0;
|
|
2961
3297
|
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
|
-
]);
|
|
3298
|
+
reservedKeywords = new Set(Object.entries(KEYWORDS).filter(([, e]) => e.tokenType !== null).map(([label]) => label.toLowerCase()));
|
|
2995
3299
|
constructor(tokens) {
|
|
2996
3300
|
this.tokens = tokens;
|
|
2997
|
-
this.symbolTable = {
|
|
2998
|
-
symbols: new Map,
|
|
2999
|
-
children: [],
|
|
3000
|
-
scopeName: "global"
|
|
3001
|
-
};
|
|
3002
3301
|
}
|
|
3003
3302
|
isReservedKeyword(identifier) {
|
|
3004
3303
|
return this.reservedKeywords.has(identifier.toLowerCase());
|
|
3005
3304
|
}
|
|
3006
3305
|
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);
|
|
3306
|
+
return Object.values(KEYWORDS).some((e) => e.tokenType === tokenType);
|
|
3038
3307
|
}
|
|
3039
3308
|
createReservedKeywordError(identifier, token) {
|
|
3040
3309
|
this.errors.push({
|
|
@@ -3049,30 +3318,22 @@ class Parser {
|
|
|
3049
3318
|
});
|
|
3050
3319
|
}
|
|
3051
3320
|
parse() {
|
|
3321
|
+
let ast;
|
|
3052
3322
|
try {
|
|
3053
|
-
|
|
3054
|
-
return {
|
|
3055
|
-
ast: program2,
|
|
3056
|
-
errors: this.errors,
|
|
3057
|
-
symbolTable: this.symbolTable
|
|
3058
|
-
};
|
|
3323
|
+
ast = this.parseProgram();
|
|
3059
3324
|
} 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
|
-
};
|
|
3325
|
+
ast = { type: "PROGRAM" /* PROGRAM */, children: [] };
|
|
3326
|
+
this.errors.push({
|
|
3327
|
+
type: "ERROR",
|
|
3328
|
+
message: error instanceof Error ? error.message : "Erreur de parsing inconnue",
|
|
3329
|
+
line: 1,
|
|
3330
|
+
column: 1,
|
|
3331
|
+
position: 0,
|
|
3332
|
+
code: "PARSE_ERROR"
|
|
3333
|
+
});
|
|
3075
3334
|
}
|
|
3335
|
+
const { symbolTable, errors: semanticErrors } = new SemanticAnalyzer().analyze(ast);
|
|
3336
|
+
return { ast, errors: [...this.errors, ...semanticErrors], symbolTable };
|
|
3076
3337
|
}
|
|
3077
3338
|
parseProgram() {
|
|
3078
3339
|
const programToken = this.expect("PROGRAMME" /* PROGRAM */, 'Le programme doit commencer par le mot-clé "programme"');
|
|
@@ -3088,17 +3349,25 @@ class Parser {
|
|
|
3088
3349
|
}
|
|
3089
3350
|
parseBlock() {
|
|
3090
3351
|
const declarations = this.parseDeclarations();
|
|
3352
|
+
const subprograms = [];
|
|
3353
|
+
while (this.check(["FONCTION" /* FUNCTION */, "PROCEDURE" /* PROCEDURE */])) {
|
|
3354
|
+
if (this.check("FONCTION" /* FUNCTION */)) {
|
|
3355
|
+
subprograms.push(this.parseFunctionDeclaration());
|
|
3356
|
+
} else {
|
|
3357
|
+
subprograms.push(this.parseProcedureDeclaration());
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3091
3360
|
const compoundStatement = this.parseCompoundStatement();
|
|
3092
3361
|
return {
|
|
3093
3362
|
type: "BLOCK" /* BLOCK */,
|
|
3094
|
-
children: [declarations, compoundStatement]
|
|
3363
|
+
children: [declarations, ...subprograms, compoundStatement]
|
|
3095
3364
|
};
|
|
3096
3365
|
}
|
|
3097
3366
|
parseDeclarations() {
|
|
3098
3367
|
const declarations = [];
|
|
3099
3368
|
while (this.check("VAR" /* VAR */)) {
|
|
3100
3369
|
this.advance();
|
|
3101
|
-
while (!this.check("DEBUT" /* BEGIN */) && !this.isAtEnd()) {
|
|
3370
|
+
while (!this.check("DEBUT" /* BEGIN */) && !this.check("FONCTION" /* FUNCTION */) && !this.check("PROCEDURE" /* PROCEDURE */) && !this.isAtEnd()) {
|
|
3102
3371
|
const declaration = this.parseVariableDeclaration();
|
|
3103
3372
|
declarations.push(declaration);
|
|
3104
3373
|
if (!this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
@@ -3139,31 +3408,27 @@ class Parser {
|
|
|
3139
3408
|
}
|
|
3140
3409
|
} while (true);
|
|
3141
3410
|
this.expect("COLON" /* COLON */, "Deux-points attendus après les identificateurs");
|
|
3411
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
3412
|
+
this.advance();
|
|
3413
|
+
this.expect("LEFT_BRACKET" /* LEFT_BRACKET */, '"[" attendu après TABLEAU');
|
|
3414
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille du tableau attendue");
|
|
3415
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu après la taille du tableau');
|
|
3416
|
+
this.expect("DE" /* DE */, '"DE" attendu après la taille du tableau');
|
|
3417
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments du tableau attendu");
|
|
3418
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
3419
|
+
const size = parseInt(sizeToken.value);
|
|
3420
|
+
return {
|
|
3421
|
+
type: "ARRAY_DECLARATION" /* ARRAY_DECLARATION */,
|
|
3422
|
+
value: elemType,
|
|
3423
|
+
children: [
|
|
3424
|
+
{ type: "LITERAL" /* LITERAL */, value: size },
|
|
3425
|
+
...identifiers.map((name) => ({ type: "VARIABLE" /* VARIABLE */, value: name }))
|
|
3426
|
+
],
|
|
3427
|
+
token: elemTypeToken
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3142
3430
|
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de variable attendu");
|
|
3143
3431
|
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
3432
|
return {
|
|
3168
3433
|
type: "VAR_DECLARATION" /* VAR_DECLARATION */,
|
|
3169
3434
|
value: type,
|
|
@@ -3180,9 +3445,16 @@ class Parser {
|
|
|
3180
3445
|
while (!this.check("FIN" /* END */) && !this.isAtEnd()) {
|
|
3181
3446
|
const statement = this.parseStatement();
|
|
3182
3447
|
statements.push(statement);
|
|
3448
|
+
const isBlockStatement = [
|
|
3449
|
+
"IF_STATEMENT" /* IF_STATEMENT */,
|
|
3450
|
+
"WHILE_STATEMENT" /* WHILE_STATEMENT */,
|
|
3451
|
+
"FOR_STATEMENT" /* FOR_STATEMENT */,
|
|
3452
|
+
"REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
3453
|
+
"COMPOUND_STATEMENT" /* COMPOUND_STATEMENT */
|
|
3454
|
+
].includes(statement.type);
|
|
3183
3455
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
3184
3456
|
this.advance();
|
|
3185
|
-
} else if (!this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */)) {
|
|
3457
|
+
} else if (!isBlockStatement && !this.check("FIN" /* END */) && !this.check("FINIF" /* ENDIF */) && !this.check("FINTANTQUE" /* ENDWHILE */) && !this.check("FINPOUR" /* ENDFOR */)) {
|
|
3186
3458
|
this.errors.push({
|
|
3187
3459
|
type: "ERROR",
|
|
3188
3460
|
message: "Point-virgule attendu après l'instruction",
|
|
@@ -3225,6 +3497,16 @@ class Parser {
|
|
|
3225
3497
|
if (this.check("ECRIRE" /* WRITE */)) {
|
|
3226
3498
|
return this.parseWriteStatement();
|
|
3227
3499
|
}
|
|
3500
|
+
if (this.check("RETOURNER" /* RETURN */)) {
|
|
3501
|
+
return this.parseReturnStatement();
|
|
3502
|
+
}
|
|
3503
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_PAREN" /* LEFT_PAREN */) {
|
|
3504
|
+
const nameToken = this.advance();
|
|
3505
|
+
return this.parseFunctionCall(nameToken);
|
|
3506
|
+
}
|
|
3507
|
+
if (this.check("IDENTIFIER" /* IDENTIFIER */) && this.peekAhead(1)?.type === "LEFT_BRACKET" /* LEFT_BRACKET */) {
|
|
3508
|
+
return this.parseArrayAssignment();
|
|
3509
|
+
}
|
|
3228
3510
|
return this.parseAssignment();
|
|
3229
3511
|
}
|
|
3230
3512
|
parseIfStatement(isElseIf = false) {
|
|
@@ -3344,14 +3626,23 @@ class Parser {
|
|
|
3344
3626
|
parseReadStatement() {
|
|
3345
3627
|
const readToken = this.advance();
|
|
3346
3628
|
this.expect("LEFT_PAREN" /* LEFT_PAREN */, 'Parenthèse ouvrante attendue après "lire"');
|
|
3347
|
-
let
|
|
3629
|
+
let target;
|
|
3348
3630
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
3349
|
-
variable = this.advance();
|
|
3631
|
+
const variable = this.advance();
|
|
3632
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
3633
|
+
this.advance();
|
|
3634
|
+
const index = this.parseExpression();
|
|
3635
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
3636
|
+
target = { type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: variable.value, children: [index], token: variable };
|
|
3637
|
+
} else {
|
|
3638
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
3639
|
+
}
|
|
3350
3640
|
} else {
|
|
3351
3641
|
const currentToken = this.peek();
|
|
3352
3642
|
if (this.isKeywordToken(currentToken.type)) {
|
|
3353
|
-
variable = this.advance();
|
|
3643
|
+
const variable = this.advance();
|
|
3354
3644
|
this.createReservedKeywordError(variable.value, variable);
|
|
3645
|
+
target = { type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable };
|
|
3355
3646
|
} else {
|
|
3356
3647
|
this.expect("IDENTIFIER" /* IDENTIFIER */, 'Variable attendue dans "lire"');
|
|
3357
3648
|
throw new Error('Variable attendue dans "lire"');
|
|
@@ -3360,9 +3651,7 @@ class Parser {
|
|
|
3360
3651
|
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, 'Parenthèse fermante attendue dans "lire"');
|
|
3361
3652
|
return {
|
|
3362
3653
|
type: "READ_STATEMENT" /* READ_STATEMENT */,
|
|
3363
|
-
children: [
|
|
3364
|
-
{ type: "VARIABLE" /* VARIABLE */, value: variable.value, token: variable }
|
|
3365
|
-
],
|
|
3654
|
+
children: [target],
|
|
3366
3655
|
token: readToken
|
|
3367
3656
|
};
|
|
3368
3657
|
}
|
|
@@ -3490,7 +3779,7 @@ class Parser {
|
|
|
3490
3779
|
}
|
|
3491
3780
|
parseMultiplicativeExpression() {
|
|
3492
3781
|
let left = this.parseUnaryExpression();
|
|
3493
|
-
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */])) {
|
|
3782
|
+
while (this.check(["MULTIPLY" /* MULTIPLY */, "DIVIDE" /* DIVIDE */, "MODULO" /* MODULO */])) {
|
|
3494
3783
|
const operator = this.advance();
|
|
3495
3784
|
const right = this.parseUnaryExpression();
|
|
3496
3785
|
left = {
|
|
@@ -3539,7 +3828,7 @@ class Parser {
|
|
|
3539
3828
|
const token2 = this.advance();
|
|
3540
3829
|
return {
|
|
3541
3830
|
type: "LITERAL" /* LITERAL */,
|
|
3542
|
-
value: token2.value === "vrai",
|
|
3831
|
+
value: token2.value.toLowerCase() === "vrai",
|
|
3543
3832
|
token: token2
|
|
3544
3833
|
};
|
|
3545
3834
|
}
|
|
@@ -3553,6 +3842,20 @@ class Parser {
|
|
|
3553
3842
|
}
|
|
3554
3843
|
if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
|
|
3555
3844
|
const token2 = this.advance();
|
|
3845
|
+
if (this.check("LEFT_PAREN" /* LEFT_PAREN */)) {
|
|
3846
|
+
return this.parseFunctionCall(token2);
|
|
3847
|
+
}
|
|
3848
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
3849
|
+
this.advance();
|
|
3850
|
+
const index = this.parseExpression();
|
|
3851
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
3852
|
+
return {
|
|
3853
|
+
type: "ARRAY_ACCESS" /* ARRAY_ACCESS */,
|
|
3854
|
+
value: token2.value,
|
|
3855
|
+
children: [index],
|
|
3856
|
+
token: token2
|
|
3857
|
+
};
|
|
3858
|
+
}
|
|
3556
3859
|
if (this.isReservedKeyword(token2.value)) {
|
|
3557
3860
|
this.createReservedKeywordError(token2.value, token2);
|
|
3558
3861
|
}
|
|
@@ -3596,14 +3899,14 @@ class Parser {
|
|
|
3596
3899
|
parseRepeatStatement() {
|
|
3597
3900
|
const repeatToken = this.advance();
|
|
3598
3901
|
const statements = [];
|
|
3599
|
-
while (!this.check("
|
|
3902
|
+
while (!this.check("JUSQU'A" /* UNTIL */) && !this.isAtEnd()) {
|
|
3600
3903
|
const statement = this.parseStatement();
|
|
3601
3904
|
statements.push(statement);
|
|
3602
3905
|
if (this.check("SEMICOLON" /* SEMICOLON */)) {
|
|
3603
3906
|
this.advance();
|
|
3604
3907
|
}
|
|
3605
3908
|
}
|
|
3606
|
-
this.expect("
|
|
3909
|
+
this.expect("JUSQU'A" /* UNTIL */, `"jusqu'a" attendu à la fin de la boucle repeter`);
|
|
3607
3910
|
const condition = this.parseExpression();
|
|
3608
3911
|
return {
|
|
3609
3912
|
type: "REPEAT_STATEMENT" /* REPEAT_STATEMENT */,
|
|
@@ -3611,6 +3914,127 @@ class Parser {
|
|
|
3611
3914
|
token: repeatToken
|
|
3612
3915
|
};
|
|
3613
3916
|
}
|
|
3917
|
+
parseFunctionCall(nameToken) {
|
|
3918
|
+
this.advance();
|
|
3919
|
+
const args = [];
|
|
3920
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
3921
|
+
args.push(this.parseExpression());
|
|
3922
|
+
while (this.check("COMMA" /* COMMA */)) {
|
|
3923
|
+
this.advance();
|
|
3924
|
+
args.push(this.parseExpression());
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")` attendu après les arguments');
|
|
3928
|
+
return {
|
|
3929
|
+
type: "FUNCTION_CALL" /* FUNCTION_CALL */,
|
|
3930
|
+
value: nameToken.value,
|
|
3931
|
+
children: args,
|
|
3932
|
+
token: nameToken
|
|
3933
|
+
};
|
|
3934
|
+
}
|
|
3935
|
+
parseArrayAssignment() {
|
|
3936
|
+
const nameToken = this.advance();
|
|
3937
|
+
this.advance();
|
|
3938
|
+
const index = this.parseExpression();
|
|
3939
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, `"]" attendu après l'index`);
|
|
3940
|
+
this.expect("ASSIGN" /* ASSIGN */, `":=" attendu dans l'affectation`);
|
|
3941
|
+
const value = this.parseExpression();
|
|
3942
|
+
return {
|
|
3943
|
+
type: "ASSIGNMENT" /* ASSIGNMENT */,
|
|
3944
|
+
children: [
|
|
3945
|
+
{ type: "ARRAY_ACCESS" /* ARRAY_ACCESS */, value: nameToken.value, children: [index], token: nameToken },
|
|
3946
|
+
value
|
|
3947
|
+
],
|
|
3948
|
+
token: nameToken
|
|
3949
|
+
};
|
|
3950
|
+
}
|
|
3951
|
+
parseReturnStatement() {
|
|
3952
|
+
const token = this.advance();
|
|
3953
|
+
const expr = this.parseExpression();
|
|
3954
|
+
return {
|
|
3955
|
+
type: "RETURN_STATEMENT" /* RETURN_STATEMENT */,
|
|
3956
|
+
children: [expr],
|
|
3957
|
+
token
|
|
3958
|
+
};
|
|
3959
|
+
}
|
|
3960
|
+
parseParameterList() {
|
|
3961
|
+
this.expect("LEFT_PAREN" /* LEFT_PAREN */, '"(" attendu dans la déclaration');
|
|
3962
|
+
const params = [];
|
|
3963
|
+
if (!this.check("RIGHT_PAREN" /* RIGHT_PAREN */)) {
|
|
3964
|
+
params.push(this.parseParameter());
|
|
3965
|
+
while (this.check("SEMICOLON" /* SEMICOLON */) || this.check("COMMA" /* COMMA */)) {
|
|
3966
|
+
this.advance();
|
|
3967
|
+
if (this.check("RIGHT_PAREN" /* RIGHT_PAREN */))
|
|
3968
|
+
break;
|
|
3969
|
+
params.push(this.parseParameter());
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
this.expect("RIGHT_PAREN" /* RIGHT_PAREN */, '")" attendu après les paramètres');
|
|
3973
|
+
return { type: "PARAMETER_LIST" /* PARAMETER_LIST */, children: params };
|
|
3974
|
+
}
|
|
3975
|
+
parseParameter() {
|
|
3976
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de paramètre attendu");
|
|
3977
|
+
this.expect("COLON" /* COLON */, '":" attendu après le nom du paramètre');
|
|
3978
|
+
if (this.check("TABLEAU" /* ARRAY */)) {
|
|
3979
|
+
this.advance();
|
|
3980
|
+
let size;
|
|
3981
|
+
if (this.check("LEFT_BRACKET" /* LEFT_BRACKET */)) {
|
|
3982
|
+
this.advance();
|
|
3983
|
+
const sizeToken = this.expect("NUMBER" /* NUMBER */, "Taille attendue");
|
|
3984
|
+
this.expect("RIGHT_BRACKET" /* RIGHT_BRACKET */, '"]" attendu');
|
|
3985
|
+
size = parseInt(sizeToken.value);
|
|
3986
|
+
}
|
|
3987
|
+
this.expect("DE" /* DE */, '"DE" attendu après TABLEAU');
|
|
3988
|
+
const elemTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type des éléments attendu");
|
|
3989
|
+
const elemType = this.tokenTypeToDataType(elemTypeToken.type);
|
|
3990
|
+
const typeLabel = size !== undefined ? `TABLEAU[${size}] DE ${elemType}` : `TABLEAU DE ${elemType}`;
|
|
3991
|
+
return {
|
|
3992
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
3993
|
+
value: nameToken.value,
|
|
3994
|
+
token: nameToken,
|
|
3995
|
+
symbolInfo: { name: nameToken.value, type: typeLabel, scope: "param" }
|
|
3996
|
+
};
|
|
3997
|
+
}
|
|
3998
|
+
const typeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type du paramètre attendu");
|
|
3999
|
+
const paramType = this.tokenTypeToDataType(typeToken.type);
|
|
4000
|
+
return {
|
|
4001
|
+
type: "PARAMETER" /* PARAMETER */,
|
|
4002
|
+
value: nameToken.value,
|
|
4003
|
+
token: nameToken,
|
|
4004
|
+
symbolInfo: { name: nameToken.value, type: paramType, scope: "param" }
|
|
4005
|
+
};
|
|
4006
|
+
}
|
|
4007
|
+
parseFunctionDeclaration() {
|
|
4008
|
+
const token = this.advance();
|
|
4009
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de fonction attendu");
|
|
4010
|
+
const paramList = this.parseParameterList();
|
|
4011
|
+
this.expect("COLON" /* COLON */, '":" attendu pour le type de retour');
|
|
4012
|
+
const retTypeToken = this.expect(["ENTIER" /* INTEGER */, "REEL" /* REAL */, "BOOLEEN" /* BOOLEAN */, "CHAINE" /* STRING */], "Type de retour attendu");
|
|
4013
|
+
const retType = this.tokenTypeToDataType(retTypeToken.type);
|
|
4014
|
+
const body = this.parseCompoundStatement();
|
|
4015
|
+
return {
|
|
4016
|
+
type: "FUNCTION_DECLARATION" /* FUNCTION_DECLARATION */,
|
|
4017
|
+
value: nameToken.value,
|
|
4018
|
+
children: [
|
|
4019
|
+
paramList,
|
|
4020
|
+
{ type: "TYPE_SPECIFIER" /* TYPE_SPECIFIER */, value: retType },
|
|
4021
|
+
body
|
|
4022
|
+
],
|
|
4023
|
+
token
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
parseProcedureDeclaration() {
|
|
4027
|
+
const token = this.advance();
|
|
4028
|
+
const nameToken = this.expect("IDENTIFIER" /* IDENTIFIER */, "Nom de procédure attendu");
|
|
4029
|
+
const paramList = this.parseParameterList();
|
|
4030
|
+
const body = this.parseCompoundStatement();
|
|
4031
|
+
return {
|
|
4032
|
+
type: "PROCEDURE_DECLARATION" /* PROCEDURE_DECLARATION */,
|
|
4033
|
+
value: nameToken.value,
|
|
4034
|
+
children: [paramList, body],
|
|
4035
|
+
token
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
3614
4038
|
check(type) {
|
|
3615
4039
|
if (this.isAtEnd())
|
|
3616
4040
|
return false;
|
|
@@ -3760,6 +4184,8 @@ class CodeGenerator {
|
|
|
3760
4184
|
return this.generateBlock(node);
|
|
3761
4185
|
case "VAR_DECLARATION":
|
|
3762
4186
|
return this.generateVariableDeclaration(node);
|
|
4187
|
+
case "ARRAY_DECLARATION":
|
|
4188
|
+
return this.generateArrayDeclaration(node);
|
|
3763
4189
|
case "COMPOUND_STATEMENT":
|
|
3764
4190
|
return this.generateCompoundStatement(node);
|
|
3765
4191
|
case "ASSIGNMENT":
|
|
@@ -3776,6 +4202,12 @@ class CodeGenerator {
|
|
|
3776
4202
|
return this.generateReadStatement(node);
|
|
3777
4203
|
case "WRITE_STATEMENT":
|
|
3778
4204
|
return this.generateWriteStatement(node);
|
|
4205
|
+
case "FUNCTION_DECLARATION":
|
|
4206
|
+
return this.generateFunctionDeclaration(node);
|
|
4207
|
+
case "PROCEDURE_DECLARATION":
|
|
4208
|
+
return this.generateProcedureDeclaration(node);
|
|
4209
|
+
case "RETURN_STATEMENT":
|
|
4210
|
+
return this.generateReturnStatement(node);
|
|
3779
4211
|
case "BINARY_OP":
|
|
3780
4212
|
return this.generateBinaryOp(node);
|
|
3781
4213
|
case "UNARY_OP":
|
|
@@ -3784,6 +4216,10 @@ class CodeGenerator {
|
|
|
3784
4216
|
return this.generateLiteral(node);
|
|
3785
4217
|
case "VARIABLE":
|
|
3786
4218
|
return this.generateVariable(node);
|
|
4219
|
+
case "ARRAY_ACCESS":
|
|
4220
|
+
return this.generateArrayAccess(node);
|
|
4221
|
+
case "FUNCTION_CALL":
|
|
4222
|
+
return this.generateFunctionCall(node);
|
|
3787
4223
|
default:
|
|
3788
4224
|
this.errors.push({
|
|
3789
4225
|
type: "ERROR",
|
|
@@ -3798,14 +4234,25 @@ class CodeGenerator {
|
|
|
3798
4234
|
}
|
|
3799
4235
|
generateProgram(node) {
|
|
3800
4236
|
const lines = [];
|
|
4237
|
+
const block = node.children?.[0];
|
|
3801
4238
|
lines.push(`// Programme: ${node.value}`);
|
|
4239
|
+
if (block?.children) {
|
|
4240
|
+
for (const child of block.children) {
|
|
4241
|
+
if (child.type === "FUNCTION_DECLARATION" || child.type === "PROCEDURE_DECLARATION") {
|
|
4242
|
+
lines.push(this.generateNode(child));
|
|
4243
|
+
lines.push("");
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
3802
4247
|
lines.push("async function main() {");
|
|
3803
4248
|
this.indentLevel++;
|
|
3804
|
-
if (
|
|
3805
|
-
for (const child of
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
4249
|
+
if (block?.children) {
|
|
4250
|
+
for (const child of block.children) {
|
|
4251
|
+
if (child.type !== "FUNCTION_DECLARATION" && child.type !== "PROCEDURE_DECLARATION") {
|
|
4252
|
+
const childCode = this.generateNode(child);
|
|
4253
|
+
if (childCode) {
|
|
4254
|
+
lines.push(this.indent(childCode));
|
|
4255
|
+
}
|
|
3809
4256
|
}
|
|
3810
4257
|
}
|
|
3811
4258
|
}
|
|
@@ -3843,7 +4290,8 @@ class CodeGenerator {
|
|
|
3843
4290
|
for (const child of node.children) {
|
|
3844
4291
|
const childCode = this.generateNode(child);
|
|
3845
4292
|
if (childCode) {
|
|
3846
|
-
|
|
4293
|
+
const stmt = child.type === "FUNCTION_CALL" ? childCode.replace(/^\(|\)$/g, "") + ";" : childCode;
|
|
4294
|
+
lines.push(this.indent(stmt));
|
|
3847
4295
|
}
|
|
3848
4296
|
}
|
|
3849
4297
|
}
|
|
@@ -3854,15 +4302,107 @@ class CodeGenerator {
|
|
|
3854
4302
|
if (!node.children || node.children.length < 2) {
|
|
3855
4303
|
return "";
|
|
3856
4304
|
}
|
|
3857
|
-
const
|
|
4305
|
+
const target = node.children[0];
|
|
3858
4306
|
const expression = node.children[1];
|
|
3859
|
-
if (!
|
|
4307
|
+
if (!target || !expression) {
|
|
3860
4308
|
return "";
|
|
3861
4309
|
}
|
|
3862
|
-
const variableName = variable.value;
|
|
3863
4310
|
const expressionCode = this.generateNode(expression);
|
|
4311
|
+
if (target.type === "ARRAY_ACCESS") {
|
|
4312
|
+
const arrayName = target.value;
|
|
4313
|
+
const index = this.generateNode(target.children[0]);
|
|
4314
|
+
return `${arrayName}[${index}] = ${expressionCode};`;
|
|
4315
|
+
}
|
|
4316
|
+
const variableName = target.value;
|
|
3864
4317
|
return `${variableName} = ${expressionCode};`;
|
|
3865
4318
|
}
|
|
4319
|
+
generateArrayDeclaration(node) {
|
|
4320
|
+
if (!node.children || node.children.length < 2)
|
|
4321
|
+
return "";
|
|
4322
|
+
const size = node.children[0].value;
|
|
4323
|
+
const elemType = node.value;
|
|
4324
|
+
const defaultValue = elemType === "BOOLEEN" ? "false" : elemType === "CHAINE" ? '""' : "0";
|
|
4325
|
+
const names = node.children.slice(1).map((c) => c.value);
|
|
4326
|
+
return names.map((name) => `let ${name} = new Array(${size}).fill(${defaultValue});`).join(`
|
|
4327
|
+
`);
|
|
4328
|
+
}
|
|
4329
|
+
generateArrayAccess(node) {
|
|
4330
|
+
const name = node.value;
|
|
4331
|
+
const index = this.generateNode(node.children[0]);
|
|
4332
|
+
return `${name}[${index}]`;
|
|
4333
|
+
}
|
|
4334
|
+
generateFunctionCall(node) {
|
|
4335
|
+
const name = node.value;
|
|
4336
|
+
const args = (node.children ?? []).map((c) => {
|
|
4337
|
+
const code = this.generateNode(c);
|
|
4338
|
+
if (c.type === "VARIABLE") {
|
|
4339
|
+
const sym = this.symbolTable.symbols.get(c.value);
|
|
4340
|
+
if (sym?.type?.startsWith("TABLEAU"))
|
|
4341
|
+
return `${code}.slice()`;
|
|
4342
|
+
}
|
|
4343
|
+
return code;
|
|
4344
|
+
});
|
|
4345
|
+
switch (name.toLowerCase()) {
|
|
4346
|
+
case "abs":
|
|
4347
|
+
return `Math.abs(${args[0]})`;
|
|
4348
|
+
case "max":
|
|
4349
|
+
return `Math.max(${args.join(", ")})`;
|
|
4350
|
+
case "min":
|
|
4351
|
+
return `Math.min(${args.join(", ")})`;
|
|
4352
|
+
case "mod":
|
|
4353
|
+
return `(${args[0]} % ${args[1]})`;
|
|
4354
|
+
case "racine_carree":
|
|
4355
|
+
return `Math.sqrt(${args[0]})`;
|
|
4356
|
+
case "taille":
|
|
4357
|
+
return `${args[0]}.length`;
|
|
4358
|
+
case "sous_chaine":
|
|
4359
|
+
return `${args[0]}.substring(${args[1]}, ${args[1]} + ${args[2]})`;
|
|
4360
|
+
case "concat":
|
|
4361
|
+
return args.join(" + ");
|
|
4362
|
+
case "entier_en_reel":
|
|
4363
|
+
return `${args[0]}`;
|
|
4364
|
+
case "reel_en_entier":
|
|
4365
|
+
return `Math.trunc(${args[0]})`;
|
|
4366
|
+
default:
|
|
4367
|
+
return `(await ${name}(${args.join(", ")}))`;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
generateFunctionDeclaration(node) {
|
|
4371
|
+
const name = node.value;
|
|
4372
|
+
const [paramList, , body] = node.children ?? [];
|
|
4373
|
+
const params = this.generateParamList(paramList);
|
|
4374
|
+
const bodyCode = this.generateNode(body);
|
|
4375
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
4376
|
+
this.indentLevel++;
|
|
4377
|
+
lines.push(this.indent(bodyCode));
|
|
4378
|
+
this.indentLevel--;
|
|
4379
|
+
lines.push("}");
|
|
4380
|
+
return lines.join(`
|
|
4381
|
+
`);
|
|
4382
|
+
}
|
|
4383
|
+
generateProcedureDeclaration(node) {
|
|
4384
|
+
const name = node.value;
|
|
4385
|
+
const [paramList, body] = node.children ?? [];
|
|
4386
|
+
const params = this.generateParamList(paramList);
|
|
4387
|
+
const bodyCode = this.generateNode(body);
|
|
4388
|
+
const lines = [`async function ${name}(${params}) {`];
|
|
4389
|
+
this.indentLevel++;
|
|
4390
|
+
lines.push(this.indent(bodyCode));
|
|
4391
|
+
this.indentLevel--;
|
|
4392
|
+
lines.push("}");
|
|
4393
|
+
return lines.join(`
|
|
4394
|
+
`);
|
|
4395
|
+
}
|
|
4396
|
+
generateParamList(paramList) {
|
|
4397
|
+
if (!paramList?.children)
|
|
4398
|
+
return "";
|
|
4399
|
+
return paramList.children.map((p) => p.value).join(", ");
|
|
4400
|
+
}
|
|
4401
|
+
generateReturnStatement(node) {
|
|
4402
|
+
if (!node.children?.[0])
|
|
4403
|
+
return "return;";
|
|
4404
|
+
return `return ${this.generateNode(node.children[0])};`;
|
|
4405
|
+
}
|
|
3866
4406
|
generateIfStatement(node) {
|
|
3867
4407
|
if (!node.children || node.children.length < 2) {
|
|
3868
4408
|
return "";
|
|
@@ -3964,34 +4504,20 @@ class CodeGenerator {
|
|
|
3964
4504
|
return code;
|
|
3965
4505
|
}
|
|
3966
4506
|
generateReadStatement(node) {
|
|
3967
|
-
if (!node.children || node.children.length === 0)
|
|
4507
|
+
if (!node.children || node.children.length === 0)
|
|
3968
4508
|
return "";
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
if (!variable) {
|
|
4509
|
+
const target = node.children[0];
|
|
4510
|
+
if (!target)
|
|
3972
4511
|
return "";
|
|
4512
|
+
const isArray = target.type === "ARRAY_ACCESS";
|
|
4513
|
+
const varName = isArray ? `${target.value}[${this.generateNode(target.children[0])}]` : target.value;
|
|
4514
|
+
const symbolName = target.value;
|
|
4515
|
+
const symbolInfo = this.symbolTable.symbols.get(symbolName);
|
|
4516
|
+
const typeHint = symbolInfo?.type ?? "";
|
|
4517
|
+
if (typeHint.startsWith("ENTIER") || typeHint === "REEL" || typeHint.startsWith("TABLEAU")) {
|
|
4518
|
+
return `${varName} = parseInt(await lire(""));`;
|
|
3973
4519
|
}
|
|
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
|
-
}
|
|
4520
|
+
return `${varName} = await lire("");`;
|
|
3995
4521
|
}
|
|
3996
4522
|
generateWriteStatement(node) {
|
|
3997
4523
|
if (!node.children || node.children.length === 0) {
|
|
@@ -4090,6 +4616,8 @@ class CodeGenerator {
|
|
|
4090
4616
|
return ">";
|
|
4091
4617
|
case ">=":
|
|
4092
4618
|
return ">=";
|
|
4619
|
+
case "%":
|
|
4620
|
+
return "%";
|
|
4093
4621
|
case "et":
|
|
4094
4622
|
return "&&";
|
|
4095
4623
|
case "ou":
|