#vm #compiler #interpreter

bin+lib dark-vm

一个名为 DarkVM 的新虚拟机,专注于速度和简单性

7 个不稳定版本 (3 个破坏性更新)

0.4.3 2020 年 8 月 2 日
0.4.2 2020 年 8 月 2 日
0.3.0 2020 年 7 月 24 日
0.2.0 2020 年 7 月 24 日
0.1.0 2020 年 7 月 24 日

#465编程语言

MIT 许可证

105KB
1.5K SLoC

DarkVM

一个专注于在不牺牲速度和简单性的前提下提供当前功能的虚拟机。

这个项目也是我学习更多关于虚拟机和编译器的一种方式。

DarkVM 详细信息

DarkVM 在栈上运行并使用不同的操作来操作它。

DarkVM 中的类型

目前,DarkVM 支持以下类型

  • void
  • any
  • int
  • float
  • boolean
  • string

指令集

在本节中,将对每个指令进行详细说明,并附上示例

DarkVM 当前支持的指令

  • push
  • pop
  • peek
  • add
  • sub
  • mul
  • div
  • lt
  • lte
  • gt
  • gte
  • eq
  • neq
  • jmp
  • rjmp
  • jmpt
  • jmpf
  • set
  • call

Push 指令

Push 指令接受一个参数:要压入栈的值。

示例

push 1

Push 指令接受一个 int 类型的值 1,并将其压入栈中。

示例

push 1

执行此指令后,栈将看起来像这样

[1]

Pop 指令

Pop 指令不接受任何参数。

示例

pop

Pop 指令从栈中移除顶部值并返回此值。

示例

push 1
pop

执行此指令后,栈将看起来像这样

[]

返回的值将是 int 类型的 1。

Peek 指令

Peek 指令不接受任何参数。

示例

peek

Peek 指令返回栈顶的值。它不会从栈中移除它。

示例

push 1
peek

执行此指令后,栈将看起来像这样

[1]

返回的值将是 int 类型的 1。

Add 指令

Add 指令不接受任何参数。

示例

add

Add 指令从栈中移除顶部两个值并将它们相加。然后返回此值。

示例

push 1
push 2
add

执行此指令后,栈将看起来像这样

[]

返回的值将是 int 类型的 3。

Sub 指令

Sub 指令不接受任何参数。

示例

sub

Sub 指令从栈中移除顶部两个值并从它们中减去。然后返回此值。

示例

push 1
push 2
sub

执行此指令后,栈将看起来像这样

[]

返回的值将是 int 类型的 1。

Mul 指令

Mul 指令不接受任何参数。

示例

mul

Mul 指令从栈中移除顶部两个值并将它们相乘。然后返回此值。

示例

push 1
push 2
mul

执行此指令后,栈将看起来像这样

[]

返回的值将是 int 类型的 2。

Div 指令

Div 指令不接受任何参数。

示例

div

div 指令从栈中弹出两个值并执行除法操作。然后返回这个值。

示例

push 1
push 2
div

执行此指令后,栈将看起来像这样

[]

返回的值将是整数 0,因为 1 除以 2 等于 0(整数除法)。如果使用浮点除法,结果将是 0.5。

Lt 指令

Lt 指令接受两个参数。

示例

lt 1 5

Lt 指令检查第一个参数是否小于第二个参数。然后返回一个表示比较结果的布尔值。

示例

lt 1 5

执行此指令后,栈将看起来像这样

[]

返回的值将是布尔值 true,因为 1 小于 5。

Lte 指令

Lte 指令接受两个参数。

示例

lte 1 5

Lte 指令检查第一个参数是否小于或等于第二个参数。然后返回一个表示比较结果的布尔值。

示例

lte 5 5

执行此指令后,栈将看起来像这样

[]

返回的值将是布尔值 true,因为 5 小于或等于 5。

Gt 指令

Gt 指令接受两个参数。

示例

gt 1 5

Gt 指令检查第一个参数是否大于第二个参数。然后返回一个表示比较结果的布尔值。

示例

gt 1 5

执行此指令后,栈将看起来像这样

[]

返回的值将是布尔值 false,因为 1 小于 5。

Gte 指令

Gte 指令接受两个参数。

示例

gte 1 5

Gte 指令检查第一个参数是否大于或等于第二个参数。然后返回一个表示比较结果的布尔值。

示例

gte 5 5

执行此指令后,栈将看起来像这样

[]

返回的值将是布尔值 true,因为 5 大于或等于 5。

Eq 指令

Eq 指令接受两个参数。

示例

eq 1 5

Eq 指令检查第一个参数是否等于第二个参数。然后返回一个表示比较结果的布尔值。

示例

eq 1 5

