Tutorial piszemy interpreter wlasnego prostego jezyka programowania
Więc zaczynamy
Potrzebne nam będzie: kompilator c++ , znajomosc c++ i podstaw asma.
W tym przykladzie pokaze bardzo prosta implemenetacje jezyka o dosc ograniczonych mozliwosciach
ale to tylko przyklad ktory polecam rozbudowac wg. uznania
Najpierw słowem wstępu troche teorii:
zacznijmy od tego jak wywolywana jest funkcja w asmie:
najpierw wszystkie parametry sa rzucane na stos ale co bardzo wazne w odwrotnej kolejnosci niz w c++ czy Delphi
np. chcemy wywołać funkcja printf
printf(bufor,"%i",5);
ale rzucamy na stos parametry w kolejnosci 5,"%i",bufor
nastepnie wywolujemy ta funkcje a ona bierze parametry ze stosu
Co to interpreter:
Jest to program ktory WYKONUJE tylko kod a nie kompilujacy kod do postaci wykonywalnej np. exe
Teraz troche praktyki:
Jedną z najwazniejszych rzeczy w programie jest pamięć
ja postanowiłem zorganizować ją w następujacy sposob- mamy 3 tablice, w jednej przechowywane sa stringi
w jednej liczby,a w jednej wskazniki na zaalokowana przez nas pamiec.
Przy rzucaniu na stos jako parametr podajemy tylko numer indexu tablicy(numerowana od 0)
Ja dla prostoty przykladu ustaliłem tylko 8 słów kluczowych:
AS - alokuje string
AD - Alokuje liczbę
AV - alokuje pamiec
PS - rzuca na stos string
PD - rzuca na stos liczbe
PV - rzuca na stos wskaznik do zaalokowanej przez nas pamieci
C-wywoluje funkcje
E-konczy dzialanie i sprzata po sobie
teraz troche kodu:
W powyzszym kodzie dolaczamy niezbedne pliki naglowkowe i definujemy nasze tablice na zmienneKod:#include <windows.h> #include <string> #include <vector> std::vector <PVOID> sVar; //tablica przechowujaca stringi std::vector <PVOID> vVar; //tablica na dowolne zaalokowane dane i wskazniki std::vector <DWORD> dVar; //tablica na liczby całkowite
Funkcje alokujace dane:Kod:char* GetLine(char* code,int line) //funkcja pomocnicza pobierajaca tresc pojedynczej lini kodu { ****std::string str=code; ****std::string b; ****b=str; ****int i=0; ****int n=0; ****if(line==0) ****{ ****} ****int n2=0; ****while(i<line) ****{ n=str.find("\n",n+1); i++; ****} n2=str.find("\n",n+1); if(line==0) { n=-1; } b=str.substr(n+1,n2-n-1); char* bf=new char[b.size()]; memcpy(bf,b.c_str(),b.size()+1); return bf; }
Funkcja wywolujaca funkcje:Kod:void alloc_s(PVOID buf,UINT size) // funkcja alokujaca stringi { PVOID b=malloc(size); memcpy(b,buf,size+1); sVar.push_back(b); } void alloc_v(UINT size) //funkcja alokujaca pamiec na dowolne dane { PVOID b=malloc(size); vVar.push_back(b); } void alloc_d(DWORD num)//funkcja alokujaca liczby { dVar.push_back(num); }
Funkcje rzucajace na stos:Kod:void call(char* func,char* lib,char* type) // funkcja wywolujaca funkcja; func - nazwa funkcji //lib-biblioteka dll z ktorej trzeba ja wyexportowac //type - typ zwracany { HMODULE lb=LoadLibrary(lib);//pobieramy adres biblioteki dll FARPROC proc=GetProcAddress(lb,func);//pobieramy adres funckcji if(proc!=0)//jesli udało sie pobrac prawidlowy adres to nalezy funkcje wykonac { __asm call proc if(type[0]=='D')//zwracane wartosci typu DWORD { DWORD x; __asm mov x,eax; alloc_d(x); } if(type[0]=='S')//zwracane wartosci typu char* { char* str; __asm mov str,eax alloc_s(str,strlen(str)); } if(type[0]=='V')//zwracane wartosci typu PVOID { PVOID p; __asm mov p,eax vVar.push_back(p); } } }
teraz jedna z najwazniejszych rzeczy- funkcja rozpoznajaca i wykonujaca kod:Kod:void push(PVOID a)//funkcja rzucajaca**na stos wskaznik { __asm push a } void push_d(DWORD a)//funkcja rzucajaca na stos liczbe { __asm push a }
Kod:void parse(char* code)//funkcja wykonujaca kod { **** ****std::string line; ****std::string par1,par2; ****int i=0; ****while(1) ****{ line = GetLine(code,i); if(line[0]=='E')//koniec kodu { memset(code,0,strlen(code)); sVar.clear(); dVar.clear(); vVar.clear(); break; } if(line[0]=='A')//alokacja { if(line[1]=='S')//stringu { par1=line.substr(3,0xFFFF); alloc_s((PVOID)par1.c_str(),par1.size()); } if(line[1]=='V')//wlasnej pamieci { par1=line.substr(3,0xFFFF); DWORD g=atoi(par1.c_str()); alloc_v(g); } if(line[1]=='D')//liczby { par1=line.substr(3,0xFFFF); DWORD g=atoi(par1.c_str()); alloc_d(g); } } if(line[0]=='P')//rzucamy na stos { if(line[1]=='D')//liczbę { par1=line.substr(3,0xFFFF); DWORD g=atoi(par1.c_str()); push_d(dVar[g]); } if(line[1]=='S')//wskaznik na string { par1=line.substr(3,0xFFFF); DWORD g=atoi(par1.c_str()); push(sVar[g]); } if(line[1]=='V')//wskaznik na wlasne dane { par1=line.substr(3,0xFFFF); DWORD g=atoi(par1.c_str()); push(vVar[g]); } } if(line[0]=='C')//wywolujemy funkcje { int n1=line.find("|"); int n2=line.find("|",n1+1); par1=line.substr(2,n1-2); par2=line.substr(n1+1,n2-n1-1); char z; z=line[n2+1]; call((char*)par1.c_str(),(char*)par2.c_str(),&z); } i++; ****} }
to na tyle funkcji
przykład uzycia:
wewnatrz funkcji main
i słowne wyjasnienie znaczenia kodu:Kod:parse("AS Hello World\nAS program\nAD 0\nPD 0\nPS 1\nPS 0\nPD 0\nC MessageBoxA|user32.dll|D\nE");
Uwagi końcowe:Kod:alokujemy string o tresci Hello World alokujemy string o tresci program alokujemy liczbe 0 rzucamy na stos liczbe z tabeli liczb o indexie 0 (0) rzucamy na stos stringa z tabeli stringow o indexie 1 (program) rzucamy na stos stringa z tabeli stringow o indexie 0 (Hello World) rzucamy na stos liczbe z tabeli liczb o indexie 0 (0) wywolujemy funkcje MessageBoxA z biblioteki user32.dll , funkcja zwraca DWORD konczymy program
1) pamietajmy ze przy rzucaniu na stos podajemy tylko index tablicy a nie to co rzucamy.
2) To jest bardzo ograniczony jezyk zeby był bardziej uzyteczny trzeba zaimplementowac m.in:
a ) operacje arytmetyczne
b ) instrukcje warunkowe
c ) petle
mam nadzieje ze sie komus przyda
//Jesli sie spodoba to prosze o przneniesienie do tutoriali



Odpowiedź z Cytatem