Rebol 编程/高级/解释器
作者:Ladislav Mecir
Rebol 源代码的解释(文本文件中的文本、Rebol 字符串中的文本、从键盘输入的文本等)由三个基本步骤组成
- make 阶段
- load 阶段
- do 阶段
在这个阶段,make 函数创建一个 Rebol 块。
创建的块由 make 填充,以引用通过根据相应 Rebol 数据类型的规则解析提供的源文本而获得的 Rebol 值。
新块引用的所有单词(即所有 any-word! 数据类型的值)都是未绑定的,即它们没有上下文信息。有关上下文的更多详细信息,请参阅Bindology 论文。
在这个阶段,load 函数扩展全局上下文,以包含由上一阶段创建的块引用的所有单词(any-word! 数据类型的值),但细化除外。
创建的块中的所有单词(细化除外)都替换为其全局上下文对应项。
这就是为什么load 结果块中包含的所有单词都是全局单词(细化除外,如上所述)。
对于 do 函数来说,解释的块是前一阶段的结果还是任何其他操作的结果无关紧要。此模型描述了 do 函数处理所有情况下 Rebol 块和括号的行为。
do 函数以我们称为隐式求值的方式依次处理解释的块中包含的值。
对于某些值,隐式求值的结果是遇到的值本身。例如,整数、小数、字符、字符串和块。
与隐式地求值为自身的块相反,在解释的块中遇到括号时,会像 do 函数解释块一样进一步解释。
当遇到 any-function! 数据类型的值时,需要收集它们的实参并将其提供给求值的函数。
具体来说,如果求值的是 do 函数,它会以我们称为显式求值的方式求值其实参。特别是,如果实参是一个块,do 函数会像“DO 阶段”部分中描述的那样递归地解释它。因此,对于块来说,显式求值与隐式求值不同。这就是使 Rebol 特别的原因。
当隐式求值的值是具有 word! 数据类型的单词时,它被视为一个变量,并且会查找该变量的值并根据其数据类型进行处理。我们称之为间接隐式求值。
还存在间接显式求值,它发生在将单词或路径作为实参提供给 do 函数时。
当 do 函数完成对块中包含的所有值的隐式求值时,最后获得的值将成为块解释的结果。
例外:如果 do 函数获得 error! 数据类型的值,并且该值没有用作接受错误值的函数的实参,则解释器将导致错误。
在我看来,这个例外是多余的,可以在将来版本的解释器中轻松避免。
下面的 simulation 函数是一个简单的解释器,能够解释您从键盘输入的一行文本。
simulation: func [/local input-line created-block loaded-block] [ ; step #0, INPUT input-line: ask ">> " ; step #1, MAKE created-block: make block! input-line ; step #2, LOAD loaded-block: load created-block ; step #3, DO do loaded-block ; done ]
它说明了上面所写的描述。
Rebol 单词(word! 数据类型的值)表现出最复杂的行为。当 do 函数对一个单词进行求值时,do 函数首先会选择单词所引用的值。如果求值的单词没有上下文,则 do 函数将无法选择该值,并且它会改为引发错误。
>> b: make block! "a" ; == [a] == [a] >> do b ** Script Error: a word has no context ** Near: a
接下来的操作取决于所选值的类型。一个特殊情况是,当单词未设置时,即单词的值是 unset! 数据类型时。
>> do [a] ** Script Error: a has no value ** Near: a
对于某些值,不需要进一步的操作,并且所选的值将成为单词求值的结果;对于其他值,操作将根据数据类型执行。第二种类型的值可以称为单词活动值或单词活动数据类型。最近版本的解释器使用越来越少的单词活动数据类型。
单词活动值的典型代表是 any-function! 值。当在求值的单词的值中遇到此类值时,do 函数会收集该函数的所有实参并像上面一样“调用该函数”。
单词活动数据类型的不断缩减列表中仍然包含 lit-words 和 lit-paths(在我看来,这是多余的)。另一方面,unset! 值的行为也可能变得更加像函数。
word-active?: func [ {finds out, if a Rebol value is word-active} value [any-type!] ] [ parse head insert/only copy [] get/any 'value [ unset! | any-function! | lit-word! | lit-path! ] ]
单词活动值可以表现出递归行为,例如
; recursive function factorial: func [n] [ either n <= 1 [1] [n * factorial n - 1] ]
>> factorial 5 == 120
实际上,如果我们适当地使用活动值,我们甚至可以使非活动值表现出递归行为
; recursive block factorial-block: [ either n <= 1 [1] [n * (n: n - 1 do factorial-block)] ]
>> n: 5 do factorial-block == 120