# Introduction

Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.

Dans ce didacticiel, vous allez découvrir comment définir vos propres instructions.

Le temps de lecture de ce didacticiel est estimé à 20 minutes.

# Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier instructions.svm_plugin en utilisant ce code :

    PLUGIN instruction

    DEFINE

# Prototype

## Objet instruction

Modifiez le fichier d'extension pour ajouter une première instruction :

    PLUGIN instruction

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION instruction.simple
    %{
    	std::cout << "Simple instruction called" << std::endl;
    %}

Une instruction s'écrit simplement :

1. en écrivant le prototype de l'instruction tel qu'il apparaît dans le débugueur dans la fenêtre des extensions,
2. suivi d'un bloc de code en C/C++ réalisant la fonctionnalité.

## Variables magiques

Générez le code C++ de l'extension, ouvrez le fichier "src/plugin.cpp" dans le dossier de l'extension, puis cherchez la fonction C++ qui correspond à l'instruction :

    SVM_Value instruction_simple(const void *svm, SVM_Size argc, SVM_Parameter argv[])

- Le type de retour de la fonction est `SVM_Value`, ce qui indique qu'une instruction peut retourner une valeur.
- Le premier paramètre est un pointeur lié à la variable magique `svm`. C'est ce paramètre qu'il faut transmettre à toutes les fonctions de l'interface programmatique.
- Les deux derniers paramètres sont liés aux variables magiques `argc` et `argv` que nous verrons juste après.

Vous pouvez également compiler l'extension, puis écrire une application minimaliste qui appelle l'instruction :

    ./instructions.svm
    Simple instruction called

- Une instruction se présente comme une fonction en C/C++ avec deux différences : le prototype est celui de la déclaration Simple Virtual Machine, et le code est entouré de `%{` et `%}`.
- Les variables magiques des instructions sont `svm`, `argc` et `argv`.

# Paramètres

Les variables magiques `argc` et `argv` permettent d'accéder aux arguments de l'instruction, un peu comme les arguments de ligne de commande avec la fonction "main" du C/C++ :

- "argc" contient le nombre d'arguments passés à l'instruction,
- "argv" est un tableau contenant les arguments passés à l'instruction,
- chaque élément du tableau n'est pas une valeur mais un `SVM_Parameter`, car certains arguments de l'instruction peuvent être des marqueurs ou des mots-clefs.

## Valeurs

Modifiez le fichier d'extension pour remplacer l'instruction simple par une autre :

    PLUGIN instruction

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION instruction.parameters ( INT STR ) +
    %{
    	long long int s = 0;
    	for(SVM_Size i = 0 ; i<argc ; i+=2)
    	{
    		SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[i]);
    		SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[i+1]);
    		long long int ri = ::svm_value_integer_get(svm,vi);
    		SVM_String rs = ::svm_value_string_get(svm,vs);
    		s += ri;
    		std::cout << std::string(rs.string,rs.size) << std::endl;
    	}
    	std::cout << "Sum: " << s << std::endl;
    %}

Puis créez le fichier exécutable d'application dans le fichier instructions.svm :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugininstruction/libsvminstruction.so"
    PROCESS "instructions"
    	CODE "main" INLINE
    		:instruction.parameters 1 "a" 2 "b" # argc = 4
    		:instruction.parameters 1 "a" 2 "b" 3 "c" 4 "d" 5 "e" # argc = 10
    	END
    END

L'exécution de l'application donne :

    ./instructions.svm
    a
    b
    Sum: 3
    a
    b
    c
    d
    e
    Sum: 15

Ici, les paramètres sont lus deux à deux :

- Il n'est pas nécessaire de vérifier que le nombre d'arguments est bien pair, car l'expression rationnelle de paramètres de l'instruction le garantit ici.
- Il n'est pas nécessaire non plus d'analyser les arguments, car la machine virtuelle le fait déjà.
- Les arguments de l'instruction sont tous des valeurs : il faut appeller la fonction interface `svm_parameter_value_get` sur l'élément de `argv` pour le transformer en valeur.

