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