Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

21 May 2021

The Racket Reference阅读笔记【七】再读Syntax Model

The Racket Reference 阅读笔记。

1.2 Syntax Model

Racket程序的语句集可以定义为:

  • 在read过程将源代码中的字符流转化为语句标的
  • 在expand过程将语句标的进行处理,使其呈现完全解析形态

Racket所提供的read-syntax可以将源代码转为语句标的。

在expand过程对语句标解析是递归式的,可以将其完全展开,并转化为核心语句构造,成为完全解析形态。此过程是由语句标的中的标识符对照信息驱动的,解析过程中遇到的新的对照会被囊括进语句标的中。

1.2.1 Identifiers, Binding, and Scopes

一个标识符是一个源程序中的实体。解析程序的时候会发现标识符可以对照不同的含义,比如:变量,语句构造,化集的构化器,符号名,语句标的等等。标识符之间还可以互相对照,即处于作为变量或者语句构造的标识符可以对照到引用它的标识符。

对照和引用是根据作用域集合来敲定的。作用域既可以是程序源码中的一块区域,也可以是通过对程序的阐述而获得的。

处于嵌套的对照(比如嵌套的函数)产生嵌套的作用域。化集展开的时候会产生更复杂的嵌套层叠的作用域。看法上,每个作用域可以用唯一的号牌表示,虽然此号牌不可直接访问。也就是说,作用域的表示方式是程序的内部事务,对外则不可见。

一个构造是一个代码片段,比如一个标识符或者函数调用。构造被表示为关联有作用域集合的语句标的。

如果构造的解析结果是一个标识符的对照,标识符的符号名和作用域集合到这个对照的映射会被记录到一个全局的表中。这个记录反应了这个标识符的含义:是变量,还是语句构造,亦或是一个构化器。在两个具有引用关系的同名标识符间会形成特殊的对照,此时一个标识符的作用域应是另一个标识符的超集。如果一个标识符有多个这种类型的对照,那么各个对照之间的作用域可能会不一样,必须找到一个对照,其作用域集合是所有其他对照作用域的超级,这样的标识符引用才是无歧义的,否则会因歧义触发语句错误。也可以说一个对照遮蔽了其他作用域为其子集的对照。

可以在顶层级别定义对照,叫顶级对照。同理,独部级别定义的对照叫部级对照。除这两种情况以外,以他的对照都是局部的。独部内部是不能有顶级对照的引用的。一个无有对照的标识符叫野标识符(unbound)。

文档中,标识符有特殊的型设。型设为超链接的标识符其对照到变量或者语句构造。型设为空白的标识符要么是变量,要么是到未指明的顶级变量的引用。

每个对照都有关联的位阶,只有在关联的位阶上才可以引用此对照。位阶通常表征为整数(但是存在一个与执行无关的标签位阶,并不用整数表征)。位阶0代表所在独部(或者未采用独部形式的顶层表达式)的运行阶段,也就是基础环境。位阶1表征所在独部(或者未采用独部形式的顶层表达式)的展开阶段,也就是转化环境。还有位阶为-1的情况,表征的是以位阶1导入的其他独部的运行阶段。位阶-1是相对于发起导入的模块而言的,处于-1位阶的对照构成了模板环境。而特殊的标签位阶不对应任何运行阶段,只是用来跟踪对照(处于文档中的标识符没有运行的必要)。

同一标识符在不同的位阶可以有不同的对照。更准确地说,一个构造所关联的作用域集合不同的位阶可以不同。每个位阶都有自己独有的顶级或者部级的上下文,用来添加从化集展开或者其他语句构造得到的作用域。对照和引用的上下文用来确定其作用域的位阶。

1.2.2 Syntax Objects

语句标的将所表征的Racket量值,以及对应的文法信息,原址信息,语句辖属,拨弄状态等等整合在一起。其中,文法信息由一组作用域构成,每个成员对应一个位阶。值得注意的是,一个标识符被表征成这样一个语句标的:含有一个符号名,并且其文法信息可以合并到全局对照表,以此来决定此标识符在各个位阶的对照。

举例,car标识符的文法信息可能给出此car是来自racket/base的一个构造。同理,一个lambda标识符的文法信息可以给出此标识符表征一个执行决构造。对于其他的标识符,其文法信息可能会给出它们是到顶级变量的引用。

当语句标的表征的是比标识符或者简单常量更复杂的表达式时,其内部组件可以被提取出来。即便提取的是一个标识符,其详细的对照信息也是可以通过间接手段来获取的。可以使用free-identifier=?判断两个标识符是否指向相同的对照,或者可以使用bound-identifier?判断两个标识符有相同的作用域集合(即两个标识符一个处于对照定义之中,另外一个处于表达式之中)

举例说明:

(let ([x 5]) (+ x 6))

以语句标的来表示上面的这个构造中,可以从中提取出两个子语句标的,均为x标识符。这俩使用free-identifier=?和bound-identifier=?做判定时返回的结果都是它们是等同的。但是let标识符却和任意的x都不等同。

每个语句标的中的文法信息都是独立的,一个标的的文法信息可以被拷贝到另一个标的。所以标识符的对照不仅跟标识符的符号名有关,还跟其文法信息相关。即便文法信息相同,但是标的所含量值不同,其判定结果也不一样。

在上面的例子中,如果把let的文法信息传给符号名’x,并不会生成一个跟其他x能够让free-identifier=?返回真的值。因为let的文法信息不在x对照的作用域中。但是如果把6的文法信息传给’x,则可以生成一个和了其他两个x让 bound-identifier=? 返回真的语句标的。

quote-syntax构造在程序的运算和程序的表示之间架起一座桥梁。特别地,(quote-syntax datum #:local)可以制造一个语句标的,会自动接受datum所有的文法信息。(quote-syntax datum)有类似功能,但是会抛弃部分作用域信息。

(未完待续)