# Introduction

Ce didacticiel est destiné aux nouveaux utilisateurs de la Simple Virtual Machine.

Dans ce didacticiel, vous allez utiliser les instructions provenant d'extensions.

Le temps de lecture de ce didacticiel est estimé à 20 minutes si l'écriture d'application simple a été abordée.

# Mise en place

Pour commencer, créez le canevas de l'application dans le fichier exécutable instructions.svm en utilisant ce code :

    #!/usr/bin/env svm
    DESCRIPTION
    Plugin instructions example
    END
    LOG
    DEBUG "Plugin instructions" STYLE "default"
    PROCESS "application"
    	CODE "main" INLINE
    	END
    END

# Utiliser une instruction extension

Il y a deux catégories d'instructions disponibles : les instructions internes de la machine virtuelle, et les instructions des extensions de la machine virtuelle.

## Charger l'extension dans la machine

Avant de pouvoir utiliser une instruction extension, il faut indiquer à la machine virtuelle de charger l'extension avant de lancer l'application.

Modifiez le code pour ajouter des extensions :

    #!/usr/bin/env svm
    DESCRIPTION
    Plugin instructions example
    END
    LOG
    DEBUG "Plugin instructions" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmint.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:debug BREAK
    	END
    END

Lancez l'application en mode débugueur, et ouvrez depuis le menu principal la fenêtre "Plugins" et filtrez sur les instructions :

Plugin instructions
Plugins

Definitions
TYPE com.device
STRUCT com.file
STRUCT com.tcp
STRUCT com.terminal
SYSTEM WAITING INSTRUCTION com.command com.device . * -> VALUE ?
INSTRUCTION com.equal com.device 2 -> BLN
WAITING INSTRUCTION com.idle ( ( > | < ( PEP | { PEP [ VALUE KEYWORD ] * } ) ? : protocol ) : mode PTR + : device_arrays ) + -> PTR : active_device
INSTRUCTION com.message [ 'STDOUT' 'STDERR' ] ? VALUE +
SYSTEM WAITING INSTRUCTION com.open PEP : device_type . * : parameters -> com.device
WAITING INSTRUCTION com.prompt ( [ 'STDOUT' 'STDERR' ] ? STR ) : prompt ? [ 'STR' 'INT' ] : output_type -> [ STR INT ]
WAITING INSTRUCTION com.read com.device PEP : protocol [ VALUE KEYWORD ] * : parameters -> STR ?
WAITING INSTRUCTION com.write com.device VALUE *
INSTRUCTION int.add ( INT INT + | PTR ) -> INT
INSTRUCTION int.cmp INT [ < > = <> <= => ] INT -> BLN
INSTRUCTION int.div INT INT -> INT
INSTRUCTION int.mod INT INT -> INT
INSTRUCTION int.mul ( INT INT + | PTR ) -> INT
INSTRUCTION int.parse STR -> INT
INSTRUCTION int.print INT ( [ 'OCT' 'DEC' 'HEX' ] INT ) ? -> STR
INSTRUCTION int.rand 'DICE' ? INT : limit -> INT
INSTRUCTION int.sub INT INT -> INT
FUNCTION com.device STRUCT -> com.device ?
FUNCTION com.device_file_close $com.file -> BLN
FUNCTION com.device_file_command $com.file . * -> VALUE ?
FUNCTION com.device_file_open [ < > <> >> ] STR [ 'EXEC' 'PRIV' ] * -> $com.file
FUNCTION com.device_file_print $com.file -> STR
FUNCTION com.device_file_read $com.file -> STR ?
FUNCTION com.device_file_write $com.file STR
FUNCTION com.device_tcp_close $com.tcp -> BLN
FUNCTION com.device_tcp_command $com.tcp . * -> VALUE ?
FUNCTION com.device_tcp_idle $com.tcp MUTABLE INT MUTABLE INT MUTABLE INT
FUNCTION com.device_tcp_open [ < > ] STR [ STR INT ] -> $com.tcp
FUNCTION com.device_tcp_print $com.tcp -> STR
FUNCTION com.device_tcp_read $com.tcp -> STR ?
FUNCTION com.device_tcp_write $com.tcp STR
FUNCTION com.device_terminal_close $com.terminal -> BLN
FUNCTION com.device_terminal_command $com.terminal . * -> VALUE ?
FUNCTION com.device_terminal_idle $com.terminal MUTABLE INT MUTABLE INT MUTABLE INT
FUNCTION com.device_terminal_open [ 'STDIN' 'STDOUT' 'STDERR' ] -> $com.terminal
FUNCTION com.device_terminal_print $com.terminal -> STR
FUNCTION com.device_terminal_read $com.terminal -> STR ?
FUNCTION com.device_terminal_write $com.terminal STR
FUNCTION com.protocol_all STR BLN : eof -> INT ?
FUNCTION com.protocol_available STR BLN : eof -> INT ?
FUNCTION com.protocol_line STR BLN : eof STR ? -> INT ?
FUNCTION com.protocol_size STR BLN : eof INT -> INT ?
INTERRUPTION com.interrupted