## Marqueurs

Modifiez le fichier d'extension pour ajouter une instruction :

    PLUGIN instruction

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION instruction.parameters ( INT STR ) +
    %{
    	long long int s = 0;
    	for(SVM_Size i = 0 ; i<argc ; i+=2)
    	{
    		SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[i]);
    		SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[i+1]);
    		long long int ri = ::svm_value_integer_get(svm,vi);
    		SVM_String rs = ::svm_value_string_get(svm,vs);
    		s += ri;
    		std::cout << std::string(rs.string,rs.size) << std::endl;
    	}
    	std::cout << "Sum: " << s << std::endl;
    %}

    INSTRUCTION instruction.markers MARKER *
    %{
    	for(SVM_Size i = 0 ; i<argc ; ++i)
    	{
    		SVM_String m = ::svm_parameter_marker_get(svm,argv[i]);
    		std::cout << std::string(m.string,m.size) << std::endl;
    	}
    %}

Générez et compilez l'extension, puis modifiez le fichier d'application :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugininstruction/libsvminstruction.so"
    PROCESS "instructions"
    	CODE "main" INLINE
    		:instruction.markers { < , > , <= , => , = , <> }
    	END
    END

L'exécution donne maintenant :

    ./instructions.svm
    {
    <
    ,
    >
    ,
    <=
    ,
    =>
    ,
    =
    ,
    <>
    }

L'instruction affiche chaque marqueur placé en argument. Les marqueurs sont obtenus grâce à la fonction interface `svm_parameter_marker_get` qui renvoient une chaine de caractères contenant le marqueur.

## Mots-clef

De la même manière, les mots-clefs peuvent être récupérés avec la fonction interface `svm_parameter_keyword_get` :

    PLUGIN instruction

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION instruction.parameters ( INT STR ) +
    %{
    	long long int s = 0;
    	for(SVM_Size i = 0 ; i<argc ; i+=2)
    	{
    		SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[i]);
    		SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[i+1]);
    		long long int ri = ::svm_value_integer_get(svm,vi);
    		SVM_String rs = ::svm_value_string_get(svm,vs);
    		s += ri;
    		std::cout << std::string(rs.string,rs.size) << std::endl;
    	}
    	std::cout << "Sum: " << s << std::endl;
    %}

    INSTRUCTION instruction.markers MARKER *
    %{
    	for(SVM_Size i = 0 ; i<argc ; ++i)
    	{
    		SVM_String m = ::svm_parameter_marker_get(svm,argv[i]);
    		std::cout << std::string(m.string,m.size) << std::endl;
    	}
    %}

    INSTRUCTION instruction.keywords KEYWORD *
    %{
    	for(SVM_Size i = 0 ; i<argc ; ++i)
    	{
    		SVM_String k = ::svm_parameter_keyword_get(svm,argv[i]);
    		std::cout << std::string(k.string,k.size) << std::endl;
    	}
    %}

Les mots-clefs sont également passés au code C/C++ sous forme de chaine de caractères.

Après génération et compilation de l'extension, vous pouvez modifier le code de l'application pour appeller cette instruction :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugininstruction/libsvminstruction.so"
    PROCESS "instructions"
    	CODE "main" INLINE
    		:instruction.keywords A B C D E
    	END
    END

Sans surprise, l'exécution donne :

    ./instructions.svm
    A
    B
    C
    D
    E

## Détection de paramètre

Lorsque la nature de l'argument ne peut être garanti par l'expression rationnelle de l'instruction, un appel à `svm_parameter_value_get`, `svm_parameter_marker_get` ou `svm_parameter_keyword_get` peut être fatal à l'exécution de l'instruction. Heureusement, l'interface permet de détecter la nature des paramètres.

