# 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 créer des types pour la 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 types.svm_plugin en utilisant ce code :

    PLUGIN type

    DEFINE

# Définition

## Structure

La première chose qui nous intéresse lorsque l'on crée un type, ce sont les valeurs qui sont contenues dans ce type.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.example
    %{
    	std::string _s;
    	std::string _t;
    %}

La syntaxe est inspirée du C ou du C++ : une déclaration du type, façon Simple Virtual Machine, suivi du contenu d'une structure en C ou C++ dans un bloc.

A ce stade, l'extension ne peut pas être générée.

## Fonctions

### Destructeur

La première fonction à ajouter au type pour qu'il soit utilisable par la machine virtuelle est le destructeur des valeurs de ce type.

Cette fonction est obligatoire pour que le type soit valide.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.example
    %{
    	std::string _s;
    	std::string _t;
    %}
    delete default: %{}

Générez l'extension. Vous pouvez aussi la compiler, mais cela ne sera pas encore nécessaire pour l'instant. Ouvrez le fichier de code généré et constatez que :

- Une struct "type_example" est générée, avec le contenu dans la structure. Attention, dans le nom de cette structure, la chaine "type_" n'est pas liée au nom de l'extension, mais une chaine constante ajoutée par le générateur d'extension : même dans une extension nommée "toto", la structure C ou C++ d'un type commencera par "type_".
- Une fonction "type_example_delete" est créée. Cette fonction prend une valeur sous la forme d'un "void *handler".
- Le corps de cette fonction n'est pas vide : la présente de l'option `default` permet de générer un contenu générique pour la fonction de destruction, qui appelle implicitement le destructeur de la structure du type en C++.
- Le code éventuellement placé dans le bloc de la fonction de destruction est ajouté entre le "reinterpret_cast" et le "delete" généré.
- Le fait que cette fonction accepte la valeur sous forme d'un "void *" supprimée par un "delete" implique que la valeur soit créée via un "new" en C++.

### Copie

Parfois, les valeurs d'un type extension peuvent être copiées.

La fonction de copie n'est pas obligatoire, et parfois n'est carrément pas souhaitable, selon ce que représente le type.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.example
    %{
    	std::string _s;
    	std::string _t;
    %}
    delete default: %{}
    copy default: %{}

Générez l'extension, puis ouvrez le code généré :

- Une nouvelle fonction "type_example_copy" a été générée.
- Grâce à l'option `default`, le contenu de la fonction n'est pas vide : un code de copie générique, appellant le constructeur par copie qui peut être explicitement déclaré dans la structure.
- Le code éventuellement placé dans le bloc de la fonction de copie est ajouté après la copie générique.

### Comparaison

Il est possible de comparer des valeurs entre elles. Pour que deux valeurs du type puissent être comparées, il peut être intéressant de spécifier cette comparaison.

La fonction de comparaison n'est pas obligatoire, et une comparaison par défaut peut s'appliquer lorsque la fonction n'est pas définie.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.example
    %{
    	std::string _s;
    	std::string _t;
    %}
    delete default: %{}
    copy default: %{}
    compare object:
    %{
    	if(object_left->_s<object_right->_s) return ORDER_INFERIOR;
    	if(object_left->_s>object_right->_s) return ORDER_SUPERIOR;
    	if(object_left->_t<object_right->_t) return ORDER_INFERIOR;
    	if(object_left->_t>object_right->_t) return ORDER_SUPERIOR;
    	return ORDER_EQUAL;
    %}

Générez l'extension, puis ouvrez le code généré :

- Une nouvelle fonction "type_example_compare" a été générée.
- Grâce à l'option `object`, le contenu de la fonction est préfixé par la conversion des pointeurs en objets utilisables.
- Le contenu de la fonction est copié à la bonne place, et réalise une comparaison d'ordre total.
- Notez que l'option `default` génère une fonction de comparaison par équivalence en utilisant l'opérateur == de la structure du type.

