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:
Kod:
#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
W powyzszym kodzie dolaczamy niezbedne pliki naglowkowe i definujemy nasze tablice na zmienne

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;
}
Funkcje alokujace dane:
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);
}
Funkcja wywolujaca funkcje:
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]==&#39;D&#39;)//zwracane wartosci typu DWORD
{
DWORD x;
__asm mov x,eax;
alloc_d(x);
}
if(type[0]==&#39;S&#39;)//zwracane wartosci typu char*
{
char* str;
__asm mov str,eax
alloc_s(str,strlen(str));
}
if(type[0]==&#39;V&#39;)//zwracane wartosci typu PVOID
{
PVOID p;
__asm mov p,eax
vVar.push_back(p);
}
}
}
Funkcje rzucajace na stos:
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
}
teraz jedna z najwazniejszych rzeczy- funkcja rozpoznajaca i wykonujaca kod:
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]==&#39;E&#39;)//koniec kodu
{
memset(code,0,strlen(code));
sVar.clear();
dVar.clear();
vVar.clear();
break;
}
if(line[0]==&#39;A&#39;)//alokacja
{
if(line[1]==&#39;S&#39;)//stringu
{
par1=line.substr(3,0xFFFF);
alloc_s((PVOID)par1.c_str(),par1.size());
}
if(line[1]==&#39;V&#39;)//wlasnej pamieci
{
par1=line.substr(3,0xFFFF);
DWORD g=atoi(par1.c_str());
alloc_v(g);
}

if(line[1]==&#39;D&#39;)//liczby
{
par1=line.substr(3,0xFFFF);
DWORD g=atoi(par1.c_str());
alloc_d(g);
}

}


if(line[0]==&#39;P&#39;)//rzucamy na stos
{
if(line[1]==&#39;D&#39;)//liczbę
{
par1=line.substr(3,0xFFFF);
DWORD g=atoi(par1.c_str());
push_d(dVar[g]);
}
if(line[1]==&#39;S&#39;)//wskaznik na string
{
par1=line.substr(3,0xFFFF);
DWORD g=atoi(par1.c_str());
push(sVar[g]);
}

if(line[1]==&#39;V&#39;)//wskaznik na wlasne dane
{
par1=line.substr(3,0xFFFF);
DWORD g=atoi(par1.c_str());
push(vVar[g]);
}

}


if(line[0]==&#39;C&#39;)//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
Kod:
parse("AS Hello World\nAS program\nAD 0\nPD 0\nPS 1\nPS 0\nPD 0\nC MessageBoxA|user32.dll|D\nE");
i słowne wyjasnienie znaczenia kodu:
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
Uwagi końcowe:
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