Pisanie interpretera...

0

Prosiłbym o jakieś ogólne porady jak napisać szybko działający interpreter. Tworzę bowiem pewien język skryptowy na swoje potrzeby (gra) i chciałbym się dowiedzieć jak ten problem ugryźć. Nie chcę stosować istniejących rozwiązań typu lua, python, itd. gdyż chcę aby język który zastosuję był skrojony namoje potrzeby.

Parsowanie komand jest najłatwiejsze, wystarczy rozbić linię na poszczególne części składowe jak nazwa procedury, parametry, itd. Większy problem stanowi dla mnie właściwa interpretacja, tzn. można zrobić tablicę zawierającą wszystkie możliwe komendy a potem loopować się przez nią forem, ale takie rozwiązanie nie będzie zbyt szybkie, a jak mówiłem ja chcę tego użyć do oskryptowania gry (by w rekompilować dopiero w ostateczności gdy będzie jakiś bug w interpreterze, a tak to tylko zmieniać skrypty gry), więc powinno być szybkie.

Co do parsowania jeszcze to może też być problem z usuwaniem komentarzy. Tzn jak komentarze są w osobnej linii to problemu nie będzie, gorzej jeśli znajdzie się w linii z komendą (komentarze w moim języku zaczynają się od znaku #). Jeszcze będzie problem jeśli chodzi o wielowyrazowe stringi gdyż przykładowe wywołanie będzie wyglądać tak:

procedura parametr1 parametr2 parametr3
czyli delimiterem będzie spacja. Oczywiście do stringów będzie używany cudzysłów, jednak jak zrobić, aby parser sparsował to właściwie, czyli zamiast takiej tablicy:

procedura
123
abcdegf
"To
jest
string"

dostać coś takiego:

procedura
123
abcdegf
"To jest string"

?

Pomożecie mi z tym?

0

Możesz najpierw wyciąć stringi przez parowanie cudzysłowów, a następnie pozostałe oddzielone spacjami wyrażenia. Co do komentarzy to możesz wszystko za znakiem # wycinać jako komentarz

0
Sarrus napisał(a)

Możesz najpierw wyciąć stringi przez parowanie cudzysłowów, a następnie pozostałe oddzielone spacjami wyrażenia. Co do komentarzy to możesz wszystko za znakiem # wycinać jako komentarz

Co do parowania cudzysłowów, to mógłbyś mi podać jakiś przykład w kodzie, bo tak na sucho to do mnie nie trafia. A co do wycinania wszystkiego ze znakiem # to to mogło by wyciąć taką instrukcję

zmienna = add 2 4 # wynikiem będzie prawdopodobnie 6
co byłoby niepożądane.

0
Darkhog napisał(a)

Co do parowania cudzysłowów, to mógłbyś mi podać jakiś przykład w kodzie, bo tak na sucho to do mnie nie trafia.

Przykładu w kodzie Ci nie podam, bo nie programuję w delphi i nie pamiętam funkcji operowania na znakach. Koncepcyjnie to by było mniej więcej tak:

while not koniec_lini do
   przesuń się do następnego wyrażenia
   if wyraz zaczyna się od " then
       znajdż drugi "
       dodaj wyrażenie ze wszystkim co jest pomiędzy "
   else
       dodaj wyrażenie
Darkhog napisał(a)

A co do wycinania wszystkiego ze znakiem # to to mogło by wyciąć taką instrukcję

zmienna = add 2 4 # wynikiem będzie prawdopodobnie 6
co byłoby niepożądane.

Nie ze znakiem #, a ZA znakiem.

0

Nie chcę stosować istniejących rozwiązań typu lua, python, itd. gdyż chcę aby język który zastosuję był skrojony namoje potrzeby.

Skoro nie masz problemu z parsowaniem, możesz tłumaczyć skrypt ze swojego języka na np. Lua i odpalać Luę. Kiedyś właśnie tak zrobiłem.

można zrobić tablicę zawierającą wszystkie możliwe komendy a potem loopować się przez nią forem, ale takie rozwiązanie nie będzie zbyt szybkie
A sprawdzałeś? bo jest szansa, że będzie wystarczająco szybkie do twoich zastosowań. Najszybsza będzie kompilacja just-in-time, ale to bardzo grząskie bagno.

Co do parsowania jeszcze to może też być problem z usuwaniem komentarzy.
Obcinaj stringa na pierwszym znaku #.

A w ogóle zainteresuj się narzędziami typu BISON/FLEX, LALR itp.

0

Hm... Co do komentarzy to chyba racja. Sprawdzę Posem i obetnę a co do stringów, to chyba zrobię to inaczej, parametry będą rozdzielane przecinkiem.

0

Nie zapomnij, że jak będzie np.tak:
zmienna = "To jest zmienna # a tego nie powinno obciąć" #komentarz
To nie powinno traktować string'a, jako część kodu (trochę źle się chyba wyraziłem?) i przypadkiem nie zinterpretować tego tak:
zmienna = "To jest zmienna "

Tutaj masz mój stary:
Jest do niego IDE (napisane przeze mnie).
Około tysiąc linijek kodu (rzecz jasna - niedokończone) bez komentarzy (nie chciało mi się ich pisać).
Obsługuje if'y (chociaż źle działają), pętle for (też nie do końca działają), tworzenie procedur (OMG !), systemy cząsteczek (NIE UŻYWAJ ICH ! niedokończone) oraz obliczanie, lecz tylko proste działania np.5*2+25.
Nie ma funkcji, tylko procedury.
Ma listę procedur, których możesz użyć 'normalnie', tj.możesz poczytać, w if'ach są ich nazwy.
IDE pokazuje niektóre błędy, większość błędów = Access Violation, lub coś podobnego ;)
IDE wymaga SynEdit'a (do kompilacji).
Pisałem go daawno temu, więc...
Ehm...
PS: Wrzucam go z moją starą grą, do której był stworzony.
Gra też niedokończona.
PS2: Nie pytaj się mnie, jak się w tym pisze skrypty, ponieważ sam nie pamiętam, później jeszcze zajrzę w ten kod.
PS3: Komentarze to ## mój komentarz
PS4: Procedury wywołuje się @NazwaProceduryDomyślnej oraz call MojaProcedura
PS5: Na pewno ma on wiele błędów, lecz podstawowe rzeczy działają chyba dobrze.
PS6: Aż mnie naszło, aby w nim popisać :)
PS7: Dałem to tylko tak do wglądu. Nie ma on praktycznych zastosowań.
PS8: Nie czepiajcie się kodu :)

