Nekoliko sam se godina okušao u razvoju vlastitog programskog jezika. Želio sam stvoriti, po mom mišljenju, najjednostavniji, potpuno funkcionalan i najprikladniji mogući jezik.
U ovom članku želim istaknuti glavne faze svog rada i, za početak, opisati stvoreni koncept jezika i njegovu prvu implementaciju, na kojoj trenutno radim.
Unaprijed ću reći da sam cijeli projekt napisao u Free Pascalu, jer... programi na njemu se mogu sastaviti za ogroman broj platformi, a sam prevodilac proizvodi vrlo optimizirane binarne datoteke (sve komponente projekta skupljam s O2 zastavom).
Izvođenje jezika
Prije svega, vrijedi razgovarati o virtualnom stroju koji sam morao napisati kako bih pokretao buduće aplikacije na mom jeziku. Odlučio sam implementirati arhitekturu hrpa, možda zato što je to bio najlakši način. Nisam našao niti jedan normalan članak o tome kako to učiniti na ruskom, pa sam, nakon što sam se upoznao s materijalom na engleskom jeziku, sjeo na dizajn i pisanje vlastitog bicikla. Zatim ću predstaviti svoje "napredne" ideje i razvoj po ovom pitanju.
Implementacija stoga
Očito, na vrhu VM-a je stog. U mojoj implementaciji radi u blokovima. U biti ovo je jednostavan niz pokazivača i varijabla za pohranjivanje indeksa vrha stoga.
Kada se inicijalizira, stvara se niz od 256 elemenata. Ako se više pokazivača gurne na stog, njegova se veličina povećava za sljedećih 256 elemenata. Sukladno tome, prilikom uklanjanja elemenata iz hrpe, njegova veličina se prilagođava.
VM koristi nekoliko skupova:
- Glavni stog.
- Stog za pohranu povratnih točaka.
- Skupljač smeća.
- Pokušajte/uhvatite/konačno blokirajte snop rukovatelja.
Konstante i varijable
Ovaj je jednostavan. Konstante se obrađuju u zasebnom malom komadu koda i dostupne su u budućim aplikacijama putem statičkih adresa. Varijable su niz pokazivača određene veličine, pristup njegovim ćelijama vrši se indeksom - tj. statička adresa. Varijable se mogu gurnuti na vrh stoga ili odatle čitati. Zapravo, jer Dok naše varijable u biti pohranjuju pokazivače na vrijednosti u VM memoriju, jezikom dominira rad s implicitnim pokazivačima.
Sakupljač smeća
U mom VM-u je poluautomatski. Oni. programer sam odlučuje kada će pozvati sakupljača smeća. Ne radi korištenjem uobičajenog brojača pokazivača, kao u Pythonu, Perlu, Rubyju, Lui itd. Provodi se putem sustava markera. Oni. kada se varijabli namjerava dodijeliti privremena vrijednost, pokazivač na tu vrijednost dodaje se na stog skupljača smeća. U budućnosti, sakupljač brzo prolazi kroz već pripremljenu listu pokazivača.
Rukovanje blokovima try/catch/finally
Kao u svakom modernom jeziku, rukovanje iznimkama je važna komponenta. Jezgra VM-a omotana je blokom try..catch, koji se može vratiti na izvršavanje koda nakon hvatanja iznimke guranjem nekih informacija o tome na stog. U kodu aplikacije možete definirati blokove koda try/catch/finally, navodeći ulazne točke na catch (rukovatelj iznimkama) i finally/end (kraj bloka).
Višenitnost
Podržano je na razini VM-a. Jednostavan je i praktičan za korištenje. Radi bez sustava prekida, tako da bi se kod trebao izvršavati u nekoliko niti nekoliko puta brže.
Vanjske biblioteke za VM
Nema načina bez ovoga. VM podržava uvoze, slično kao što je to implementirano u drugim jezicima. Možete napisati dio koda u Mash-u, a dio koda na izvornim jezicima, a zatim ih povezati u jedan.
Prevoditelj s Mash jezika visoke razine na bajt kod za VM
Srednji jezik
Kako bih brzo napisao prevoditelj iz složenog jezika u VM kod, prvo sam razvio srednji jezik. Rezultat je bio užasan spektakl nalik asembleru koji ovdje nema posebnog smisla razmatrati. Reći ću samo da na ovoj razini prevoditelj obrađuje većinu konstanti i varijabli, izračunava njihove statičke adrese i adrese ulaznih točaka.
Arhitektura prevoditelja
Nisam odabrao najbolju arhitekturu za implementaciju. Prevoditelj ne gradi kodno stablo, kao što to rade drugi prevoditelji. Gleda na početak građevine. Oni. ako dio koda koji se raščlanjuje izgleda kao "while <uvjet>:", onda je očito da je to konstrukcija while petlje i treba se obraditi kao konstrukcija while petlje. Nešto poput složene sklopke.
Zahvaljujući ovom arhitektonskom rješenju, prevoditelj se pokazao ne baš brzim. Međutim, lakoća njegove izmjene značajno se povećala. Dodao sam potrebne strukture brže nego što mi se kava mogla ohladiti. Potpuna OOP podrška implementirana je za manje od tjedan dana.
Optimizacija koda
Ovdje se, naravno, moglo bolje provesti (i provodit će se, ali kasnije, čim se dođe do toga). Za sada, optimizator zna samo kako odrezati neiskorišteni kod, konstante i uvoze iz sklopa. Također, nekoliko konstanti s istom vrijednošću zamjenjuju se jednom. To je sve.
Mash jezik
Osnovni pojam jezika
Glavna ideja bila je razviti što funkcionalniji i najjednostavniji jezik. Mislim da se razvoj sa svojim zadatkom nosi s praskom.
Blokovi koda, postupci i funkcije
Sve konstrukcije u jeziku otvaraju se dvotačkom. : a zatvara ih operater kraj.
Procedure i funkcije su deklarirane kao proc odnosno func. Argumenti su navedeni u zagradama. Sve je kao u većini drugih jezika.
Operater povratak možete vratiti vrijednost iz funkcije, operatora razbiti omogućuje izlazak iz procedure/funkcije (ako je izvan petlji).
Primjer koda:
...
func summ(a, b):
return a + b
end
proc main():
println(summ(inputln(), inputln()))
end
Podržani dizajni
- Petlje: za..kraj, dok..kraj, dok..kraj
- Uvjeti: if.[else..]end, switch..[case..end..][else..]end
- Metode: proc <name>():... end, func <name>():... end
- Oznaka i idi: <ime>:, skok <ime>
- Enum enumeracije i konstantni nizovi.
varijable
Prevoditelj ih može odrediti automatski ili ako programer napiše var prije nego ih definira.
Primjeri koda:
a ?= 10
b ?= a + 20
var a = 10, b = a + 20
Podržane su globalne i lokalne varijable.
OOP
Pa, došli smo do najukusnije teme. Mash podržava sve paradigme objektno orijentiranog programiranja. Oni. klase, nasljeđe, polimorfizam (uključujući dinamički), dinamička automatska refleksija i introspekcija (potpuna).
Bez daljnjeg odlaganja, bolje je dati samo primjere koda.
Jednostavna klasa i rad s njom:
uses <bf>
uses <crt>
class MyClass:
var a, b
proc Create, Free
func Summ
end
proc MyClass::Create(a, b):
$a = new(a)
$b = new(b)
end
proc MyClass::Free():
Free($a, $b)
$rem()
end
func MyClass::Summ():
return $a + $b
end
proc main():
x ?= new MyClass(10, 20)
println(x->Summ())
x->Free()
end
Izdat će: 30.
Nasljeđe i polimorfizam:
uses <bf>
uses <crt>
class MyClass:
var a, b
proc Create, Free
func Summ
end
proc MyClass::Create(a, b):
$a = new(a)
$b = new(b)
end
proc MyClass::Free():
Free($a, $b)
$rem()
end
func MyClass::Summ():
return $a + $b
end
class MyNewClass(MyClass):
func Summ
end
func MyNewClass::Summ():
return ($a + $b) * 2
end
proc main():
x ?= new MyNewClass(10, 20)
println(x->Summ())
x->Free()
end
Izdat će: 60.
Što je s dinamičkim polimorfizmom? Da, ovo je odraz!:
uses <bf>
uses <crt>
class MyClass:
var a, b
proc Create, Free
func Summ
end
proc MyClass::Create(a, b):
$a = new(a)
$b = new(b)
end
proc MyClass::Free():
Free($a, $b)
$rem()
end
func MyClass::Summ():
return $a + $b
end
class MyNewClass(MyClass):
func Summ
end
func MyNewClass::Summ():
return ($a + $b) * 2
end
proc main():
x ?= new MyClass(10, 20)
x->Summ ?= MyNewClass::Summ
println(x->Summ())
x->Free()
end
Izdat će: 60.
Sada uzmimo trenutak da pogledamo jednostavne vrijednosti i klase:
uses <bf>
uses <crt>
class MyClass:
var a, b
end
proc main():
x ?= new MyClass
println(BoolToStr(x->type == MyClass))
x->rem()
println(BoolToStr(typeof(3.14) == typeReal))
end
Hoće izlaz: istina, istina.
O operatorima dodjele i eksplicitnim pokazivačima
Operator ?= koristi se za dodjeljivanje pokazivača varijabli na vrijednost u memoriji.
Operator = mijenja vrijednost u memoriji pomoću pokazivača iz varijable.
A sada malo o eksplicitnim pokazivačima. Dodao sam ih u jezik tako da postoje.
@<varijabla> — uzeti eksplicitni pokazivač na varijablu.
?<varijabla> — dobivanje varijable pomoću pokazivača.
@= — dodijelite vrijednost varijabli eksplicitnim pokazivačem na nju.
Primjer koda:
uses <bf>
uses <crt>
proc main():
var a = 10, b
b ?= @a
PrintLn(b)
b ?= ?b
PrintLn(b)
b++
PrintLn(a)
InputLn()
end
Izbacit će: neki broj, 10, 11.
Pokušajte.[catch..][finally..]end
Primjer koda:
uses <bf>
uses <crt>
proc main():
println("Start")
try:
println("Trying to do something...")
a ?= 10 / 0
catch:
println(getError())
finally:
println("Finally")
end
println("End")
inputln()
end
Planovi za budućnost
Stalno gledam i gledam GraalVM & Truffle. Moje runtime okruženje nema JIT kompajler, tako da je u smislu performansi trenutno konkurentno samo Pythonu. Nadam se da ću moći implementirati JIT kompilaciju temeljenu na GraalVM ili LLVM.
spremište
Možete se igrati s razvojem i sami pratiti projekt.
Hvala vam što ste pročitali do kraja ako jeste.
Izvor: www.habr.com