in
Filter

Cette liste contient toutes les instructions apportées par les extensions "svmcom.so" et "svmint.so". Elles augmentent les capacités de traitement de la machine virtuelle et sont essentielles pour écrire une application.

## Appeller des instructions extension

Modifiez le code pour ajouter des instructions d'extension :

    #!/usr/bin/env svm
    DESCRIPTION
    Plugin instructions example
    END
    LOG
    DEBUG "Plugin instructions" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmint.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:memory INT/a, INT/b, INT/r
    		:debug BREAK
    		:com.prompt "a" INT -> &a
    		:com.prompt "b" INT -> &b
    		:int.add @&a @&b -> &r
    		:int.mod @&r 10 -> &r
    		:com.message "r: " @&r
    	END
    END

Dans ce code, à part l'instruction `:memory`, toutes les instructions proviennent des deux extensions ajoutées précédemment. Cherchez dans la liste des instructions celles présentes dans le code.

Lancez les deux commandes suivantes dans un terminal pour accéder à la documentation des deux extensions :

    man svm_plugin_com
    man svm_plugin_int

Lancez l'application pour confirmer le comportement :

    ./instructions.svm
    a: 12
    b: 5
    r: 7

- Les instructions extension nécessitent de charger l'extension contenant l'instruction dans la machine virtuelle, en utilisant la directive `PLUGIN "fichier d'extension"` en début de fichier d'application.
- La documentation des extensions est disponible en utilisant la commande `man svm_plugin_nom d'extension` dans le terminal.
- Une instruction extension s'écrit `:nom d'instruction comportant un point liste d'arguments` où le nom d'instruction doit correspondre à une instruction connue de la machine virtuelle, et la liste d'argument doit respecter le prototype de l'instruction.
- Lorsqu'une instruction extension retourne un résultat, celui-ci peut être écrit en mémoire avec l'instruction `instruction extension -> adresse mémoire`.

# Comprendre le prototype d'une instruction extension

## Nom de l'instruction

Dans le code, vous pouvez remarquer deux catégories d'instructions :

1. Les instructions telles que `:memory` sans caractère point (.) qui sont les quelques instructions internes de la machine virtuelle. Ces instructions sont implémentées directement dans la machine et sont toujours accessibles.
2. Les instructions telles que `:int.add` avec un caractère point (.) qui sont les instructions définies dans les extensions de la machine virtuelle. Ces instructions sont implémentées à l'extérieur de la machine (dans des fichiers .so) par des utilisateurs avancés et sont chargées à la demande au démarrage de la machine.

Ici, c'est cette seconde catégorie qui nous intéresse.

## Arguments de l'instruction

Les instructions extension, comme les instructions internes, acceptent des arguments. Dans le code de la machine virtuelle, ces arguments sont une liste placée après le nom de l'instruction :

- de valeurs, immédiates ou lues en mémoire,
- de marqueurs, qui sont des séquences de caractères précises, servant souvent de délimiteurs,
- de mots-clef, qui sont des mots en lettres capitales, servant souvent de nom d'option.

