Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
105 changes: 95 additions & 10 deletions doc/mlang.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,63 @@

# 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.
Expand All @@ -10,8 +67,9 @@ traitement sera équivalent au traitement d'un seul et même fichier dans lequel
serait concatené le contenu de chaque fichier.
%%

Les deux options principales sont :
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
Expand Down Expand Up @@ -49,7 +107,8 @@ NB: le dossier `output` doit avoir été créé en amont.

### Options DGFiP

Les options DGFiP sont à usage interne.
Les options DGFiP sont à usage interne. Elles sont spécifiées dans
l'option `--dgfip_options`.

```
-b VAL
Expand Down Expand Up @@ -94,16 +153,29 @@ Les options DGFiP sont à usage interne.
-Z Colored output in chainings
```

## Comportement de mlang
## 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

% A faire : traduction du M en M_AST
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.
Son but est triple :
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
Expand Down Expand Up @@ -289,18 +361,31 @@ BX = BX + B09;
BX = BX + B10;
BZ = BZ + B09;
BZ = BZ + B10;
````
```

### Vérification de cohérence

% A faire : documentation de la verification
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

Lecture du fichier IRJ et interpretation du code.
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

Ecriture du code C équivalent au code M.
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`.
138 changes: 65 additions & 73 deletions src/mlang/index.mld
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,88 @@
The Mlang compiler has a traditionnal architecture consisting of various
intermediate representations going from the source code to the target backend.

{1 M Frontend}
{1 Frontend}

First, the source code is parsed according to the Menhir grammar specified in
{!module: Mlang.Mparser}. The grammar is not exactly LR(1) so we rely on {!module: Mlang.Parse_utils}
to backtrack, especially on symbol parsing. The target intermediate representation
is {!module: Mlang.Mast}, which is very close to the concrete syntax and can be
printed using {!module: Mlang.Format_mast}.
First, the source code is parsed according to the Menhir grammar specified in {!module: Mlang.M_frontend.Mparser}.
The grammar is not exactly LR(1) so we rely on {!module: Mlang.M_frontend.Parse_utils} to backtrack, especially on symbol parsing. The target intermediate representation is {!module: Mlang.M_frontend.Mast}, which is very close to the concrete syntax and can be printed using {!module: Mlang.M_frontend.Format_mast}.
The frontend also handles ast expansion with {!module: Mlang.M_frontend.Expander} and validation with {!module: Mlang.M_frontend.Validator}.

{!modules: Mlang.Mast Mlang.Format_mast Mlang.Mparser Mlang.Parse_utils }
{!modules:
Mlang.M_frontend.Expander
Mlang.M_frontend.Mast
Mlang.M_frontend.Mlexer
Mlang.M_frontend.Mparser
Mlang.M_frontend.Parse_utils
Mlang.M_frontend.Validator }

{1 M Intermediate representation}
{1 Intermediate Representation}

The M language has a lot of weird syntactic sugars and constructs linked to its
usage inside multiple DGFiP applications. {!module: Mlang.Mast_to_mir } extracts from the
AST the computational core corresponding to a DGFiP application into the M Variable
Graph ({!module: Mlang.Mir}), which consists basically of a flat map of all the definitions of
usage inside multiple DGFiP applications. {!module: Mlang.M_frontend.Mast_to_mir} extracts from the AST of {!module: Mlang.M_frontend.Mast} the computational core corresponding to a DGFiP application into the M Variable Graph ({!module: Mlang.M_ir.Mir}), which consists basically of a flat map of all the definitions of
the variables used in the application. The type system of M is very primitive,
and basically all programs typecheck ; however {!module: Mlang.Mir_typechecker} provides a top-down typechecking algorithm to split simple variables from tables.
and basically all programs typecheck ; however {!module: Mlang.M_frontend.Validator} provides a top-down typechecking algorithm to split simple variables from tables.

{!modules:
Mlang.M_ir.Com
Mlang.M_ir.Format_mir
Mlang.M_ir.Mir
Mlang.M_ir.Mir_interpreter
Mlang.M_ir.Mir_number
Mlang.M_ir.Mir_roundops }

At this point, the {!module: Mlang.Mir_dependency_graph} modules interprets the MIR as a first-class
graph and computes various reachability and cycle analysis in order to determine
the computational flow inside the program. This dependency order is encapsulated
with the program in {!module: Mlang.Mir_interface}.
Some typechecking and macro expansion is performed
by {!module: Mlang.Mir_typechecker}.
{1 Testing}

{!modules: Mlang.Mir Mlang.Mast_to_mir Mlang.Format_mir Mlang.Mir_typechecker Mlang.Mir_interface Mlang.Mir_dependency_graph }

{1 M++ Frontend }

The M code can be called several times through a sort of driver that saves
some variables between calls. This driver is encoded in the M++ domain-specific
language that Mlang handles together with the M. M++ is parsed with
{!module: Mlang.Mpp_parser}

