fichier main contient le code des fonctions Plus de détails...
Aller au code source de ce fichier.
Fonctions | |
int | sc_increment (mmachine m) |
auto increment | |
int | sc_hello (mmachine m) |
fonction example_hello | |
int | sc_division (mmachine m) |
fonction example_calcul | |
int | sc_calcul_list (mmachine m) |
fonction example_list | |
int | SCOLinitEXAMPLEclass (mmachine m) |
Fonction défininssant l'API Scol de la bibliothèque. | |
__declspec (dllexport) | |
int | SCOLloadEXAMPLE (mmachine m) |
int | SCOLfreeEXAMPLE () |
Fonction de déchargement / libération. | |
Variables | |
cbmachine | ww |
mmachine | mm |
char * | example_name [EXAMPLE_PKG_NB] |
Définition de l'API. | |
int(* | example_fun [EXAMPLE_PKG_NB])(mmachine m) |
Définition des fonctions internes. | |
int | example_narg [EXAMPLE_PKG_NB] |
Nombre d'arguments ou type d'objets. | |
char * | example_type [EXAMPLE_PKG_NB] |
Prototypage Scol. |
fichier main contient le code des fonctions
Définition dans le fichier main.c.
__declspec | ( | dllexport | ) |
Définition à la ligne 508 du fichier main.c.
00509 { 00510 int k = 0; 00511 /* Affectation aux variables globales ww et mm des valeurs envoyées par le noyau des structures partagées. 00512 Il est impératif de les ajouter AVANT tout appel à des fonctions ou macros Scol sous peine de crash immédiat 00513 lors de l'exécution de Scol. */ 00514 ww = w; 00515 mm = m; 00516 00517 MMechostr (0, "EXAMPLE library : loading\n"); 00518 00519 if ((k = SCOLinitEXAMPLEclass (m))) return k; 00520 00521 MMechostr (0, "\nEXAMPLE library loaded !\n"); 00522 return k; 00523 }
int sc_calcul_list | ( | mmachine | m | ) |
fonction example_list
mmachine | m |
Code interne de la fonction Scol 'example_list'
Celle-ci attend trois arguments : une liste d'entiers, un entier et un drapeau. Selon la valeur du drapeau, chaque élément de la liste sera incrémenté ou décrémenté de la valeur du second argument. En retour, la fonction Scol donne la nouvelle liste d'entiers. Son prototype est donc :
example_list : fun [[I r1] I I] [I r1]
Nous allons donc devoir extraire les éléments de la liste et en construire une nouvelle en sortie.
L'extraction d'une liste suit la même logique que pour un tuple sauf qu'on utilise une boucle 'while' puisqu'on ne connaît pas a priori le nombre d'éléments.
WHILE (liste non vide) e = premier élément de liste SI e non vide instructions liste = suite de la liste
Si 'e' est lui-même une liste ou un tuple, on traite de la même façon. Par exemple, si le type de l'argument est [[u0 u1 u2] r1], on aura :
while (mlist != NIL) { element = MMfetch (m, mlist, 0)>>1; if (element != NIL) { mvalue0 = MMfetch (m, element, 0)>>1; mvalue1 = MMfetch (m, element, 1)>>1; mvalue2 = MMfetch (m, element, 2)>>1; ... } mlist = MMfetch(m, mlist, 1)>>1; }
Pour créer une liste en sortie, le processus est similaire à celui vu à la fonction précédente.
On boucle pour possuer nos éléments un à un Une fois la liste terminée, on pousse un 'NIL' (toute liste se termine par 'nil' en Scol) Et on crée le tuple final (le premier élément de la liste et le reste de la liste)
Exemple de pseudo-code pour un type en retour : [[u0 u1 u2] r1]
compteur = 0 WHILE (liste_traitée) { values = liste_traitée->data x = values->x y = values->y z = values->z MMpush (m, PTOM (x)) MMpush (m, PTOM (y)) MMpush (m, PTOM (z)) MMpush (m, ITOM (3)) MBdeftab (m) compteur++ liste_traitée = liste_traitée->next } MMpush (m, NIL) FOR (i=0; i< compteur; i++) { MMpush (m, ITOM (2)) MBdeftab (m) }
Dans la boucle WHILE, on crée chaque tuple de trois éléments ([u0 u1 u2]) de valeur x, y, z Une fois cette boucle finie, on ajoute un NIL à la pile et on commence la boucle FOR d'un nombre de tours équivalent àcelui de la boucle WHILE. Là, on crée des tuples de deux éléments correspondant parfaitement (logique !) à la structure d'une liste.
Notez que la boucle WHILE peut être remplacéepar n'importe quel code C (telle une boucle FOR) selon vos besoins. Ce qui importe est que l'empilement des éléments et des tuples soit correct.
Pour rappel, une liste est définie de la sorte :
liste = [first next]
où next a lui aussi la même structure, jusqu'au nil final :
liste = [1 [2 [3 [4 [5 nil]]]]]
liste des 5 premiers entiers (qui peut également être écrite
liste = 1 :: 2 :: 3 ::4 :: 5 :: nil
notation plus lisible mais moins parlante sur sa structure sous-jacente (et c'est elle qui est importante ici !
Dans cette fonction 'sc_calcul_list', on pratique l'extraction des entiers d'entrées et l'incorporation des entiers de sortie dans la même boucle WHILE pour éviter d'avoir à sauvegarder les entiers entre les deux, ce qui alourdirait le code C inutilement.
Définition à la ligne 306 du fichier main.c.
00307 { 00308 int mlist_enter, mnumber, mflag; 00309 int element_list, i, compteur = 0; 00310 00311 MMechostr (MSKDEBUG, "sc_calcul_list : entering\n"); 00312 00313 mflag = MTOI (MMpull (m)); 00314 mnumber = MTOI (MMpull (m)); 00315 mlist_enter = MMpull (m)>>1; 00316 00317 while (mlist_enter != NIL) 00318 { 00319 element_list = MTOI (MMfetch (m, mlist_enter, 0)); 00320 if (element_list != NIL) 00321 { 00322 MMpush (m, ITOM (element_list + (mnumber * mflag))); 00323 compteur++; 00324 } 00325 mlist_enter = MTOI (MMfetch(m, mlist_enter, 1)); 00326 } 00327 00328 MMpush (m, NIL); 00329 00330 for (i = 0; i < compteur; i++) 00331 { 00332 MMpush (m, ITOM (2)); 00333 MBdeftab (m); 00334 } 00335 return 0; 00336 }
int sc_division | ( | mmachine | m | ) |
fonction example_calcul
mmachine | m |
Code interne de la fonction Scol 'example_calcul' Elle attend deux entiers en entrée sous la forme d'un tuple c'est-à-dire d'un seul argument contenant deux valeurs Elle retourne un autre tuple de deux entiers qui sont respectivement le quotient e tle reste de deux nombres passés en entrée.
Pour récupérer les valeurs contenues dans un tuple, on commence par dépiler ledit tuple, grâce à MMpull (1) puis on extraie les deux valeurs grâce à MMfetch. Elle ressemble quelque peu à MMget. MMfetch prend, en plus de la machine Scol courante, le tuple récupéré et l'index de l'élément désiré. Rien de plus simple :)
En sortie, la construction d'un tuple se construit diféremment puisqu'il faut construire la pile dans l'ordre voulu des éléments en les poussant (MMpush) (3) Puis on pousse le nombre d'éléments de notre tuple toujours avec MMpush (4), ici 2 puisque notre tuple contient le quotient et le reste. Enfin, le tuple est contruit grâce à ces informations (MBdeftab) (5) : le dernier élément de la pile est lu, il contient le nombre d'éléments à placer dans le tuple. Scol va alors chercher les N éléments sur les N étages de la pile.
On a ainsi une fonction Scol litérale : (quotient, reste) example_calcul (dividende, diviseur) soit,
example_calcul : fun [[I I]] [I I]
Remarque : il aurait été tout à fait possible d'avoir un prototype Scol : example_calcul : fun [I I] [I I] Il n'y aurait alors pas eu de tuple à récupérer et nous aurions alors eut :
En revanche, impossible de faire autrement que de passer par un tuple pour retourner deux ou plus valeurs à une fonction. Si ce nombre est inconnu (variable suivant les cas), on passera par une liste, c'est ce qu'illustre la fonction suivante.
Définition à la ligne 190 du fichier main.c.
00191 { 00192 int mtuple, mdividende, mdiviseur; 00193 int quotient, reste; 00194 00195 MMechostr (MSKDEBUG, "sc_division : entering\n"); 00196 00197 mtuple = MTOI (MMpull (m)); /* 1 */ 00198 mdividende = MTOI (MMfetch (m, mtuple, 0)); /* 2 */ 00199 mdiviseur = MTOI (MMfetch (m, mtuple, 1)); /* 2 */ 00200 00201 if (mdiviseur == 0) /* on teste le cas de la division par zéro */ 00202 return MMpush (m, NIL); 00203 00204 quotient = mdividende / mdiviseur; 00205 reste = mdividende % mdiviseur; 00206 00207 MMpush (m, ITOM (quotient)); /* 3 */ 00208 MMpush (m, ITOM (reste)); /* 3 */ 00209 MMpush (m, ITOM (2)); /* 4 */ 00210 return MBdeftab (m); /* 5 */ 00211 }
int sc_hello | ( | mmachine | m | ) |
fonction example_hello
mmachine | m |
Code interne de la fonction de l'API 'example_hello' Son prototype est : fun [S] S L'argument de type chaine de caractères n'est pas à garder dans la pile car on n'en a plus besoin. on utilisera alors de préférence MMpull En sortie, on ajoutera l'élément non pas avec MMpull mais Mpushstrbloc qui remplit exactement la même fonction mais uniquement pour les chaînes
menter < contenu de la pile, ici son dernier élément
enter < chaine entrée
result < chaine résultante
MTOP < convertit le pointeur de la pile en un pointeur système
MMstartstr < replace le cursuer au début de la chaîne
Mpushstrbloc < on pousse le résultat chaine dans la pile, à la dernière position
Définition à la ligne 127 du fichier main.c.
00128 { 00129 int menter; 00130 char * enter = NULL; 00131 char * result = NULL; 00133 MMechostr (MSKDEBUG, "sc_hello : entering\n"); 00134 00135 menter = MTOP (MMpull (m)); 00136 enter = MMstartstr (m, menter); 00138 result = (char *) malloc (sizeof (char) * (strlen (enter) +7)); /* allocation dynamique selon la taille de la chaine */ 00139 sprintf (result, "%s %s", "Hello", enter); 00140 00141 Mpushstrbloc (m, result); 00142 free (result); /* on libère ce qu'on a alloué précédemment */ 00143 return 0; 00144 }
int sc_increment | ( | mmachine | m | ) |
auto increment
mmachine | structure |
Code interne de la fonction de l'API Scol "example_increment" tel que définie plas bas, lors du chargement de la bibliothèque par le noyau. Le but de cette fonction est de simplement incrémenter de 1 la valeur passée en argument de la fonction Scol
MSKDEBUG est une macro permettant l'écriture d'un message dans le log, dépendant du masque choisi lors de l'exécution. Les différents masques possibles :
(aucun) -> 0 MSKFOO -> 1 MSKRUNTIME -> 2 MSKWARNING -> 4 MSKTRACE -> 8 MSKDEBUG -> 16
Une fonction Scol peut avoir 0 ou n arguments qui sont contenus dans la pile lors de l'appel. Sauf s'il n'y en a aucun, bien évidemment, il faut dépiler chaque argument grâce à l'une des deux fonctions suivantes : MMpull ou MMget.
MMpull dépile le dernier élément de la pile (celui d'index 0) ; il est par conséquent supprimé de la pile et l'élémentd'index 1 précédemment prend sa place, et ainsi de suite.
int p_var = MMpull (m);
MMget a contrario, lit simplement un élément de la pile sans le dépiler donc : il reste ainsi à sa place dans la pile et peut être réutilisé / modifié par la suite.
int p_var = Mget (m, 2);
lit le contenu de la pile à l'index 2 (antépénultième entrée).
int p_var = MMget (m, 0);
n'est pas équivalent à
int p_var = MMpull (m);
car dans le second cas, l'objet est dépilé et n'est donc plus accessible pour un traitement ultérieur.
On peut dépiler plusieurs fois de suite : si on souhaite les objets d'indices 0, 1, 2, on pourra, si on ne souhaite pas les garder dans la pile, appeler MMpull trois fois.
Les deux macros MTOI et ITOM : la première convertit une valeur de la machine Scol en un int système classique, la seconde est la réciproque : du int habituel à la machine Scol.
Deux autres macros du même type, plus générale : MTOP et PTOM qui converti vers et depuis un pointeur système.
Pour empiler un nouvel objet dans la pile, utilisez simplement la fonction MMpush (pousser) : l'objet qui était en position 0 passe alors en position 1. C'est le fonctionnement de base d'une pile ... :)
MMpush (m, p);
où p devrait être un pointeur (ou un entier).
Il est possible de modifier la valeur présente à un étage quelconque de la pile : c'est lafonction MMset.
MMset (m, pos, p);
où pos est l'index dans la pile et p le nouveau pointeur que contient l'objet de la machine Scol.
Ces différentes fonctions retournent 0 si succès. Le retour de la fonction interne n'a aucune influence sur ce que retourne la fonction Scol.
Dans le cas proposé ici, nous aurions pu coder la fonction avec le couple MMpull / MMpush plutôt que MMget / MMset. Cela n'aurait pas eu d'effets apparemment. Dans la plupart des cas, il est nécessaire d'utiliser l'un plutôt que l'autre ou, souvent, de les combiner.
int SCOLfreeEXAMPLE | ( | ) |
Fonction de déchargement / libération.
La fonction est appelée lorsque Scol se termine. Elle définit, comme pour la fonction de chargement, dans le fichier usm.ini (voir ci-dessus).
Les éventuels objets Scol présents devront avoir été détruits au préalable. Ici, on peut, par exemple, libérer des ressources tierces telle qu'une bibliothèque
Définition à la ligne 561 du fichier main.c.
00570 { 00571 MMechostr(MSKDEBUG, "\nEXAMPLE library release !\n"); 00572 return 0; 00573 }
int SCOLinitEXAMPLEclass | ( | mmachine | m | ) |
Fonction défininssant l'API Scol de la bibliothèque.
structure | mmachine |
Une API Scol est définie par 4 tableaux et une fonction.
Les 4 tableaux ont tous une taille identique. Ceux-ci définissent respectivement le nom Scol des objets de l'API, le nom des fonctions internes correspondantes, le nombre d'arguments ou le type d'objet, et le prototypage Scol.
La fonction interne 'PKhardpak' les chargera dans l'environnement minimal. Ses arguments sont respectivement définis comme suit :
Dans notre exemple, cette API est définie par 4 tableaux de EXAMPLE_PKG_NB cases précisément. Il est impératif que les 4 tableaux aient le même nombre de cases et que cenombre soit strictement équivalent au nombre de fonctions de l'API.
Le comppilateur pourra ne rien dire si le nombre de cases est plus grand que le nombre réelle de fonctions de l'API (le cas inverse engendrera un warning si vous avez suivi mes options de compilation). Cependant, à l'exécution, la bibliothèque ne sera pas chargée et il n'y aura aucun message d'erreur. Ŝoyez attentif à ce niveau (comme au reste !). Notez que "nombre de fonctions" inclue les fonctions proprement dit mais aussi les objets Scol (les types Scol), les flags, etc ... définis dans votre API.
Les tableaux doivent correspondre, c'est-à-dire que le nom de l'élément de l'API situé à la première case doit correspondre la fonction interne situé à la même case du second tableau, qui doit correspondre au nombre d'argument situé à la même case du troisième tableau et même chose pour le quatrième tableau.
Enfin, il va de soi que le nombre d'arguments défini pour un élément de l'API dans le troisème tableau doit être strictement cohérent avec le typage défini au quatrième tableau.
Par exemple : si le nombre d'argument est 2, le typage de la fonction pourra être "fun [I S] I" ou "fun [Chn S]" mais pas "fun [I S I]" ou "fun [S] I". Là encore, aucune erreur ne peut être détecté lors de la compilation mais lorsque la tentative de chargement de votre bibliothèque par le noyau échouera immanquablement sans la moindre erreur apparente.
Définition à la ligne 471 du fichier main.c.
00472 { 00473 int k = 0; 00474 00475 MMechostr (MSKDEBUG, "SCOLinitEXAMPLEclass library : loading\n"); 00476 00477 k = PKhardpak (m, "EXAMPLEengine", EXAMPLE_PKG_NB, example_name, example_fun, example_narg, example_type); 00478 return k; 00479 }
int SCOLloadEXAMPLE | ( | mmachine | m | ) |
Définition à la ligne 527 du fichier main.c.
00528 { 00529 int k = 0; 00530 /* Affectation à la variable globale mm des valeurs envoyées par le noyau des structures partagées. 00531 Il est impératif de l'ajouter AVANT tout appel à des fonctions ou macros Scol sous peine de crash immédiat 00532 lors de l'exécution de Scol. */ 00533 mm = m; 00534 00535 MMechostr (0, "EXAMPLE library : loading\n"); 00536 00537 if ((k = SCOLinitEXAMPLEclass (m))) return k; 00538 00539 MMechostr (0, "\nEXAMPLE library loaded !\n"); 00540 return k; 00541 }
int(* example_fun[EXAMPLE_PKG_NB])(mmachine m) |
{ sc_increment, sc_hello, sc_division, sc_calcul_list, (void*) ((-1)*2), (void*) (1*2) }
Définition des fonctions internes.
Ce tableau donne la correspondance des fonctions Scol vers le code interne. À ce sujet, en C, les flags peuvent poser problèmes. Le code présenté ici n'est pas valide ANSI C (une option -ansi de gcc provoquera un warning non bloquant).
Dans le code source existant, il existe parfois ceci :
#define bullshit int (__cdecl *)(struct Mmachine *) (bullshit) (1*2)
Cette solution fonctionne dans la plupart des cas mais n'est pas garantie (outre le fait que gcc lancera au mieux un warning , probablement une erreur à moins d'être très laxiste sur ces options.
char* example_name[EXAMPLE_PKG_NB] |
{ "example_increment", "example_hello", "example_calcul", "example_list", "EXAMPLE_MOINS", "EXAMPLE_PLUS" }
Définition de l'API.
\return | sans intérêt ici |
Quatre tableaux qui sont présentés à la fonction 'SCOLinitEXAMPLEclass' Les éléments définis dans ce preimer tableau seront alors accessibles depuis n'importe quel script Scol.
int example_narg[EXAMPLE_PKG_NB] |
{ 1, 1, 1, 3, TYPVAR, TYPVAR }
Nombre d'arguments ou type d'objets.
Lorsqu'il s'agit d'une fonction Scol, le nombre exact d'arguments doit être indiqué. Sinon, divers flags sont possibles. Les plus courants sont TYPVAR pour une valeur numérique et TYPTYPE pour un type objet Scol. Les autres sont listés sur le tutoriel ou, de façon moins expliquée, sur scol_plugin_xxx.h
char* example_type[EXAMPLE_PKG_NB] |
{ "fun [I] I", "fun [S] S", "fun [[I I]] [I I]", "fun [[I r1] I I] [I r1]", "I", "I" }
Prototypage Scol.
Les prototypes Scol des fonctions doivent correspondre avec le nmbre d'arguments notés au tableau 3. Dans le cas contraire, il y aura une erreur à l'exécution lors de l'appel de la fonction