Après qlq jours de "vacances", et une "réflexion" plus approfondie, voici
comment j'ai solutionné le problème (utiliser la bonne méthode sur le bon objet).
Je vous invite à lire et analyser ce code, faire toute remarques, critiques
ou erreurs détectées, de ne pas hésiter a demander plus d'explications si
celles que je donne ne sont pas claires, si certaines sont manquantes où s'il
reste une erreur de raisonnement dans ma solution.
Le tout, et je n'en doute pas, en restant poli et courtois.
Merci à vous, et bonne lecture.
DECLARATION DES APIS
CREATION DE TYPES REVENDIQUANT L'UTILISATION D'UNE OU PLUSIEURS API(S)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 api apiActions: jump, fire api apiAnims: explode, fall
Ici, 2 types sont crées, le type player et le type superplayer.
Tout 2 déclarent utiliser au minimum l'api apiActions, via l'opérateur
|= suivit d'une liste d'apis qu'ils disent implémenter. Je n'explique
pas ici le choix du symbole |= en lieu est place du mot-clef impl,
mais j'aborderai ce point ultérieurement ou en réponse à vos éventuelles.
questions.
UTILISATION
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8type player |= apiActions: int8 x, y end type superplayer |= apiActions, apiAnims: int16 x end
Ici, on instancie p comme étant de type player. Il appelle l'api
jump() exactement de la même manière que s'il appelait une méthode de
son type qui ne serait pas une api, mais une simple méthode.
Il n'y a dans le code source, aucune différence entre l'implémentation d'une
api ou l'implémentation d'une simple method.
Une api est juste une method listée faisant partie d'une api.
On voit aussi qu'une api peut appeler une autre api ou une
method du type.
Le type superplayer implémente lui 2 apis, et on crée une instance
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22mtd player.stop(int8 a) <- int16 value: me.y := a + 1 ; access type player x variable using "me". value := my.y ; load value with the result. end mtd player.fire(): end mtd player.jump(): int8 local_value local_value := 5 me.y := me.y + 3 + me.x + local_value me.fire() me.stop(5) end player p p.jump() int16 result result := p.stop(10)
sp de ce type.
POO "static"
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11type superplayer |= apiActions, apiAnims: int16 x end mtd superplayer.jump(): end mtd superplayer.explode(): end mtd superplayer.fire(): end mtd superplayer.fall(): end superplayer sp
On crée ici 2 instances de type objects, a savoir actors_1 et actors_2.
Un objects est un type contenant une liste d'instances de types.
On crée aussi 1 instances de type actions, a savoir actions_1.
Une actions est un type contenant une liste de noms d'api.
Et voici comment on utilise les [objects] et les [actions]:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 objects actors_1[sp, p] objects actors_2[p, sp] actions actions_1[jump, fire]
using est un mot-clef, suivit du nom d'1 objects, du symbole
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 using actors_1: actions_1 end using actors_2: actions_1 end
':', et d'1 actions (mais il pourrait en avoir plusieurs séparés
par une 'virgule ,'), puis clôturée via le mot-clef end.
En code "IRC" (Intermediate Represation Code), voici comment le using
est implémenté:
C'est complètement statique, mais une approche similaire pourrait être utilisée
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70;----------------------------------------------- USING - :CONST-using-1: !int16 = _using-1-objs=2 !int16 = _using-1-apis=2 :CONST-using-1-OBJS-actors_1: (:sp:) (:p:) :CONST-using-1-APIS-actions_1: (:CODE-superplayer-jump:) (:CODE-superplayer-fire:) (:CODE-player-jump:) (:CODE-player-fire:) :DATA-using-1: @int16 = _using-1-i_obj=0 @int16 = _using-1-i_api=0 @int16 = _using-1-adr_obj=(:CONST-using-1-OBJS-actors_1:) @int16 = _using-1-adr_api=(:CONST-using-1-APIS-actions_1:) :CODE-using-1: asm_SUB _using-1-adr_obj 2 asm_SUB _using-1-adr_api 2 :LOOP-1: asm_ADD _using-1-adr_obj 2 :LOOP-2: asm_ADD _using-1-adr_api 2 asm_PUSH int16 (:AFTER-5:) asm_PUSH int16 _using-1-adr_obj asm_JUMP TO _using-1-adr_api :AFTER-5: asm_ADD _using-1-i_api 1 asm_CMP _using-1-i_api _using-1-apis asm_BRNEQ (:LOOP-2:) asm_ADD _using-1-i_obj 1 asm_CMP _using-1-i_obj _using-1-objs asm_BRNEQ (:LOOP-1:) ;----------------------------------------------- USING - :CONST-using-2: !int16 = _using-2-objs=2 !int16 = _using-2-apis=2 :CONST-using-2-OBJS-actors_2: (:p:) (:sp:) :CONST-using-2-APIS-actions_1: (:CODE-player-jump:) (:CODE-player-fire:) (:CODE-superplayer-jump:) (:CODE-superplayer-fire:) :DATA-using-2: @int16 = _using-2-i_obj=0 @int16 = _using-2-i_api=0 @int16 = _using-2-adr_obj=(:CONST-using-2-OBJS-actors_2:) @int16 = _using-2-adr_api=(:CONST-using-2-APIS-actions_1:) :CODE-using-2: asm_SUB _using-2-adr_obj 2 asm_SUB _using-2-adr_api 2 :LOOP-3: asm_ADD _using-2-adr_obj 2 :LOOP-4: asm_ADD _using-2-adr_api 2 asm_PUSH int16 (:AFTER-6:) asm_PUSH int16 _using-2-adr_obj asm_JUMP TO _using-2-adr_api :AFTER-6: asm_ADD _using-2-i_api 1 asm_CMP _using-2-i_api _using-2-apis asm_BRNEQ (:LOOP-4:) asm_ADD _using-2-i_obj 1 asm_CMP _using-2-i_obj _using-2-objs asm_BRNEQ (:LOOP-3:)
avec une liste dynamique d'objects et d'actions.
Ici, la table d'indirection est "codée en dur", mais est courte, et l'accès (une fois
l'IRC transformé pour un assembleur pourrait écrire le même genre de code en
utilisant une adresse de base et un registre d'index. Mais j'ai voulu ici rester
simple, c'est une "proof of concept".
Chacun des using pourrait être mit dans une fonction (pour ne pas générer
le même code si usage multiples), et il pourrait même être possible d'avoir une
seule fonction gérant tous les using, réduisant fortement la taille du
code assembleur.
Je trouve cette approche intéressante (séparation des objects et des actions)
dans 2 "listes" différentes, pouvant ensuite être "combinées".
Il y a certainement qlq détails a améliorer, mais cela a le mérite de pouvoir
assez simplement faire exécuter une liste d'api (les actions) sur des types
ne dépendant pas l'un de l'autre, c'est à dire une liste d'objects (les objects)
hétérogènes.
Tout le principe peut être "validé" à la compilation. Si une api n'est pas implémentée, si un "objects"
utilise une 'actions' dont il n'implémente pas une des api.
Merci de votre attention et de vos réactions, qui me sont précieuses.
BàV et Peace & Love.







Répondre avec citation


Partager