[TUTORIAL]Piszemy interpreter wlasnego prostego jezyka programowania

grzonu

Były Moderator
Dołączył
Grudzień 26, 2006
Posty
1390
Tutorial piszemy interpreter wlasnego prostego jezyka programowania


Więc zaczynamy
smile.gif

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
smile.gif


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]=='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);
}
}
}

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]=='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
smile.gif


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
smile.gif


//Jesli sie spodoba to prosze o przneniesienie do tutoriali
 
Do góry Bottom