{!modules: Mlang.Mpp_parser Mlang.Mpp_ast}

{1 M++ Intermediate representation }

From the M++ AST, {!module: Mlang.Mpp_frontend} translates to {!module: Mlang.Mpp_ir}
by eliminating some syntactic sugars.

{!modules: Mlang.Mpp_frontend Mlang.Mpp_ir Mlang.Mpp_format}

{1 M/M++ Common backend intermediate representation}

The module {!module: Mlang.Mpp_ir_to_bir} performs the inlining of all the M
calls in the M++ programs and yields a single program in a new common intermediate
representation, {!module: Mlang.Bir}. Inputs and outputs for this representation
can be specified using {!module: Mlang.Bir_interface}.

The BIR representation is equipped with a fully fledged interpreter,
{!module: Mlang.Bir_interpreter}. The interpreter is instrumented for code coverage
via {!module: Mlang.Bir_instrumentation}, and can be parametrized via multiple
sorts of custom floating point values for precision testing ({!module: Mlang.Bir_number}).

{!modules: Mlang.Bir_instrumentation Mlang.Bir_interface Mlang.Bir_interpreter Mlang.Bir_number Mlang.Bir Mlang.Format_bir}

{1 Optimizations }

While BIR is an AST-based representation, {!module: Mlang.Oir} defines a dual
of BIR in a control-flow-graph (CFG) for. You can go back and forth between
those two implementations using {!module: Mpp.Bir_to_oir}.

OIR is the right place to perform some basic program optimizations. {!module: Mlang.Inlining}
expands some of the small rules into their full expressions, while
{!module: Mlang.Partial_evaluation} and {!module: Mlang.Dead_code_removal} simplify the
program depending on the inputs and outputs. The main optimizing loop is
implemented in {!module: Mlang.Oir_optimizations}.

{!modules: Mlang.Oir_optimizations Mlang.Inlining Mlang.Partial_evaluation Mlang.Dead_code_removal}

{1 Testing M and M++ programs }

Mlang comes with a testing framework for M and M++ programs that is based on
Mlang comes with a testing framework for M programs that is based on
the test format used by the DGFiP. The test files are parsed with
{!module: Mlang.Test_parser} into {!module: Mlang.Test_ast}. Then, single
or batch testing can be performed using {!module: Mlang.Test_interpreter}.
{!module: Mlang.Irj_utils.Irj_file}. Then, single
or batch testing can be performed using {!module: Mlang.Irj_utils.Test_interpreter}.

{!modules: Mlang.Test_ast Mlang.Test_interpreter Mlang.Test_parser}
{!modules:
Mlang.Irj_utils.Irj_ast
Mlang.Irj_utils.Irj_file
Mlang.Irj_utils.Irj_lexer
Mlang.Irj_utils.Irj_parser
Mlang.Irj_utils.ParserMessages
Mlang.Irj_utils.Test_interpreter }

{1 Compiling M and M++ programs}
{1 Compiling}

M/M++ programs can be compiled to other programming languages using
several backends that take BIR and produce source code files in their
respective languages.

{!modules: Mlang.Bir_to_python Mlang.Bir_to_c Mlang.Bir_to_dgfip_c}
{!modules:
Mlang.Backend_compilers.Bir_to_dgfip_c
Mlang.Backend_compilers.DecoupledExpr
Mlang.Backend_compilers.Dgfip_compir_files
Mlang.Backend_compilers.Dgfip_gen_files
Mlang.Backend_compilers.Dgfip_varid
Mlang.Backend_compilers.Prelude
}

{1 Utils }

{!modules: Mlang.Cli Mlang.Errors Mlang.Pos}
{!modules:
Mlang.Utils.CharMap
Mlang.Utils.Cli
Mlang.Utils.Config
Mlang.Utils.Dgfip_m
Mlang.Utils.Dgfip_options
Mlang.Utils.Dict
Mlang.Utils.Errors
Mlang.Utils.IntMap
Mlang.Utils.IntSet
Mlang.Utils.IntSetMap
Mlang.Utils.MapExt
Mlang.Utils.Pos
Mlang.Utils.Pp
Mlang.Utils.SetExt
Mlang.Utils.SetSetExt
Mlang.Utils.Sorting
Mlang.Utils.StrMap
Mlang.Utils.StrSet
Mlang.Utils.StrSetMap
Mlang.Utils.StrSetSet
Mlang.Utils.Strings
Mlang.Utils.TopologicalSorting
}
22 changes: 22 additions & 0 deletions src/mlang/mlang.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(* Copyright (C) 2019-2021 Inria, contributor: Denis Merigoux
<denis.merigoux@inria.fr>

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>. *)

module Backend_compilers = Backend_compilers
module Driver = Driver
module Irj_utils = Irj_utils
module M_ir = M_ir
module M_frontend = M_frontend
module Utils = Utils
Loading