(home)尽管我可能在较早的文章中曾说过,但是直接在wat中编写代码并不是很好,所以我开始着手于编译器的工作。一个非常瘦的编译器。该语言应该与我们心爱的wat非常相似,但可以避免我们键入所有i32.const,local.get。
没有自我托管的人,我什至没有malloc可以玩,更不用说越野车,执行不佳的通用Lisp的部分副本了。
语法检查是最新技术,错误以可读格式输出(即,如果犯了最轻微的错误,您将得到一个神秘的sbcl调试跟踪)。
为什么加糖?就是那个,或者是沃森,沃特普,沃萨玛塔,watulookin。还因为它只是语法糖,懂吗?
没有手册;甚至没有mul / div或所有关系运算符都被编写,例如,< =尚未被编写!
结果不是独立的,而是wasm文件。到目前为止,我发现最好的独立运行时是wasm-interp。请不要说出node.js。可以说是v8。
这是之前关于webasm的博客文章中的malloc.wat示例,它用糖重新编写,比原始的原始wat更加简洁和方便(请参见下文)。
; malloc.sugar-糖示例代码(内存(import" js"" mem")10)(import" console"" log"(func js_log(i i32)))(全局命令i32#x1000)(全局sbrk(mut i32)0); -----在wasm-space中分配一些内存(defun(export" malloc")(len i32):result i32(locals(i32(newbrk(+ sbrk len))(mem sbrk))) (如果(> = newbrk memend)(返回0))(设置sbrk newbrk)mem); -----使用js回调在wasm空间内存中写入字节(defun(export dump_range)(start i32 len i32)(locals(i32 i(end(+ start len)))))(for(i start end)( js_log(get.u8 * i)))
如果您直接在wat中进行编码,这就是您应该编写的内容。
(模块(内存(导入(" js"" mem")10)(导入"控制台"" log"(func $ js_log(param $ i i32)))(全局$ memend i32(i32.const 4096))(全局$ sbrk(mut i32)(i32.const 0));; -----在wasm空间中分配一些内存(func(export " malloc")(参数$ len i32)(结果i32)(本地$ newbrk i32)(本地$ mem i32)(local.set $ newbrk(i32.add(global.get $ sbrk))(本地.get $ len)))(if(i32.ge_u(local.get $ newbrk)(global.get $ memend))(返回(i32.const 0)))(local.set $ mem(global.get $ sbrk ))(global.set $ sbrk(local.get $ newbrk))(local.get $ mem));; -----使用js回调将字节写入wasm空间内存中(func(export" dump_range& #34;)(param $ start i32)(param $ len i32)(local $ i i32)(local $ end i32)(local.set $ end(i32.add(local.get $ start)(local.get $ len)))(local.set $ i(local.get $ start))(块$ break2(循环$ head1(br_if $ break2(i32.eq(local.get $ i)(local.get $ end)))) (致电$ js_log(i32.load8_u(local.get $ i)))(local.set $ i(i32.add( i32.const 1)(local.get $ i)))(br $ head1))))))
在您喜欢的文本编辑器中编写一个your-source.sugar源文件。然后在上面放糖以产生your-source.wat
它包括一个宏系统,我曾经用它来实现inc,但是它尚不能从sugaritself中使用。同样,完整的CL defmacro / destructuring-bind lambda列表语法很难复制;更不用说反引号了。
循环:从源文件中读取一个表格(编译表格)=> stdout编译形式:integer => (i32.const int)符号=> (local / global.get varname)[在环境/范围内签入] list / s-expr / cons => while :(是宏形式)形式:=(扩展宏形式)是特殊形式吗?形式=> (特殊编译形式)[defun / import / export / global / set ...]是功能吗? =>对于所有args(编译arg),请使用args调用func,否则=>错误未定义函数
括号太多?规则1:没有方括号,只有缩进。规则0:我们遭受了括号cos的折磨,这给我们带来了defmacro的痛苦,这值得很多痛苦。
您曾经生成代码吗?您认为这很酷吗?您会爱上普通的口吻。请试用2周。如果不满意,请保证退款。
Lisp具有value-IF,即您可以在使用值的任何地方使用IF。就像C的三元函数一样,但是功能更强大。起初我以为WAT没有它,但是(if)有一个可选的(result type)子句,例如(func)以便它39; s分支可以返回(类型匹配)值。因此,您可以执行以下此类聪明的操作:请注意if和condition之间的(result i32)子句。
wat(选择)类似于C三元运算符,尽管它内部可以具有块/语句序列。
select v if:select总是对所有3个子句求值,而(if)仅对条件和selected分支求值。 other(if)分支未评估。
目前,我只有按位运算符和/或运算符'&'和' bor'列表上的下一个是快捷键逻辑和/或类似CL的运算符。
理想情况下,我希望具有Lisp样式和/或返回表达式值而不是仅返回0或1的地方。也可以是独立的和/或我们可以拥有类似的东西
快捷方式和/或实际上是编译为嵌套(if)表达式。缺少其他类型(i64,f32,f64)的全部功能副本。编译器无法类型推断哪个(见下文),因此它们将具有显式名称,例如+ f64,set.f64等。
目前,我正在使用指针get * / set *,但是我想拥有aref和指向指针算术的隐式索引。
还有更多循环。同时重复等。底层的block,loop,br_if,br被公开,因此可以进行写循环。只有在添加一些宏才能启用的情况下:
某种结构可能很不错。即使最初所有字段都必须是i32,即仅是一个带有命名字段而不是编号字段的向量。
即使是当前的错误消息,也会使您进入SBCL调试器,这可能是一个令人恐惧的地方-[ctrl-d]退出。
知道值的类型将使编译器可以检查用户函数和内建函数调用的签名,还可以使我推断出set的正确类型,而不需要set.f32等。
必须在函数开始时定义本地。这意味着实现LET(具有局部变量的作用域块)并非易事。当前的单遍编译器需要变得更加复杂,才能首先确定函数中需要多少个局部变量,然后预先分配它们并可能如果有冲突,请重命名它们。
编译器作者的圣杯是他们用新语言重写了编译器。 Common Lisp远远超出了水准,这将是一项艰巨的任务。首先,我需要编写malloc ...
设置糖/普通口香糖可能微不足道或困难,具体取决于您对普通口香糖的熟悉程度:) 安装common lisp(我使用SBCL)超出了本文的范围。 (告诉我,这比npm难...) sugar作为Unix脚本运行。 因为我更喜欢迭代而不是循环,所以我制作了一个自定义的sbcl内核。以下是如何制作这样一个内核的简短说明,从普通的sbcl开始。