# 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 la manipulation des valeurs mémoire de la machine virtuelle.

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

# Mise en place

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

    PLUGIN value

    DEFINE

    INSTRUCTION value.example VALUE
    %{
    %}

# Variables Simple Virtual Machine

## Types

Modifiez le code de l'instruction :

    PLUGIN value

    DEFINE

    INSTRUCTION value.example VALUE
    %{
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[0]);
    %}

Ici, le prototype indique que le premier paramètre est une valeur, sans préciser son type : le code mentionne ce fait en utilisant le type `SVM_Value`.

Dans le cas où le type du paramètre est connu, la variable Simple Virtual Machine peut utiliser un type parmi :

- `SVM_Value_Integer`
- `SVM_Value_String`
- `SVM_Value_Boolean`
- `SVM_Value_Pointer`
- `SVM_Value_Library`
- `SVM_Value_Symbol`
- `SVM_Value_PluginEntryPoint`
- `SVM_Value_Interruption`
- `SVM_Value_Plugin` (ce type est en réalité la classe de toutes les valeurs extensions)
- `SVM_Value_Automatic`

Cela permet d'indiquer le type de la valeur dans le code C ou C++, mais n'altère en rien le fonctionnement de l'extension, le type réel de la valeur n'étant pas déduit du type de la variable Simple Virtual Machine.

## Portée et destruction

La portée des valeurs est exactement la même que les variables Simple Virtual Machine : les fonctions `svm_variable_scope_set_global`, `svm_variable_scope_set_local`, `svm_variable_delete`, `svm_variable_scope_set_shared` et `svm_variable_scope_is_local` peuvent être employées sur les valeurs.

Les valeurs sont des variables Simple Virtual Machine qui contiennent tout ce qui peut être écrit dans une mémoire de la machine virtuelle.

# Opérations

## Création

Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.example BLN -> INT ?
    %{
    	SVM_Boolean b = ARGV_VALUE(0,boolean);
    	SVM_Value_Integer n = ::svm_value_integer_new_null(svm);
    	SVM_Value_Integer i = ::svm_value_integer_new(svm,17);
    	return b?i:n;
    %}

Cette instruction renvoie ici soit un entier avec une valeur, soit un entier nul. Pour vous en convaincre, vous pouvez utiliser cette petite application après avoir généré et compilé l'extension :

    #!/usr/bin/env svm
    LOG
    DEBUG "values"
    LOCAL PLUGIN "svmpluginvalue/libsvmvalue.so"
    PLUGIN "svmrun.so"
    PROCESS "values"
    	CODE "main" INLINE
    		:memory INT*2/i
    		[ 0 , 0 ] -> i
    		:value.example TRUE -> (i/0)
    		:value.example FALSE -> (i/1)
    		:run.coredump STDOUT
    	END
    END

Pour chaque type, une fonction de l'interface programmatique permet de créér une valeur nulle de ce type : cette fonction utilise le canevas `svm_value_new_null_<type>`.

Pour chaque type sauf le type automatique, une fonction de l'interface programmatique permet de créér une valeur de ce type : cette fonction utilise le canevas `svm_value_new_<type>` ou des variantes de ce canevas.

## Tests

### Types

Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.example [ INT STR BLN ] -> BLN
    %{
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[0]);
    	if(::svm_value_type_is_integer(svm,v))
    	{
    		return NEW_VALUE(boolean,TRUE);
    	}
    	else if(::svm_value_type_is_string(svm,v))
    	{
    		return NEW_VALUE(boolean,FALSE);
    	}
    	else //if(::svm_value_type_is_boolean(svm,v))
    	{
    		return v;
    	}
    %}

Comme cette instruction peut avoir plusieurs types possibles pour son premier paramètre, il peut être nécessaire de vérifier le type de la valeur avant de pouvoir manipuler la valeur.