### Construction par constante

Il peut être commode de créer des valeurs de notre type directement dans le code de la machine.

La fonction de création par constante, facultative, doit permettre de créer une valeur du type à partir d'une chaine de caractères.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.example
    %{
    	explicit type_example(const std::string& s)
    	{
    		auto separator = s.find(';');
    		_s = s.substr(0,separator);
    		if(separator!=std::string::npos)
    		{
    			_t = s.substr(separator+1);
    		}
    	}
    	std::string _s;
    	std::string _t;
    %}
    delete default: %{}
    copy default: %{}
    compare object:
    %{
    	if(object_left->_s<object_right->_s) return ORDER_INFERIOR;
    	if(object_left->_s>object_right->_s) return ORDER_SUPERIOR;
    	if(object_left->_t<object_right->_t) return ORDER_INFERIOR;
    	if(object_left->_t>object_right->_t) return ORDER_SUPERIOR;
    	return ORDER_EQUAL;
    %}
    constant default: %{}

Générez l'extension, puis ouvrez le code généré :

- Une nouvelle fonction "type_example_constant" est générée.
- Ici aussi, le contenu n'est pas vide : l'option `default` a généré un code générique appellant le constructeur par conversion depuis une chaine.
- Le code éventuellement placé dans le bloc de la fonction de constante est ajouté après la création par chaine.

### Affichage

Par défaut, toute valeur de type extension est affichée par son pointeur en mémoire, ce qui n'est pas toujours très confortable en cas d'investigation.

La fonction d'affichage, facultative, permet d'afficher la valeur de manière plus explicite. Notez que lorsque les fonctions de construction par constante et d'affichage sont définies, il est fortement recommandé de rendre ses deux fonctions inverses l'une de l'autre.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.example
    %{
    	explicit type_example(const std::string& s)
    	{
    		auto separator = s.find(';');
    		_s = s.substr(0,separator);
    		if(separator!=std::string::npos)
    		{
    			_t = s.substr(separator+1);
    		}
    	}
    	operator std::string () const
    	{
    		return _s + ";" + _t;
    	}
    	std::string _s;
    	std::string _t;
    %}
    delete default: %{}
    copy default: %{}
    compare object:
    %{
    	if(object_left->_s<object_right->_s) return ORDER_INFERIOR;
    	if(object_left->_s>object_right->_s) return ORDER_SUPERIOR;
    	if(object_left->_t<object_right->_t) return ORDER_INFERIOR;
    	if(object_left->_t>object_right->_t) return ORDER_SUPERIOR;
    	return ORDER_EQUAL;
    %}
    constant default: %{}
    print default: %{}

Générez l'extension, puis ouvrez le code généré :

- Une fonction "type_example_print" est générée.
- Grâce à l'option `default`, le contenu de la fonction n'est pas vide : un code appellant l'opérateur de conversion vers une chaine est appellé.
- Le code éventuellement placé dans le bloc de la fonction d'affichage est ajouté après la création de la chaine.

- Un type se définit avec le prototype `TYPE plugin.entry` suivi d'un bloc décrivant le contenu de la structure C ou C++ associée au type.
- A cela, s'ajoute une fonction obligatoire de destruction dont la forme la plus simple se résume à `delete default: %{}`, forme appellant le destructeur de la structure C++.
- En plus, quatre fonctions facultatives peuvent être ajoutées :
  - Une fonction de copie,
  - Une fonction de comparaison,
  - Une fonction de construction par constante,
  - Une fonction d'affichage.
- Les fonctions associées à un type peuvent être complétement générées grâce à l'option `default`, en particulier quand l'extension est écrite en C++.

# Création de valeur

