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 :

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é :

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é :

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é :

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é :

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

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.

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.