Notez qu'il est aussi possible de récupérer le type d'une valeur avec la fonction `svm_value_type_get_internal`, ainsi que la fonction `svm_value_type_get_external` lorsque la valeur a un type défini dans une extension.

### Nullité

Modifiez le code de l'extension :

    PLUGIN value

    code:
    %{
    	SVM_Value _v;
    %}

    initialisation:
    %{
    	_v = ::svm_value_automatic_new_null(svm);
    	::svm_variable_scope_set_global(svm,_v);
    %}

    finalisation:
    %{
    	::svm_variable_scope_set_local(svm,_v);
    %}

    DEFINE

    INSTRUCTION value.set VALUE
    %{
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_variable_scope_set_local(svm,_v);
    	_v = v;
    	::svm_variable_scope_set_global(svm,_v);
    %}

    INSTRUCTION value.get -> VALUE
    %{
    	return _v;
    %}

    INSTRUCTION value.test -> BLN
    %{
    	return NEW_VALUE(boolean,::svm_value_state_is_null(svm,_v));
    %}

Vous pouvez générer puis compiler l'extension, puis la tester avec cette application :

    #!/usr/bin/env svm
    LOG
    DEBUG "values"
    LOCAL PLUGIN "svmpluginvalue/libsvmvalue.so"
    PLUGIN "svmrun.so"
    PROCESS "values"
    	CODE "main" INLINE
    		:memory BLN*2/b
    		:value.test -> (b/0)
    		:value.set 17
    		:value.test -> (b/1)
    		:run.coredump STDOUT
    	END
    END

Ici, la fonction `svm_value_state_is_null` permet de savoir si la variable valeur contient effectivement une valeur.

### Constance

Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.const VALUE -> BLN
    %{
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[0]);
    	return NEW_VALUE(boolean,::svm_value_state_is_constant(svm,v));
    %}

Après génération et compilation de l'extension, exécutez cette application :

    #!/usr/bin/env svm
    LOG
    DEBUG "values"
    LOCAL PLUGIN "svmpluginvalue/libsvmvalue.so"
    PLUGIN "svmrun.so"
    PROCESS "values"
    	CODE "main" INLINE
    		:memory BLN*2/b
    		:value.const FALSE -> (b/0)
    		:value.const @(b/0) -> (b/1)
    		:run.coredump STDOUT
    	END
    END

Ici, l'instruction renvoie un booléen vrai lorsque la valeur passée en paramètre est une valeur présente dans le code de l'application, et faux sinon.

### Comparaison

Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.compare VALUE [ = <> < <= > => ] VALUE -> BLN
    %{
    	SVM_Value l = ::svm_parameter_value_get(svm,argv[0]);
    	auto o = ARGV_MARKER(1);
    	SVM_Value r = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Value_Comparison c = ::svm_value_compare(svm,l,r);
    	if(c.weak)
    		::svm_machine_trace__raw(svm,"Comparison is weak");
    	if(o=="=")
    		return NEW_VALUE(boolean,c.equal);
    	if(o=="<>")
    		return NEW_VALUE(boolean,c.different);
    	if(not c.order)
    		ERROR_INTERNAL(FAILURE,"Invalid operation");
    	if(not c.total)
    		ERROR_INTERNAL(FAILURE,"Order is partial");
    	if(o=="<")
    		return NEW_VALUE(boolean,c.inferior);
    	if(o=="<=")
    		return NEW_VALUE(boolean,c.inferior_or_equal);
    	if(o==">")
    		return NEW_VALUE(boolean,c.superior);
    	if(o=="=>")
    		return NEW_VALUE(boolean,c.superior_or_equal);

    %}

