“空白”是相当多人听说过的一种深奥的编程语言。我敢肯定,这和它的源代码打印出来时效率惊人的笑话一点关系都没有。语言实际上是一个笑话(至少当其中一位作者在他的一些演讲中提到这种语言时,他是这么说的),这一点我相信是没有任何关系的,比如它的源代码在打印出来时效率如此之高。实际上,它的本意是一个笑话(至少当它的一位作者在他的一些演讲中提到这种语言时是这样说的)。但是,即使人们意识到了这种语言的存在,他们也很少知道它是如何运作的。让我们改变这一点,因为这真的不是那么难。
该语言的核心是描述堆栈机器上的操作,该机器可以访问堆。机器的唯一数据类型是任意大小的整数值。机器也有一些操作,它们也使用这些值对字符进行编码。仅此而已。其余部分只是使用三个ASCII空格字符(TAB、SPACE和LINEFEED)对操作和值进行编码的一种特殊方式。
操作按功能分组。这些组确定语言规范称为“指令修改参数”的操作sencoding前缀。许多操作没有参数,因为它们在机器的堆栈上创建和使用参数。然而,有些确实带有参数:整数和标签。流控制操作使用标签参数;一些堆栈操作使用整数参数。
它们的编码相似:都使用由空格和制表符组成的字符串,以换行符结束。在标签中,空格和制表符没有特殊的语义。至少不在语言规范中;稍后会详细介绍。在整数中,制表符编码1,空格编码0。关于这样的整数文字需要注意的是,它们不使用两个补码来编码负数。取而代之的是,他们使用文字最左边的位作为符号位:制表符表示负数,空格表示正数。这使得对任意宽的整数进行直接编码。
当您查看实际的空格程序时,您有时会注意到非常长的标签。通常情况下,似乎有一种默默的惯例,即使用8个字符的块来编码8位(在数字文字中,哪些字符编码1和0与数字相同),这些位被转换成一个正数,然后映射到ASCII编码(7个本来就足够了,但这不是我的程序所看到的)。
当您尝试并实现这种语言时,您会注意到您的机器实现需要一些东西:显然是一个堆栈,因为空格是一种错误操作的语言。另一个堆栈,用作调用堆栈,因为该语言有调用和返回操作。它还需要一个堆,将地址映射到整数。最后,您将需要程序内存和程序计数器。您可能还需要一个跳转表,以处理标签到地址的转换。不过,这并不是严格要求:在将程序加载到您的机器之前,您只需将所有标签转换为地址即可。
当我深入研究语言规范来弄清楚这一点时,我非常感兴趣,实际上做了另一个语言的实现。它的名字叫Space Man,可以在gitlab.com/ft/space eman和github.com/ft/space eman上找到。
我已经添加了原始语言主页的组织模式转换,因为该转换目前只能通过Archive.org获得。在尝试一些你可以在网上找到的更复杂的例子时,我遇到了一些问题。我的实现甚至无法解析它们。我验证了我的代码很长一段时间,直到我得出结论认为它正确地实现了解析器。所以我研究了其他实现。事实证明,它们中的大多数都实现了两个额外的堆栈操作:Copy和Slide,显然,它们被添加到该语言的后来的规范中。不过,我在网上找不到这样的规格(并不是说我花了很多时间)。然而,在实现了这两个之后,太空人可以运行我可以在网上找到的最精细的例子,比如数独解算器。我已经在包含的语言版本中添加了这两个额外的操作。
我正在使用兆秒进行解析。有了普京提供的几个实用程序,编写解析器变得相当轻松:
StackParser::parser StackOperationstackParser=do(try$imp[space])(try$PUSH<;$>;number[space])<;|>;(try$operation[linefeed,space]Duplicate)<;|>;(try$operation[linefeed,table]exchange)<;|>;(try$operation[linefeed,linefeed]drop)<;|>;(try$copy<;$>;Number[表格,空格])<;|>;(尝试$Slide<;$>;number[表格,换行符])。
在实现该语言的操作时,您会发现您面对的是许多操纵虚拟机的通用指令。当然,你把这些常见的任务放入函数中,就像任何称职的汇编语言设计者一样,你显然给你的指令起了有点隐晦的三个字母的名字。有了这些,实现堆栈操作操作如下所示:
评估::空白计算机->;堆栈操作->;IO空白Machineeval m(Push N)=return$pci$psh[n]meval m Duplicate=return$pci$psh h m其中h=peek 1 meval m exchange=return$pci$psh[b,a]$drp 2 m其中[a,b]=peek 2 meval drop=return$pci$drp 1 meval m(Copy I)=return$pci$psh[n]m其中n=ref I meval m(幻灯片n)=return$pci$psh$DRP(n+1)m其中h=peek 1 m。
实现其他操作组看起来类似。我有点喜欢。每个都可以放在头顶的滑梯上。
事实证明,编辑空格程序是一项艰巨的工作。直接操作最好是在十六进制编辑器中完成。但是太空人有一个特性,那就是将程序语法树转储到标准输出。这些程序转储实际上是可执行的程序。因此,如果你想编辑一个空格程序,你可以把它压缩成一个文件,编辑它的AST,然后运行该程序来产生变化后的空格程序。