Modifiez le fichier d'extension pour retirer les deux dernières instructions, et ajoutez une autre instruction :

    PLUGIN instruction

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION instruction.parameters ( INT STR ) +
    %{
    	long long int s = 0;
    	for(SVM_Size i = 0 ; i<argc ; i+=2)
    	{
    		SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[i]);
    		SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[i+1]);
    		long long int ri = ::svm_value_integer_get(svm,vi);
    		SVM_String rs = ::svm_value_string_get(svm,vs);
    		s += ri;
    		std::cout << std::string(rs.string,rs.size) << std::endl;
    	}
    	std::cout << "Sum: " << s << std::endl;
    %}

    INSTRUCTION instruction.detect . *
    %{
    	for(SVM_Size i = 0 ; i<argc ; ++i)
    	{
    		if(::svm_parameter_type_is_value(svm,argv[i]))
    		{
    			std::cout << i << ": value" << std::endl;
    			continue;
    		}
    		if(::svm_parameter_type_is_marker(svm,argv[i]))
    		{
    			std::cout << i << ": marker" << std::endl;
    			continue;
    		}
    		if(::svm_parameter_type_is_keyword(svm,argv[i]))
    		{
    			std::cout << i << ": keyword" << std::endl;
    			continue;
    		}
    		ERROR_INTERNAL(FAILURE,"Unknown parameter type");
    	}
    %}

Générez et compilez à nouveau l'extension, puis modifiez le code de l'application :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugininstruction/libsvminstruction.so"
    PROCESS "instructions"
    	CODE "main" INLINE
    		:instruction.detect 1 < "a" IS "weird"
    	END
    END

L'exécution donne :

    ./instructions.svm
    0: value
    1: marker
    2: value
    3: keyword
    4: value

## Macros

L'accès aux arguments d'une instruction est relativement fréquent dans les instructions. L'interface programmatique offre des fonctions qui permettent de réaliser toutes les opérations sur les arguments, mais au prix d'une certaine verbosité.

Pour cela, le générateur d'extension définit des macros C/C++ facilitant l'accès aux paramètres dans les cas les plus courants :

    PLUGIN instruction

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION instruction.parameters ( INT STR ) +
    %{
    	long long int s = 0;
    	for(SVM_Size i = 0 ; i<argc ; i+=2)
    	{
    		SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[i]);
    		SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[i+1]);
    		long long int ri = ::svm_value_integer_get(svm,vi);
    		SVM_String rs = ::svm_value_string_get(svm,vs);
    		s += ri;
    		std::cout << std::string(rs.string,rs.size) << std::endl;
    	}
    	std::cout << "Sum: " << s << std::endl;
    %}

    INSTRUCTION instruction.macros INT MARKER STR KEYWORD BLN
    %{
    	long long int i = ARGV_VALUE(0,integer);
    	std::string m = ARGV_MARKER(1);
    	SVM_String s = ARGV_VALUE(2,string);
    	std::string k = ARGV_KEYWORD(3);
    	SVM_Boolean b = ARGV_VALUE(4,boolean);
    	std::cout << i << " " << m << " " << std::string(s.string,s.size) << " " << k << " " << (b?"TRUE":"FALSE") << std::endl;
    %}

Après avoir généré et compilé l'extension, modifiez le code de l'application :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugininstruction/libsvminstruction.so"
    PROCESS "instructions"
    	CODE "main" INLINE
    		:instruction.macros 1 < "a" IS FALSE
    	END
    END

Puis exécutez l'application :

    ./instructions.svm
    1 < a IS FALSE

Ici, les macros permettent :

- d'accéder aux valeurs entières, chaines et booléenes dans un format directement exploitable en C/C++,
- d'accéder aux marqueurs et mots-clefs dans un format de chaine de caractères natif au C ou au C++.

