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.
Pour commencer, créez le canevas de l'extension dans le fichier code.svm_plugin en utilisant ce code :
PLUGIN code
DEFINE
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.
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.
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é.
svm_value_library_get_code
) et symboles (fonction svm_value_symbol_get_code
).svm_processor_get_currentcode
.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.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
.
svm_code_get_name
.svm_code_get_source
.svm_code_print
permet de récupérer une version annotée du code.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 :
svm_code_label_list
ne donne que les étiquettes, et pas les symboles.svm_code_label_has_address
et svm_code_label_get_address
tiennent compte des symboles comme étant des étiquettes.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).svm_code_label_list
et les symboles avec la fonction svm_code_symbol_list
.svm_code_label_has_address
et svm_code_label_get_address
.svm_code_symbol_has_address
et svm_code_symbol_get_address
.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.
svm_code_get_size
permet de connaître le nombre d'instructions d'un code Simple Virtual Machine.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.svm_code_instruction_is_system
et svm_code_instruction_is_waiting
permettent de récupérer les drapeaux des instructions.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.