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 *)