halo,大家好,我是 132,今天我要宣布一件大事,那就是,我终于搞好 joke 了
前天我写了一篇很直白的编译原理的文章
按照我自己的冥思,想出来一种能在千行代码以内实现一门语言(引擎)的思路,因为评论里有人感兴趣,所以我也趁热打铁,马上捋了一遍出来
github地址在这里:github.com/yisar/joke
然后接下来,按步骤描述一下:
用例
console.log(123)
是的你没有看错,就这一句最常见的代码,但其实它是 console 方法的成员 log 方法,然后执行 log 方法传进去一个 123 参数,然后把 123 给打印出来
lexer
lexer 做的事情最简单,就是拆字符串,得到 tokens,其实就是把字符串全部拆开:
["console",".","log","(",123,")"]
parser
parser 做的事情其实也不难,主要是遍历 tokens,然后得到一颗 AST 的树,长这样:
[NodeList([Call(Member(Identifier("console"), "log"), [Number(123.0)])])]
这是 rust 中,用来描述 tree 的方式,使用结构体,虽然不如 js 使用对象直白,但其实更容易操作
剩下的就是遍历这棵树,值得一提的是,rust 遍历很有意思,通常有两种方式,一种是类似 js 的 function 方式,一个 fn 搞定;另一种是和 class 类似,使用 impl struct,这种方式一般是实现 next() 这种迭代器方法……
我选择后者,毕竟未来这层遍历将会是一个很大的遍历,impl struct 的方式更容易拓展
codegen
codegen 是用来生成字节码的,它做的事情是将 AST 转换为 bytecodes,关于为什么需要转字节码,这部分目前我也没 get 到精髓
但是至少对于 joke 来说,目前它的作用就是拆 AST,拆成字节码和状态,字节码让 vm 去跑,状态用来实现执行堆栈和 gc
字节码长这样,十六进制的:
[1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 1, 0, 0, 0, 3, 5, 1, 0]
vm
vm 要做的事情将代码跑起来,比如按照用例,它做的事情就比较简单,就是全局维护一个状态栈,上面挂了 console.log 方法
未来还要有作用域,原型链,闭包,event loop……
思考
之前一直写 js,但是其实我对它内部的执行原理不是很懂,但是突然自己写 js 引擎,就不得不去思考这些曾经不关注的东西
- gc
说实话,gc 是我最初的灵感来源,为什么我要用 rust 写 js 引擎的初衷
我们都知道 rust 独有的所有权,可以很安全的管理内存,所以我就想,利用这一特性,有没有可能实现【绝对安全】的 gc?
如果能做到,那么就区别于 v8 等其他动不动就内存泄漏的引擎了……
答案是肯定可以的~ deno 提供了一种 isolate 思路,这种思路就是比较安全的,虽然我不知道 deno 是否能做到,毕竟有 v8
- 作用域,原型链,闭包,event loop
这些东西是 js 的基本,过去每次面试,被问到这些内容,就很拉分
现在轮到我自己写,这部分内容是重中之重,我需要好好冥思,力求设计出最简单的心智模型
其实很重要的是原型链,搞定这玩意,那么基本上所有 userland 的 API(map、some、bind 这些) 都不需要内置了,都可以基于原型在 js 层进行 polyfill
- Deno
很多人肯定很好奇,Deno 隐藏的全局变量放在哪儿的
Deno core 除了 isolate,还有很重要的 binding 和 shareQueue,虽然它是绑 v8 和 rust,但是换到 joke 来说,还是能够给 joke 带来新的灵感
最终完全可以搞一个类似的机制,那么所有的内置 API 都可以外置(Array、Ojbect、Date、Math、Josn等)
总结
通过精巧的设计,joke core 完全可以做到最小化,预期可以在一千行以内
顺便还可以利用 rust 的特性,解决 v8 的内存问题
顺便还可以外置所有 API
说实话,每次我写一个东西,评价都褒贬不一,很多人会说,不能用,没人用,不业务……
但是我写一个东西,真的不是为了让它能用,也不在意性能好坏,标准与否
我更在意的是一种新思路,巧妙的设计……
最后,放一下群号:813783512,欢迎来玩
然后,放一下 github:https://github.com/yisar/joke ,欢迎 star,欢迎 pr~