几周前我开始阅读Writing An Interpreter In Go和
在阅读并实施解释器时,一个问题弹出了:如果我可以运行此问题
怎么办
我网站的终端中的口译员?
回顾一下,几个月前,我决定将我的个人website转变为
终端(阅读更多有关here的信息)。
在今天的帖子中,我将概述整个实施,但首先是预览:
您可以通过在here端子中键入simia
来尝试一下
这本书:学习口译员的绝佳资源
尽管这篇文章不是书评,但我想说这是一个非常好的阅读。
您可以从令牌化的基础上学习到分析输入的评估。
作者在用具体示例的所有概念中指导读者做得很好。
如果您想了解口译员和重复工作,这本书绝对是一个不错的开始!
扩展猴子语言
在书中,作者向我们介绍了Monkey
语言。
它的语法非常简单,这激发了我添加更多的表达式和语法,从本质上扩展了
我渴望的语言。我想创建一种可以结合go
,rust
和
语法的语言
elixir
。这就是simia出生的方式。
我仍在努力,但到目前为止,我已经增加了对:
-
Range
表达式 -
for-loop
表达式以及Infix Operatorin
。例如
for a in 1..10 {
log(a);
}
- 支持
for-loop
中布尔条件的支持6
for a > 0 {
log(a);
}
- 括号是
if
和for-loop
表达式的可选 - 支持Elixir的
pipe
操作员|>
8 |> factorial() |> add(100)
- 用
=
运算符重新分配变量
let foo = "bar";
foo = "1234";
和我的待办事项清单:
- 支持模块
- 支持
pub
键 - 为数组,字符串和地图添加更多的内置in
- 支持
mut
键和控制Mustability
总的来说,我希望simia
成为某种现代功能编程语言。
在浏览器中加载解释器:WebAssembly
如果您不熟悉wasm
,这里是https://webassembly.org/
WebAssembly(缩写WASM)是基于堆栈的虚拟机的二进制指令格式。
WASM被设计为编程语言的便携式编译目标,从而在
上进行部署 客户和服务器应用程序的网络。
基本上,它允许我们将simia
解释器(用Golang编写)转换为浏览器
的一组指令
可以理解和执行。
在Monkey
中这样做真的很简单,这要归功于syscall/js
包装和Arch wasm
编译解释器时。
让我们看看一个简单的例子:
// main.go
package main
import (
"syscall/js"
)
func main() {
done := make(chan struct{}, 0)
js.Global().Set("wasmHash", js.FuncOf(hash))
<-done
}
func hello(this js.Value, args []js.Value) any {
return "hello " + args[0].String()
}
注意channel
我们一直在等待输入。这是必要的,因为WASM模块它是一个应用程序
而不是图书馆。因此,此应用程序应保持运行,并且在执行js.Global()...
后不立即退出。
现在我们将应用程序编译到WASM:
GOOS=js GOARCH=wasm go build -o hello.wasm ./main.go
为了加载我们的WASM,我们需要使用WASM工具GO提供
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
现在,最后一个位,我们从JS加载WASM并使用我们的奇妙的hello
函数:
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("hello.wasm"), go.importObject).then((result) => {
go.run(result.instance);
console.log(window.hello("universe")); // hello universe
});
</script>
</head>
<body>
</body>
就是这样,服务文件并在浏览器中打开它应显示开发人员控制台的结果。
我采取了完全相同的步骤来编译和使用浏览器中的simia
解释器。
WASM模块导出到对象:
-
simia
:评估Simia代码并返回结果 的函数
-
simia_version
:解释器的版本
警告
一切都很漂亮,但我不得不退后考虑一个问题:simia
具有内置功能
称为log
,它使用fmt
软件包将值打印到stdout。
当汇编为WASM时,fmt.Println
将使用console.log
作为缓冲区,因此任何log
指令都将是打印的
进入浏览器控制台。
解决方案是修改内置以使用缓冲区,然后通过JavaScript传递某种缓冲区,
因此,我可以使用该缓冲区将其打印到终端元素。我可能会在
期间解决这个问题
周末,但是如果您有一个更好的主意,请在评论中告诉我。
扩展我的网站终端
当我想出将plect添加到网站中的想法时,我看了我网站中的现有代码
并确定实现这一目标所需的要求:
- 可以按需加载WASM模块,即用户第一次从终端调用
simia
- 命令应能够 std进出。
- REPL将使用
wasm
模块评估用户输入 -
ctrl-c
应该“杀死”该过程并返回外壳
在其他实施此要求中,我准备了一些重构:
- 改善错误处理,例如“命令丢失”,“错误的参数”等
- 改进钥匙按操作并提取共同功能
- 介绍
processId
。当命令执行返回进程ID时,这意味着主函数,让我们调用它 外壳,应停止处理输入。命令退出后,应调用回调以还原主函数。
就是这样,我可以说现在我的终端有一个完全有效的simia repl。
其他
我很有趣地写口译并将其加载到我的终端网站上,我肯定学会了
在此过程中很多事情。我认为写口译员对于任何开发人员来说都是一个很好的做法。
我对网站进行的所有更改均在此PR
中介绍您还会添加什么语言或终端?
我打算在我的网站上进行某种寻宝活动,请继续关注!
ð½