La création de valeur d'un type extension n'est pas comprise dans les fonctions associées à un type. Cependant, il est possible de créer une valeur à l'intérieur d'une instruction.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.example
    %{
    	type_example(const std::string& s, const std::string& t)
    	:_s(s), _t(t) {}
    	explicit type_example(const std::string& s)
    	{
    		auto separator = s.find(';');
    		_s = s.substr(0,separator);
    		if(separator!=std::string::npos)
    		{
    			_t = s.substr(separator+1);
    		}
    	}
    	operator std::string () const
    	{
    		return _s + ";" + _t;
    	}
    	std::string _s;
    	std::string _t;
    %}
    delete default: %{}
    copy default: %{}
    constant default: %{}
    print default: %{}

    INSTRUCTION type.new STR STR -> type.example
    %{
    	auto s = ARGV_VALUE(0,string);
    	auto t = ARGV_VALUE(1,string);
    	auto v = new type_example(RAW_STRING(s),RAW_STRING(t));
    	return NEW_PLUGIN(type,example,v);
    %}

Générez l'extension, puis (enfin) compilez la.

Puis, dans un fichier, vous pouvez créer cette petite application de test, et l'utiliser pour tester le type nouvellement créé :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugintype/libsvmtype.so"
    PLUGIN "svmcom.so"
    PROCESS "type"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory type.example/v, type.example/c
    		:type.new "a" "b" -> &v
    		CONST type.example "A;B" -> &c
    		:com.message "V=" @&v " C=" @&c
    	END
    END

- Une valeur de type extension peut être créé par une instruction, qui peut la retourner pour être écrite en mémoire.
- Notez que la valeur retournée par l'instruction de création peut ne pas être copiable : si elle n'est pas référencée ailleurs, elle sera déplacée en mémoire.

# Usages

## Valeur avec variable Simple Virtual Machine

Certains types ont pour but de contenir des variables Simple Virtual Machine. Dans ce cas, il faut s'assurer de la portée de ces variables pour lier leur existence avec celle des valeurs les contenant.

Modifiez le code de l'extension :

    PLUGIN type

    DEFINE

    TYPE type.symbol
    %{
    	SVM_Code _code;
    	size_t _address;
    %}
    delete default:
    %{
    	::svm_variable_scope_set_local(svm,object->_code);
    %}
    print object:
    %{
    	std::ostringstream oss;
    	SVM_String s = ::svm_code_get_name(svm,object->_code);
    	oss << "<" << RAW_STRING(s) << "/" << object->_address << ">";
    	return NEW_STRING(oss.str());
    %}

    INSTRUCTION type.new STR INT -> type.symbol
    %{
    	SVM_Value_String s = ::svm_parameter_value_get(svm,argv[0]);
    	auto i = ARGV_VALUE(1,integer);
    	if(i<0)
    	{
    		ERROR_INTERNAL(PROCESSOR,"Invalid address");
    	}
    	SVM_Variable c = ::svm_code_compile(svm,::svm_value_string_new__raw(svm,"code"),s);
    	if(svm_variable_type_is_code(svm,c))
    	{
    		auto v = new type_symbol { c , static_cast<size_t>(i) };
    		::svm_variable_scope_set_global(svm,c);
    		return NEW_PLUGIN(type,symbol,v);
    	}
    	::svm_processor_current_raise_error(svm,::svm_value_interruption_new_internal(svm,PROCESSOR,c,SOFTWARE));
    %}

    INSTRUCTION type.jump type.symbol
    %{
    	auto s = ARGV_PLUGIN(0,type,symbol);
    	SVM_Value_Symbol vs = ::svm_value_symbol_new(svm,s->_code,s->_address);
    	::svm_processor_jump_global(svm,CURRENT(kernel),vs);
    %}

Dans ce type, un des membres de la structure C++ est une variable de type `SVM_Code`. Notez que cette variable devient globale dans l'instruction qui crée la valeur, et devient locale dans la fonction de destruction du type : cela garantit que la variable est connue durant toute la vie de la valeur !

Générez l'extension, puis compilez la. Vous pouvez également regarder le code C++ généré, notament les fonctions de destruction et d'affichage de ce type.

