(mlang)= # Le compilateur MLang ## Installer Mlang est implanté en OCaml. L'utilisation du gestionnaire de paquets OCaml `opam` est fortement recommandée. Vous pouvez l'installer via votre gestionnaire de paquet préféré s'il distribue `opam`, ou bien en vous referant à la [documentation d'opam](https://opam.ocaml.org/doc/Install.html). Mlang a également quelques autres dépendances, dont une vers la librairie de calcul de flotants MPFR. Si vous êtes sous Debian, vous pouvez simplement utiliser la commande suivante pour installer toutes les dépendances externes à OCaml : ``` $ sudo apt install \ libgmp-dev \ libmpfr-dev \ git \ patch \ unzip \ bubblewrap \ bzip2 \ opam ``` Si vous n'avez jamais utilisé `opam`, commencez par lancer : ``` $ opam init $ opam update ``` Enfin, vous pouvre initialiser le projet `mlang` avec ``` $ make init ``` **Note pour les utilisateurs d'opam confirmés** : la commande `make init` crée un switch local où seront installées les dépendances OCaml de mlang. Cette commande initialise le dossier `ir-calcul` dans lequel sont poussés le code de calcul primitif de l'impot sur le revenu. Si besoin, la commande ``` $ make deps ``` réinstallera les dépendances OCaml et mettra à jour le dossier `ir-calcul`. Une fois compilé, vous pouvez soit appeler mlang via la commande : ``` $ opam exec -- mlang ``` tant que vous êtes dans le dossier depuis lequel vous avez compilé, soit l'installer localement avec la commande : ``` opam install ./mlang.opam ``` ## Utiliser MLang Le binaire `mlang` prend en argument le fichier *M* à exécuter. Il peut également prendre en argument une liste de fichiers, auquel cas leur traitement sera équivalent au traitement d'un seul et même fichier dans lequel serait concatené le contenu de chaque fichier. %% Les options principales sont : * `-A`: le nom de l'application à traiter; * `-b`: le mode d'utilisation, ou `backend`; * `--mpp_function`: le nom de la fonction principale à traiter. ### Mode interpreteur Le mode interpreteur de `mlang` utilise un fichier *IRJ* (voir {ref}`syntax_irj`) pour exécuter le code *M* directement depuis sa représentation abstraite. Voici une commande simple pour invoquer l'interpreteur : ``` $ mlang test.m \ -A mon_application \ -b interpreter \ --mpp_function hello_world \ --run_test test.irj \ --without_dfgip_m ``` ### Mode transpilation Le mode transpilation de `mlang` permet de traduire le code *M* dans un autre langage. En 2025, seul le langage C est supporté. Voici une commande simple pour traduire un fichier *M* en *C* : ``` $ mlang test.m \ -A mon_application \ -b dgfip_c \ --mpp_function hello_world --dgfip_options='' --output output/mon-test.c ``` NB: le dossier `output` doit avoir été créé en amont. ### Options DGFiP Les options DGFiP sont à usage interne. Elles sont spécifiées dans l'option `--dgfip_options`. ``` -b VAL Set application to "batch" (b0 = normal, b1 = with EBCDIC sort) -D Generate labels for output variables -g Generate for test (debug) -I Generate immediate controls -k VAL (absent=0) Number of debug files -L Generate calls to ticket function -m VAL (absent=1991) Income year -O Optimize generated code (inline min_max function) -o Generate overlays -P Primitive calculation only -r Pass TGV pointer as register in rules -R Set application to both "iliad" and "pro" -s Strip comments from generated output -S Generate separate controls -t Generate trace code -U Set application to "cfir" -x Generate cross references -X Generate global extraction -Z Colored output in chainings ``` ## Comportement de Mlang % A faire : des modules sont référencés plus bas. % Ca serait bien d'avoir des liens vers les docs de ces modules. Le compilateur Mlang effectue son traitement en quatre étapes : * la traduction dans un format abstrait interne; * un pré-traitement pour le simplifier; * une vérification pour analyser la cohérence du code; * le traitement du code M, que ce soit son interprétation ou sa compilation. Le module `Driver` (et plus précisément la fonction `Driver.main`) correspond au point d'entrée de mlang. ### Traduction Le langage M est parsé selon les règles spécifiées dans {ref}`syntax`. Elle est effecuée par les modules `Mparser`, `Mlexer` et `Parse_utils`. ### Pré-traitement Le prétraitement est une opération purement syntaxique. Elle est effectuée par les modules `Expander` et `Mir`. Son but est triple : - éliminer les constructions relatives aux applications non-sélectionnées ; - remplacer les constantes par leur valeur numérique ; - remplacer les expressions numériques débutant par `somme` avec des additions ; - éliminer les ``s en les remplaçant par des séries de ``s. Toute substitution transformant un programme M syntaxiquement valide en un texte ne correspondant à aucun programme M provoque l'échec du traitement. #### Cas des applications On élimine du programme M: - les déclarations des applications non-sélectionnées ; - les déclarations des enchaîneurs ne spécifiant pas une application sélectionnée ; - les déclarations des règles ne spécifiant pas une application sélectionnée ; - les déclarations des vérifications ne spécifiant pas une application sélectionnée ; - les déclarations des cibles ne spécifiant pas une application sélectionnée. Dans les déclarations des règles spécifiant une application sélectionnée, on élimine les enchaîneurs qui ne sont plus déclarés dans le programme M obtenu. #### Cas des constantes Pour prétraiter un programme, on le parcourt du début à la fin. Pour chaque `` rencontrée, si elle correspond à une contante défini précédemment, alors il est remplacé dans le programme par la valeur numérique correspondante. Un intervalle de la forme `..` est converti par substitution de la constante `const` en l'intervalle `..`, avec `fin` le naturel correspondant à `const`. *Exemple* : considérons le programme suivant : ``` fin : const = 10; … pour i = 9-fin : Bi = Bi + fin; ``` Par substitution de la constante `fin`, il est remplacé dans un premier temps remplacée par le programme : ``` fin : const = 10;` … pour i = 9..10 : Bi = Bi + fin; ``` puis dans un second temps par le programme : ``` fin : const = 10; … pour i = 9..10 : Bi = Bi + 10; ``` #### Interprétation des indices Pour rappel, les *indices* ont la forme suivante : ``` ::= ; … ::= = , … ``` avec : * ` ::= [a-z]` * ` ::= [A-Z]` * ` ::= + | .. | (.. | - )?` Chaque `` associe à une lettre minuscule une série de chaînes de caractères de même taille. Cette série est composée de la succession de chaque à droite du symbole `=`. Les séries associées aux sont définis comme suit : * `+` est la série composée de chacune des majuscules prises séparément, donc de taille 1 (*par exemple* : `AXF` représente la série `A`, `X`, `F`); * `****` est la série composée de toutes les majuscules comprises entre /début/ et /fin/, bornes comprises, suivant l'ordre alphabétique, donc de taille 1 (*par exemple* : `A..D` représente la série `A`, `B`, `C`, `D`); * `..` est la série composée de tous les nombres naturels entre `début` et `fin`, bornes comprises, la taille des éléments étant égale à la taille du plus grand naturel en base 10; les naturels trop petits pour avoir la taille requise sont complétés par des 0 à gauche (*par exemple* : `9..11` représente la série `09`, `10`, `11`); * `-` est converti lors du prétraitement des constantes en un intervalle de la forme `..`. #### Cas des expressions `somme(…)` Pour une expression numérique `somme ( : )`, l'expression *expr* sera remplacée par la somme des expressions construites ainsi : pour chaque chaîne de caractères de la série introduite par *ind*, les symboles apparaîssant dans *expr* sont remplacés par ceux dans lesquels la lettre minuscule de /ind/ est substituéé par la chaine de caractère. % La somme ainsi générée est parenthésée. Une expression numérique `somme ( ; : )`, est remplacée par la somme des expressions *`somme`*` : expr'> )` avec `expr'` prenant sa valeur dans la série `sub(ind, expr)`. % La procédure est ensuite appliquée récursivement à cette somme. % La somme ainsi générée est parenthésée. % Notons que les sous-sommes récursivement produites n'ont pas besoin de l'être car l'addition est associative. On applique cette transformation à toutes les expressions `somme(…)` tant qu'il en existe dans le programme. **Exemple**. Considérons l'expression suivante : * `somme(i = XZ ; j = 9..10 : Bi + Bj)` Elle est dans un premier temps remplacée par la somme suivante : * `(somme(j = 9..10 : BX + Bj) + somme(j = 9..10 : BZ + Bj))` puis dans un second temps par la somme : * `(BX + B09 + BX + B10 + BZ + B09 + BZ + B10)` On remarque que seule l'expression globale est parenthésée car l'addition est associative. #### Cas des multi-formules On commence par substituer toutes les expressions `somme(…)` apparaissant dans les multi-formules. % Puis on transforme les multi-formules en séries de formules en suivant la méthode décrite ci-dessous. Pour chaque multi-formule rencontrée, de la forme `pour : `, dès que les constantes apparaîssant dans les ont été substitués, on remplace cette multi-formule par la série de formules générée de la manière suivante. Pour une multi-formule `pour : `, la formule `f` sera remplacée par la série des formules construites ainsi : pour chaque chaîne de caractères de la série introduite par `ind`, les symboles apparaîssant dans `f` sont remplacés par ceux dans lesquels la lettre minuscule de `ind` est substituéé par la chaïne de caractère. % On notera `sub(ind, f)` la série constituée des formules ainsi régérées. Une multi-formule `pour ; : `, est remplacée par la série des multi-formules `pour : f'>` avec `f'` prenant sa valeur dans la série `sub(ind, f)`. % La procédure est ensuite appliquée récursivement à ces multi-formules. **Exemple**. Considérons la multi-formule suivante : ``` pour i = XZ ; j = 9..10 : Bi = Bi + Bj; ``` Elle est dans un premier temps remplacée par la série suivante : ``` pour j = 9..10 : BX = BX + Bj; pour j = 9..10 : BZ = BZ + Bj; ``` Puis dans un second temps par la série des formules : ``` BX = BX + B09; BX = BX + B10; BZ = BZ + B09; BZ = BZ + B10; ``` ### Vérification de cohérence De nombreuses constructions sont valides à la traduction, mais brisent certains invariants nécessaires à la bonne exécution du code : double déclaration d'attributs, nom de variable déjà utilisé, variable mal typée... L'ensemble de ces vérifications est accessible dans le module `Validator` du frontend. Le sous module `Validator.Err` définit l'ensemble des erreurs levées par cette étape de vérification. **NB** : seule la première erreur rencontrée par le validateur est levée. ### Traitement #### Interpreteur L'interpréteur utilise la représentation interne du code M pour lancer le calcul à partir d'un fichier IRJ (voir {ref}`syntax_irj`). L'interprétation est effecuée par le module `Test_interpreter`. #### Transpilation La transpilation traduit le code dans le langage spécifié (en 2025, seul le C est transpilable). La transpilation est effecutée dans le module `Bir_to_dgfip_c`.