# 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 manipuler le code Simple Virtual Machine depuis une extension.

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 code.svm_plugin en utilisant ce code :

    PLUGIN code

    DEFINE

# Obtenir un code

## Depuis une valeur

Modifiez le code de l'extension :

    PLUGIN code

    DEFINE

    INSTRUCTION code.library LIB
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    %}

    INSTRUCTION code.symbol SYM
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_symbol_get_code(svm,s);
    %}

Pour le moment, vu que les instructions se contentent de récupérer le code sans agir dessus, il n'est pas nécessaire de générer l'extension et de la compiler.

Notez également, dans l'interface programmatique, la présence des fonctions `svm_value_symbol_get_address` qui permet de récupérer l'adresse de l'instruction dans le code et `svm_value_symbol_get_location` qui permet de récupérer sous forme de chaine la localisation du symbole dans le code.

## Depuis le processeur

Modifiez le code de l'extension :

    PLUGIN code

    DEFINE

    INSTRUCTION code.current -> LIB
    %{
    	SVM_Code c = ::svm_processor_get_currentcode(svm,CURRENT(kernel));
    	return NEW_VALUE(library,c);
    %}

Générez le code de l'extension et compilez la. Voici une petite application permettant de tester cette instruction :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugincode/libsvmcode.so"
    PLUGIN "svmcom.so"
    PROCESS "code"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory LIB/l
    		:code.current -> &l
    		:goto $(@&l/"s")
    		:com.message "avoided"
    	:symbol s
    		:com.message "reached"
    	END
    END

Lancez l'application. Puis, modifiez le code de l'application pour remplacer l'instruction de saut par `:goto $"s"`. Le comportement de l'application est le même.

## Par compilation

Modifiez le code de l'extension :

    PLUGIN code

    DEFINE

    INSTRUCTION code.new STR:name STR:source -> LIB
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_String s = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Code c = ::svm_code_new(svm,n,s);
    	return NEW_VALUE(library,c);
    %}

    INSTRUCTION code.compile STR:name STR:source -> [ LIB STR ]
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_String s = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Variable c = ::svm_code_compile(svm,n,s);
    	if(::svm_variable_type_is_code(svm,c))
    	{
    		return NEW_VALUE(library,c);
    	}
    	return c;
    %}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile un bout de code avec les deux instructions, puis qui réalise un appel de fonction à l'intérieur de ces code compilés.

Vous pouvez noter que la différence entre les deux fonctions `svm_code_new` et `svm_code_compile` réside dans leur gestion des erreurs de compilation. La première lève une interruption, alors que la seconde rapporte l'erreur sous forme d'une chaine, permettant à l'instruction de continuer son exécution malgré un code erroné.

- Un code Simple Virtual Machine peut être obtenu à travers des valeurs bibliothèque (fonction `svm_value_library_get_code`) et symboles (fonction `svm_value_symbol_get_code`).
- Le code actuellement en cours d'exécution peut être récupéré par la fonction `svm_processor_get_currentcode`.
- Un code peut aussi être compilé par une instruction via la fonction `svm_code_new` qui lève une interruption en cas de code erroné, et la fonction `svm_code_compile` qui permet une gestion des erreurs plus souple.

# Nom et code

Modifiez le code de l'extension :

    PLUGIN code

    DEFINE

    INSTRUCTION code.name LIB -> STR
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_String n = ::svm_code_get_name(svm,c);
    	return NEW_VALUE(string,n);
    %}

    INSTRUCTION code.source LIB -> STR
    %{
    	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,l);
    	SVM_String s = ::svm_code_get_source(svm,c);
    	return NEW_VALUE(string,s);
    %}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile un bout de code avec l'instruction `:library` et affiche le résultat des deux instructions.

Pour retrouver le code avec toutes les annotations, il faut plutôt utiliser la fonction `svm_code_print`.

- Le nom d'un code peut être récupéré avec la fonction `svm_code_get_name`.
- Le contenu d'un code peut être récupéré avec la fonction `svm_code_get_source`.
- La fonction `svm_code_print` permet de récupérer une version annotée du code.

# Etiquettes et symboles