执行此指令后,栈将看起来像这样

[]

返回的值将是布尔值 false,因为 1 不等于 5。

Neq 指令

Neq 指令接受两个参数。

示例

neq 1 5

Neq 指令检查第一个参数是否不等于第二个参数。然后返回一个表示比较结果的布尔值。

示例

neq 5 5

执行此指令后,栈将看起来像这样

[]

返回的值将是布尔值 false,因为 5 等于 5。

Jmp 指令

Jmp 指令接受一个参数。

示例

jmp 1

Jmp 指令检查传入的参数是否在程序范围内,如果是,则跳转到指定的位置。

示例

jmp 4
push "Failed"
push "Succeeded"

执行此指令后,栈将看起来像这样

["Succeeded"]

Jmp 指令不返回任何值。

Rjmp 指令

Rjmp 指令接受一个参数。

示例

rjmp 1

Rjmp 指令检查当前位置加上传入的参数是否在程序范围内,如果是,则跳转到当前位置加上指定的位置。这个参数可以是正数或负数,只要它们的和在范围内。

示例

rjmp 4
push "Failed"
push "Succeeded"

执行此指令后,栈将看起来像这样

["Succeeded"]

Rjmp 指令不返回任何值。

Jmpt 指令

Jmpt 指令接受一个参数。

示例

push true
jmpt 3

Jmpt 指令首先检查栈顶的值是否为 true。如果是,它检查传入的参数是否在程序范围内,如果是,则跳转到指定的位置。

示例

push true
jmpt 6
push "Failed"
push "Succeeded"

执行此指令后,栈将看起来像这样

["Succeeded", true]

Jmpt 指令不返回任何值。

Jmpf 指令

Jmpf 指令接受一个参数。

示例

push false
jmpf 3

Jmpf 指令首先检查栈顶的值是否为 false。如果是,它检查传入的参数是否在程序范围内,如果是,则跳转到指定的位置。

示例

push false
jmpf 6
push "Failed"
push "Succeeded"

执行此指令后,栈将看起来像这样

["Succeeded", false]

Jmpf 指令不返回任何值。

Print 指令

Print 指令接受一个参数。

示例

print "Hello, World!"

Print 指令输出参数。然而,它不会在后面打印换行符。如果您需要这种行为,请查看 "printn" 指令。

示例

push 1
push 1
push add
print "1 + 1 = "
print pop

执行此指令后,栈将看起来像这样

[]

Print 指令不返回任何值。

Printn 指令

打印指令(Printn Instruction)需要一个参数。

示例

printn "Hello, World!"

打印指令(printn instruction)将参数打印出来,并在后面添加换行符。

示例

printn "Hello, World!"
push 1
push 1
push add
printn "1 + 1 = "
printn pop

执行此指令后,栈将看起来像这样

[]

Print 指令不返回任何值。

设置指令(Set Instruction)

设置指令(Set Instruction)需要两个参数。

示例

set message "Hello, World!"

设置指令(set instruction)使用第一个参数的名称定义一个变量,在这个例子中是 message。这个变量的值是第二个参数。

示例

set message "Hello, World!"
printn message

执行此指令后,栈将看起来像这样

[]

设置指令(set instruction)不会返回任何值。

调用指令(Call Instruction)

调用指令(Call Instruction)需要一个参数。

示例

call greet

调用指令(call instruction)调用传入的标签。有关标签的更多信息,请访问专门的章节。

示例

call greet

@greet
  printn "Hello, World!"
end

在上面的例子中,运行了 greet 标签内的代码。因此,“Hello, World!”被打印到屏幕上。在标签内的代码执行完毕后,代码从函数调用之后的点继续执行。

执行此指令后,栈将看起来像这样

[]

调用指令(call instruction)不会返回任何值。

DarkVM中的标签(Labels In DarkVM)

在 DarkVM 中,标签提供了局部作用域。根据上下文,可以将其视为方法或块。以下是如何定义标签的方法:

@label_name
end

在这里,您可以看到标签以 @ 开头,然后是标签的名称。最后,它们有 end 关键字。标签名称和 end 关键字之间可以放置任意数量的代码。

标签可以访问父作用域和局部作用域。这意味着什么?如果标签是在另一个标签内部定义的,那么子标签可以访问父标签内部的所有变量。否则,它只能访问自己的作用域。尽管子标签可以访问父标签,但父标签不能访问子标签。

DarkVM中的注释(Comments In DarkVM)

DarkVM 支持单行和多行注释。

可以使用以下语法创建单行注释:


-- This is a comment.

可以使用以下语法创建多行注释:

-!
  This is a multiline comment.
  I can write anything I want here.
!-

无运行时依赖(No runtime deps)