新的编程语言 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 <condition>:”,那么很明显这是一个 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。

关于赋值运算符和显式指针

?= 运算符用于为变量分配指向内存中值的指针。
= 运算符使用变量中的指针更改内存中的值。
现在介绍一下显式指针。 我将它们添加到语言中以便它们存在。
@<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竞争。 我希望能够基于GraalVM或者LLVM实现JIT编译。

存储库

您可以亲自参与开发并跟踪项目。

网站
GitHub 上的存储库

如果您读到最后,谢谢您。

来源: habr.com

添加评论