Jauna programmēŔanas valoda Mash

Vairākus gadus izmēģināju savus spēkus savas programmÄ“Å”anas valodas izstrādē. Es gribēju izveidot, manuprāt, pēc iespējas vienkārŔāku, pilnÄ«bā funkcionālāku un ērtāku valodu.

Å ajā rakstā vēlos izcelt sava darba galvenos posmus un iesākumā aprakstÄ«t izveidoto valodas koncepciju un tās pirmo realizāciju, pie kuras Å”obrÄ«d strādāju.

Ä»aujiet man pateikt priekŔā, ka visu projektu rakstÄ«ju Free Pascal, jo... tajā esoŔās programmas var montēt ļoti daudzām platformām, un pats kompilators rada ļoti optimizētus bināros failus (es apkopoju visas projekta sastāvdaļas ar O2 karogu).

Valodas izpildlaiks

Pirmkārt, ir vērts runāt par virtuālo maŔīnu, kas man bija jāraksta, lai palaistu turpmākās lietojumprogrammas manā valodā. Es nolēmu ieviest steka arhitektÅ«ru, iespējams, tāpēc, ka tas bija vienkārŔākais veids. Es neatradu nevienu normālu rakstu par to, kā to izdarÄ«t krievu valodā, tāpēc pēc iepazÄ«Å”anās ar angļu valodas materiālu, es ķēros pie sava velosipēda projektÄ“Å”anas un rakstÄ«Å”anas. Tālāk es iepazÄ«stināŔu ar savām "progresÄ«vām" idejām un attÄ«stÄ«bu Å”ajā jautājumā.

Stack ievieŔana

AcÄ«mredzot virtuālās maŔīnas augÅ”pusē ir steks. Manā realizācijā tas darbojas blokos. BÅ«tÄ«bā tas ir vienkārÅ”s rādÄ«tāju masÄ«vs un mainÄ«gais, lai saglabātu kaudzes augÅ”daļas indeksu.
Kad tas ir inicializēts, tiek izveidots 256 elementu masīvs. Ja uz kaudze tiek uzspiests vairāk rādītāju, tā lielums palielinās par nākamajiem 256 elementiem. Attiecīgi, noņemot elementus no kaudzes, tiek pielāgots tā izmērs.

VM izmanto vairākus stekus:

  1. Galvenā kaudze.
  2. Kaudze atgrieŔanās punktu glabāŔanai.
  3. Atkritumu savācēja kaudze.
  4. Izmēģiniet/tveriet/beidzot bloķējiet apdarinātāja steku.

Konstantes un mainīgie

Å is ir vienkārÅ”s. Konstantes tiek apstrādātas atseviŔķā nelielā koda daļā un ir pieejamas turpmākajās lietojumprogrammās, izmantojot statiskas adreses. MainÄ«gie ir noteikta izmēra rādÄ«tāju masÄ«vs, piekļuvi tā Ŕūnām veic indekss - t.i. statiskā adrese. MainÄ«gos var virzÄ«t uz steka augÅ”daļu vai nolasÄ«t no turienes. PatiesÄ«bā, jo Lai gan mÅ«su mainÄ«gie bÅ«tÄ«bā saglabā norādes uz vērtÄ«bām VM atmiņā, valodā dominē darbs ar netieÅ”iem rādÄ«tājiem.

Atkritumu savācējs

Manā virtuālajā maŔīnā tas ir pusautomātisks. Tie. pats izstrādātājs izlemj, kad zvanÄ«t atkritumu savācējam. Tas nedarbojas, izmantojot parasto rādÄ«tāja skaitÄ«tāju, piemēram, Python, Perl, Ruby, Lua utt. Tas tiek Ä«stenots, izmantojot marÄ·ieru sistēmu. Tie. ja mainÄ«gajam ir paredzēts pieŔķirt pagaidu vērtÄ«bu, atkritumu savācēja kaudzē tiek pievienots rādÄ«tājs uz Å”o vērtÄ«bu. Turpmāk kolekcionārs ātri izskrien cauri jau sagatavotajam norādes sarakstam.