Après génération et compilation de l'extension, exécutez cette application :

    #!/usr/bin/env svm
    LOG
    DEBUG "values"
    LOCAL PLUGIN "svmpluginvalue/libsvmvalue.so"
    PLUGIN "svmreal.so"
    PLUGIN "svmrun.so"
    PROCESS "values"
    	CODE "main" INLINE
    		:memory BLN*10/b
    		:interruption FAILURE ignore
    	:symbol a
    		:value.compare 1 = 1 -> (b/0)
    		:value.compare 1 = 2 -> (b/1)
    		:value.compare 1 < 2 -> (b/2)
    		:value.compare 1 = CONST real.number "1" -> (b/3)
    		:value.compare CONST real.number "1" < CONST real.number "2" -> (b/4)
    		:value.compare &3*5 <> &4*2 -> (b/5)
    		:value.compare $"a" = $"a" -> (b/6)
    		:value.compare $"a" = $"b" -> (b/7)
    		:value.compare $"a" < $"b" -> (b/8)
    		:value.compare TRUE <= FALSE -> (b/9)
    	:symbol b
    		:run.coredump STDOUT
    		:shutdown
    	:label ignore
    		:return
    	END
    END

Le résultat de cette application montre que la fonction `svm_value_compare` :

- permet de comparer des valeurs de types différents en indiquant que les valeurs sont différentes,
- permet de comparer les valeurs de tous les types internes de la machine virtuelle,
- permet de comparer les valeurs de tous les types extension,
- retourne la valeur pour les six comparaisons possibles : égalité, différence, inférieur, inférieur ou égal, supérieur, supérieur ou égal,
- supporte trois types de comparaisons :
  1. l'équivalence : cette comparaison est binaire, soit les valeurs sont égales soit différentes,
  2. l'ordre total : cette comparaison est ternaire, soit les valeurs sont égales, soit une est inférieure à l'autre (deux possibilités),
  3. l'ordre partiel : cette comparaison est quaternaire, car les valeurs peuvent aussi être non comparables.
- enfin, la comparaison peut être forte lorsqu'elle est bien définie, ou faible lorsque l'implémentation est partielle ou arbitraire.

- Le type d'une valeur peut être récupéré avec les fonctions `svm_value_type_get_internal` et `svm_value_type_get_external`, où de tester si la valeur est d'un type précis avec la fonction `svm_value_is_<type>`.
- Une valeur nulle peut être détectée avec la fonction `svm_value_state_is_null`.
- Une valeur constante peut être détectée avec la fonction `svm_value_state_is_constant`.
- Deux valeurs peuvent être comparées avec la fonction `svm_value_compare` qui supporte l'équivalence, l'ordre total et l'ordre partiel. La comparaison peut être forte ou faible selon l'implémentation.

## Valeur

### Récupération

Pour manipuler une valeur en C ou C++, il faut avant tout convertir les variables valeur en types C ou C++. Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.in INT PTR -> BLN
    %{
    	SVM_Value_Integer v = ::svm_parameter_value_get(svm,argv[0]);
    	auto i = ::svm_value_integer_get(svm,v);
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Address a = ::svm_value_pointer_get_address(svm,p);
    	SVM_Size s = ::svm_value_pointer_get_size(svm,p);
    	return ::svm_value_boolean_new__raw(svm,((i>=a) and (i<(a+s))));
    %}

Ce sont les fonctions `svm_value_get_<type>` et leurs variantes qui permettent de récupérer les valeurs en C ou C++ pour les exploiter.

Notez ici que pour une valeur générique comme un entier, la récupération de la valeur se fait dans un type du C ou C++. Pour une valeur spécifique à la machine comme un pointeur, la récupération de la valeur se fait dans des types de l'interface programmatique.

### Modification

Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.increment MUTABLE INT
    %{
    	SVM_Value_Integer v = ::svm_parameter_value_get(svm,argv[0]);
    	auto i = ::svm_value_integer_get(svm,v);
    	++i;
    	::svm_value_integer_set(svm,v,i);
    %}

Ici, la fonction à l'honneur est la fonction `svm_value_set_<type>`, qui permet de remplacer la valeur directement dans la variable.