Nie chciało wejść na 4p, to masz na Przeklej:
http://www.przeklej.pl/plik/stragholme-rar-002aj52ui96u

0

Wiem i dlatego to będzie specialnie obsługiwanie (najpierw będzie szukał znaku przypisania, potem cudzysłowów (przy których będzie sprawdzał czy pomiędzy dwoma nie ma znaku komentarza)), a dopiero potem zajmował się komentarzami. Myślę nawet nad "pseudokompilacją" kodu przed startem (zamiast intepretować linia po linii), tzn. zamiast sprawdzać przed wykonaniem parametry, która to procedura itd. będzie to robił przy ładowaniu i tworzył w pamięci listę z recordów typu:

komenda = record
id:LongInt; //id komendy w tablicy komend;
user_defined: boolean; //czy komenda jest zdefiniowana przez użytkownika, wtedy id dotyczy tablicy komend użytkownika
parameters:string // w recordzie nie można tablic przechowywać, więc rozbice nastąpi podczas wykonania.
end;
0

Patryk, a to jest w Delphi? Bo tak się złożyło że ja w Lazarusie robię. No ale może zrozumiem sam z plików pas.

0

w recordzie nie można tablic przechowywać, więc rozbice nastąpi podczas wykonania

Musiało ci się z czymś innym pomylić, bo stary Turbo Pascal nawet pozwala na tablice w rekordzie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1