Modifiez le code de l'extension :

    PLUGIN code

    DEFINE

    INSTRUCTION code.label LIB STR -> INT ?
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_Value_String l = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Boolean b = ::svm_code_label_has_address(svm,c,l);
    	if(not b)
    	{
    		return NEW_NULL_VALUE(integer);
    	}
    	SVM_Address a = ::svm_code_label_get_address(svm,c,l);
    	return NEW_VALUE(integer,a);
    %}

    INSTRUCTION code.labels LIB -> PTR
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_Value_String *l = ::svm_code_label_list(svm,c);
    	SVM_Size s = 0;
    	for(SVM_Value_String *it=l ; *it ; ++it)
    	{
    		::svm_value_state_set_movable(svm,*it);
    		++s;
    	}
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,STRING,s);
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_memory_write_pointer(svm,CURRENT(kernel),p,l);
    	return p;
    %}

    INSTRUCTION code.symbol LIB STR -> SYM ?
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_Value_String l = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Boolean b = ::svm_code_symbol_has_address(svm,c,l);
    	if(not b)
    	{
    		return NEW_NULL_VALUE(symbol);
    	}
    	SVM_Value_Symbol s = ::svm_code_symbol_get_address(svm,c,l);
    	return s;
    %}

    INSTRUCTION code.symbols LIB -> PTR
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_Value_String *l = ::svm_code_symbol_list(svm,c);
    	SVM_Size s = 0;
    	for(SVM_Value_String *it=l ; *it ; ++it)
    	{
    		::svm_value_state_set_movable(svm,*it);
    		++s;
    	}
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,STRING,s);
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_memory_write_pointer(svm,CURRENT(kernel),p,l);
    	return p;
    %}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile un bout de code ayant plusieurs étiquettes et symboles avec l'instruction `:library`. Ensuite, essayez d'appeller ces instructions sur la bibliothèque.

Vous pouvez alors remarquer que :

- La fonction `svm_code_label_list` ne donne que les étiquettes, et pas les symboles.
- En revanche, les fonctions `svm_code_label_has_address` et `svm_code_label_get_address` tiennent compte des symboles comme étant des étiquettes.
- La fonction `svm_code_label_get_address` renvoie une valeur de type `SVM_Address` (adresse utilisable dans un saut local), alors que `svm_code_symbol_get_address` renvoie une valeur de type `SVM_Value_Symbol` (adresse utilisable dans un saut global).

- L'interface programmatique permet de lister les étiquettes d'un code avec la fonction `svm_code_label_list` et les symboles avec la fonction `svm_code_symbol_list`.
- Elle permet aussi de tester la présence et de récupérer les adresses des étiquettes (et symboles en tant qu'étiquettes) avec les fonctions `svm_code_label_has_address` et `svm_code_label_get_address`.
- Elle permet aussi de tester la présence et de récupérer les adresses des symboles avec les fonctions `svm_code_symbol_has_address` et `svm_code_symbol_get_address`.

# Introspection

L'interface programmatique offre quelques fonctions permettant de décortiquer le contenu du code.

Modifiez le code de l'extension :

    PLUGIN code

    DEFINE

    INSTRUCTION code.size LIB -> INT
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_Size s = ::svm_code_get_size(svm,c);
    	return NEW_VALUE(integer,s);
    %}

    INSTRUCTION code.text LIB INT -> STR
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	auto a = ARGV_VALUE(1,integer);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_String t = ::svm_code_instruction_get_text(svm,c,a);
    	return NEW_VALUE(string,t);
    %}

    INSTRUCTION code.location LIB INT -> STR
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	auto a = ARGV_VALUE(1,integer);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_String l = ::svm_code_instruction_get_location(svm,c,a);
    	return NEW_VALUE(string,l);
    %}

    INSTRUCTION code.system LIB INT -> BLN
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	auto a = ARGV_VALUE(1,integer);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_Boolean s = ::svm_code_instruction_is_system(svm,c,a);
    	return NEW_VALUE(boolean,s);
    %}

    INSTRUCTION code.waiting LIB INT -> BLN
    %{
    	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
    	auto a = ARGV_VALUE(1,integer);
    	SVM_Code c = ::svm_value_library_get_code(svm,lib);
    	SVM_Boolean w = ::svm_code_instruction_is_waiting(svm,c,a);
    	return NEW_VALUE(boolean,w);
    %}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile du code dans une bibliothèque, puis qui récupère la taille du code. Pour chaque entier entre zéro et cette taille moins un, appellez les autres instructions pour afficher l'adresse, le texte de l'instruction, sa localisation dans le code, et ses drapeaux système et attente.

Vous remarquerez que les étiquettes et les symboles n'apparaissent pas dans la liste des instructions.

- La fonction `svm_code_get_size` permet de connaître le nombre d'instructions d'un code Simple Virtual Machine.
- Les fonctions `svm_code_instruction_get_text` et `svm_code_instruction_get_location` permettent de récupérer respectivement le texte de l'instruction sous forme normalisée, et sa position dans le code source.
- Les fonctions `svm_code_instruction_is_system` et `svm_code_instruction_is_waiting` permettent de récupérer les drapeaux des instructions.

# Conclusion

Vous venez de voir comment manipuler le code de la machine virtuelle depuis les extensions.

Savoir compiler du code au sein d'une instruction et le manipuler au besoin peut être très utile pour créer des portions modulaires d'une application.