Vous pouvez vérifier que ce type fonctionne grâce à ce code :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmrun.so"
    LOCAL PLUGIN "svmplugintype/libsvmtype.so"
    PROCESS "type"
    	CODE "main" INLINE
    		:memory type.symbol/s
    		:type.new """:run.trace 1
                    :run.trace 2
                    :run.trace 3
                    """ 1 -> &s
    		:run.coredump STDOUT
    		:type.jump @&s
    	END
    END

Pour vous en assurez, retirez la ligne où la variable devient globale, puis regénérez l'extension, compilez la et relancez l'application.

De même que n'importe quelle autre variable Simple Virtual Machine, si cette variable doit être partagée entre plusieurs instances du type, il convient de la rendre partagée avec la fonction `svm_variable_scope_set_shared`.

## Interface programmatique

Il est possible d'appeller les fonctions de copie, de comparaison, de construction par constante et d'affichage explicitement depuis l'interface programmatique.

Modifiez le code de l'extension :

    PLUGIN type

    includes:
    %{
    #include <iostream>
    %}

    DEFINE

    TYPE type.string
    %{
    	explicit type_string(const std::string& s)
    	:_s(s) {}
    	operator std::string () const
    	{
    		return _s;
    	}
    	bool operator==(const type_string& t) const
    	{
    		return _s==t._s;
    	}
    	std::string _s;
    %}
    delete default:
    %{
    	std::cout << "delete function" << std::endl;
    %}
    copy default:
    %{
    	std::cout << "copy function" << std::endl;
    %}
    compare default:
    %{
    	std::cout << "compare function" << std::endl;
    %}
    constant default:
    %{
    	std::cout << "constant function" << std::endl;
    %}
    print default:
    %{
    	std::cout << "print function" << std::endl;
    %}

    INSTRUCTION type.new STR -> type.string
    %{
    	auto s = ARGV_VALUE(0,string);
    	auto v = new type_string { RAW_STRING(s) };
    	return NEW_PLUGIN(type,string,v);
    %}

    INSTRUCTION type.copy type.string -> type.string
    %{
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[0]);
    	return ::svm_value_copy(svm,v);
    %}

    INSTRUCTION type.compare type.string type.string -> BLN
    %{
    	SVM_Value l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value r = ::svm_parameter_value_get(svm,argv[1]);
    	return NEW_VALUE(boolean,::svm_value_compare(svm,l,r).equal);
    %}

    INSTRUCTION type.constant STR -> type.string
    %{
    	SVM_Value_String s = ::svm_parameter_value_get(svm,argv[0]);
    	return ::svm_value_plugin_new_const(svm,CONST_PEP(type,string),s);
    %}

    INSTRUCTION type.print type.string -> STR
    %{
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_String s = ::svm_value_print(svm,v);
    	return NEW_VALUE(string,s);
    %}

Générez l'extension et compilez la. Créez ensuite une application qui manipule ce type et ces instructions. A chaque copie, construction par constante, affichage ou suppression, une ligne sera affichée sur le terminal.

- Une variable Simple Virtual Machine placée dans une structure de type doit normalement être mise globale quand la valeur est construite, et locale quand elle est détruite.
- Une variable Simple Virtual Machine peut être partagée si cela est nécessaire.
- Les fonctions de copie, comparaison, construction par constante et affichage sont accessibles depuis l'interface programmatique respectivement avec les fonctions `svm_value_copy`, `svm_value_compare`, `svm_value_plugin_new_const` et `svm_value_print`.

# Conclusion

Vous venez de voir comment créer et manipuler un type extension.

La création des types extensions ouvrent d'innombrables possibilités aux développeurs, car ils peuvent organiser leurs données comme bon leur semble, et contenir des objets qui ne sont pas présent de base sur la machine ou propre aux applications écrites avec la machine virtuelle.

Il est important de bien gérer les variables Simple Virtual Machine placées dans les valeurs de type extension, sous peine d'erreurs complexes à investiguer.