基于文本替换的解释器:小结

\(\newcommand{\mt}[1]{\text{#1}}\)

关于语法

我们现在用的这种充满括号和前缀表达式的语法叫做“S表达式”。 S表达式看似奇怪,其实是一种简约风格的语法。 S表达式的表达式一般是这么设计的: 首先第一个词表示这个表达式的类别(如if表达式还是let表达式), 然后后面依次列出这个表达式的所有元素, 最后用一对括号把整个表达式括起来。 比如if表达式有三个元素条件表达式和两个分支表达式,所以if表达式是$(\mt{if} \; L \; M \; N)$; 加法则是一个加号后面跟上要相加的表达式:$(+ \; M \; N)$。

与其说S表达式语法简单,不如说S表达式几乎没有语法。 S表达式的代码已经是一棵抽象语法树。 它的一个很明显的好处是解释器几乎不用做语法分析。 关于语法分析,编译原理课程已是阐之未尽,我也没什么可说的。 我想说的都是一些语义方面的内容,所以我希望尽量避免语法分析。 减掉一些东西意味着减少许多麻烦(位移归约冲突够玩一阵子了)。 语法分析有时候会出现一些意想不到的bug。 比如《UNIX痛恨者手册》里有个例子,假设p是C语言的一个指向浮点数的指针。 现在要计算*p的倒数,于是有这么一小段代码:

1 1/*p

嗯?

S表达式一直被诟病括号多。 其实许多人(这些人应该都没学过Lisp)不喜欢S表达式的原因不在于括号多,而是因为S表达式与C语言(或者JAVA、Python)“不够像”。

See you later, alligator

为了引用方便,应该给我们正在实现的这门语言起个名字,就叫Alligator吧。

Alligator的语法: \begin{equation*}\begin{array}{lcl} M, N, L &=& X \\ &|& b \\ &|& \lambda X.M \\ &|& (\mt{fix} \; X_1 \; X_2 \; M) \\ &|& (+ \; M \; N) \\ &|& (- \; M \; N) \\ &|& (\mt{iszero} \; M) \\ &|& (M \; N) \\ \end{array}\end{equation*}

值: \begin{equation*}\begin{array}{lcl} V &=& X \\ &|& b \\ &|& \lambda X.M \end{array}\end{equation*}

宏: \begin{equation*}\begin{array}{lcl} (\mt{if} \; L \; M \; N) &=& (((L \; \lambda X.M) \; \lambda X.N) \; 0) \\ && \text{其中}X \notin FV(M), X \notin FV(N) \\ (\mt{let} \; X \; N \; M) &=& (\lambda X.M \; N) \\ (\mt{letrec} \; X_1 \; X_2 \; N \; M) &=& (\lambda X_1.M \; (\mt{fix} \; X_1 \; X_2 \; N)) \end{array}\end{equation*}

Alligator采用call-by-value的调用方式,它的求值过程如下(continuation passing): \begin{equation*}\begin{array}{lcl} \left<X, \kappa\right>_v &\rightarrow_v& \left<X, \kappa\right>_c \\ \left<b, \kappa\right>_v &\rightarrow_v& \left<b, \kappa\right>_c \\ \left<\lambda X.M, \kappa\right>_v &\rightarrow_v& \left<\lambda X.M, \kappa\right>_c \\ \left<(\mt{fix} \; X_1 \; X_2 \; M), \kappa\right>_v &\rightarrow_v& \left<\lambda X_2.M[X_1 \leftarrow (\mt{fix} \; X_1 \; X_2 \; M)], \kappa\right>_c \\ \left<(o^n \; M_1 \; M_2 \; ... \; M_n), \kappa\right>_v &\rightarrow_v& \left<M_1, \left<\mt{opd}, \kappa, o^n, (M_2 \; ... \; M_n), ()\right>\right>_v \\ \left<V, \left<\mt{opd}, \kappa, o^n, (M_{i+1} \; ... \; M_n), (V_1 \; ... \; V_{i-1})\right>\right>_c &\rightarrow_c& \left<M_{i+1}, \left<\mt{opd}, \kappa, o^n, (... \; M_n), (V_1 \; ... \; V_{i-1} V)\right>\right>_v \\ \left<V, \left<\mt{opd}, \kappa, o^n, (), (V_1 \; ... \; V_{n-1})\right>\right>_c &\rightarrow_c& \left<V', \kappa\right>_c \\ && \text{其中} V' = o^n(V_1, ..., V_{n-1}, V) \\ \left<(M \; N), \kappa\right>_v &\rightarrow_v& \left<M, \left<\mt{arg}, \kappa, N\right>\right>_v \\ \left<V, \left<\mt{arg}, \kappa, N\right>\right>_c &\rightarrow_c& \left<N, \left<\mt{fun}, \kappa, V\right>\right>_v \\ \left<V, \left<\mt{fun}, \kappa, \lambda X.L\right>\right>_c &\rightarrow_c& \left<L[X \leftarrow V], \kappa\right>_v \end{array}\end{equation*}

===我是穿越的分割线================================================

本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/project/

由于写解释器太欢乐了,所以我不知不觉就把Alligator写到这个系列大约三分之二的进度。下面链接是Alligator解释器的代码:

https://github.com/sKabYY/Alligator

这个Alligator包含多参数函数、互递归、赋值、垃圾回收、letcc与cc、异常处理等新功能。

testcases.rkt是测试用的例子。运行alligator.rkt文件会测试testcases.rkt里的例子。

1 racket alligator.rkt

alligator-cps.rkt对Alligator做了CPS变换和一些简单的CPS代码优化。一个很有意思的地方是做完CPS变换后,一些语句如异常处理就消失了。CPS代码优化写得比较乱,做了beta展开、eta归约和常数折叠三个优化。是处于试验阶段的代码,都是拍脑袋就写的,估计bug比较多。运行alligator-cps.rkt文件会测试testcases.rkt里的例子。

1 racket alligator-cps.rkt

输出比较多,可能需要重定向到文件再看。

长路漫漫

本来只想打发郁闷的时间而随便写写。现在发现写文章不仅能改善心情,对加深理解也有好处。我把后面想写的内容列下了,以激励自己。

1.基于文本替换的解释器。

2.环境。引入环境,CEK,作用域(静态,动态),SECD,无名变量,加入多参数。

3.状态。显式引用,隐式引用,垃圾回收,按需传值,按引用传值。

4.Continuation。letcc与cc,异常处理,多线程。

5.CPS变换,CPS代码优化?

6.类型系统……(待定)

7.面向对象……(待定)

程序语言这领域我才刚跨过半条腿,如有谬误,欢迎斧正。

时间: 2024-08-03 04:12:07

基于文本替换的解释器:小结的相关文章

基于文本替换的解释器:let表达式,布尔类型,if表达式

let表达式 let表达式用来声明一个变量. 比如我们正在写一个模拟掷骰子游戏的程序. 一个骰子有6个面. 所以这个程序多次用到了6这个数字. 有一天,我们忽然改变主意,要玩12个面的骰子. 于是我们不得不仔细查找源代码,把里面的6改成12. 对于一个较大的程序,这是灾难的开始. 有时我们会漏掉几个6,有时我们会把几个指的不是骰子面数的6误改成12. 这种灾难被称作"魔术数字". 避免魔术数字的方法一般是声明一个变量--比如说变量\(a\)--让这个变量等于6(\(a=6\)). 这个

基于文本替换的解释器及lambda演算

最近比较闲,打算整理一下之前学习的关于程序语言的知识.主要的内容其实就是一边设计程序语言一边写解释器实现它.这些知识基本上来自Programming Languages and Lambda Calculi和Essentials of Programming Languages这两本书. 我还记得高中奥数竞赛培训时的老师这样说过:"解题时一定要抓住定义." 编程和解题一样,也要抓住定义. 所以在写解释器前,得先定义好这门要解释的程序语言. 这门程序语言基于Lambda演算. 从\(\l

基于文本替换的解释器:加入continuation,重构解释器

或许在加入continuation之前要先讲讲费这么大劲做这个有什么意义. 毕竟用不用continuation的计算结果都是一样的. 不过,这是一个兴趣使然的系列,学习这些知识应该完全出于好奇与好玩的想法. 所以我才不会告诉你们通过控制continuation可以实现call-with-current-continuation和异常处理等功能呢. 我先简要描述一下加入continuation后解释器是怎么工作的. 加入continuation后的解释器是以迭代的方式工作的. 迭代的状态量有两个,

基于文本替换的解释器:引入continuation

当我写到这里的时候,我自己都吃了一惊. 环境.存储这些比较让人耳熟的还没讲到,continuation先出来了. 维基百科里对continuation的翻译是"延续性". 这翻译看着总有些违和感而且那个条目也令人不忍直视. 总之continuation似乎没有好的中文翻译,仿佛中国的计算机科学里没有continuation这个概念似的. Continuation这个概念相当于过程式语言里的函数调用栈. 它是用于保存"现在没空处理,待会再处理的事"的数据结构. 这样说

基于文本替换的解释器:递归,如何构造递归函数,Y组合子

递归.哦,递归. 递归在计算机科学中的重要性不言而喻. 递归就像女人,即令人烦恼,又无法抛弃. 先上个例子,这个例子里的函数double输入一个非负整数$n$,输出$2n$. \[ {double} = \lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ({double} \; (- \; n \; 1)))) \] 现在的问题是,这个递归函数在我们的语言里没法直接定义. 我说的直接定义是指像这个用let表达式: \[ ({let} \;

基于文本替换的解释器:加入整数类型

为了有条不紊地实现一个解释器,我将按以下三个步骤走: 1.明确语法 2.针对语法描述求值过程 3.根据求值过程编写代码实现 语法 \(\lambda\)演算不适合作为一门实际使用的程序语言. \(\lambda\)演算只有变量和函数两种类型,而其他常用类型如整数.布尔.字符等都没有. 虽然可以通过编码的方式表示这些常用类型,但这样也很麻烦. 通常直接扩展\(\lambda\)演算,加入一些常用类型以及针对这些类型的基本运算. 这种扩展后的语言简称为ISWIM,全称未知-- 为简单起见,我只加入整

基于文本替换的解释器:递归定义与lambda演算的一些额外说明

这一篇接在第一篇lambda演算的后面.讲讲一些数学知识. 经常有些看似很容易理解的东西,一旦要描述得准确无误,就会变得极为麻烦. 软件工程里也有类似情况:20%的代码实现了核心功能,剩下80%的代码处理边界情况. 于是,所谓的准确描述里的大部分文字都在说明边界情况,核心概念只有寥寥几字--好比一件打满补丁的衣服,完全看不出原来的样子. 出现这种现象要么是人类的大脑有缺陷,难以严谨而又准确的理解概念,也就是说人类太笨: 要么就是语言系统有问题,难以简洁地表达概念,而发明不出新的语言系统的人类还是

基于文本替换的解释器:递归,不动点,fix表达式,letrec表达式

这个系列有个显著的特点,那就是标题越来越长.忽然发现今天是读书节,读书节多读书. ==下面是没有意义的一段话 我是一个喜欢从学习知识中获得乐趣并乐于分享这种乐趣的人.我认为大部分知识只要花点时间都是能学会的.几年前,我迷上微分几何.我对每个朋友说这东西很有意思花点时间精力就能学会.他们回答说唉没时间时间不知去哪儿了.后来,我迷上量子力学.我对每个朋友说这东西值得一学,只要花点时间精力.他们回答说唉烦心事太多没精力去学.再后来,我迷上程序语言.我对每个朋友说只要使劲挤点时间就能掌握这个.他们回答说

深入浅出基于Java的解释器设计模式

设计 一.引子 其实没有什么好的例子引入解释器模式,因为它描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中:在实际应用中,我们可能很少碰到去构造一个语言的文法的情况. 虽然你几乎用不到这个模式,但是看一看还是能受到一定的启发的. 二.定义与结构 解释器模式的定义如下:定义语言的文法,并且建立一个解释器来解释该语言中的句子.它属于类的行为模式.这里的语言意思是使用规定格式和语法的代码. 在GOF的书中指出:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各