Notez ici la présence du mot-clef `MUTABLE` dans le prototype de l'instruction : il évite les appels de l'instruction avec des valeurs constantes, ce qui ferait échouer la fonction `svm_value_integer_set` dans ce contexte.

- Les fonctions `svm_value_get_<type>` et leurs variantes permettent d'exploiter des valeurs en C ou C++.
- Les fonctions `svm_value_set_<type>` et leurs variantes permettent de remplacer le contenu d'une valeur non constante.

## Affichage

Modifiez le code de l'extension :

    PLUGIN value

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    INSTRUCTION value.display VALUE
    %{
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_String s = ::svm_value_print(svm,v);
    	std::cout << "[Value=" << RAW_STRING(s) << "]" << std::endl;
    %}

Générez et compilez l'extension, puis utilisez l'instruction dans une petite application de test de votre crû.

La fonction `svm_value_print` permet de récupérer la forme textuelle d'une valeur, telle qu'elle apparaît dans le débugueur.

## Copie

Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.shift INT PTR -> PTR
    %{
    	auto i = ARGV_VALUE(0,integer);
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Size s = ::svm_value_pointer_get_size(svm,p);
    	SVM_Value c = ::svm_value_copy(svm,p);
    	::svm_value_pointer_set_addresssize__raw(svm,c,i,s);
    	return c;
    %}

Ici, l'usage de la fonction `svm_value_copy` est intéressant, car il permet à cette instruction de fonctionner sur un pointeur constant passé en argument.

La fonction `svm_value_copy` permet d'obtenir une copie d'une valeur. La copie est une nouvelle instance de la valeur. Cette fonction échoue avec une interruption si la valeur est d'un type extension dont la fonction de copie n'est pas définie.

## Déplacement

Lorsqu'une valeur est écrite en mémoire, elle est copiée par défaut. Cela peut être gênant lorsque la valeur ne peut pas être copiée. Dans ce cas, il est possible de marquer une valeur comme étant déplaçable en mémoire : cela signifie que la valeur sera placée une fois en mémoire directement, sans aucune copie. Une seconde écriture en mémoire forcera une copie.

C'est notamment le cas par défaut pour les valeurs de retour des instructions, ce qui permet à une instruction de retourner une valeur de type extension sans fonction de copie créée par l'instruction d'être placée en mémoire !

Modifiez le code de l'extension :

    PLUGIN value

    DEFINE

    INSTRUCTION value.move INT PTR -> PTR
    %{
    	auto i = ARGV_VALUE(0,integer);
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Size s = ::svm_value_pointer_get_size(svm,p);
    	SVM_Value c = ::svm_value_copy(svm,p);
    	::svm_value_state_set_movable(svm,c);
    	::svm_value_pointer_set_addresssize__raw(svm,c,i,s);
    	return c;
    %}

Ici, la valeur copiée est explicitement marquée comme déplaçable. (Ce qui aurait été fait de toute manière au retour de l'instruction.)

- La notion de déplacement d'une valeur n'est pas identique à celle du C++ moderne.
- Cette notion concerne uniquement la manière de placer la valeur en mémoire : par défaut, une valeur écrite en mémoire est copiée.
- Une valeur marquée déplaçable sera en revance placée en mémoire sans copie.
- Il est possible de marquer une valeur comme déplaçable avec la fonction `svm_value_state_set_movable`.

# Conclusion

Vous venez de voir comment manipuler les variables Simple Virtual Machine qui contiennent des valeurs mémoire.

La gestion des valeurs de la machine virtuelle est essentielle pour l'écriture des instructions extension. En particulier car ces valeurs font partie de l'interface entre le code de l'instruction et le code de la machine virtuelle.

Connaître l'existence et le fonctionnement des différentes fonctions de l'interface programmatique dédiées aux valeurs est important pour une gestion raisonnable des valeurs de la machine virtuelle.

Vous avez maintenant en main tous les concepts vous permettant d'écrire vos premières instructions extension, celles permettant de réaliser des calculs sans altérer la machine virtuelle.