diff --git a/.github/workflows/publish_doc.yml b/.github/workflows/publish_doc.yml
index 716fcc165..40702611c 100644
--- a/.github/workflows/publish_doc.yml
+++ b/.github/workflows/publish_doc.yml
@@ -44,11 +44,11 @@ jobs:
- name: Build
run: |
eval $(opam env)
- make build-doc
+ make doc
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
# if: ${{ github.ref == 'refs/heads/main' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: _build/default/_doc/_html/
+ publish_dir: _build/default/full-doc/html/
diff --git a/Makefile b/Makefile
index 52a0a137b..3adc2622e 100644
--- a/Makefile
+++ b/Makefile
@@ -37,18 +37,5 @@ all: FORCE quick_test tests test_dgfip_c_backend
clean: FORCE remise_a_zero_versionnage
$(call make_in,$(DGFIP_DIR),clean_backend_all)
rm -f doc/doc.html
+ rm -rf examples/doc
dune clean
-
-doc-deps: FORCE
- python3 -m venv .venv
- .venv/bin/pip install sphinx myst-parser
-
-sphinx-doc: FORCE
- @command -v .venv/bin/sphinx-build >/dev/null 2>&1 || \
- { echo "Pour construire la documentation, vous avez besoin de sphinx-build avec \
- l'extension 'myst-parser'. Lancez `make doc-deps`."; exit 1; }
- rm -rf _build/default/doc/*
- cp -rf doc/* _build/default/doc/
- mkdir -p examples/doc
- .venv/bin/sphinx-build -M html _build/default/doc/ examples/doc
- .venv/bin/sphinx-build -M latexpdf _build/default/doc/ examples/doc
diff --git a/doc/architecture.png b/doc/architecture.png
new file mode 100644
index 000000000..20c96e2f8
Binary files /dev/null and b/doc/architecture.png differ
diff --git a/doc/conf.py b/doc/conf.py
index 5b231de11..ef7f41d8f 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -24,4 +24,7 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'alabaster'
-html_static_path = ['_static']
+html_static_path = ['_static','_static/dev']
+
+# When building, we move the dev documentation in this 'dev' folder.
+html_extra_path = ['_static/dev']
diff --git a/doc/exemples/fonctions.md b/doc/exemples/fonctions.md
index a2a7f7795..9400809dc 100644
--- a/doc/exemples/fonctions.md
+++ b/doc/exemples/fonctions.md
@@ -14,8 +14,9 @@ evenement
: valeur ev_val
: variable ev_var;
-X : calculee mon_attribut = 0 : "";
-TAB : tableau[10] calculee mon_attribut = 2 : "";
+X : calculee mon_attribut = 0 : "" type REEL;
+Y : calculee mon_attribut = 1 : "";
+TAB : tableau[10] calculee mon_attribut = 2 : "" type ENTIER;
cible init_tab:
application : mon_application;
@@ -32,45 +33,54 @@ iterer : variable I : entre 0..(taille(TAB) - 1) increment 1 : dans (
cible test_abs:
application : mon_application;
afficher "\n__ABS__";
-afficher "\n abs(indefini) = ";
-afficher (abs(indefini));
-afficher "\n abs(1) = ";
+afficher indenter(2);
+afficher "\nabs(indefini) = ";
+afficher (abs(indefini));
+afficher "\nabs(1) = ";
afficher (abs(1));
-afficher "\n abs(-1) = ";
+afficher "\nabs(-1) = ";
afficher (abs(-1));
afficher "\n";
+afficher indenter(-2);
cible test_arr:
application : mon_application;
afficher "\n__ARR__";
-afficher "\n arr(indefini) = ";
+afficher indenter(2);
+afficher "\narr(indefini) = ";
afficher (arr(indefini));
-afficher "\n arr(1.8) = ";
+afficher "\narr(1.8) = ";
afficher (arr(1.8));
-afficher "\n arr(-1.7) = ";
+afficher "\ arr(-1.7) = ";
afficher (arr(-1.7));
afficher "\n";
+afficher indenter(-2);
cible test_attribut:
application : mon_application;
afficher "\n__ATTRIBUT__";
-afficher "\n attribut(X, mon_attribut) = ";
+afficher indenter(2);
+afficher "\nattribut(X, mon_attribut) = ";
afficher (attribut(X, mon_attribut));
-afficher "\n attribut(TAB, mon_attribut) = ";
+afficher "\nattribut(TAB, mon_attribut) = ";
afficher (attribut(TAB, mon_attribut));
afficher "\n";
+afficher indenter(-2);
cible test_champ_evenement_base:
application : mon_application;
-afficher "\n champ_evenement(0, ev_val) = ";
+afficher indenter(2);
+afficher "\nchamp_evenement(0, ev_val) = ";
afficher (champ_evenement (0,ev_val));
-afficher "\n champ_evenement(0, ev_var) = ";
+afficher "\nchamp_evenement(0, ev_var) = ";
afficher (champ_evenement (0,ev_var));
afficher "\n";
+afficher indenter(-2);
cible test_champ_evenement:
application : mon_application;
afficher "\n__CHAMP_EVENEMENT__";
+afficher indenter(2);
arranger_evenements
: ajouter 1
: dans (
@@ -79,75 +89,87 @@ arranger_evenements
champ_evenement (0,ev_var) reference X;
champ_evenement (0,ev_val) = 42;
X = 2;
- afficher "\n Après avoir initialisé les champs de l'événement:";
+ afficher "\nAprès avoir initialisé les champs de l'événement:";
calculer cible test_champ_evenement_base;
X = indefini;
)
+afficher indenter(-2);
cible test_inf:
application : mon_application;
afficher "\n__INF__";
-afficher "\n inf(indefini) = ";
+afficher indenter(2);
+afficher "\ninf(indefini) = ";
afficher (inf(indefini));
-afficher "\n inf(1.8) = ";
+afficher "\ninf(1.8) = ";
afficher (inf(1.8));
-afficher "\n inf(-1.7) = ";
+afficher "\ninf(-1.7) = ";
afficher (inf(-1.7));
afficher "\n";
+afficher indenter(-2);
cible test_max:
application : mon_application;
afficher "\n__MAX__";
-afficher "\n max(indefini, indefini) = ";
+afficher indenter(2);
+afficher "\nmax(indefini, indefini) = ";
afficher (max(indefini, indefini));
-afficher "\n max(-1, indefini) = ";
+afficher "\nmax(-1, indefini) = ";
afficher (max(-1, indefini));
-afficher "\n max(indefini, -1) = ";
+afficher "\nmax(indefini, -1) = ";
afficher (max(indefini, -1));
-afficher "\n max(1, indefini) = ";
+afficher "\nmax(1, indefini) = ";
afficher (max(1, indefini));
afficher "\n";
+afficher indenter(-2);
cible test_meme_variable:
application : mon_application;
afficher "\n__MEME_VARIABLE__";
-afficher "\n meme_variable(X,X) = ";
+afficher indenter(2);
+afficher "\nmeme_variable(X,X) = ";
afficher (meme_variable(X,X));
-afficher "\n meme_variable(X,TAB) = ";
+afficher "\nmeme_variable(X,TAB) = ";
afficher (meme_variable(X,TAB));
-afficher "\n meme_variable(TAB[0],TAB) = ";
+afficher "\nmeme_variable(TAB[0],TAB) = ";
afficher (meme_variable(TAB[0],TAB));
afficher "\n";
+afficher indenter(-2);
cible test_min:
application : mon_application;
afficher "\n__MIN__";
-afficher "\n min(indefini, indefini) = ";
+afficher indenter(2);
+afficher "\nmin(indefini, indefini) = ";
afficher (min(indefini, indefini));
-afficher "\n min(1, indefini) = ";
+afficher "\nmin(1, indefini) = ";
afficher (min(1, indefini));
-afficher "\n min(indefini, 1) = ";
+afficher "\nmin(indefini, 1) = ";
afficher (min(indefini, 1));
-afficher "\n min(-1, indefini) = ";
+afficher "\nmin(-1, indefini) = ";
afficher (min(-1, indefini));
afficher "\n";
+afficher indenter(-2);
cible test_multimax_base:
application : mon_application;
-afficher "\n multimax(indefini, TAB) = ";
+afficher indenter(2);
+afficher "\nmultimax(indefini, TAB) = ";
afficher (multimax(indefini, TAB));
-afficher "\n multimax(7, TAB) = ";
+afficher "\nmultimax(7, TAB) = ";
afficher (multimax(7, TAB));
-afficher "\n multimax(taille(TAB) + 1, TAB) = ";
+afficher "\nmultimax(taille(TAB) + 1, TAB) = ";
afficher (multimax(taille(TAB) + 1, TAB));
-afficher "\n multimax(0, TAB) = ";
+afficher "\nmultimax(0, TAB) = ";
afficher (multimax(0, TAB));
-afficher "\n multimax(-1, TAB) = ";
+afficher "\nmultimax(-1, TAB) = ";
afficher (multimax(-1, TAB));
+afficher indenter(-2);
cible test_multimax:
application : mon_application;
afficher "\n__MULTIMAX__";
+afficher indenter(2);
afficher "\nAvant initialisation du tableau :";
calculer cible test_multimax_base;
calculer cible init_tab;
@@ -155,16 +177,20 @@ afficher "\nAprès initialisation du tableau :";
calculer cible test_multimax_base;
calculer cible reinit_tab;
afficher "\n";
+afficher indenter(-2);
cible test_nb_evenements_base:
application : mon_application;
-afficher "\n nb_evenements() = ";
+afficher indenter(2);
+afficher "\nnb_evenements() = ";
afficher (nb_evenements ());
afficher "\n";
+afficher indenter(-2);
cible test_nb_evenements:
application : mon_application;
afficher "\n__NB_EVENEMENTS__";
+afficher indenter(2);
afficher "\nAvant la définition d'un événement :";
calculer cible test_nb_evenements_base;
arranger_evenements
@@ -175,89 +201,129 @@ arranger_evenements
)
afficher "\nAprès la définition d'un événement :";
calculer cible test_nb_evenements_base;
+afficher indenter(-2);
cible test_null:
application : mon_application;
afficher "\n__NULL__";
-afficher "\n null(indefini) = ";
+afficher indenter(2);
+afficher "\nnull(indefini) = ";
afficher (null(indefini));
-afficher "\n null(0) = ";
+afficher "\nnull(0) = ";
afficher (null(0));
-afficher "\n null(1) = ";
+afficher "\nnull(1) = ";
afficher (null(1));
afficher "\n";
+afficher indenter(-2);
cible test_positif:
application : mon_application;
afficher "\n__POSITIF__";
-afficher "\n positif(indefini) = ";
+afficher indenter(2);
+afficher "\npositif(indefini) = ";
afficher (positif(indefini));
-afficher "\n positif(0) = ";
+afficher "\npositif(0) = ";
afficher (positif(0));
-afficher "\n positif(1) = ";
+afficher "\npositif(1) = ";
afficher (positif(1));
-afficher "\n positif(-1) = ";
+afficher "\npositif(-1) = ";
afficher (positif(-1));
afficher "\n";
+afficher indenter(-2);
cible test_positif_ou_nul:
application : mon_application;
afficher "\n__POSITIF OU NUL__";
-afficher "\n positif_ou_nul(indefini) = ";
+afficher indenter(2);
+afficher "\npositif_ou_nul(indefini) = ";
afficher (positif_ou_nul(indefini));
-afficher "\n positif_ou_nul(0) = ";
+afficher "\npositif_ou_nul(0) = ";
afficher (positif_ou_nul(0));
-afficher "\n positif_ou_nul(1) = ";
+afficher "\npositif_ou_nul(1) = ";
afficher (positif_ou_nul(1));
-afficher "\n positif_ou_nul(-1) = ";
+afficher "\npositif_ou_nul(-1) = ";
afficher (positif_ou_nul(-1));
afficher "\n";
+afficher indenter(-2);
cible test_present:
application : mon_application;
afficher "\n__PRESENT__";
-afficher "\n present(indefini) = ";
+afficher indenter(2);
+afficher "\npresent(indefini) = ";
afficher (present(indefini));
-afficher "\n present(0) = ";
+afficher "\npresent(0) = ";
afficher (present(0));
-afficher "\n present(1) = ";
+afficher "\npresent(1) = ";
afficher (present(1));
afficher "\n";
+afficher indenter(-2);
cible test_somme:
application : mon_application;
afficher "\n__SOMME__";
-afficher "\n somme() = ";
+afficher indenter(2);
+afficher "\nsomme() = ";
afficher (somme());
-afficher "\n somme(indefini) = ";
+afficher "\nsomme(indefini) = ";
afficher (somme(indefini));
-afficher "\n somme(1, indefini) = ";
+afficher "\nsomme(1, indefini) = ";
afficher (somme(1, indefini));
-afficher "\n somme(1, 2, 3, 4, 5) = ";
+afficher "\nsomme(1, 2, 3, 4, 5) = ";
afficher (somme(1, 2, 3, 4, 5));
afficher "\n";
+afficher indenter(-2);
cible test_supzero:
application : mon_application;
afficher "\n__SUPZERO__";
-afficher "\n supzero(indefini) = ";
+afficher indenter(2);
+afficher "\nsupzero(indefini) = ";
afficher (supzero(indefini));
-afficher "\n supzero(42) = ";
+afficher "\nsupzero(42) = ";
afficher (supzero(42));
-afficher "\n supzero(-1) = ";
+afficher "\nsupzero(-1) = ";
afficher (supzero(-1));
-afficher "\n supzero(0) = ";
+afficher "\nsupzero(0) = ";
afficher (supzero(0));
afficher "\n";
+afficher indenter(-2);
cible test_taille:
application : mon_application;
afficher "\n__TAILLE__";
-afficher "\n taille(TAB) = ";
+afficher indenter(2);
+afficher "\ntaille(TAB) = ";
afficher (taille(TAB));
-afficher "\n taille(X) = ";
+afficher "\ntaille(X) = ";
afficher (taille(X));
afficher "\n";
+afficher indenter(-2);
+
+cible test_type:
+application : mon_application;
+afficher "\n__TYPE__";
+afficher indenter(2);
+afficher "\ntype(X, REEL) = ";
+afficher (type(X, REEL));
+afficher "\ntype(X, ENTIER) = ";
+afficher (type(X, ENTIER));
+afficher "\ntype(TAB, ENTIER) = ";
+afficher (type(TAB, ENTIER));
+afficher "\ntype(Y, ENTIER) = ";
+afficher (type(Y, ENTIER));
+afficher "\ntype(Y, REEL) = ";
+afficher (type(Y, REEL));
+afficher "\ntype(Y, BOOLEEN) = ";
+afficher (type(Y, BOOLEEN));
+afficher "\ntype(Y, DATE_AAAA) = ";
+afficher (type(Y, DATE_AAAA));
+afficher "\ntype(Y, DATE_JJMMAAAA) = ";
+afficher (type(Y, DATE_JJMMAAAA));
+afficher "\ntype(Y, DATE_MM) = ";
+afficher (type(Y, DATE_MM));
+afficher "\n";
+afficher indenter(-2);
cible fun_test:
application : mon_application;
@@ -295,6 +361,8 @@ calculer cible test_somme;
calculer cible test_supzero;
# Taille
calculer cible test_taille;
+# Type
+calculer cible test_type;
```
## Fichier irj : test.irj
diff --git a/doc/fonctions.md b/doc/fonctions.md
index cda4e8ea1..41950f6e6 100644
--- a/doc/fonctions.md
+++ b/doc/fonctions.md
@@ -140,6 +140,11 @@ S'il s'agit d'une variable, `taille` renvoie `1`, sinon elle renvoie la taille d
tableau.
Elle échoue si l'argument est une constante ou une valeur.
+## type(V, type)
+
+Cette fonction prend en argument une variable et un type. Elle
+renvoie 1 si V est du type donné, 0 sinon.
+
% TODO
% ## numero_compil()
diff --git a/doc/index.md b/doc/index.md
index d3cd2fb4b..f0c371bc8 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -34,5 +34,6 @@ exemples/valeurs
exemples/fonctions
```
+## Documentation développeur
-
+- Index
diff --git a/doc/mlang.md b/doc/mlang.md
index bdf599e54..cb481d5f5 100644
--- a/doc/mlang.md
+++ b/doc/mlang.md
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -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`.
diff --git a/makefiles/mlang.mk b/makefiles/mlang.mk
index 26cd49d47..a1277b852 100644
--- a/makefiles/mlang.mk
+++ b/makefiles/mlang.mk
@@ -124,11 +124,36 @@ test_irj: FORCE build-dev
# Doc
##################################################
-doc: FORCE build
+TARGET_DIR_SPHINX_DOC_SRC:= _build/default/sphinx-doc-src
+TARGET_DIR_DOC_BUILD:= _build/default/full-doc
+
+doc-deps: FORCE
+ python3 -m venv .venv
+ .venv/bin/pip install sphinx myst-parser
+
+sphinx-doc: FORCE build dev-doc
+ @command -v .venv/bin/sphinx-build >/dev/null 2>&1 || \
+ { echo "Pour construire la documentation, vous avez besoin de sphinx-build avec \
+ l'extension 'myst-parser'. Lancez `make doc-deps`."; exit 1; }
+ rm -rf $(TARGET_DIR_SPHINX_DOC_SRC)/*
+ cp -r doc $(TARGET_DIR_SPHINX_DOC_SRC)
+ mkdir -p $(TARGET_DIR_DOC_BUILD)/html/_static/dev
+ cp -r $(shell pwd)/_build/default/_doc/_html $(TARGET_DIR_SPHINX_DOC_SRC)/_static/dev
+ .venv/bin/sphinx-build -M html $(TARGET_DIR_SPHINX_DOC_SRC) $(TARGET_DIR_DOC_BUILD)
+ .venv/bin/sphinx-build -M latexpdf $(TARGET_DIR_SPHINX_DOC_SRC) $(TARGET_DIR_DOC_BUILD)
+
+dev-doc: FORCE build
ifeq ($(call is_in,),)
$(call make_in,,$@)
else
dune build @doc
- ln -fs $(shell pwd)/_build/default/_doc/_html/index.html doc/doc.html
endif
+doc: FORCE build dev-doc sphinx-doc
+ifeq ($(call is_in,),)
+ $(call make_in,,$@)
+else
+ rm -rf examples/doc
+ mkdir -p examples/doc
+ cp -r $(TARGET_DIR_DOC_BUILD)/* examples/doc
+endif
diff --git a/src/irj_checker/dune b/src/irj_checker/dune
index dd5165adc..10b783929 100644
--- a/src/irj_checker/dune
+++ b/src/irj_checker/dune
@@ -10,3 +10,7 @@
(package irj_checker)
(public_name irj_checker)
(libraries mlang cmdliner dune-build-info))
+
+(documentation
+ (package irj_checker)
+ (mld_files ("index")))
diff --git a/src/irj_checker/index.mld b/src/irj_checker/index.mld
new file mode 100644
index 000000000..a38df7146
--- /dev/null
+++ b/src/irj_checker/index.mld
@@ -0,0 +1,31 @@
+{0 Irj checker}
+
+The Irj_checker Module is a simple entry point to use the Mlang IRJ file
+parser in order to perform syntactic checks on test files or produce other
+IR test formats.
+
+{1 Usage}
+ irj_checker \[--message-format=VAL\] \[--validation-mode=VAL\] \[OPTION\]…
+ \[FILE\] \[TARGET\]
+
+{1 Arguments}
+ - FILE : Test file (usually with the .irj extension)
+
+ - TARGET (absent=none) :
+ Transformation target, among the following list: none (only checks
+ test syntax), pasp (API PAS-CALC for primitive computation
+ resources), pasc (API PAS-CALC for corrective computation
+ resources).
+
+{1 Options}
+ - -m VAL, --message-format=VAL (absent=human) :
+ Selects the format of error and warning messages emitted by the
+ compiler. If set to human, the messages will be nicely displayed
+ and meant to be read by a human. If set to gnu, the messages will
+ be rendered according to the GNU coding standards.
+
+ - -v VAL, --validation-mode=VAL (absent=strict) :
+ Select the validation criteria. If set to strict, the whole grammar
+ is applied. If set to corrective or primitive, only the
+ corresponding files are accepted, for instance primitive file in
+ corrective mode will raise an error.
\ No newline at end of file
diff --git a/src/mlang/index.mld b/src/mlang/index.mld
index 2c2be98ac..8d409e935 100644
--- a/src/mlang/index.mld
+++ b/src/mlang/index.mld
@@ -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
+ }
diff --git a/src/mlang/m_frontend/expander.mli b/src/mlang/m_frontend/expander.mli
index 0dc7e5e8c..890dc8580 100644
--- a/src/mlang/m_frontend/expander.mli
+++ b/src/mlang/m_frontend/expander.mli
@@ -12,3 +12,9 @@
this program. If not, see . *)
val proceed : Mast.program -> Mast.program
+(** Expands the program by:
+ - keeping elements that only belong to the selected applications defined in
+ {!Utils.Config.application_names};
+ - inlining constant's values and removes their definition;
+ - unrolling loops on variable names (such as "sum(i=05,06,07:Xi)" that
+ becomes "sum(x05, X06, X07)". *)
diff --git a/src/mlang/m_frontend/mast.ml b/src/mlang/m_frontend/mast.ml
index a8fec03fa..aa458d56c 100644
--- a/src/mlang/m_frontend/mast.ml
+++ b/src/mlang/m_frontend/mast.ml
@@ -25,8 +25,7 @@
(**{2 Names}*)
type application = string
-(** Applications are rule annotations. The 3 main DGFiP applications seem to be:
-
+(** Applications are rule annotations. The 3 main DGFiP applications are:
- [batch]: deprecated, used to compute the income tax but not anymore;
- [bareme]: seems to compute the income tax;
- [iliad]: usage unkown, much bigger than [bareme]. *)
@@ -42,12 +41,17 @@ type error_name = string
(**{2 Literals}*)
-type table_size = LiteralSize of int | SymbolSize of string
+type table_size =
+ | LiteralSize of int (** The table size is an integer literal. *)
+ | SymbolSize of string (** The table size is a named constant. *)
+(** Returns the literal value of a table size. This function expects to take a
+ LiteralSize value. *)
let get_table_size = function
| LiteralSize i -> i
| SymbolSize _ -> assert false
+(** Same as [get_table_size] on an optional size. *)
let get_table_size_opt = function
| Some (Pos.Mark (LiteralSize i, pos)) -> Some (Pos.mark i pos)
| None -> None
@@ -75,14 +79,23 @@ type instruction = (Com.m_var_name, error_name) Com.instruction
type m_instruction = instruction Pos.marked
type rule = {
- rule_number : int Pos.marked;
+ rule_number : int Pos.marked; (** The rule's unique identifier. *)
rule_tag_names : string Pos.marked list Pos.marked;
+ (** The rule's domains. *)
rule_apps : application Pos.marked StrMap.t;
+ (** The applications of this rule. The key and the marked bound value seem
+ to be equal. *)
+ (* TODO: simplify map *)
rule_chainings : chaining Pos.marked StrMap.t;
+ (** The chainings of this rule. The key and the marked bound value seem to
+ be equal. *)
+ (* TODO: simplify map *)
rule_tmp_vars : (string Pos.marked * table_size Pos.marked option) list;
- rule_formulaes : instruction Pos.marked list;
- (** A rule can contain many variable definitions *)
+ (** The temporary variables of a rule. *)
+ rule_formulaes : instruction Pos.marked list; (** The rule's instructions. *)
}
+(** A rule is a list of instruction associated to one or several domains and to
+ a list of applications. *)
type target = {
target_name : string Pos.marked;
@@ -93,6 +106,8 @@ type target = {
target_tmp_vars : (string Pos.marked * table_size Pos.marked option) list;
target_prog : m_instruction list;
}
+(** A target is a meta rule that can call rules. Targets are entrypoints of the
+ M program. *)
type 'a domain_decl = {
dom_names : string Pos.marked list Pos.marked list;
@@ -100,6 +115,9 @@ type 'a domain_decl = {
dom_by_default : bool;
dom_data : 'a;
}
+(** The declaration of a generic domain. It will either be a rule domain (with
+ 'a = {!rule_domain_data}) or a verification domain ('a =
+ {!verif_domain_data}). *)
type rule_domain_data = { rdom_computable : bool }
@@ -116,28 +134,46 @@ type rule_domain_decl = rule_domain_data domain_decl
(**{3 Input variables}*)
type variable_attribute = string Pos.marked * int Pos.marked
+(** Variables are defined in families (saisie, calculee, ...) which all define
+ attributes. Each variable must assign a value for each of its attributes. *)
type input_variable = {
- input_name : string Pos.marked;
+ input_name : string Pos.marked; (** The unique identifier of the variable. *)
input_category : string Pos.marked list;
- input_attributes : variable_attribute list;
- input_alias : string Pos.marked; (** Unused for now *)
- input_is_givenback : bool;
+ (** The variable's categories (which define the variable's attributes). *)
+ input_attributes : variable_attribute list; (** The variable's attributes. *)
+ input_alias : string Pos.marked;
+ (** The variable can be used by its name or its alias. *)
+ input_is_givenback : bool; (** Whether it is "restituee" or not. *)
input_description : string Pos.marked;
+ (** The documentation of the variable. *)
input_typ : Com.value_typ Pos.marked option;
+ (** An optional type for the variable. By default, it will be treated as a
+ real. *)
}
+(** An input variable (variables from the family "saisie", which is a keyword).
+*)
type computed_variable = {
- comp_name : string Pos.marked;
+ comp_name : string Pos.marked; (** The unique identifier for the variable. *)
comp_table : table_size Pos.marked option;
- (** size of the table, [None] for non-table variables *)
- comp_attributes : variable_attribute list;
+ (** Size of the table if the variable is a table, [None] for non-table
+ variables *)
+ comp_attributes : variable_attribute list; (** The variable's attributes. *)
comp_category : string Pos.marked list;
+ (** The variable's categories (which define the variable's attributes). *)
comp_typ : Com.value_typ Pos.marked option;
- comp_is_givenback : bool;
+ (** An optional type for the variable. By default, it will be treated as a
+ real, but tests on types will always result to 0. *)
+ comp_is_givenback : bool; (** Whether it is "restituee" or not. *)
comp_description : string Pos.marked;
+ (** The documentation of the variable. *)
}
+(** A computed variable (variables from the family "calculee", which is a
+ keyword. They basically are all the variables that are not from the "saisie"
+ family. *)
+(** A variable declaration. *)
type variable_decl =
| ComputedVar of computed_variable Pos.marked
| ConstVar of string Pos.marked * Com.m_var_name Com.atom Pos.marked
@@ -147,29 +183,35 @@ type variable_decl =
type var_type = Input | Computed
type var_category_decl = {
- var_type : var_type;
- var_category : string Pos.marked list;
+ var_type : var_type; (** Are variables inputs or computed? *)
+ var_category : string Pos.marked list; (** The subcategories. *)
var_attributes : string Pos.marked list;
+ (** The attributes for variables of this category. *)
}
+(** A variable category (and subcategory) declaration. *)
+
+(** {3 Standard categories} *)
-(* standard categories *)
+(** The category for {!Input} variables. *)
let input_category = "saisie"
+(** The category for {!Computed} variables. *)
let computed_category = "calculee"
let base_category = "base"
+(** The category for given back variables. *)
let givenback_category = "restituee"
(**{2 Verification clauses}*)
(** These clauses are expression refering to the variables of the program. They
- seem to be dynamically checked and trigger errors when false. *)
+ are dynamically checked and trigger errors when false. *)
type verification_condition = {
verif_cond_expr : expression Pos.marked;
verif_cond_error : error_name Pos.marked * string Pos.marked option;
- (** A verification condition error can ba associated to a variable *)
+ (** A verification condition error can be associated to a variable *)
}
type verification = {
@@ -198,19 +240,21 @@ type error_ = {
type source_file_item =
| Application of application Pos.marked (** Declares an application *)
| Chaining of chaining Pos.marked * application Pos.marked list
- | VariableDecl of variable_decl
- | EventDecl of Com.event_field list
- | Function of target
- | Rule of rule
- | Target of target
- | Verification of verification
+ (** Declares a chaining with its applications *)
+ | VariableDecl of variable_decl (** Declares a variable *)
+ | EventDecl of Com.event_field list (** Declares an event *)
+ | Function of target (** Defines a function *)
+ | Rule of rule (** Defines a rule *)
+ | Target of target (** Defines a target *)
+ | Verification of verification (** Defines a verification *)
| Error of error_ (** Declares an error *)
| Output of string Pos.marked (** Declares an output variable *)
| Func (** Declares a function, unused *)
| VarCatDecl of var_category_decl Pos.marked
- | RuleDomDecl of rule_domain_decl
- | VerifDomDecl of verif_domain_decl
- | VariableSpaceDecl of Com.variable_space
+ (** Defines a variable category *)
+ | RuleDomDecl of rule_domain_decl (** Declares a rule domain *)
+ | VerifDomDecl of verif_domain_decl (** Declares a verification domain *)
+ | VariableSpaceDecl of Com.variable_space (** Declares a variable space *)
(* TODO: parse something here *)
diff --git a/src/mlang/m_frontend/mparser.mly b/src/mlang/m_frontend/mparser.mly
index 51f38dbe8..91fa7e7b4 100644
--- a/src/mlang/m_frontend/mparser.mly
+++ b/src/mlang/m_frontend/mparser.mly
@@ -484,6 +484,10 @@ rule_etc:
in
aux [] begPos uname
in
+ List.iter (fun (Pos.Mark (i, _)) ->
+ Format.printf "Tag %S@." i;
+ )
+ (Pos.unmark rule_tag_names);
let rule_number =
try Pos.map int_of_string num
with _ ->
diff --git a/src/mlang/m_ir/com.ml b/src/mlang/m_ir/com.ml
index 5e3db3e24..fd47c7f92 100644
--- a/src/mlang/m_ir/com.ml
+++ b/src/mlang/m_ir/com.ml
@@ -53,11 +53,11 @@ module CatVar = struct
let from_string_list = function
| Pos.Mark ([ Pos.Mark ("*", _) ], id_pos) ->
- one (Input (StrSet.one "*")) id_pos
+ one all_inputs id_pos
|> add (Computed { is_base = false }) id_pos
|> add (Computed { is_base = true }) id_pos
| Pos.Mark ([ Pos.Mark ("saisie", _); Pos.Mark ("*", _) ], id_pos) ->
- one (Input (StrSet.one "*")) id_pos
+ one all_inputs id_pos
| Pos.Mark (Pos.Mark ("saisie", _) :: id, id_pos) ->
one (Input (StrSet.from_marked_list id)) id_pos
| Pos.Mark (Pos.Mark ("calculee", _) :: id, id_pos) -> (
diff --git a/src/mlang/m_ir/com.mli b/src/mlang/m_ir/com.mli
index 25ba1f587..81c19012e 100644
--- a/src/mlang/m_ir/com.mli
+++ b/src/mlang/m_ir/com.mli
@@ -1,13 +1,42 @@
+(*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 . *)
+
+(** {2 Categories} *)
+
+(** High level representation of categories of variable.
+ In practice, there only are two types of categories:
+ * "calculee" (Input) variables, with several sub categories;
+ * "saisie" (Computed) variable, that can either be base or non-base.
+
+ Such representation allows to manipulate sets of variable (for example
+ in iterations). *)
module CatVar : sig
- type t = Input of StrSet.t | Computed of { is_base : bool }
+ type t =
+ | Input of StrSet.t
+ (** An input variable category with its sub categories *)
+ | Computed of { is_base : bool }
+ (** A computed var with a flag marking if it is a base varible. *)
val all_inputs : t
+ (** A category containing all inputs. *)
val is_input : t -> bool
+ (** Returns [true] if the category in argument is an input variable category. *)
val is_computed : t -> bool
+ (** Returns [true] if the category in argument is a computed variable category. *)
val pp : Format.formatter -> t -> unit
+ (** Pretty printer. *)
val compare : t -> t -> int
@@ -17,6 +46,13 @@ module CatVar : sig
include MapExt.T with type key = t
val from_string_list : string Pos.marked list Pos.marked -> Pos.t t
+ (** [from_string_list l]
+
+ From a category identifier [l] seen as a list of strings (for example
+ [\["saisie"; "revenu"; "corrective"\]] for the category
+ ["saisie revenu corrective"]),
+ returns a map binding all [CatVar.t] values refered by [l] to the location
+ of [l]. *)
end
type loc = LocComputed | LocBase | LocInput
@@ -28,17 +64,17 @@ module CatVar : sig
module LocMap : MapExt.T with type key = loc
type data = {
- id : t;
- id_str : string;
- id_int : int;
- loc : loc;
- pos : Pos.t;
- attributs : Pos.t StrMap.t;
+ id : t; (** The category *)
+ id_str : string; (** A unique string identifier for the category *)
+ id_int : int; (** A unique int identifier *)
+ loc : loc; (** Quick access to the category kind *)
+ pos : Pos.t; (** Variable category declaration *)
+ attributs : Pos.t StrMap.t; (** Attributes of the category *)
}
+ (** Data for a given category. Defined in the validator. *)
end
-(** Here are all the types a value can have. Date types don't seem to be used at
- all though. *)
+(** Here are all the types a value can have. Used for the M function [type]. *)
type value_typ =
| Boolean
| DateYear
@@ -47,78 +83,123 @@ type value_typ =
| Integer
| Real
+(** {2 Variables} *)
+
+(** Note: TGV stands for "Tableau Général des Variables".
+ This is where the variables are declared. *)
+
type loc_tgv = {
- loc_cat : CatVar.loc;
- loc_idx : int;
- loc_tab_idx : int;
- loc_cat_id : CatVar.t;
- loc_cat_str : string;
- loc_cat_idx : int;
+ loc_cat : CatVar.loc; (** Its category kind *)
+ loc_idx : int; (** TODO *)
+ loc_tab_idx : int; (** TODO *)
+ loc_cat_id : CatVar.t; (** Full details on its category *)
+ loc_cat_str : string; (** String representation of its category *)
+ loc_cat_idx : int; (** The variable's identifier in its category. *)
}
+(** Location of a TGV variable in the compiler's context. *)
-type loc_tmp = { loc_idx : int; loc_tab_idx : int; loc_cat_idx : int }
+type loc_tmp = {
+ loc_idx : int; (** TODO *)
+ loc_tab_idx : int; (** TODO *)
+ loc_cat_idx : int; (** The variable's identifier in its category *)
+}
+(** Location of a temporary variable in the compiler's context. *)
+(** The different kinds of variables; the 'string' is the variable's name. *)
type loc =
- | LocTgv of string * loc_tgv
- | LocTmp of string * loc_tmp
- | LocRef of string * int
+ | LocTgv of string * loc_tgv (** A TGV variable *)
+ | LocTmp of string * loc_tmp (** A temporary variable *)
+ | LocRef of string * int (** A reference (the integer is the 'loc_idx') *)
module Var : sig
type id = int
+ (** Data on a TGV variable. *)
type tgv = {
table : t Array.t option;
+ (** The array of cells if the variable is a table. *)
alias : string Pos.marked option; (** Input variable have an alias *)
descr : string Pos.marked;
(** Description taken from the variable declaration *)
attrs : int Pos.marked StrMap.t;
- cat : CatVar.t;
- is_given_back : bool;
- typ : value_typ option;
+ (** Variable's attributes and their values *)
+ cat : CatVar.t; (** Category *)
+ is_given_back : bool; (** Is the variable 'restituee'? *)
+ typ : value_typ option; (** Optional variable type *)
}
+ (** Exhaustive data on a TGV variable. *)
- and scope = Tgv of tgv | Temp of t Array.t option | Ref
+ (** Where can the variable be found? *)
+ and scope =
+ | Tgv of tgv (** This variable belongs to the TGV. *)
+ | Temp of t Array.t option
+ (** This variable is temporary, maybe an array. *)
+ | Ref (** This references another variable. *)
and t = {
name : string Pos.marked; (** The position is the variable declaration *)
id : id;
+ (** A unique identifier, enforced by [Var.new_{tgv,temp,ref,arg,res}] *)
loc : loc;
- scope : scope;
+ scope : scope; (** Data on the variable. *)
}
+ (** {2 Getters and setters} *)
+
val tgv : t -> tgv
+ (** Returns the tgv data of a variable; fails if it is not a TGV variable. *)
val name : t -> string Pos.marked
+ (** Returns a markzs variable name. *)
val name_str : t -> string
+ (** Same as [name] without the mark. *)
val get_table : t -> t Array.t option
+ (** Returns the table represented by the variable, if relevant.
+ Returns [None] on references. *)
val is_table : t -> bool
+ (** Returns true if the variable represents a table. *)
val set_table : t -> t Array.t option -> t
+ (** Sets a table to the given variable. *)
val cat_var_loc : t -> CatVar.loc
+ (** Returns the category of a TGV variable; fails if it is not a TGV variable. *)
val size : t -> int
+ (** Returns the size of a variable: the size of the array if it is a table; 1
+ otherwise. *)
val alias : t -> string Pos.marked option
+ (** Returns the variable's alias if any, otherwise returns None.
+ Aliases only are set for input variables. *)
val alias_str : t -> string
+ (** Same as [alias] without the mark, or "" if no alias was given. *)
val descr : t -> string Pos.marked
+ (** Returns the description of the variable; fails if it is not a TGV variable. *)
val descr_str : t -> string
+ (** Same as [descr] without the mark. *)
val attrs : t -> int Pos.marked StrMap.t
+ (** Returns the attributes of a TGV variable; fails if it is not a TGV variable. *)
val cat : t -> CatVar.t
+ (** Returns the category of a TGV variable; fails if it is not a TGV variable. *)
val typ : t -> value_typ option
+ (** Returns the type of a TGV variable; fails if it is not a TGV variable. *)
val is_given_back : t -> bool
+ (** Returns [true] if TGV variable in argument is 'restituee'; fails if it is
+ not a TGV variable. *)
val loc_tgv : t -> loc_tgv
+ (** Returns the loc data of a TGV variable; fails if it not a TGV variable. *)
val loc_cat_idx : t -> int
@@ -135,12 +216,13 @@ module Var : sig
val set_loc_tab_idx : t -> int -> t
val is_tgv : t -> bool
+ (** Returns true if it is a TGV variable. *)
val is_temp : t -> bool
+ (** Returns true if it is a temporary variable. *)
val is_ref : t -> bool
-
- val init_loc : CatVar.t -> loc_tgv
+ (** Returns true if it is a reference. *)
val new_tgv :
name:string Pos.marked ->
@@ -152,16 +234,22 @@ module Var : sig
cat:CatVar.t ->
typ:value_typ option ->
t
+ (** Creates a new tgv variable with a unique id. *)
val new_temp : name:string Pos.marked -> table:t Array.t option -> t
+ (** Creates a new temporary variable with a unique id. *)
val new_ref : name:string Pos.marked -> t
+ (** Creates a new reference with a unique id. *)
val new_arg : name:string Pos.marked -> t
+ (** Creates a new temporary variable for a function's argument. *)
val new_res : name:string Pos.marked -> t
+ (** Creates a new temporary variable for a function's result. *)
val pp : Format.formatter -> t -> unit
+ (** Pretty printer. *)
val compare : t -> t -> int
@@ -176,9 +264,16 @@ module Var : sig
val compare_name : string -> string -> int*)
end
-type event_field = { name : string Pos.marked; index : int; is_var : bool }
+type event_field = {
+ name : string Pos.marked; (** The field name *)
+ index : int; (** Position of the event field. Set in the validator. *)
+ is_var : bool; (** Is a reference to a variable or a value? *)
+}
+(** Data on an event field. *)
-type ('n, 'v) event_value = Numeric of 'n | RefVar of 'v
+type ('n, 'v) event_value =
+ | Numeric of 'n
+ | RefVar of 'v (** The values of an event. *)
module DomainId : StrSet.T
@@ -216,19 +311,37 @@ type variable_space = {
type verif_domain = verif_domain_data domain
+(** {2 Values and expressions} *)
+
+(* Careful, the link may be unstable! *)
+(** For a complete documentation on values and simple expressions semantics,
+ check {{:../../../../../syntax.html#valeurs}the full documentation}. *)
+
+(** A literal can either be a float value or undefined. *)
type literal = Float of float | Undefined
+(** A case for switches (aiguillages). *)
type case = Default | Value of literal
(** Unary operators *)
type unop = Not | Minus
-(** Binary operators *)
+(** Binary operators. *)
type binop = And | Or | Add | Sub | Mul | Div | Mod
-(** Comparison operators *)
+(** Comparison operators.
+ Comparison always return 0 or 1 when the compared values are both
+ defined; otherwise, it returns 'undefined'.
+
+ Note that this is even true when comparing undefined with undefined:
+ the proper way to test if a value is undefined is through the function
+ 'PresentFunc' (present). *)
type comp_op = Gt | Gte | Lt | Lte | Eq | Neq
+(** Functions callable in M.
+ There is only a subset of the whole M functions; some are translated
+ into dedicated expressions at parsing (champ_evenement for example
+ is directly transformed into an {!access} to a variable). *)
type func =
| SumFunc (** Sums the arguments *)
| AbsFunc (** Absolute value *)
@@ -236,23 +349,23 @@ type func =
| MaxFunc (** Maximum of a list of values *)
| GtzFunc (** Greater than zero (strict) ? *)
| GtezFunc (** Greater or equal than zero ? *)
- | NullFunc (** Equal to zero ? *)
+ | NullFunc (** Equal to zero *)
| ArrFunc (** Round to nearest integer *)
| InfFunc (** Truncate to integer *)
- | PresentFunc (** Different than zero ? *)
- | Multimax (** ??? *)
- | Supzero (** ??? *)
- | VerifNumber
- | ComplNumber
- | NbEvents
- | Func of string
+ | PresentFunc (** Different than undefined *)
+ | Multimax (** Max of a subset of a table *)
+ | Supzero (** Identity if > 0, undefined otherwise *)
+ | VerifNumber (** -- unsupported -- *)
+ | ComplNumber (** -- unsupported -- *)
+ | NbEvents (** Total number of events *)
+ | Func of string (** A M function *)
(** The M language has an extremely odd way to specify looping. Rather than
having first-class local mutable variables whose value change at each loop
iteration, the M language prefers to use the changing loop parameter to
instantiate the variable names inside the loop. For instance,
- {v somme(i=1..10:Xi) v}
+ {v somme(i=1..9:Xi) v}
should evaluate to the sum of variables [X1], [X2], etc. Parameters can be
number or characters and there can be multiple of them. We have to store all
@@ -267,11 +380,16 @@ type var_name = Normal of string | Generic of var_name_generic
type m_var_name = var_name Pos.marked
type var_space = (m_var_name * int) option
+(** The prefix of a variable that defines its space.
+ No space is equivalent to the default space. *)
+(** A generic representation of an access to a variable, read or write. *)
type 'v access =
- | VarAccess of var_space * 'v
+ | VarAccess of var_space * 'v (** Simple variable occurence *)
| TabAccess of var_space * 'v * 'v m_expression
+ (** Access to a cell of a table *)
| FieldAccess of var_space * 'v m_expression * string Pos.marked * int
+ (** Call to 'champ_evenement' *)
and 'v m_access = 'v access Pos.marked
@@ -293,11 +411,13 @@ and 'v loop_variables =
| ValueSets of 'v loop_variable list
| Ranges of 'v loop_variable list
+(** A set of values. *)
and 'v set_value =
- | FloatValue of float Pos.marked
- | VarValue of 'v m_access
- | IntervalValue of int Pos.marked * int Pos.marked
+ | FloatValue of float Pos.marked (** A literal singleton *)
+ | VarValue of 'v m_access (** A variable singleton *)
+ | IntervalValue of int Pos.marked * int Pos.marked (** A literal interval *)
+(** Basic M expressions. *)
and 'v expression =
| TestInSet of bool * 'v m_expression * 'v set_value list
(** Test if an expression is in a set of value (or not in the set if the
@@ -326,6 +446,7 @@ and 'v expression =
and 'v m_expression = 'v expression Pos.marked
+(** Handling of errors. *)
module Error : sig
type typ = Anomaly | Discordance | Information
@@ -354,15 +475,26 @@ module Error : sig
end
end
+(** {2 Printing in M} *)
+
+(** The print function is treated as a dedicated instruction. *)
+
+(** Where to print *)
type print_std = StdOut | StdErr
+(** How to print a variable *)
type print_info = Name | Alias
+(** Something to print *)
type 'v print_arg =
| PrintString of string
| PrintAccess of print_info * 'v m_access
| PrintIndent of 'v m_expression
+ (** Evaluates the expression and indents as much *)
| PrintExpr of 'v m_expression * int * int
+ (** Prints an expression with a minimal and maximal precision. *)
+
+(** {2 Loops} *)
(** In the M language, you can define multiple variables at once. This is the
way they do looping since the definition can depend on the loop variable
@@ -378,13 +510,17 @@ type 'v formula =
| SingleFormula of 'v formula_decl
| MultipleFormulaes of 'v formula_loop * 'v formula_decl
+(** {2 Stopping} *)
+
type stop_kind =
- | SKApplication (* Leave the whole application *)
- | SKTarget (* Leave the current target *)
- | SKFun (* Leave the current function *)
+ | SKApplication (** Leave the whole application *)
+ | SKTarget (** Leave the current target *)
+ | SKFun (** Leave the current function *)
| SKId of string option
-(* Leave the iterator with the selected var
- (or the current if [None]) *)
+ (** Leave the iterator with the selected var
+ (or the current if [None]) *)
+
+(** {2 Instructions} *)
type ('v, 'e) instruction =
| Affectation of 'v formula Pos.marked
@@ -432,6 +568,8 @@ type ('v, 'e) instruction =
and ('v, 'e) m_instruction = ('v, 'e) instruction Pos.marked
+(** A target is a list of instructions. They are very similar to rules,
+ except targets are entrypoints of the M program. *)
type ('v, 'e) target = {
target_name : string Pos.marked;
target_file : string option;
@@ -444,6 +582,10 @@ type ('v, 'e) target = {
target_nb_refs : int;
target_prog : ('v, 'e) m_instruction list;
}
+(* TODO: target doc *)
+
+(** {2 Utils} *)
+(* TODO: doc *)
val target_is_function : ('v, 'e) target -> bool
@@ -484,6 +626,8 @@ val get_var_name : var_name -> string
val get_normal_var : var_name -> string
+(** {2 Pretty printing functions} *)
+
val format_value_typ : Pp.t -> value_typ -> unit
val format_literal : Pp.t -> literal -> unit
diff --git a/src/mlang/mlang.ml b/src/mlang/mlang.ml
new file mode 100644
index 000000000..44622282d
--- /dev/null
+++ b/src/mlang/mlang.ml
@@ -0,0 +1,22 @@
+(* Copyright (C) 2019-2021 Inria, contributor: Denis Merigoux
+
+
+ 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 . *)
+
+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
diff --git a/src/mlang/utils/config.mli b/src/mlang/utils/config.mli
index 42a1b38bf..a4399afc8 100644
--- a/src/mlang/utils/config.mli
+++ b/src/mlang/utils/config.mli
@@ -10,7 +10,7 @@ type value_sort =
(** Rounding operations to use in the interpreter. They correspond to the
rounding operations used by the DGFiP calculator in different execution
contexts.
-
+
- RODefault: rounding operations used in the PC/single-thread context
- ROMulti: rouding operations used in the PC/multi-thread context
- ROMainframe rounding operations used in the mainframe context *)
@@ -47,8 +47,8 @@ val var_info_flag : bool ref
(** Print infomation about variables declared, defined ou used incorrectly *)
val var_info_debug : string list ref
-(** Prints even more information but only about some variables members of a
- list *)
+(** Prints even more information but only about some variables members of a list
+*)
val warning_flag : bool ref
(** Print warning info *)