Neue Mash-Programmiersprache

Ich habe mich mehrere Jahre lang bemüht, meine Programmiersprache zu entwickeln. Ich wollte meiner Meinung nach die einfachste, voll funktionsfähigste und bequemste Sprache schaffen.

In diesem Artikel möchte ich die Hauptphasen meiner Arbeit hervorheben und zunächst das erstellte Konzept der Sprache und ihre erste Implementierung beschreiben, an der ich gerade arbeite.

Ich werde im Voraus sagen, dass ich das gesamte Projekt in Free Pascal geschrieben habe, weil Programme darauf können für eine große Anzahl von Plattformen zusammengestellt werden, und der Compiler selbst erzeugt sehr optimierte Binärdateien (ich sammle alle Komponenten des Projekts mit dem O2-Flag).

Sprachlaufzeit


Das erste, worüber ich sprechen musste, war eine virtuelle Maschine, die ich schreiben musste, um zukünftige Anwendungen in meiner Sprache auszuführen. Ich habe mich vielleicht für die Implementierung einer Stack-Architektur entschieden, weil dies der einfachste Weg war. Ich habe keinen einzigen normalen Artikel darüber gefunden, wie man das auf Russisch macht. Nachdem ich das englischsprachige Material gelesen hatte, setzte ich mich hin, um mein Fahrrad zu entwerfen und zu schreiben. Weiter werde ich meine "fortgeschrittenen" Ideen und Entwicklungen in dieser Angelegenheit geben.

Stapelimplementierung


Offensichtlich befindet sich die VM an der Spitze des Stapels. In meiner Implementierung funktioniert es in Blöcken. Im Wesentlichen ist dies ein einfaches Array von Zeigern und eine Variable zum Speichern des Index der Oberseite des Stapels.
Bei der Initialisierung wird ein Array mit 256 Elementen erstellt. Wenn mehr Zeiger auf den Stapel geworfen werden, wird seine Größe um die nächsten 256 Elemente erhöht. Dementsprechend ist beim Entfernen von Elementen aus dem Stapel die Größe einstellbar.

Eine VM verwendet mehrere Stapel:

  1. Der Hauptstapel.
  2. Stapel zum Speichern von Rückgabepunkten.
  3. Stapel Müllsammler.
  4. Der Stack-Handler von try / catch / finally blockiert.

Konstanten und Variablen


Damit ist alles einfach. Konstanten werden in einem separaten kleinen Code verarbeitet und sind in zukünftigen Anwendungen unter statischen Adressen verfügbar. Variablen sind ein Array von Zeigern einer bestimmten Größe. Der Zugriff auf ihre Zellen erfolgt über den Index - d. H. statische Adresse. Variablen können oben im Stapel platziert oder von dort gelesen werden. Eigentlich weil Unsere Variablen speichern im Wesentlichen Zeiger auf Werte im Speicher der VM und arbeiten dann mit impliziten Zeigern, die in der Sprache vorherrschen.

Müllsammler


In meiner VM ist es halbautomatisch. Das heißt, Der Entwickler entscheidet, wann der Garbage Collector aufgerufen werden soll. Es funktioniert nicht mit dem üblichen Zeigerzähler, wie in Python, Perl, Ruby, Lua usw. Es wird durch ein Markersystem implementiert. Das heißt, Wenn verstanden wird, dass einer Variablen ein temporärer Wert zugewiesen wird, wird dem Garbage Collector Stack ein Zeiger auf diesen Wert hinzugefügt. In Zukunft durchläuft der Kollektor schnell eine bereits vorbereitete Liste von Zeigern.

Umgang mit try / catch / finally-Blöcken


Wie in jeder modernen Sprache ist die Ausnahmebehandlung ein wichtiger Bestandteil davon. Der Kernel der VM ist in einen try..catch-Block eingeschlossen, der nach dem Abfangen einer Ausnahme zur Codeausführung zurückkehren kann, indem ein paar Informationen dazu auf den Stapel gelegt werden. Im Anwendungscode können Sie Codeblöcke try / catch / finally angeben und dabei die Einstiegspunkte für catch (Ausnahmebehandlungsroutine) und finally / end (Blockende) angeben.

Multithreading


Es wird auf VM-Ebene unterstützt. Es ist einfach und bequem zu bedienen. Es funktioniert ohne Interrupt-System, daher muss der Code in mehreren Threads jeweils mehrmals schneller ausgeführt werden.

Externe Bibliotheken für VM


Es gibt keine Möglichkeit, darauf zu verzichten. VM unterstützt Importe, genau wie es in anderen Sprachen implementiert ist. Sie können einen Teil des Codes in Mash und einen Teil des Codes in Muttersprachen schreiben und diese dann miteinander verknüpfen.

Übersetzer von der Hochsprache Mash zum Bytecode für VM


Zwischensprache


Um schnell einen Übersetzer von einer komplexen Sprache in VM-Code zu schreiben, habe ich zuerst eine Zwischensprache entwickelt. Es stellte sich heraus, dass es sich um einen Assembler-ähnlichen beängstigenden Anblick handelte, den man hier nicht besonders berücksichtigen sollte. Ich kann nur sagen, dass der Übersetzer auf dieser Ebene die meisten Konstanten und Variablen verarbeitet, ihre statischen Adressen und Adressen von Einstiegspunkten berechnet.

Übersetzer-Architektur


Ich habe nicht die beste Architektur für die Implementierung ausgewählt. Der Übersetzer erstellt keinen Codebaum, wie es sich für andere Übersetzer gehört. Er schaut auf den Beginn des Baus. Das heißt, Wenn der analysierte Code wie "while <Bedingung>:" aussieht, ist es offensichtlich, dass dies eine while-Konstruktion der Schleife ist und Sie ihn als while-Konstrukt der Schleife verarbeiten müssen. So etwas wie ein komplizierter Schaltkasten.

Dank einer solchen architektonischen Lösung war der Übersetzer nicht sehr schnell. Die Einfachheit seiner Verfeinerung hat jedoch erheblich zugenommen. Ich fügte die notwendigen Designs schneller hinzu, als mein Kaffee abkühlen konnte. Die vollständige Unterstützung für OOP wurde in weniger als einer Woche implementiert.

Codeoptimierung


Hier könnte es natürlich besser realisiert werden (und es wird realisiert, aber später, wenn die Hände reichen). Bisher weiß der Optimierer nur, wie nicht verwendeter Code, Konstanten und Importe aus der Assembly abgeschnitten werden. Außerdem werden mehrere Konstanten mit demselben Wert durch eine ersetzt. Das ist alles.

Maische Sprache


Das Grundkonzept der Sprache


Die Hauptidee war es, die funktionalste und einfachste Sprache zu entwickeln. Ich glaube, dass die Entwicklung ihre Aufgabe mit einem Knall bewältigt.

Codeblöcke, Prozeduren und Funktionen


Alle Konstruktionen in der Sprache werden mit einem Doppelpunkt geöffnet und mit dem Endoperator geschlossen.

Prozeduren und Funktionen werden als proc bzw. func deklariert. Argumente sind in Klammern aufgeführt. Genau wie die meisten anderen Sprachen.

Die return-Anweisung kann einen Wert von einer Funktion zurückgeben. Mit der break- Anweisung können Sie eine Prozedur / Funktion beenden (wenn sie sich außerhalb von Schleifen befindet).

Codebeispiel:

...

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

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


  • : for..end, while..end, until..end
  • : if..[else..]end, switch..[case..end..][else..]end
  • : proc <>():… end, func <>():… end
  • Label & goto: <>:, jump <>
  • Enum .


, var .

:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

.


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


?= .
= .
. .
@<> — .
?<> — .
@= — .

:

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.

Try..[catch..][finally..]end


:

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 , . , JIT GraalVM LLVM.


.


GitHub

, , .

Source: https://habr.com/ru/post/de434966/


All Articles