Apstrāde ar try/catch/finals blokiem

Tāpat kā jebkurā mÅ«sdienu valodā, izņēmumu apstrāde ir svarÄ«ga sastāvdaļa. Virtuālās maŔīnas kodols ir iesaiņots try..catch blokā, kas var atgriezties pie koda izpildes pēc izņēmuma uztverÅ”anas, nospiežot kādu informāciju par to stekā. Lietojumprogrammas kodā varat definēt koda izmēģināŔanas/tverÅ”anas/beidzot blokus, norādot ieejas punktus uztverÅ”anas brÄ«dÄ« (izņēmumu apstrādātājs) un visbeidzot/beigās (bloka beigas).

Daudzpavedienu veidoŔana

Tas tiek atbalstÄ«ts VM lÄ«menÄ«. Tas ir vienkārÅ”i un ērti lietojams. Tas darbojas bez pārtraukumu sistēmas, tāpēc kods ir jāizpilda vairākos pavedienos vairākas reizes ātrāk.

Ārējās bibliotēkas virtuālajām maŔīnām

Bez Ŕī nekādi nevar iztikt. VM atbalsta importÄ“Å”anu, lÄ«dzÄ«gi kā tas tiek ieviests citās valodās. Daļu koda varat rakstÄ«t valodā Mash un daļu koda dzimtajā valodā, pēc tam saistÄ«t tos vienā.

Tulkotājs no augsta lÄ«meņa Mash valodas uz baitu kodu virtuālajām maŔīnām

Vidēja valoda

Lai ātri ierakstÄ«tu tulkotāju no sarežģītas valodas VM kodā, es vispirms izstrādāju starpvalodu. Rezultāts bija montierim lÄ«dzÄ«ga Å”ausmÄ«ga izrāde, par kuru Å”eit nav Ä«paÅ”as jēgas. TeikÅ”u tikai to, ka Å”ajā lÄ«menÄ« tulkotājs apstrādā lielāko daļu konstantu un mainÄ«go, aprēķina to statiskās adreses un ieejas punktu adreses.

Tulkotāja arhitektūra

ÄŖstenoÅ”anai neizvēlējos labāko arhitektÅ«ru. Tulkotājs neveido kodu koku, kā to dara citi tulkotāji. ViņŔ aplÅ«ko struktÅ«ras sākumu. Tie. ja parsējamā koda daļa izskatās kā ā€œwhile :ā€, tad ir acÄ«mredzams, ka Ŕī ir while cilpas konstrukcija un tā ir jāapstrādā kā while cilpas konstrukcija. Kaut kas lÄ«dzÄ«gs sarežģītam slēdža korpusam.

Pateicoties Å”im arhitektoniskajam risinājumam, tulks izrādÄ«jās ne pārāk ātrs. Tomēr tā modifikācijas vieglums ir ievērojami palielinājies. Es pievienoju nepiecieÅ”amās struktÅ«ras ātrāk, nekā mana kafija spēja atdzist. Pilns OOP atbalsts tika ieviests mazāk nekā nedēļas laikā.

Koda optimizācija

Å eit, protams, to varēja ieviest labāk (un tiks ieviests, bet vēlāk, tiklÄ«dz kāds tiks pie tā). Pagaidām optimizētājs zina tikai to, kā no montāžas nogriezt neizmantoto kodu, konstantes un importÄ“Å”anu. Tāpat vairākas konstantes ar vienādu vērtÄ«bu tiek aizstātas ar vienu. Tas ir viss.

Mash valoda

Valodas pamatjēdziens

Galvenā ideja bija izstrādāt pēc iespējas funkcionālāku un vienkārŔāku valodu. Domāju, ka attÄ«stÄ«ba ar savu uzdevumu tiek galā ar uzviju.

Kodu bloki, procedūras un funkcijas

