Νέα γλώσσα προγραμματισμού Mash

Για αρκετά χρόνια δοκίμασα τις δυνάμεις μου στην ανάπτυξη της δικής μου γλώσσας προγραμματισμού. Ήθελα να δημιουργήσω, κατά τη γνώμη μου, την πιο απλή, πλήρως λειτουργική και βολική γλώσσα.

Σε αυτό το άρθρο θέλω να επισημάνω τα κύρια στάδια της δουλειάς μου και, αρχικά, να περιγράψω τη δημιουργημένη έννοια της γλώσσας και την πρώτη εφαρμογή της, πάνω στην οποία εργάζομαι αυτήν τη στιγμή.

Επιτρέψτε μου να πω εκ των προτέρων ότι έγραψα ολόκληρο το έργο σε Free Pascal, γιατί... τα προγράμματα σε αυτό μπορούν να συναρμολογηθούν για έναν τεράστιο αριθμό πλατφορμών και ο ίδιος ο μεταγλωττιστής παράγει πολύ βελτιστοποιημένα δυαδικά αρχεία (συλλέγω όλα τα στοιχεία του έργου με τη σημαία O2).

Χρόνος εκτέλεσης γλώσσας

Πρώτα απ 'όλα, αξίζει να μιλήσω για την εικονική μηχανή που έπρεπε να γράψω για να τρέξω μελλοντικές εφαρμογές στη γλώσσα μου. Αποφάσισα να εφαρμόσω μια αρχιτεκτονική στοίβας, ίσως, γιατί ήταν ο πιο εύκολος τρόπος. Δεν βρήκα ούτε ένα κανονικό άρθρο για το πώς να το κάνω αυτό στα ρωσικά, οπότε αφού εξοικειώθηκα με το αγγλόφωνο υλικό, κάθισα να σχεδιάσω και να γράψω το δικό μου ποδήλατο. Στη συνέχεια θα παρουσιάσω τις «προχωρημένες» ιδέες και εξελίξεις μου σε αυτό το θέμα.

Εφαρμογή στοίβας

Προφανώς, στην κορυφή του VM βρίσκεται η στοίβα. Στην υλοποίησή μου λειτουργεί σε μπλοκ. Ουσιαστικά πρόκειται για έναν απλό πίνακα δεικτών και μια μεταβλητή για την αποθήκευση του ευρετηρίου της κορυφής της στοίβας.
Όταν αρχικοποιηθεί, δημιουργείται ένας πίνακας 256 στοιχείων. Εάν ωθηθούν περισσότεροι δείκτες στη στοίβα, το μέγεθός της αυξάνεται κατά τα επόμενα 256 στοιχεία. Αντίστοιχα, κατά την αφαίρεση στοιχείων από τη στοίβα, το μέγεθός της προσαρμόζεται.

Το VM χρησιμοποιεί πολλές στοίβες:

  1. Κύρια στοίβα.
  2. Μια στοίβα για την αποθήκευση σημείων επιστροφής.
  3. Στοίβα συλλέκτη σκουπιδιών.
  4. Δοκιμάστε/πιάστε/τελικά μπλοκάρετε τη στοίβα χειριστή.

Σταθερές και Μεταβλητές

Αυτό είναι απλό. Οι σταθερές χειρίζονται σε ένα ξεχωριστό μικρό κομμάτι κώδικα και είναι διαθέσιμες σε μελλοντικές εφαρμογές μέσω στατικών διευθύνσεων. Οι μεταβλητές είναι μια σειρά δεικτών συγκεκριμένου μεγέθους, η πρόσβαση στα κελιά της πραγματοποιείται με ευρετήριο - δηλ. στατική διεύθυνση. Οι μεταβλητές μπορούν να ωθηθούν στην κορυφή της στοίβας ή να διαβαστούν από εκεί. Στην πραγματικότητα, επειδή Ενώ οι μεταβλητές μας αποθηκεύουν ουσιαστικά δείκτες σε τιμές στη μνήμη VM, η γλώσσα κυριαρχείται από την εργασία με άρρητους δείκτες.

Συλλέκτης σκουπιδιών

Στο VM μου είναι ημιαυτόματο. Εκείνοι. ο ίδιος ο προγραμματιστής αποφασίζει πότε θα καλέσει τον συλλέκτη σκουπιδιών. Δεν λειτουργεί χρησιμοποιώντας έναν κανονικό μετρητή δείκτη, όπως στα Python, Perl, Ruby, Lua κ.λπ. Υλοποιείται μέσω ενός συστήματος σήμανσης. Εκείνοι. Όταν μια μεταβλητή πρόκειται να εκχωρηθεί μια προσωρινή τιμή, ένας δείκτης σε αυτήν την τιμή προστίθεται στη στοίβα του σκουπιδιού. Στο μέλλον, ο συλλέκτης τρέχει γρήγορα μέσω της ήδη προετοιμασμένης λίστας δεικτών.

