Autres articles / Other articles

Les variables locales avec eFORTH

publication: 18 mars 2023 / mis à jour 18 mars 2023

Read this page in english

 


Introduction

Le langage FORTH traite les données essentiellement par la pile de données. Ce mécanisme très simple offre une performance inégalée. A contrario, suivre le cheminement des données peut rapidement devenir complexe. Les variables locales offrent une alternative intéressante.

Le faux commentaire de pile

Si vous suivez les différents articles et exemples FORTH, vous avez noté les commentaires de pile encadrés par ( et ). Exemple:

\ add two unsigned values, leave sum and carry on stack 
: um+ ( u1 u2 -- sum carry ) 
    \ here the definition 
  ; 

Ici, le commentaire ( u1 u2 -- sum carry ) n'a absolument aucune action sur le reste du code FORTH. C'est un pur commentaire.

Quand on prépare une définition complexe, la solution est d'utiliser des variables locales encadrées par { et }. Exemple:

: 2OVER { a b c d } 
    a b c d a b 
  ; 

On définit quatre variables locales a, b, c et d.

Les mots { et } ressemblent aux mots ( et ) mais n'ont pas du tout le même effet. Les codes placés entre { et } sont des variables locales. Seule contrainte: ne pas utiliser de noms de variables qui pourraient être des mots FORTH du dictionnaire FORTH. On aurait aussi bien pu écrire notre exemple comme ceci:

: 2OVER { varA varB varC varD } 
    varA varB varC varD varA varB 
  ; 

Chaque variable va prendre la valeur de la donnée de pile dans l'ordre de leur dépôt sur la pile de données. ici, 1 va dans varA, 2 dans varB, etc..:

--> 1 2 3 4
 ok
1 2 3 4 --> 2over
 ok
1 2 3 4 1 2 -->

Notre faux commentaire de pile peut être complété comme ceci:

: 2OVER { varA varB varC varD -- varA varB } 
  ...... 

Les caractères qui suivent -- n'ont pas d'effet. Le seul intérêt est de rendre notre faux commentaire semblable à un vrai commentaire de pile.

Action sur les variables locales

Les variables locales agissent exactement comme des pseudo-variables définies par value. Exemple:

: 3x+1 { var -- sum } 
    var 3 * 1 + 
  ; 

A le même effet que ceci:

0 value var 
: 3x+1 ( var -- sum ) 
    to var 
    var 3 * 1 + 
  ; 

Dans cet exemple, var est défini explicitement par value.

On affecte une valeur à une variable locale avec le mot to ou +to pour incrémenter le contenu d'une variable locale. Dans cet exemple, on rajoute une variable locale result initialisée à zéro dans le code de notre mot:

: a+bEXP2 { varA varB -- (a+b)EXP2 } 
    0 { result } 
    varA varA *      to result 
    varB varB *     +to result 
    varA varB * 2 * +to result 
    result 
  ; 

Est-ce que ce n'est pas plus lisible que ceci?

: a+bEXP2 ( varA varB -- result ) 
    2dup 
    * 2 * >r 
    dup * 
    swap dup * + 
    r> + 
  ; 

Voici un dernier exemple, la définition du mot um+ qui additionne deux entiers non signés et laisse sur la pile de données la somme et la valeur de débordement de cette somme:

\ add two unsigned values, leave sum and carry on stack 
: um+ { u1 u2 -- sum carry } 
    0 { sum } 
    cell for 
        aft 
            u1 $100 /mod to u1 
            u2 $100 /mod to u2 
            + 
            cell 1- i - 8 * lshift  +to sum 
        then 
    next 
    sum 
    u1 u2 + abs 
  ; 

Voici un exemple plus complexe, la réécriture DUMP en exploitant des variables locales:

\ locals variables in DUMP: 
\ START_ADDR      \ first address to dump 
\ END_ADDR        \ latest address to dump 
\ 0START_ADDR     \ first address for dump loop 
\ LINES           \ number of lines for dump loop 
\ myBASE          \ current numeric base 
internals        
: dump ( start len -- ) 
    cr cr ." --addr---  " 
    ." 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  ------chars-----" 
    2dup + { END_ADDR }             \ store latest address to dump 
    swap { START_ADDR }             \ store START address to dump 
    START_ADDR 16 / 16 * { 0START_ADDR } \ calc. addr for loop start 
    16 / 1+ { LINES } 
    base @ { myBASE }               \ save current base 
    hex 
    \ outer loop 
    LINES 0 do 
        0START_ADDR i 16 * +        \ calc start address for current line 
        cr <# # # # #  [char] - hold # # # # #> type 
        space space     \ and display address 
        \ first inner loop, display bytes 
        16 0 do 
            \ calculate real address 
            0START_ADDR j 16 * i + + 
            c@ <# # # #> type space \ display byte in format: NN 
        loop  
        space 
        \ second inner loop, display chars 
        16 0 do 
            \ calculate real address 
            0START_ADDR j 16 * i + + 
            \ display char if code in interval 32-127 
            c@     dup 32 < over 127 > or 
            if      drop [char] . emit 
            else    emit 
            then 
        loop  
    loop 
    myBASE base !               \ restore current base 
    cr cr 
  ; 
forth 

L'emploi des variables locales simplifie considérablement la manipulation de données sur les piles. Le code est plus lisible. On remarquera qu'il n'est pas nécessaire de pré-déclarer ces variables locales, il suffit de les désigner au moment de les utiliser, par exemple: base @ { myBASE }.

ATTENTION: si vous utilisez des variables locales dans une définition, n'utilisez plus les mots >r et r>, sinon vous risquez de perturber la gestion des variables locales. Il suffit de regarder la décompilation de cette version de DUMP pour comprendre la raison de cet avertissement:

: dump  cr cr s" --addr---  " type 
    s" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  ------chars-----" type 
    2dup + >R SWAP >R -4 local@ 16 / 16 * >R 16 / 1+ >R base @ >R 
    hex -8 local@ 0 (do) -20 local@ R@ 16 * + cr 
    <# # # # # 45 hold # # # # #> type space space 
    16 0 (do) -28 local@ j 16 * R@ + + CA@ <# # # #> type space 1 (+loop) 
    0BRANCH rdrop rdrop space 16 0 (do) -28 local@ j 16 * R@ + + CA@ DUP 32 < OVER 127 > OR 
    0BRANCH DROP 46 emit BRANCH emit 1 (+loop) 0BRANCH rdrop rdrop 1 (+loop) 
    0BRANCH rdrop rdrop -4 local@ base ! cr cr rdrop rdrop rdrop rdrop rdrop ;

Legal: site web personnel sans commerce / personal site without seling