Remplacez le code de l'application par :

    #!/usr/bin/env svm
    DESCRIPTION
    Plugin instructions example
    END
    LOG
    DEBUG "Plugin instructions" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmint.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:memory INT*5/array, INT/result1, INT/result2, INT/result3, BLN/test
    		[ 1 , 3 , 5 , 7 , 9  ] -> array
    		:debug BREAK
    		:int.add 12 5 -> &result1
    		:int.add 12 5 3 2 -> &result2
    		:int.add array -> &result3
    		:int.cmp @&result2 < @&result3 -> &test
    		:com.message @&result1 " " @&result2 " " @&result3 " -> " @&test
    	END
    END

Lancez cette application :

    ./instructions.svm
    17 22 25 -> TRUE

Maintenant, constatez que :

- L'instruction `:int.add` accepte 2 entiers ou plus comme arguments, et en fait la somme.
- L'instruction `:int.add` accepte également un pointeur, et fait la somme des entiers référencés par ce pointeur !
- L'instruction `:int.cmp` accepte deux entiers séparé par le caractère <. Ce caractère est un marqueur qui est ici utilisé pour matérialiser la comparaison entre deux entiers.
- L'instruction `:com.message` accepte plusieurs valeurs de types différents.

En regardant dans la fenêtre des extensions du débugueur, vous pouvez trouver les prototypes de ces instructions :

- `INSTRUCTION int.add ( INT INT + | PTR ) -> INT` : les parenthèses et le caractère | indiquent que les arguments ont deux alternatives. Soit l'instruction accepte un entier suivi d'au moins un entier (indiqué avec le caractère +), soit elle accepte un unique pointeur,
- `INSTRUCTION int.cmp INT [ < > = <> <= => ] INT -> BLN` : l'instruction accepte un entier, suivi d'un marqueur parmi ceux indiqués entre les crochets, puis un autre entier,
- `INSTRUCTION int.rand 'DICE' ? INT : limit -> INT` : l'instruction accepte un mot-clef (indiqué par les simples quotes ') optionel (indiqué par le point d'interrogation), suivi d'un entier auquel un nom a été attribué à titre de documentation,
- `INSTRUCTION com.message [ 'STDOUT' 'STDERR' ] ? VALUE +` : l'instruction accepte un des mots-clef entre crochet(indiqués par les simples quotes ') optionel (indiqué par le point d'interrogation), suivi d'au moins une valeur quelque soit son type (il s'agit d'un joker qui signifie "n'importe quel type").

La page de manuel de la machine virtuelle (que vous pouvez ouvrir avec la commande `man svm` dans le terminal, et dans laquelle vous pouvez chercher le texte "regular expression") contient toutes les clefs pour déchiffrer et interpréter ces expressions rationnelles d'arguments.

## Valeur de retour

Quand le prototype contient une flèche vers la droite (comme l'instruction d'affectation), cela signifie que l'instruction renvoie une valeur.

Cette expression rationnelle est limitée à une seule valeur, qui peut être optionnelle pour indiquer que la valeur renvoyée peut être nulle : si une telle valeur nulle est écrite à une certaine adresse, cette adresse devient non initialisée.

- La liste d'arguments peut donc varier pour une même instruction. Cette liste doit respecter le prototype de l'instruction.
- Le prototype contient non pas une liste d'arguments possibles, mais une expression rationnelle d'arguments. Autrement dit, une "forme" que doit adopter la liste des arguments placés après le nom de l'instruction dans le code.
- Le prototype peut contenir une clause de retour (identifiée avec `->` suivi d'une expression rationnelle limitée) lorsque l'instruction peut produire une valeur.

# Conclusion

Vous venez de voir comment intégrer à votre code les instructions implémentées dans les extensions.

Ces instructions offrent une grande souplesse dans leur utilisation, et confère à la machine virtuelle une grande variabilité dans ses capacités.

De plus, les instructions extension peuvent réaliser des choses extrêmement complexes : le langage de la machine virtuelle devient un langage bas niveau (langage machine avec des paradigmes simples) avec des instructions de haut niveau (avec des paradigmes complexes). C'est ainsi qu'il est possible de réaliser des applications complexes en très peu d'instructions, offrant à la fois versatilité et compacité des applications.