跳轉到內容

Rebol 程式設計/高階/直譯器

來自華夏公益教科書

作者:Ladislav Mecir

解釋階段

[編輯 | 編輯原始碼]

Rebol 原始碼的解釋(文字檔案中的文字、Rebol 字串中的文字、從鍵盤輸入的文字等)由三個基本步驟組成

  1. make 階段
  2. load 階段
  3. do 階段

MAKE 階段

[編輯 | 編輯原始碼]

在這個階段,make 函式建立一個 Rebol 塊。

建立的塊由 make 填充,以引用透過根據相應 Rebol 資料型別的規則解析提供的源文字而獲得的 Rebol 值。

新塊引用的所有單詞(即所有 any-word! 資料型別的值)都是未繫結的,即它們沒有上下文資訊。有關上下文的更多詳細資訊,請參閱Bindology 論文。

LOAD 階段

[編輯 | 編輯原始碼]

在這個階段,load 函式擴充套件全域性上下文,以包含由上一階段建立的塊引用的所有單詞(any-word! 資料型別的值),但細化除外。

建立的塊中的所有單詞(細化除外)都替換為其全域性上下文對應項。

這就是為什麼load 結果塊中包含的所有單詞都是全域性單詞(細化除外,如上所述)。

DO 階段

[編輯 | 編輯原始碼]

對於 do 函式來說,解釋的塊是前一階段的結果還是任何其他操作的結果無關緊要。此模型描述了 do 函式處理所有情況下 Rebol 塊和括號的行為。

do 函式以我們稱為隱式求值的方式依次處理解釋的塊中包含的值。

簡單隱式求值

[編輯 | 編輯原始碼]

對於某些值,隱式求值的結果是遇到的值本身。例如,整數、小數、字元、字串和塊。

括號的隱式求值

[編輯 | 編輯原始碼]

與隱式地求值為自身的塊相反,在解釋的塊中遇到括號時,會像 do 函式解釋塊一樣進一步解釋。

函式的隱式求值

[編輯 | 編輯原始碼]

當遇到 any-function! 資料型別的值時,需要收集它們的實參並將其提供給求值的函式。

具體來說,如果求值的是 do 函式,它會以我們稱為顯式求值的方式求值其實參。特別是,如果實參是一個塊,do 函式會像“DO 階段”部分中描述的那樣遞迴地解釋它。因此,對於塊來說,顯式求值與隱式求值不同。這就是使 Rebol 特別的原因。

單詞的隱式求值

[編輯 | 編輯原始碼]

當隱式求值的值是具有 word! 資料型別的單詞時,它被視為一個變數,並且會查詢該變數的值並根據其資料型別進行處理。我們稱之為間接隱式求值

還存在間接顯式求值,它發生在將單詞或路徑作為實參提供給 do 函式時。

解釋的結果

[編輯 | 編輯原始碼]

do 函式完成對塊中包含的所有值的隱式求值時,最後獲得的值將成為塊解釋的結果。

例外:如果 do 函式獲得 error! 資料型別的值,並且該值沒有用作接受錯誤值的函式的實參,則直譯器將導致錯誤。

在我看來,這個例外是多餘的,可以在將來版本的直譯器中輕鬆避免。

模擬(用 Rebol 編寫的簡單 Rebol 直譯器)

[編輯 | 編輯原始碼]

下面的 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 單詞的求值

[編輯 | 編輯原始碼]

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
華夏公益教科書