- Les arguments de l'instruction sont accessibles au travers des variables magiques `argc` (nombre d'arguments) et `argv` (tableau d'arguments).
- Les fonctions `svm_parameter_value_get`, `svm_parameter_marker_get` et `svm_parameter_keyword_get` permettent d'accéder aux arguments.
- Les fonctions `svm_parameter_type_is_value`, `svm_parameter_type_is_marker` et `svm_parameter_type_is_keyword` permettent de connaître la nature d'un argument.
- Les macros "ARGV_VALUE", "ARGV_MARKER" et "ARGV_KEYWORD" permettent de faciliter l'accès aux arguments.

# Valeur de retour

Les instructions peuvent renvoyer des valeurs. Pour cela, le prototype de l'instruction doit indiquer une valeur de retour. Dans ce cas, le code doit également retourner une valeur :

    PLUGIN instruction

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION instruction.parameters ( INT STR ) + -> INT
    %{
    	long long int s = 0;
    	for(SVM_Size i = 0 ; i<argc ; i+=2)
    	{
    		auto ri = ARGV_VALUE(i,integer);
    		auto rs = ARGV_VALUE(i+1,string);
    		s += ri;
    		std::cout << RAW_STRING(rs) << std::endl;
    	}
    	return NEW_VALUE(integer,s);
    %}

Générez et compilez à nouveau l'extension, puis modifiez le code de l'application :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmplugininstruction/libsvminstruction.so"
    PROCESS "instructions"
    	CODE "main" INLINE
    		:memory INT/i
    		:instruction.parameters 1 "a" 2 "b" 3 "c" -> &i
    		:com.message "[" @&i "]"
    	END
    END

Cette fois, l'exécution donne :

    ./instructions.svm
    a
    b
    c
    [6]

Cette fois, la dernière ligne construit une valeur de type entier et retourne cette valeur à la machine.

Notez que lorsqu'une instruction ne retourne pas de valeur, la fonction C/C++ associée doit toujours renvoyer un `SVM_Value`. Par convention, il faut retourner un pointeur nul (au sens C/C++) dans un tel cas. La macro `RETURN` permet de garder un code cohérent tout en renvoyant ce pointeur nul.

- Une instruction retournant une valeur doit contenir un "return" du C/C++ avec une variable Simple Virtual Machine de classe valeur.
- Si l'instruction ne retourne pas de valeur, il est possible d'utiliser la macro `RETURN` pour sortir de l'instruction.

# Drapeaux

## Système

Une instruction d'extension peut être classée comme système (son exécution devient alors interdite dans un noyau protégé) simplement en ajoutant le drapeau `SYSTEM` devant le mot `INSTRUCTION`. Certaines fonctions de l'interface programmatique vérifient qu'elles sont exécutées dans une instruction système.

Modifiez le code de l'extension pour ajouter le drapeau à l'instruction, et exécutez le code en mode débugueur. Dans le débugueur, le drapeau apparaît dans la définition de l'instruction.

## Attente

Une instruction d'extension peut être notée comme pouvant attendre un événement extérieur simplement en ajoutant `WAITING` devant le mot `INSTRUCTION`. Notez que certaines fonctions de l'interface programmatique peuvent vérifier que l'instruction porte bien le drapeau d'attente, car elles réalisent une telle attente.

Modifiez le code de l'extension pour ajouter le drapeau à l'instruction, et exécutez le code en mode débugueur. Dans le débugueur, le drapeau apparaît dans la définition de l'instruction.

- Une instruction peut être classée système en ajoutant le drapeau `SYSTEM` au début de sa définition.
- Une instruction peut être notée comme introduisant une attente en ajoutant le drapeau `WAITING` au début de sa définition.
- Certaines fonctions de l'interface programmatique vérifient la présence de ces drapeaux dans la définition des instructions qui les appellent.

# Conclusion

Vous venez de voir comment créer vos propres instructions pour la Simple Virtual Machine.

L'écriture d'instructions personnalisées est le cœur de l'utilisation de la machine virtuelle pour des applications conséquentes.

De même, la majorité des fonctionnalités fournies avec la machine sont des instructions d'extension, et il est très confortable de pouvoir les utiliser sans avoir à les réimplémenter.

Une application conséquente écrite avec la Simple Virtual Machine devrait contenir au moins une extension dédiée à l'application avec des instructions spécifiques pour le fonctionnement de cette application.

Enfin, grâce à cette capacité à introduire rapidement des instructions fait de la machine virtuelle un orchestrateur de fonctions écrites en C/C++, au même titre qu'un shell orchestre des processus.