Χειρισμός δοκιμής/πιάσε/τελικά μπλοκ

Όπως σε κάθε σύγχρονη γλώσσα, ο χειρισμός εξαιρέσεων είναι ένα σημαντικό στοιχείο. Ο πυρήνας VM είναι τυλιγμένος σε ένα μπλοκ try..catch, το οποίο μπορεί να επιστρέψει στην εκτέλεση κώδικα αφού πιάσει μια εξαίρεση σπρώχνοντας κάποιες πληροφορίες σχετικά με αυτήν στη στοίβα. Στον κώδικα εφαρμογής, μπορείτε να ορίσετε μπλοκ κώδικα δοκιμής/αλίευσης/τελικά, προσδιορίζοντας σημεία εισόδου στο catch (χειριστής εξαίρεσης) και στο τέλος/τέλος (τέλος του μπλοκ).

Multithreading

Υποστηρίζεται σε επίπεδο VM. Είναι απλό και βολικό στη χρήση. Λειτουργεί χωρίς σύστημα διακοπής, επομένως ο κώδικας θα πρέπει να εκτελείται σε πολλά νήματα αρκετές φορές πιο γρήγορα, αντίστοιχα.

Εξωτερικές βιβλιοθήκες για VM

Δεν υπάρχει τρόπος να γίνει χωρίς αυτό. Το VM υποστηρίζει εισαγωγές, παρόμοια με τον τρόπο που υλοποιείται σε άλλες γλώσσες. Μπορείτε να γράψετε μέρος του κώδικα στο Mash και μέρος του κώδικα σε μητρικές γλώσσες και, στη συνέχεια, να τα συνδέσετε σε μία.

Μεταφραστής από υψηλού επιπέδου γλώσσα Mash σε bytecode για VM

Ενδιάμεση γλώσσα

Για να γράψω γρήγορα έναν μεταφραστή από μια σύνθετη γλώσσα σε κώδικα VM, ανέπτυξα πρώτα μια ενδιάμεση γλώσσα. Το αποτέλεσμα ήταν ένα τρομερό θέαμα που μοιάζει με συναρμολογητή που δεν έχει ιδιαίτερο νόημα να εξετάσουμε εδώ. Θα πω μόνο ότι σε αυτό το επίπεδο ο μεταφραστής επεξεργάζεται τις περισσότερες σταθερές και μεταβλητές, υπολογίζει τις στατικές διευθύνσεις τους και τις διευθύνσεις των σημείων εισόδου.

Αρχιτεκτονική του μεταφραστή

Δεν επέλεξα την καλύτερη αρχιτεκτονική για υλοποίηση. Ο μεταφραστής δεν δημιουργεί ένα δέντρο κώδικα, όπως κάνουν άλλοι μεταφραστές. Κοιτάζει την αρχή της δομής. Εκείνοι. εάν το κομμάτι του κώδικα που αναλύεται μοιάζει με "while <condition>:", τότε είναι προφανές ότι πρόκειται για κατασκευή βρόχου while και πρέπει να υποβληθεί σε επεξεργασία ως κατασκευή βρόχου while. Κάτι σαν περίπλοκη θήκη διακόπτη.

Χάρη σε αυτή την αρχιτεκτονική λύση, ο μεταφραστής αποδείχθηκε ότι δεν ήταν πολύ γρήγορος. Ωστόσο, η ευκολία της τροποποίησής του έχει αυξηθεί σημαντικά. Πρόσθεσα τις απαραίτητες δομές πιο γρήγορα από ό,τι μπορούσε να κρυώσει ο καφές μου. Η πλήρης υποστήριξη OOP υλοποιήθηκε σε λιγότερο από μία εβδομάδα.

Βελτιστοποίηση κώδικα

Εδώ, βέβαια, θα μπορούσε να είχε εφαρμοστεί καλύτερα (και θα εφαρμοστεί, αλλά αργότερα, μόλις προλάβει κανείς). Μέχρι στιγμής, ο βελτιστοποιητής ξέρει μόνο πώς να αποκόψει τον αχρησιμοποίητο κώδικα, τις σταθερές και τις εισαγωγές από τη συναρμολόγηση. Επίσης, πολλές σταθερές με την ίδια τιμή αντικαθίστανται από μία. Αυτό είναι όλο.

Mash language

Βασική έννοια της γλώσσας

Η κύρια ιδέα ήταν να αναπτυχθεί η πιο λειτουργική και απλή γλώσσα. Νομίζω ότι η ανάπτυξη αντεπεξέρχεται στο έργο της με μεγάλη έκρηξη.

