bon , voila, j'y suis arrivé a faire mon compilateur a deux balle !
pour bien comprendre ce dont je vais parler , le postula suivant :
un interpreteur , "interprete" a la volée chaque ligne de code, pour effectuer une action
un compilateur , "compile" ou creer un prg pour qu'il soi autonome ...
un patcheur "patch" un fichier ou un executable pour modifier son comportement ..
de ce point de vue, mon compilateur a deux balle , tiens plus du patcher, que d'un réel compilo
mais au final, l'utilisateur, ne verra pas la différence, car le compilo a deux balles génère un Exe !
je repart donc sur mon petit interpréteur ci dessus, qui interprète un langage tres simple , comprenant 3 instructions
boucle x (lance une boucle et tiens a jour son compteur
Print (demande a afficher le resultat du compteur dans un requester )
fin_boucle qui termine la boucle pour pouvoir remonter (un Next quoi ) et met a jour le compteur
que je vais appeler notre langage maison, le "XXXlangage"/ou "XXXcode" pour qu'on ne se mélange pas les pinceaux
pour les besoins de mise en oeuvre du compilateur, j'ai légèrement modifié l’interpréteur pour que celui-ci interprète le contenu d'un fichier
et non plus le contenu d'une variable ... c'est a peine différent, mais nécessaire pour la suite ....
donc si vous voulez faire la démonstration que je vous propose , il vous faut creer un dossier exemple "
interpreteur_base"
dans lequel vous poserez tout les listings qui suivent : (il n'y en que 3
)
le premier listing que voici , c'est un créateur de code en "XXXlangage" , qui va recopier un certain nombre de fois
la ligne de XXXcode suivante :
histoire de montrer que l’interpréteur fonctionne avec un fichier multi ligne, et que le compilateur fonctionnera aussi
ce prg va donc generer un fichier "test.txt" , ce sera notre XXXCode a compiler (et interpreter ) ...
laissez bien ce fichier generé dans notre dossier de travail ....
Code : Tout sélectionner
OpenFile(1, GetCurrentDirectory()+"test.txt")
For i=1 to 10
WriteStringN (1,"boucle 5 print fin_boucle ")
Next i
CloseFile(1)
; Epb
le listing numero deux , va etre un Exe_Squelette ! , j'en ai parlé l'autre jour , lorsque j'affirmais qu'on pouvais utiliser un *.exe
et le détourner pour en faire un exécutable a nous ...
c'est ce que je vais faire , avec ce listing ...
il faudra donc le compiler avec Purebasic ce listing
SANS CHANGER LA MOINDRE LIGNE ,c'est important pour le fonctionnement
donc, tel quel , vous le sauverez dans notre dossier de travail
sous le nom de
"Programme_container_x86.exe"
Code : Tout sélectionner
;***********************************************
;Titre :*Programme_container_x86.exe
;Auteur : Zorro
;Date :31/12/2017
;Heure :18:15:53
;Version Purebasic : PureBasic 5.60 (Windows - x86)
;Version de l'editeur :EPB V2.68
; Libairies necessaire : Aucune
;***********************************************
; interpreteur bidon
Info.s="Programme_container_x86.exe"
newlist Pile.s()
Restore code:
While ligne_code.s<> "fin_code"
read.s ligne_code.s
; Analyse syntaxique (et pose des instructions dans notre pile )
For i=1 to len(ligne_code.s)
extrait.s=stringfield(ligne_code.s,i," ")
select extrait.s
Case "boucle" ; on tombe sur une commande de boucle
addElement(pile.s()) ; on l'ajoute dans la pile d'instruction
pile.s()="boucle"
i=i+1
nb=val(stringfield(ligne_code.s,i," ")) ; comme c'est une boucle, on recupe alors le nombre de boucle a faire (le parametre en fait )
addElement(pile.s()) ; on le pose aussi sur la pile
pile.s()=str(nb)
Case "print" ; on veux afficher un truc ??
addElement(pile.s()) ; on ajoute cette instruction sur la pile
pile.s()= "print"
Case "fin_boucle" ; tien une fin de boucle se presente
addElement(pile.s()) ; comme c'est une instruction reconnu par notre langage, on la pose aussi sur la pile
pile.s()= "fin_boucle"
EndSelect
Next i
Wend
;
ResetList(Pile()) ; on met le pointeur de pile a zero
; Execution (l'interpreteur !! )
; Lecture de notre pile d'instruction ET execution a la volée
ForEach Pile() ; pour chaque elements de notre pile , on va traiter
commande.s=Pile()
select commande.s
Case "boucle" ; on tombe sur la fonction boucle (For)
NextElement(pile()) ; comme c'est une boucle, on recupere le parametre de notre boucle (le nombre de fois qu'on va boucler ), il se trouve a la suite de la fonction 'boucle' dans notre pile
nb=val(Pile()); voila parametre recupéré dans notre variable systeme "nb"
pointeur_de_pile_retour=ListIndex(Pile()) ; on met en memoire la position du debut de boucle, comme ça on pourra revenir si boucle pas finie
Case "fin_boucle" ; tiens, on tombe sur la fonction de fin de boucle , il faut verifier si elle est finie ou pas
compteur=compteur+1 ; on met a jour la variable systeme de notre boucle
if compteur<nb ; boucle pas finie
SelectElement(Pile(), pointeur_de_pile_retour) ; on retourn au debut de la boucle (on saute dans la pile a l'emplacement du debut de boucle
Else ; la boucle est fini on a egalé ou depassé nb
compteur=0
Endif
Case "print" ; on tombe sur l'instruction d'affichage
MessageRequester("sortie",str(compteur)) ; on execute l'affichage avec un requester, ais ça aurai pu etre une sortie ecran ou que sais-je
EndSelect
Next
Fin:
Datasection
; le buffer pour le code reservé dans notre prg porteur, le compilo a 2 balles, va poser notre xxxcode dedans (en principe )
Code:
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s " "
data.s "fin_code"
EndDatasection
; Epb
j'ai deja commencé a commenter ce code , si vous etes perspicace, vous aurez compris que je vais utiliser la DATASECTION
pour poser notre XXXCode a interpréter !! , ne changez rien dans ce listing, sinon, ça ne marchera pas ....
et oui , si vous crée une datasection , et que vous laisser un signe dedans genre : data.s "toto"
si vous compilez avec Purebasic, en utilisant un viewer Hexadecimal , vous verrez "toto" ecris en clair dans le fichier *.exe ....
c'est un truc lié a l'assembleur , de memoire, on appel ça un segment .. je sais plus si c'est le Data,le Bss , ou autre...
bref, c'est cette particularité que je vais utiliser pour stocker mon XXXCode , et le faire exécuter par mon interpreteur
ben oui , mon executable ci dessus c'est en fait mon interpreteur compilé , qui cherchera a lire , le contenu de la datasection !!!
(qui pour le moment est vide)
donc, mon compilateur a deux balles qui suit, se contentera de poser le XXXcode , dans la Datasection du prg compilé !
voici le "compilateur" le plus petit du monde : (a sauver aussi (pas besoin de la compiler , vous pouvez le lancer dans l'editeur )
mais attendez de lire l'utilisation plus bas ....
Le compilateur a deux Balle :
Code : Tout sélectionner
;**********²*********************²****************
;Titre :*compilateur_a_deux_balles
;Auteur : Zorro
;Date :03/01/2018
;Heure :00:20:00
;Version Purebasic : PureBasic 5.60 (Windows - x86)
;Version de l'editeur :EPB V2.68
; Libairies necessaire : Aucune
;***********************************************
file.s=OpenFileRequester( "ouvrir test.txt",GetCurrentDirectory() ,"Fichiers Textes|*.txt",0)
OpenFile(0 ,file.s)
While Eof(0) = 0
code.s=code.s+ReadString (0,#PB_Ascii )
Wend
CloseFile(0)
; code.s ..... contient le code a patcher dans notre Prg porteur
CopyFile(GetCurrentDirectory() +"Programme_container_x86.exe", GetCurrentDirectory() +"Programme_compile.exe")
; on ouvre le prg porteur
If OpenFile( 0, GetCurrentDirectory()+"Programme_compile.exe")
length = Lof(0) ; Lit la taille en octets du fichier
FileSeek(0, 10936) ; place le pointeur a la position 10936 (l'emplacement de notre zone Datasection dans le fichier )
longueur_max= (46996-10936)/2 ; attention de ne pas depasser la zone reservé du prg Container
if Len(code.s)>longueur_max
code.s=left(code.s,longueur_max) ; on tronque si plus long
Endif
WriteString(0,code.s ,#PB_Unicode)
Endif
CloseFile(0)
MessageRequester("fini", "le prg Programme_compile.exe est genéré")
les lignes 27 et 28 determine a quel endroit commence et fini la datasection
c'est pourquoi il est important de ne rien toucher dans le code du prg container ....
explication de la mise en oeuvre :
en principe en lançant le premier listing, ça a du vous generer notre pseudo listing de XXXCode nommé
"test.txt"
ensuite vous avez du compiler le deuxieme listing pour avoir dans le dossier l'executable
"Programme_container_x86.exe"
donc si vous lancez le compilo a deux balles , celui ci vous demande d'ouvrir le listing
"test.txt"
et va generer un prg executable nommé
"Programme_compile.exe"
c'est ce dernier prg qui est le resultat de la compilation !
si vous modifiez le listing
"test.txt" , il suffit seulement dorénavant de lancer le compilo a deux balle , pour avoir la nouvelle version
de notre prg compilé "Programme_compile.exe"
si vous lancez le prg "Programme_compile.exe" , celui ci doit vous afficher un requester et augmenter le chiffre , qui prouve que la boucle
en langage XXX fonctionne .... chaque ligne de code compte de 0 a 4 (5 boucles )
pour sortir de ce prg un passage par Ctrl+Alt+Del , sera obligatoire, a moins de cliquer longtemps pour finir le prg ...