fr_anubis_doc.txt 38.6 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046

   
                                        Le Projet Anubis. 
   
                          Petite Documentation sur le Langage Anubis. 
   
                             Copyright (c) Alain Prouté 2001...2005. 
                               
                                     

   
   Auteur: Alain Prouté
   
   Dernière révision de ce fichier: juin 2005. 

   
   
   -------------------------------- Table des Matières -----------------------------------
   
   *** (1) Introduction. 
   
   *** (2). Le langage. 
      *** (2.1) Les symboles. 
      *** (2.2) Opérateurs. 
      *** (2.3) La syntaxe des listes. 
      *** (2.4) Paragraphes. 
         *** (2.4.1) 'read'. 
         *** (2.4.2) 'type'.
         *** (2.4.3) 'define'. 
         *** (2.4.4) 'public'. 
         *** (2.4.5) 'global'. 
      *** (2.5) Types. 
         *** (2.5.1) Types primitifs. 
         *** (2.5.2) Types d'adresse. 
         *** (2.5.3) Types fonctionnels. 
         *** (2.5.4) Types définis. 
      *** (2.6) Définitions. 
      *** (2.5) Termes. 
         *** (2.7.1) Termes applicatifs. 
         *** (2.7.2) Conditionnelles. 
         *** (2.7.3) Conditionnelles abbrégées. 
            *** (2.7.3.1) 'if ... then ... else ...'.
            *** (2.7.3.2) '...; ...'.
            *** (2.7.3.3) Seulement un cas. 
         *** (2.7.4) Conditionnelles sélectives. 
         *** (2.7.5) Typage explicite. 
         *** (2.7.6) Calcul par avance ('with'). 
         *** (2.7.7) Fonctions. 
         *** (2.7.8) 'alert'. 
         *** (2.7.9) Démarrer une machine virtuelle ('delegate'). 
         *** (2.7.10) Attendre ('wait for').
         *** (2.7.11) Protection du code ('protect'). 
         *** (2.7.12) Verrouillage de fichier ('lock').
         *** (2.7.13) Autres constructions. 
   
   *** (3) Remarques. 
      *** (3.1) Outils. 
      *** (3.2) Polysémie. 
      *** (3.3) Schémas. 
      *** (3.4) Egalité entre types. 
      *** (3.5) Règle de préemption forte. 
      *** (3.6) Boucles et récursion terminale. 
      *** (3.7) Conclusion. 
  
   
   ---------------------------------------------------------------------------------------

   
   
   
   
   *** (1) Introduction. 
   
   La documentation définitive  n'étant pas encore écrite, je tente  de décrire le langage
   Anubis ci-dessous. Bien sûr, cette description  est trop courte pour être complète. Les
   fichiers  sources  dans  'anubis/library/'  sont  une autre  source  de  documentation,
   puisqu'il  s'agit  d'exemple  qui fonctionnent  (sauf  pour  ceux  qui sont  encore  en
   chantier). Le fichier:
   
                               anubis/library/predefined.anubis  
   
   est particulièrement important, car il définit ou déclare des concepts fondamentaux, et
   il  est parfaitement fiable,  puisque précompilé  et intégré  au compilateur,  sauf que
   certaines  choses  qui sont  encore  en chantier  peuvent  ne  pas avoir  d'instruction
   correspondante dans  la machine virtuelle. Dans  ce cas, la  machine virtuelle s'arrête
   avec un message.  Notez que ce programme a  été développé sous Linux et  que la version
   Windows demande encore un peu de travail. 
   
   Souvenez-vous aussi que les messages du  compilateur sont assez détaillés et sont aussi
   une bonne source d'information. 

   Dans les explications qui suivent, tout  ce qui doit être écrit textuellement est aussi
   présenté textuellement.  Par contre, tous les  concepts qui doivent  être remplacés par
   des expressions sont encadrés par '<' et '>'. 

   De manière à  avoir une première approche du langage Anubis,  vous devez comprendre les
   concepts suivants (ils sont expliqués plus loin):
   
     - les symboles (comment ils s'écrivent)
     - les paragraphes
     - les  définitions de  types (y  compris  les 'alternatives',  les 'composants',  les
        'constructeurs' et les 'destructeurs implicites'),
     - les definitions de données (y compris de fonctions)
     - les termes (y  compris les 'conditionnelles', les 'termes  applicatifs', et diverses
        choses comme 'delegate', 'with', etc...) 

   Ensuite, vous pouvez essayer l'exemple 'anubis/library/examples/hello.anubis'. 
   
   Quand vous  avez compris le langage,  et suivant ce  que vous voulez faire,  vous devez
   comprendre  quelques outils  du  système.  Les  plus  importants sont  dans le  fichier
   'anubis/library/predefined.anubis'.   Vous  en  trouverez  d'autres  par  exemple  dans
   'anubis/library/web/', si  vous voulez faire quelque  chose pour le web.   En fait, une
   exploration complète de la bibliothèque n'est pas un très gros travail.
   
  
   
   *** (2). Le langage. 
   
   
      *** (2.1) Les symboles. 
   
   Tous les symboles  sont faits de lettres ('a' à  'z' et 'A' à 'Z'),  de chiffres ('0' à
   '9')  et  de caractères  de  soulignement  ('_'). Toutefois,  un  symbole  ne peut  pas
   commencer par un  chiffre. De plus, les symboles qui commencent  par une majuscule sont
   réservés pour nommer les types (et les schémas de types), alors que les autres symboles
   servent pour tout le reste.  Dans le langage Anubis, les symboles (mais pas les noms de
   types) peuvent être surchargés.  (ils  peuvent avoir plusieurs significations: ceci est
   appelé la 'polysémie', de préférence  au terme impropre 'polymorphisme'), pourvu que le
   compilateur arrive à résoudre toutes les  ambiguïtés. Cette résolution est faite par la
   méthode d'unification.
   
   
   
      *** (2.2) Opérateurs. 
   
   Il y  a aussi un petit  nombre d'opérateurs, qui  sont soit unaires, soit  binaires. Un
   opérateur unaire accepte  un seul opérande, alors qu'un  opérateur binaire accepte deux
   opérandes. Voici des exemples d'opérateurs unaires:
      
      -    (signe 'moins')
      *    (signe 'étoile')
   
   Ces opérateurs doivent être placés devant  leur unique opérande. Pour cette raison, ils
   sont appelés 'opérateurs préfixes unaires'. Donc, si 'a' est l'opérande, ils permettent
   d'écrire les termes suivants:
   
      -a
      *a
   
   Voici des exemples d'opérateurs binaires:
   
      +   (signe 'plus')
      *   (signe 'étoile')
      ^   (signe 'chapeau')
      |   ('barre verticale')
      &   ('esperluette')
      <<  ('shift gauche')
      >>  ('shift droit')
      -   (signe 'moins')
      /   ('barre oblique')
      <   (signe 'plus petit')
      =<  (signe 'plus petit ou égal')
      >   (signe 'plus grand')
      >=  (signe 'plus grand ou égal')
      /=  (signe 'différent de')
      
   Ce sont tous des opérateurs 'binaires infixes'. Ils sont appelés 'infixes' parce qu'ils
   doivent être placés entre  leurs deux opérandes. Donc, par exemple, si  'a' et 'b' sont
   les deux opérandes, vous pouvez écrire:
   
       a + b

   Il y a aussi  un opérateur 'binaire exfixe'. Si 'a' et 'b'  sont les deux opérandes, le
   terme construit avec cet opérateur s'écrit:
   
      [a . b]

   Bien   sûr,  cette  syntaxe   serait  ambigüe   sans  des   règles  de   précédence  et
   d'association. Par exemple, si vous écrivez:
   
                                      a + b * c

   le compilateur lit 'a +  (b * c)', et non pas '(a + b) *  c', car l'opérateur '*' a une
   précédence  plus élevée  que l'opérateur  '+'. De  plus, pour  un niveau  de précédence
   donné,  les  opérateurs  s'associent  soit  à  droite, soit  à  gauche.   Par  exemple,
   l'opérateur binaire  '-' ('moins') est seul dans  son niveau de précédence,  et si vous
   écrivez:
      
                                      a - b - c
   
   le compilateur va lire '(a  - b) - c', et non pas 'a -  (b - c)' (heureusement), car ce
   niveau de précédence associe à gauche.
      
   La table ci-dessous  montre les règles de précédence et  d'association. Chaque ligne de
   la table represente un niveau de  précédence.  Les niveaux de précedence sont présentés
   dans l'ordre  croissant (la première ligne  correspond au niveau de  précédence le plus
   faible). Au  début de chaque  ligne, le mode  d'association ('gauche' ou  'droite') est
   indiqué. Notez que nous avons aussi des  mots clefs dans cette table, pas seulement des
   opérateur.
   
   mode
   d'association      operateurs
   ---------------------------------------------------------------------------------------
   droite              protect lock
   droite              ,
   droite              |-> |-nom->
   droite              :
   droite              is with
   droite              then else
   droite              ;
   droite              |
   droite              &
   droite              <<  >>
   droite              <   >   =<   >=   =   <-   /= 
   droite              +
   gauche              - (binaire)
   droite              * (unaire et binaire)
   gauche              /
   droite              ^
   droite              - (unaire)
   droite              ->
  
   
  
   
      *** (2.3) La syntaxe des listes. 
   
   Le compilateur lit
   
       [a,b,c]
   
   comme:
   
       [a . [b . [c . []]]]
   
   (c'est  une réminiscence  de  Lisp).  Ceci  permet  d'écrire des  listes  sans trop  de
   symboles. Voyez 'predefined.anubis' pour la définition des listes.
   
   
   
      *** (2.4) Paragraphes. 
      
   Un fichier source  Anubis contient une suite (éventuellement  vide) de paragraphes. Les
   commentaires  entre les  paragraphes peuvent  être écrits  librement (voir  par exemple
   'library/examples/hello.anubis').   Les  autres  commentaires  (dans  les  paragraphes)
   peuvent être placés entre  /* et */ (ils peuvent être imbriqués) ou  entre // et la fin
   de la ligne. Le  compilateur détecte le début d'un paragraphe quand  il trouve l'un des
   mots ou locutions clefs suivants:
   
read   
type 
public type
define
public define
global define

   à condition qu'ils soient écrits dans la colonne la plus à gauche. En conséquence, pour
   neutraliser un paragraphe, sans pour autant  l'effacer, il suffit de d'ajouter un blanc
   devant le mot clef qui commence le paragraphe. 

   
         *** (2.4.1) 'read'. 
   
   'read'  dit juste  au compilateur  de lire  et  compiler le  fichier dont  le nom  suit
   'read'. Le compilateur se souvient toujours des fichiers qu'il a lu, et il n'y a pas de
   risque qu'il lise deux fois le même fichier (pendant la même compilation). 
      
   C'est plus ou moins équivalent à  l'#include du langage C. Toutefois, vous pouvez aussi
   utiliser l'option -c. Dans ce cas,  seules les déclarations des fichiers référencés par
   'read' sont  lues. Seul le  fichier principal (dont  vous donnez directement le  nom au
   compilateur) est compilé. Si l'option -c est présente, le compilateur ne produit pas de
   module. Il ne fait que des vérifications.
   
   
         *** (2.4.2) 'type'.
   
   'type'  annonce une définition  de type.  Elles sont  expliqués plus  loin, et  vous en
   trouverez de nombreux exemples dans les fichiers de la bibliothèque.

   
   
         *** (2.4.3) 'define'. 
   
   'define' introduit une définition de donnée  (ou de fonction, une sorte particulière de
   donnée).
   
   
   
         *** (2.4.4) 'public'. 
   
   'public' annonce  que la portée de  la définition n'est  pas limitée au fichier  qui la
   contient. La  définition (ou la  définition de type)  peut être utilisée  dans d'autres
   fichiers (en utilisant 'read').
   
   
   
   
         *** (2.4.5) 'global'. 
   
   'global define'  est la  même chose que  'define', sauf  que le compilateur  produit un
   module,  disons: mon_module.adm (qui  sera mis  par le  compilateur dans  le répertoire
   'my_anubis/modules/'),  où  'mon_module' est  le  nom que  vous  donnez  à la  fonction
   définie. Dans ce cas, les opérandes de cette fonction doivent être déclarés comme suit:
   
global define One
  mon_module
    (
      List(String)  args  // unique argument obligatoire représentant
                          // les arguments de la ligne de commande
    ) =
  ...
   
   On  a  choisi  'One'  comme  type  de  retour  (mais  n'importe  quel  autre  type  est
   accepté). Voyez 'library/examples/hello.anubis' pour un exemple réel.

   
   
   
      *** (2.5) Types. 
   
   Les types  sont des sortes suivantes  (quelques types primitifs vont  disparaître de la
   version 2, et  seront remplacés par des types  définis). Note: dans 'predefined.anubis'
   vous trouverez quelques autres concepts, mais qui sont en général en chantier.
   
   
         *** (2.5.1) Types primitifs. 
   
      - String         chaînes de caractères
      - ByteArray      tableaux d'octets (les octets sont de type 'Int8'; voir 'predefined.anubis')
      - Int32          entiers de 32 bits signés
      - Float          nombre à virgule flottante de 64 bits
   
   
   
         *** (2.5.2) Types d'adresse. 
   
      - RAddr(T)       emplacement où des données de type T peuvent être lues
      - WAddr(T)       emplacement où des données de type T peuvent être écrites
      - RWAddr(T)      emplacement où des données de type T peuvent être lues et écrites
      
   (pour le moment, ces types ne marchent qu'avec  T = Int8). Les données dont le type est
   un type d'adresse sont pour le moment uniquement les fichiers et les connexions réseau.
   

   
         *** (2.5.3) Types fonctionnels. 
   
   Un type fonctionnel s'écrit comme ceci:
   
      (<U_1>,...,<U_n>) -> <T>
   
   où  <U_1>,...,<U_n> et <T>  sont des  types.  Les  données du  type ci-dessus  sont des
   fonctions prenant  n arguments  de types respectifs  <U_1>,...,<U_n>, et  retournant un
   résultat de type <T>.
   
   Il est  aussi permis de nommer  les arguments. Les  noms des arguments sont  ignorés du
   compilateur, mais  ils permettent d'améliorer  la lisibilité. par exemple,  vous pouvez
   écrire un type fonctionnel comme ceci:
   
     (String text, Int32 x, Int32 width, Int32) -> One
   
   plutôt que comme ceci:
   
     (String,Int32,Int32,Int32) -> One
   
   
   
   
   
         *** (2.5.4) Types définis. 
   
   De nouveaux  types peuvent être  définis à l'aide  de 'définitions de types'.   Vous en
   trouverez de nombreux  exemples dans les fichiers de  la bibliothèque. Néanmoins, voici
   la syntaxe de ces définitions et ce qu'elle veut dire.
   
type <TypeName>:
  <alternative_1>,
  ...
  <alternative_n>.
   
   Ici  '<TypeName>'  est   le  nom  du  type.   C'est  un   symbole  commençant  par  une
   majuscule. Chaque alternative décrit des données  du type défini. Notez que ce type est
   intuitivement (et mathématiquement) l'union  disjointe des ensembles de données décrits
   par ces alternatives.
      
   Chaque alternative est soit un symbole (dans ce cas, l'alternative ne représente qu'une
   seule  donnée:  c'est un  singleton),  ou  un symbole  suivi  par  des déclarations  de
   'composants' de types divers, comme ceci:
   
              <alternative_name>(<Type_1> <name_1>, ... , <Type_k> <name_k>)
   
   '<alternative_name>' est  un symbole  commençant par une  minuscule ou un  caractère de
   soulignement.  <Type_1>,...<Type_k> sont les types des composants de la donnée décrite,
   et  <name_1>,...,<name_k> sont  les  noms de  ces  composants.  Les  noms  ne sont  pas
   obligatoire (les  composants peuvent être  anonymes). Le nombre d'alternatives  dans un
   type est limité à 256.
   
   La signification  des composants est qu'une  donnée qui appartient  à cette alternative
   est un multiplet de k données de types respectifs <Type_1>,...,<Type_k>.
   
   Donc, chaque type défini est juste  une union disjointe de produits cartésiens de types
   (ce qu'on appelle un 'type polynômial'). Voyez les exemples dans 'predefined.anubis' et
   les autres fichiers de la bibliothèque.
   
   Chaque alternative  donne naissance  à une fonction  appelée un  'constructeur'.  Cette
   fonction a autant d'arguments qu'il y  a de composants dans l'alternative, dans le même
   ordre et avec les mêmes types. Le type  cible du constructeur est le type défini par la
   définition de type.
   
   Si, dans la définition d'un type, toutes les alternatives ont en commun un composant de
   même type 'T' et de même nom 'n', le compilateur produit automatiquement un 'destruteur
   implicite', qui est une fonction de nom 'n', dont le type source est le type défini, et
   dont le type cible est 'T'.

   Par exemple, vous pouvez définir le type:
   
type T:
  a(U x),
  b(U x, V y). 
   
   Intuitivement,  quand  il est  interprété  comme un  'ensemble',  ce  type est  l'union
   disjointe de 'U' et du produit cartésien 'UxV'.

   Pour ce type, le compilateur produit trois fonctions:
   
   'a'       de type U -> T             (premier constructeur)
   'b'       de type (U,V) -> T         (deuxième constructeur)
   'x'       de type T -> U             (destructeur implicite)
   
   En  fait,  le  destructeur  implicite   est  définit  par  le  compilateur  comme  suit
   (souvenez-vous que '_' est un symbole):
   
define U
  x
    (
      T _
    ) = 
  if _ is 
    {
      a(x)    then x, 
      b(x,y)  then x
    }. 

   Mais bien sûr, le  compilateur ne produit pas de destructeur implicite  de nom 'y', car
   toutes les données du type 'T' n'ont pas de composant nommé 'y'.

   Le compilateur accepte des syntaxes  exceptionnelles pour les alternatives. Ce sont les
   suivantes:
      
      [ ]                                        est traité comme un symbole (pas de composant)
      [<Type_1> <name_1> . <Type_2> <name_2>]    (2 composants)
   
   Voyez par exemple la définition du  schéma de type 'List' dans 'predefined.anubis'. Les
   syntaxes suivantes sont aussi acceptées (mais rarement utilisées):
   
      <Type_1> <name_1> + <Type_2> <name_2>
      <Type_1> <name_1> * <Type_2> <name_2>
      <Type_1> <name_1> ^ <Type_2> <name_2>
      <Type_1> <name_1> | <Type_2> <name_2>
      <Type_1> <name_1> & <Type_2> <name_2>
      <Type_1> <name_1> -> <Type_2> <name_2>
      <Type_1> <name_1> = <Type_2> <name_2>
      <Type_1> <name_1> => <Type_2> <name_2>
      <Type_1> <name_1> << <Type_2> <name_2>
      <Type_1> <name_1> >> <Type_2> <name_2>
      <Type_1> <name_1> - <Type_2> <name_2>
      <Type_1> <name_1> / <Type_2> <name_2>
      <Type_1> <name_1> (mod <Type_2> <name_2>)
      <Type_1> <name_1> < <Type_2> <name_2>
      <Type_1> <name_1> /= <Type_2> <name_2>
      <Type_1> <name_1> =< <Type_2> <name_2>
      ~ <Type_1> <name_1>
   
   
   
   
   
      *** (2.6) Définitions. 
   
   On en a déjà vu un exemple plus  haut (le destructeur implicite 'x'). la syntaxe est la
   même pour les mots clefs (ou locutions clefs):
   
define 
public define
global define

   Donc, nous  ne la  donnons que pour  'define'.  Une  définition de fonction  'du niveau
   supérieur' a l'allure suivante:
   
define <Return Type>
  <name of function>
    (
      <Type_1> <operand_1>,
      ...
      <Type_n> <operand_n>
    ) =
  <body_of_definition>. 
   
   où  '<body_of_definition>' (le  corps  de la  définition)  est un  'terme'.  Notez  les
   virgules séparant  les déclarations des  opérandes, le signe  '=' avant le corps  de la
   définition (qui peut-être n'était pas une très bonne idée de design), et le point après
   le corps de la définition. Les noms des opérandes sont obligatoires.
   
   S'il n'y a pas d'opérande, la définition s'écrit:
   
define <Type>   
  <name_of_datum>
    =
  <body_of_definition>. 
   
   et la donnée définie est de type <Type>. 

   Le   compilateur   accepte   aussi   quelques   syntaxes   exceptionnelles   pour   les
   définitions. Par  exemple, l'opérateur binaire '+' peut  recevoir plusieurs définitions
   (en fait, il  en a déjà plusieurs définies dans  'predefeined.anubis'). La syntaxe pour
   une telle définition est la suivante:
   
define <Type>
   <Type_1> <operand_1> + <Type_2> <operand_2>
     =
  <body_of_definition>. 

   Les mêmes règles s'appliquent aux opérateur binaires infixes suivants:
   
       *   ^   |   &   <<   >>   -   /   <   =<   /=

   Notez  que '>'  et '>='  ne peuvent  pas ête  définis.  Quand  vous définissez  '<', le
   compilateur considère  que vous  avez défini '<'  et '>'  ensemble. Par la  suite, s'il
   trouve le terme:
   
                a > b
   
   il le lit simplement: b < a. 
   
   De même, l'opérateur unaire préfixe '-' (signe 'moins') peut être surdéfini comme suit:
      
define <Type>
   - <Type_1> <operand_1> 
     =
  <body_of_definition>. 
   
   Votez les exemples dans 'anubis/library/tools/basis.anubis'.
   
   
   
   
   
   
      *** (2.5) Termes. 
   
   

         *** (2.7.1) Termes applicatifs. 
   
   Il s'agit de fonctions appliquées à des arguments:
   
        <f>(<a_1>,...,<a_n>)
   
   Toutefois, si  le terme '<f>'  peut être interprété  comme une fonction  sans opérande,
   '<f>' lui-même  est un terme  applicatif (C'était en  fait une mauvaise idée  de design
   d'identifier les  fonction de zéro arguments  avec le résultat de  l'application de ces
   fonctions à zéro arguments. Cette particularité sera supprimée de la version 2).
   
   
   
   
   
         *** (2.7.2) Conditionnelles. 
   
   Elle permettent de travailler avec les données des types définis selon les alternatives
   auquelles elle appartiennent. La syntaxe est la suivante: 
   
if <test> is 
  {
    <case_head_1>   then   <case_body_1>, 
    ...
    <case_head_n>   then   <case_body_n>
  }

   Le type  de 'test' doit  être un type  défini.  Il doit y  avoir exactement un  cas par
   alternative de ce type, et dans le même ordre. La syntaxe précise des cas sera aisément
   comprise d'après les exemples des fichiers de  la bibliothèque. Notez que le type de la
   conditionnelle elle-même est le  type commun à tous les corps de  cas. Les symboles qui
   apparaîssent  entre parenthèses  dans  les têtes  de  cas, sont  appelés des  'symboles
   résurgents'. Leur portée est le corps de cas correspondant.

   Il  y a  bien  sûr des  syntaxes exceptionnelles  pour  les cas  qui correspondent  aux
   syntaxes exceptionnelles pour  les alternatives. Par exemple, une  fonction qui calcule
   la longueur d'une liste peut être définie (récursivement) comme suit: 
   
define Int32
   length
     (
       List($T) l
     ) =
   if l is 
     {
       [ ]           then 0, 
       [head . tail] then 1 + length(tail)
     }. 

   Cet  exemple montre  qu'Anubis évite  systématiquement certaines  erreurs  que d'autres
   compilateurs ne peuvent pas détecter. Il est clair que vous ne pouvez pas considérer la
   tête ou la queue de la liste vide, simplement parce qu'elles ne sont syntaxiquement pas
   accessibles. En fait, les composants ne  peuvent être atteints que quand il est certain
   qu'ils existent.
    
   
   
   
         *** (2.7.3) Conditionnelles abbrégées. 
   
   Il y a des cas particuliers (abbréviations):
   
   
            *** (2.7.3.1) 'if ... then ... else ...'.
   
   'if <test> then <a> else <b>' signifie:
   
         if <test> is 
           {
             false then <b>, 
             true then <a>
           }
   
   Bien   sûr,    dans   ce   cas,   '<test>'    doit   être   de    type   'Bool'   (voir
   'anubis/library/predefined.anubis').
   
   
            *** (2.7.3.2) '...; ...'.
   
   '<a>; <b>' signifie:
   
         if <a> is 
           { 
             unique then <b> 
           }
   
   Dans ce cas, '<a>' doit être de type 'One'. 
   
   
   
   
            *** (2.7.3.3) Seulement un cas. 
   
   Si le type du test n'a qu'une alternative, la conditionnelle
   
      if <test> is
        {
          <case_head>   then   <case_body>
        }
   
   peut être abbrégée en:
   
      'if <test> is <case_head> then <case_body>'
   
   Une autre syntaxe est  autorisée, qui peut sembler plus naturelle quand  il n'y a qu'un
   seul cas:
   
      'since <test> is <case_head>, <case_body>'
      
   'since' est un mot clef comme 'if', et est donc interdit comme symbole. 
   
   
   
   
   
         *** (2.7.4) Conditionnelles sélectives. 
   
   Dans certaines circonstances, en particulier quand le type du test de la conditionnelle
   a de  nombreuses alternatives, vous  pouvez souhaiter donner un  traitement particulier
   aux  données  d'une  seule alternative,  et  un  traitement  par  défaut à  toutes  les
   autres. Dans ce cas, utilisez une 'conditionnelle sélective':
   
       if <test> is 
         <selected_case_head> then <selected_case_body>
         else <default_treatement>
   
   

   
   
   
         *** (2.7.5) Typage explicite. 
   
   Si '<t>' est un terme quelconque, qui est supposé être de type '<T>', vous pouvez aider
   le  compilateur à choisir  la bonne  interprétation en  typant explicitement  le terme,
   comme suit:
   
                                          (<T>)<t>

   C'est nécessaire dans certaines circonstances,  et dans d'autres cela peut accélérer la
   compilation.
   
   Il n'y a pas de 'transtypage' en Anubis. En d'autres termes, vous ne pouvez pas changer
   le type d'un terme.  La seule chose que vous pouvez faire  est aider le compilateur (et
   éventuellement le lecteur de votre source) à trouver le type d'un terme. 


      
   
   
         *** (2.7.6) Calcul par avance ('with'). 
   
   Vous pouvez calculer quelquechose à l'avance, pour vous en servir ensuite. Le terme:
   
          with <x> = <a>, <t>

   où '<x>' est un symbole, '<a>' et '<t>'  deux termes, est de même type que '<t>'. C'est
   juste une définition  locale (il n'y a pas  d'effet de bord, car la valeur  de '<x>' ne
   peut pas  être changée par  une affectation  depuis '<t>', qui  est la portée  de cette
   définition locale).

   Vous pouvez aussi combiner plusieurs définitions locales: 
   
          with <x> = <a>, 
               <y> = <b>, 
               <z> = <c>, 
            <t>
   
   Dans ce cas,  '<b>' peut utiliser '<x>',  et '<c>' peut utiliser '<x>'  et '<y>'.  Bien
   sûr, '<t>' peut utiliser '<x>', '<y>' et '<z>'.
   
   
   
   
         *** (2.7.7) Fonctions. 
   
   Anubis  est un  langage pleinement  fonctionnel, comme  CAML et  les versions  pas trop
   anciennes de Lisp (et aussi JavaScript !). Ceci signifie que les fonctions peuvent être
   construites non seulement au niveau supérieur,  c'est à dire en utilisant un paragraphe
   'define', mais aussi n'importe où dans un terme, et que les fonctions ainsi construites
   se souviennent du contexte précis dans lequel elles ont été construites.

   La syntaxe pour écrire une fonction de K arguments est la suivante:
   
                               (<T_1> <x_1>, ...,<T_k> <x_k>) |-> <t>
   
   où <T_1>,...,<T_k> sont  les types des arguments, où  <x_1>,...,<x_k> sont des symboles
   (les noms des arguments),  et où '<t>' est un terme. Le  terme (<T_1> <x_1>, ..., <T_k>
   <x_k>) |->  <t> est  appelé une 'lambda-expression',  en dépit  du fait que  nous avons
   préféré utiliser le symbole mathématique usuel, c'est à dire la flèche:
   
                                            |->
   
   plutôt que le mot clef Lispien 'lambda'.
   
   Le  fait important est  que les  fonctions définies  de cette  façon se  souviennent du
   contexte où elles ont été définies. Par exemple, vous pouvez écrire le terme:
   
                           with y = (Int32)3, 
                             (Int32 x) |-> x+y

   qui représente  la fonction 'qui  ajoute 3'.  Si  vous utilisez cette fonction  dans un
   autre  contexte, contenant  une définition  d'un autre  'y', la  fonction  continuera à
   ajouter 3. Ainsi, par exemple, le terme:
   
      with y = (Int32)3,            // 'y' est défini comme '3'
           f = (Int32 x) |-> x+y,   // 'f' comme la fonction qui ajoute 'y' (i.e. 3)
           y = (Int32)7,            // maintenant, définissons un autre 'y'
        f(1)                        // et appliquons 'f' à '1'

   représente l'entier '4', et non pas l'entier '8',  en dépit du fait que 'y' a la valeur
   '7' dans le contexte dans lequel 'f' est utilisée. Procéder autrement serait une faute,
   connue sous le nom de 'capture de variable'.

   Vous pouvez aussi construire des fonctions récursives avec la flèche, mais pour pouvoir
   être appelée  depuis son propre  corps, la  fonction doit avoir  un nom. Donc,  au lieu
   d'utiliser la flèche simple (anonyme) |->, vous utilisez ceci: 
      
                                          |-nom->
   
   qui  est une flèche  avec un  nom (ici  le nom  est 'nom'),  aussi appelée  une 'flèche
   labélisée'. par exemple, vous pouvez écrire: 
   
        with f = (List(String) l) |-len->
                    if l is 
                      {
                        [ ] then 0,
                        [h . t] then 1+len(t)
                      },
          f(["a","b","c","d"])

   Ce terme est de type Int32, et  sa valeur est 4. Notez que dans l'expression ci-dessus,
   le symbole 'len' est déclaré (représentant la  fonction), et que sa portée est juste le
   corps de la fonction. 
   
   Maintenant, que faire  si vous voulez construire des  fonctions mutuellement récursives
   avec  des  flèches  '|->' ?   Supposons  que  vous  vouliez construire  deux  fonctions
   récursives 'f'  et 'g',  et que vous  commencez par  'f'. Le problème  est que  vous ne
   pouvez  pas appeler  'g'  depuis  le corps  de  'f', parce  que  'g'  n'est pas  encore
   définie. Toutefois, il y a une jolie astuce fonctionnelle pour résoudre ce problème. Il
   n'est pas toujours évident de la mettre en oeuvre. Je vais juste examiner un exemple.
   

   Supposons que les fonctions 'f' et 'g' puissent être définies au niveau supérieur comme
   suit:
   
      type U:...  // déclaration pour types mutuellement récursifs

      type T: 
        a,
        b(T,U). 
   
      type U:
        c,
        d(T,U). 
   
      define T g(U u).  // déclaration pour fonctions mutuellement récursives

      define T f(T t) = if t is 
                          {
                            a        then a, 
                            b(t1,u1) then b(f(t1),g(u1))
                          }.
   
      define T g(U u) = if u is 
                          {
                            c        then a, 
                            d(t1,u1) then b(f(t1),g(u1))
                          }.
   
   On peut voir que f et g  sont mutuellement récursives, et que la récursion est correcte
   (elle termine). Avec des flèches labélisées, on peut faire comme suit: 
   
      with phi = (T t, U -> T g) |-phi-> if t is 
                                           {
                                             a        then a, 
                                             b(t1,u1) then b(phi(t1,g),g(u1))
                                           },
             g = (U u)|-g-> if u is 
                              {
                                c        then a, 
                                d(t1,u1) then b(phi(t1,g),g(u1))
                              },
             f = (T t) |-> phi(t,g), 
        // à cet endroit f et g sont disponibles
   
   La pleine fonctionalité a de  nombreuses importantes applications. Elle est utilisée de
   manière   essentielle    dans   le   monitoring   des    variables   dynamiques   (voir
   'predefined.anubis'), dans l'interface graphique, dans la base de données, etc... 
      
   
   
   
   
         *** (2.7.8) 'alert'. 
   
   'alert'  peut remplacer un  terme de  n'importe quel  type. Si  'alert' est  exécuté la
   machine virtuelle  s'arrète avec  un message  indiquant dans quel  fichier et  à quelle
   ligne se trouve  cet 'alert'. Toutefois, les autres machines  virtuelles (pour une même
   instance d'anbexec) continuent de fonctionner.  'alert' va disparaître de la version 2,
   dans laquelle la logique d'ordre supérieur offre un meilleur mécanisme. 
   
   
   
   
   
   
         *** (2.7.9) Démarrer une machine virtuelle ('delegate'). 
   
   Si '<t>' et '<u>' sont deux termes,
   
               delegate <t>, <u>
   
   est un terme de  même type que '<u>'. L'exécution de 'delegate  <t>,<u>' se passe comme
   suit. La machine virtuelle démarre une  autre machine virtuelle à laquelle elle délègue
   l'exécution de  '<t>'. La  machine virtuelle d'origine  exécute '<u>'.  Donc,  '<t>' et
   '<u>'  sont  exécutés en  parallèle.   Notez  que 'anbexec'  a  son  propre système  de
   fonctionnement multitâche, et que du point de vue du système d'exploitation il apparaît
   comme une tâche unique.
   
   
   
   
   

         *** (2.7.10) Attendre ('wait for').
   
   L'expression:
   
               checking every <n> milliseconds, wait for <t> then <u>
   
   (où '<n>'  est un terme  de type 'Int32',  '<t>' un terme de  type 'Bool', et  '<u>' un
   terme de n'importe  quel type 'T') est un  terme de type 'T'. Quand il  est exécuté, la
   machine virtuelle attend que la condition '<t>' soit satisfaite (i.e.  égale à 'true'),
   et  exécute  alors  '<u>'.  Bien  sûr,  les  autres  machines virtuelles  continuent  à
   fonctionner  pendant  ce  temps.   La  condition  '<t>' est  testée  toutes  les  '<n>'
   millisecondes  (comme  la  syntaxe  le  rappelle clairement).   Notez  que  la  machine
   virtuelle  attend aussi  '<n>' millisecondes  avant le  premier test  de  '<t>'.  Note:
   'milliseconds'  peut aussi  être écrit  'millisecond'.  Voyez  par exemple  la fonction
   'sleep' dans 'tools/basis.anubis'.
   
   
   
   
   
         *** (2.7.11) Protection du code ('protect'). 
   
   Si '<t>' est  un terme, alors 'protect <t>' est  un terme de même type  et avec la même
   sémantique que '<t>'.  La  seule différence avec '<t>' tout seul est  que, quand il est
   préfixé  par 'protect'  le  terme '<t>'  ne  peut pas  être  exécuté simultanément  par
   plusieurs machines virtuelles. En d'autres termes, quand une machine virtuelle commence
   l'exécution de '<t>', elle verrouille le  morceau de code correspondant à '<t>'. Si une
   autre machine virtuelle essaye d'exécuter  '<t>', elle trouve '<t>' verrouillé. Dans ce
   cas, la seconde  machine virtuelle rend la main  et attent que la première  en ait fini
   avec 't'.  Notez que même si une machine virtuelle verrouille un terme pendant un temps
   assez long, les  autres machines virtuelles continuent de  fonctionner.  La seule chose
   qu'elle ne peuvent pas exécuter pendant ce temps est '<t>'.
    
   'protect'  est typiquement  utilisé  pour éviter  les  mélanges d'effets  de bord,  par
   exemple,  pour  écrire  dans  un  fichier.   Une  démo  de  'protect'  se  trouve  dans
   'anubis/library/examples/try_protect.anubis'.
   
   
   
   
   
         *** (2.7.12) Verrouillage de fichier ('lock').
   
   C'est similaire à 'protect',  mais au lieu de verrouiller un morceau  de code, c'est un
   nom de fichier qui est verrouillé. La syntaxe est:
   
               lock <filename>, <body>
   
   où '<filename>' est un terme de type  'String' qui calcule le nom d'un fichier. Le type
   de ce  terme est le type  de '<body>'.  Le terme  '<body>' peut lire et  écrire dans le
   fichier sans  risquer d'être  en concurrence avec  une autre machine  virtuelle, pourvu
   que:
   
      (1) les autres machines virtuelles verrouillent  également le fichier avant d'y lire
      ou  d'y écrire.   Si  une autre  machine virtuelle  écrit  dans le  fichier sans  le
      verrouiller  elle pourra  écrire même  si  le fichier  est verrouillé  par une  aute
      machine. Ceci devrait changer dans les versions futures.
   
      (2) aucune autre  instance d'anbexec' ne doit  accéder aux même fichier,  même en le
      verrouillant.
   
   En effet, le verrouillage de nom de  fichiers est un mécanisme interne d'anbexec (il ne
   fait rien sur le  fichier lui même et ne vérifie même pas  qu'il existe; il se contente
   d'associer le nom  avec une machine virtuelle), et deux  instances d'anbexec ne peuvent
   pas savoir ce que l'autre a verrouillé.

   Bien  sûr, si  '<filename>'  est déjà  verrouillé,  la machine  virtuelle attend  avant
   d'exécuter '<body>', laissant les autres  machines virtuelles faire leur travail. Parmi
   elles se trouve celle qui a verrouillé le nom de fichier.
   
   
   
   
   
   
         *** (2.7.13) Autres constructions. 
   
   Ben sûr, il y  a d'autres constructions dans le langage, mais  vous avez vu l'essentiel
   ici. Vous  devinerez aisément le  reste en parcourant  les fichiers de  la bibliothèque
   dans 'anubis/library/', et en particulier 'predefined.anubis'. 
   
   

   
   
   
   
   *** (3) Remarques. 
   
   Pour  conclure cette  très courte  documentation,  nous discutons  quelques points  qui
   peuvent ne pas être très évidents d'après nos fichiers sources. 

   
      *** (3.1) Outils. 
   
   De nombreux outils sont disponibles dans les fichiers de la bibliothèque. Jetez un oeil
   à 'anubis/library/'  et ses  sous-répertoires. En particulier,  vous trouverez  tout ce
   qu'il faut pour faire un programme client/serveur dans 'library/predefined.anubis'.

   
   
   
      *** (3.2) Polysémie. 
   
   (improprement  appelée  'polymorphisme' dans  d'autres  langages).   Le langage  Anubis
   accepte  l'utilisation du même  nom dans  plusieurs définitions,  pourvu que  les types
   permettent de lever les ambigüités. Bien  sûr, les définitions privées (celle qui n'ont
   pas le mot clef  'public') ne peuvent pas créer de conflit  avec des définition privées
   d'autres fichiers.
   
   
   
   
      *** (3.3) Schémas. 
    
   Vous  pouvez   aussi  utiliser  des   paramètres  de  types  (représentant   des  types
   arbitraires). Ils sont  de la forme $T, où T  a la syntaxe d'un nom  de type. Voyez les
   exemples dans  les fichiers sources,  et en particulier  le schéma de type  'List' dans
   'predefined.anubis'.


   
   
      *** (3.4) Egalité entre types. 
   
   Deux types distincts ne peuvent pas avoir le même nom (sauf s'ils sont tous deux privés
   dans des  fichiers distincts). De  plus, le langage  Anubis ne considère pas  des types
   ayant des noms  distincts mais des définitons identiques comme  identiques. Ce sont des
   types différents. Ceci est une caractéristique importante, car elle aide le compilateur
   à capturer ce que vous avez en tête (sémantique intentionnelle).
   

   
   
      *** (3.5) Règle de préemption forte. 
   
   C'est diamétralement  opposé à la  polysémie. Cette  règle dit que  si un symbol  a une
   définition  (ou une déclaration)  locale, seule  cette définition  locale est  prise en
   compte dans  la portée de  cette même définition.  Donc, tout symbole local  cache tout
   symbole de même nom défini avant lui, qu'il soit local ou global. 
   
   
   
   
      *** (3.6) Boucles et récursion terminale. 
   
   Le langage Anubis n'a pas de notion de boucle, mais le compilateur élimine la récursion
   terminale  automatiquement, de  telle sorte  que des  boucles sont  réalisées  de cette
   manière.   A   nouveau,  voyez   les  exemples  et   les  explications   fournies  dans
   'anubis/library/predefined.anubis'.
   
   
  
   
   
      *** (3.7) Conclusion. 

   J'espère que  vous allez  aimer Anubis, dont  la principale  qualité est de  fournir un
   procédé de  programmation très sûr. En d'autres  termes, si le compilateur  ne dit rien
   (votre source a été compilé avec succès), il y a très peu de risque qu'il contienne des
   fautes (sauf  par exemple,  les récursions  qui ne terminent  pas). C'est  en contraste
   flagrant avec  certains langages  à la mode,  et en  particulier ceux utilisés  pour le
   web. 
   
   Veuillez envoyer vos commentaire à l'auteur:  alain.proute@free.fr