Μπλοκ κώδικα, διαδικασίες και συναρτήσεις

Όλες οι κατασκευές στη γλώσσα ανοίγουν με άνω και κάτω τελεία. : και είναι κλειστά από τον χειριστή τέλος.

Οι διαδικασίες και οι συναρτήσεις δηλώνονται ως proc και func, αντίστοιχα. Τα επιχειρήματα παρατίθενται σε παρένθεση. Όλα είναι όπως οι περισσότερες άλλες γλώσσες.

Χειριστής απόδοση μπορείτε να επιστρέψετε μια τιμή από μια συνάρτηση, τελεστή σπάσει σας επιτρέπει να βγείτε από τη διαδικασία/συνάρτηση (αν είναι εκτός των βρόχων).

Παράδειγμα κώδικα:

...

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

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

Υποστηριζόμενα σχέδια

  • Βρόχοι: για..τέλος, ενώ..τέλος, μέχρι..τέλος
  • Προϋποθέσεις: αν..[άλλο..]τέλος, εναλλαγή..[περίπτωση..τέλος..][άλλο..]τέλος
  • Μέθοδοι: proc <όνομα>():... τέλος, func <όνομα>():... τέλος
  • Label & goto: <όνομα>:, μεταπήδηση <όνομα>
  • Απαριθμήσεις και σταθεροί πίνακες.

Μεταβλητές

Ο μεταφραστής μπορεί να τα προσδιορίσει αυτόματα ή αν ο προγραμματιστής γράψει var πριν τα ορίσει.

Παραδείγματα κώδικα:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Υποστηρίζονται καθολικές και τοπικές μεταβλητές.

OOP

Λοιπόν, φτάσαμε στο πιο νόστιμο θέμα. Το Mash υποστηρίζει όλα τα αντικειμενοστραφή παραδείγματα προγραμματισμού. Εκείνοι. τάξεις, κληρονομικότητα, πολυμορφισμός (συμπεριλαμβανομένου του δυναμικού), δυναμικός αυτόματος προβληματισμός και ενδοσκόπηση (πλήρης).

Χωρίς περαιτέρω καθυστέρηση, είναι καλύτερο να δίνετε απλώς παραδείγματα κώδικα.

Ένα απλό μάθημα και εργασία με αυτό:

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

Έξοδος: 30.

Κληρονομικότητα και πολυμορφισμός:

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

Έξοδος: 60.

Τι γίνεται με τον δυναμικό πολυμορφισμό; Ναι, αυτό είναι αντανάκλαση!:

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

Έξοδος: 60.

Τώρα ας αφιερώσουμε λίγο χρόνο για να εξετάσουμε τις απλές αξίες και τάξεις:

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

Έξοδος θα: true, true.

Σχετικά με τους τελεστές ανάθεσης και τους ρητούς δείκτες

Ο τελεστής ?= χρησιμοποιείται για να εκχωρήσει μια μεταβλητή έναν δείκτη σε μια τιμή στη μνήμη.
Ο τελεστής = αλλάζει μια τιμή στη μνήμη χρησιμοποιώντας έναν δείκτη από μια μεταβλητή.
Και τώρα λίγα για τους ρητούς δείκτες. Τα πρόσθεσα στη γλώσσα για να υπάρχουν.
@<variable> — πάρτε έναν ρητό δείκτη σε μια μεταβλητή.
?<variable> — λάβετε μια μεταβλητή με δείκτη.
@= — αντιστοιχίστε μια τιμή σε μια μεταβλητή με έναν ρητό δείκτη σε αυτήν.

Παράδειγμα κώδικα:

uses <bf>
uses <crt>

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

Θα βγει: κάποιος αριθμός, 10, 11.

Δοκιμάστε..[πιάσε..][επιτέλους..]τέλος

Παράδειγμα κώδικα:

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

Σχέδια για το μέλλον

Συνεχίζω να κοιτάζω και να κοιτάζω το GraalVM & Truffle. Το περιβάλλον εκτέλεσης μου δεν έχει μεταγλωττιστή JIT, επομένως όσον αφορά την απόδοση είναι προς το παρόν ανταγωνιστικό μόνο με την Python. Ελπίζω ότι θα μπορέσω να υλοποιήσω τη μεταγλώττιση JIT με βάση το GraalVM ή το LLVM.

αποθήκη

Μπορείτε να παίξετε με τις εξελίξεις και να παρακολουθήσετε μόνοι σας το έργο.

Τοποθεσία
Αποθετήριο στο GitHub

Ευχαριστώ που διάβασες μέχρι το τέλος αν το έκανες.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο