新的程式語言 Mash

幾年來,我嘗試開發自己的程式語言。在我看來,我想創造一種盡可能簡單、功能齊全且方便的語言。

在這篇文章中,我想強調我工作的主要階段,並首先描述我目前正在研究的語言的創建概念及其首次實現。

我提前說一下,我用 Free Pascal 編寫了整個項目,因為...它上面的程式可以針對大量平台進行組裝,並且編譯器本身會產生非常優化的二進位(我收集了帶有 O2 標誌的專案的所有組件)。

語言運行時

首先,值得一提的是我必須編寫的虛擬機,以便用我的語言運行未來的應用程式。我決定實作堆疊架構,也許是因為這是最簡單的方法。我沒有找到一篇關於如何用俄語做到這一點的普通文章,因此在熟悉了英語材料後,我坐下來設計和編寫自己的自行車。接下來我將介紹我在這件事上的「先進」想法和進展。

堆疊實作

顯然,VM 的頂部是堆疊。在我的實現中,它以塊的形式工作。本質上,這是一個簡單的指標數組和用於存儲堆疊頂部索引的變數。
初始化時,會建立一個包含 256 個元素的陣列。如果將更多指標壓入堆疊,堆疊大小將增加接下來的 256 個元素。因此,當從堆疊中刪除元素時,會調整其大小。

VM 使用多個堆疊:

  1. 主堆棧。
  2. 用於儲存返回點的堆疊。
  3. 垃圾收集器堆疊。
  4. Try/catch/finally 區塊處理程序堆疊。

常數和變數

這個很簡單。常數在單獨的一小段程式碼中處理,並可透過靜態位址在未來的應用程式中使用。變數是一定大小的指標數組,對其單元格的存取是透過索引進行的 - 即靜態位址。變數可以被推入堆疊頂部或從那裡讀取。其實,因為雖然我們的變數本質上是在虛擬機器記憶體中儲存指向值的指針,但該語言主要是使用隱式指針。

垃圾收集器

在我的虛擬機器中,它是半自動的。那些。開發人員自己決定何時呼叫垃圾收集器。它不能使用常規指標計數器,如 Python、Perl、Ruby、Lua 等。它是透過標記系統實現的。那些。當要為變數指派臨時值時,指向該值的指標將會加入垃圾收集器的堆疊中。將來,收集器會快速遍歷已經準備好的指標清單。

處理 try/catch/finally 區塊

與任何現代語言一樣,異常處理是一個重要的組成部分。 VM 核心被包裝在 try..catch 區塊中,該區塊可以在捕獲異常後透過將有關異常的一些資訊壓入堆疊來返回到程式碼執行。在應用程式程式碼中,您可以定義 try/catch/finally 程式碼區塊,指示 catch(異常處理程序)和finally/end(區塊的末尾)處的入口點。

多執行緒

它在 VM 層級受支援。使用簡單方便。它在沒有中斷系統的情況下工作,因此程式碼在多個執行緒中的執行速度應該分別快幾倍。

VM 的外部函式庫

沒有這個就沒有辦法。 VM 支援導入,類似於其他語言的實作方式。您可以用 Mash 編寫部分程式碼,用本地語言編寫部分程式碼,然後將它們連結成一個。

從高階 Mash 語言到 VM 字節碼的轉換器

中級語言

為了快速編寫從複雜語言到 VM 程式碼的翻譯器,我首先開發了一種中間語言。結果是一個類似彙編程式的可怕景象,這裡沒有特別需要考慮的地方。我只會說,在這個級別,翻譯器處理大多數常數和變量,計算它們的靜態位址和入口點的位址。

翻譯器架構

我沒有選擇最好的架構來實現。翻譯器不像其他翻譯器那樣建立程式碼樹。他看著結構的開頭。那些。如果正在解析的程式碼片段看起來像“while :”,那麼很明顯這是一個 while 循環結構,需要作為 while 循環結構進行處理。像是一個複雜的開關盒之類的東西。

由於採用了這種架構解決方案,翻譯速度並不是很快。然而,其修改的容易程度卻顯著增加。我添加必要結構的速度比咖啡冷卻的速度還要快。不到一周的時間就實現了全面的 OOP 支援。

程式碼最佳化

當然,在這裡,它本來可以實現得更好(並且將會實現,但稍後,一旦有人有時間就實現)。到目前為止,優化器只知道如何從程式集中刪除未使用的程式碼、常數和匯入。另外,多個具有相同值的常數將被替換為一個。就這樣。

語言混搭

語言的基本概念

主要思想是發展盡可能實用且簡單的語言。我認為這項開發工作出色地完成了它的任務。

程式碼區塊、過程和函數

該語言中的所有結構都以冒號開頭。 : 並被運營商關閉 結束.

過程和函數分別宣告為 proc 和 func。參數列在括號中。一切都像大多數其他語言一樣。

操作員 返回 您可以從函數、運算子傳回一個值 打破 允許您退出過程/函數(如果它在循環之外)。

示例:

...

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 ():... 結束,func ():... 結束
  • 標籤&跳轉::,跳轉
  • 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。

試..[抓住..][最後..]結束

示例:

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競爭。我希望能夠基於GraalVM或LLVM實作JIT編譯。

存儲庫

您可以親自參與開發並追蹤專案。

Сайт
GitHub 上的儲存庫

如果您讀到最後,謝謝您。

來源: www.habr.com

添加評論