Visas konstrukcijas valodā tiek atvērtas ar kolu. : un operators tos aizver beigas.

Procedūras un funkcijas tiek deklarētas attiecīgi kā proc un func. Argumenti ir norādīti iekavās. Viss ir kā lielākajā daļā citu valodu.

Operators atgrieÅ”anās varat atgriezt vērtÄ«bu no funkcijas, operatora pārtraukums ļauj iziet no procedÅ«ras/funkcijas (ja tā atrodas ārpus cilpām).

Koda piemērs:

...

func summ(a, b):
  return a + b
end

proc main():
  println(summ(inputln(), inputln()))
end

Atbalstītie modeļi

  • Cilpas: uz..beigām, kamēr..beigām, lÄ«dz..beigām
  • NosacÄ«jumi: ja..[else..]beigas, pārslēgties..[lieta..beigas..][else..]beigas
  • Metodes: proc ():... end, func ():... end
  • IezÄ«me un goto: :, lēkt
  • UzskaitÄ«jumu un konstantu masÄ«vu saraksts.

Mainīgie

Tulkotājs var tos noteikt automātiski vai arÄ« tad, ja izstrādātājs pirms to definÄ“Å”anas raksta var.

Koda piemēri:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Tiek atbalstīti globālie un vietējie mainīgie.

OOP

Nu, mēs esam nonākuÅ”i pie visgarŔīgākās tēmas. Mash atbalsta visas objektorientētās programmÄ“Å”anas paradigmas. Tie. klases, iedzimtÄ«ba, polimorfisms (ieskaitot dinamisko), dinamiskā automātiskā refleksija un introspekcija (pilna).

Bez turpmākas piepūles labāk ir sniegt tikai kodu piemērus.

VienkārŔa klase un darbs ar to:

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

Iznākums: 30.

Mantojums un polimorfisms:

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

Iznākums: 60.

Kā ar dinamisko polimorfismu? Jā, tas ir pārdomas!:

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

Iznākums: 60.

Tagad veltÄ«sim brÄ«di vienkārŔām vērtÄ«bām un klasēm ieskatam:

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

Izvadīs: patiess, patiess.

Par pieŔķirÅ”anas operatoriem un precÄ«zām norādēm

Operators ?= tiek izmantots, lai pieŔķirtu mainÄ«gajam rādÄ«tājam vērtÄ«bu atmiņā.
Operators = maina vērtību atmiņā, izmantojot rādītāju no mainīgā.
Un tagad nedaudz par skaidrām norādēm. Es tos pievienoju valodai, lai tie pastāv.
@ ā€” norādiet skaidru rādÄ«tāju uz mainÄ«go.
? ā€” iegÅ«stiet mainÄ«go pēc rādÄ«tāja.
@= ā€” pieŔķiriet mainÄ«gajam vērtÄ«bu ar skaidru rādÄ«tāju uz to.

Koda piemērs:

uses <bf>
uses <crt>

proc main():
  var a = 10, b
  b ?= @a
  PrintLn(b)
  b ?= ?b
  PrintLn(b)
  b++
  PrintLn(a)
  InputLn()
end

Izvadīs: kāds skaitlis, 10, 11.

Izmēģiniet..[noķert..][beidzot..]beigt

Koda piemērs:

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

Plāni nākotnei

Es turpinu skatÄ«ties un skatÄ«ties uz GraalVM & Truffle. Manā izpildlaika vidē nav JIT kompilatora, tāpēc veiktspējas ziņā tā paÅ”laik ir konkurētspējÄ«ga tikai ar Python. Ceru, ka izdosies ieviest JIT kompilāciju uz GraalVM vai LLVM bāzes.

krātuve

Jūs varat spēlēt ar attīstību un sekot projektam pats.

Vietā
Repozitorijs vietnē GitHub

Paldies, ka izlasījāt līdz beigām, ja to izdarījāt.

Avots: www.habr.com

Pievieno komentāru