Forth 編譯程序

       解釋狀態==執行狀態         編譯地址==執行地址
      FORTH 系統內部有一個系統變量 STATE,當 STATE==0,系統處於"執行"狀態,當 STATE <> 0,系統處於"編譯"狀態 系統從輸入緩衝區中取出下一個字符串放到詞緩衝區,而後去詞典中查找該詞;若是該詞在詞典中,就把該詞的定義的編譯地址(cfa,也叫執行地址)送到參數棧頂。而後,若是系統處於執行狀態,則外部解釋程序(即文本解釋程序)就調用內部解釋程序(地址解釋程序)執行該定義;若是系統處於編譯狀態,則編譯程序就把留在參數棧頂的 cfa 移到詞典的頂部,把該定義的 cfa 編入到正在被編譯的定義的參數域中,可是,若是cfa所表明的定義是一個當即詞,無論系統處於什麼狀態,都執行它。
  // 異常狀況沒有畫出,好比轉換數字失敗。
       若輸入流中分離出來的字符串在詞典中沒有定義,系統就把它轉化成一個數字,若是成功,文本解釋程序就把該數字放置到參數棧上;若系統處於編譯狀態,則編譯程序就把該數字編入詞典成爲一個數字文字常數(literal)
      




定義 [ 和 ] :
      FORTH 提供 [] 這兩個定義以顯示改變系統的狀態, [ 使系統從編譯狀態變爲執行狀態; ] 使系統從執行狀態變爲編譯狀態。這兩個定義均只能在冒號定義中使用。若   [   ] 成對出如今冒號定義中,則包含的部分被當即執行而不被編譯。





文字常數:
      FORTH 中,有一些進行編譯工做的詞,稱爲編譯類詞。它們的效果是給詞典中編譯進這樣或那樣的內容。能夠把參數堆棧上的數字「原封不動」的搬進詞典造成「文字常數(literal)」;因此在詞典中很多狀況下是編譯地址和文字常量並存的局面。五個編譯詞:LIT、LITERAL、(DLITERAL)、COMPILE、[COMPILE]。
:   LIT    ( - n )       編譯數字的運行時間代碼
        R@  @         取出編譯在詞身內的數字
        R>  2+  >R  ;        調整IP

運行時 LIT 作兩項工做:
一、它必須把跟着它的數字放到參數堆棧頂;
二、它必須移動解釋指針 IP 越過這個數字而指向下一個編譯地址。
:  FIVE+   5  +  ;
FIVE+ ^LIT 5 ^+ ^UNNEST

1000 1002 1004 1006
開始執行 LIT 以前,IP 偏移指向 5 。即(IP)=1002。執行 LIT,因爲 LIT 是冒號定義,因此先執行的是 LIT 的代碼指針域 cfa 內的 NEST,執行過程:
一、(IP) -> 返回棧頂保存,因而返回棧首項的內容爲1002
二、(W)+2 -> W  它是 LIT 的 pfa
三、(W) -> IP  
四、執行 NEST 內最後的 NEXT ,因而就開始執行 LIT 的詞身
^R@  把返回棧頂的內容1002複製到參數棧頂  ^@ 取出1002單元的內容5   ^R> 把返回棧頂的1002送到參數棧頂  ^2+  參數棧首項變爲1004,指着下一個要執行的詞 +  ^>R 把1004送回返回棧頂保存  ^UNNEST 把返回棧頂的1004 -> IP,接着執行1004處的 +。

:  COMPILE   ( - )      只能夠用在冒號定義內部。當該冒號定義執行時,COMPILE 就把跟在它後面的定義編譯進詞典中。
         R>
         DUP  2+  >R
         @  ,  ;

與 LIT 定義相比,COMPILE 僅僅在定義的最後多了一個逗號,由它把留在堆棧頂上的跟在 COMPILE 以後的詞的 cfa 編入詞典

       // AE 和 AF 執行效果同樣是由於 AC 是當即詞,因此在AE的編譯過程當中執行,把A1和A2的代碼域編譯進了AE的詞身。

:  LITERAL   ( n -  )       把在參數棧上的單字長整數編譯成爲一個文字常數。
       COMPILE  LIT       首先編譯運行時間代碼 LIT
       ,                             接着編譯數字自己
       ;  IMMEDIATE      使其成爲當即詞
eg:
:  test   [  5  ]  LITERAL  +  ;
//數字 5 必須處於詞 [ 和 ] 中間。
:  DLITERAL   ( d -  )     把在參數棧上的雙字長整數編譯成爲一個雙字長的文字常量
        SWAP      顛倒雙字長整數的次序
        [COMPILE]  LITERAL   把文字常數的編譯推遲到 DLITERAL 執行時進行。由於 LITERAL 是當即詞,故只有用[COMPILE]才能把它編入 DLITERAL 中。
        [COMPILE]  LITERAL   強行編譯雙字長文字常數的高16位。
        ;  IMMEDIATE
:  [COMPILE]   ( - )      強行編譯跟着的當即型定義
          '           尋求跟着的詞的執行地址
          ,           編譯它
          ;  IMMEDIATE       必須當即執行





編譯循環:
      FORTH 中編譯過程與解釋過程(即執行過程)的區別在於:在編譯過程當中,不是執行從輸入流中分離出來的詞,而是把詞的編譯地址添加到詞典的頂部,在那兒系統正在構造一個新的冒號定義的參數域;若是從輸入流中分離出來的是數字,那麼編譯過程不是把數字留在參數堆棧上,而是把它編譯成爲定義中的數字文字常數。之後當執行該定義時,同一數字即可被從新取出並放置到參數棧頂。 冒號定義的編譯程序就是定義 ] ,它和解釋程序 INTERPERT 有相似操做。





冒號定義的開始和結束:
:    :    ( - )                    定義一個高級定義,新定義完成以前被"隱藏"起來,冒號的執行代碼給程序增添一層嵌套
         !CSP            把當前參數堆棧指針存入變量CSP以在定義結束時查錯之用,正常編譯不該影響參數堆棧的深度
         CURRENT  @  CONTEXT  !                   使 context 詞彙與 current 詞彙一致
         CREATE                使用跟在冒號後面的名字在詞典中創建一個首部
         HIDE                    使該首部躲過詞典搜索
         ]                          進入冒號定義的編譯程序,開始建造在參數域中的執行地址表
         ;USES                把跟着的代碼程序(即下面的NEST)的地址插入到新定義的代碼指針域,使新定義成爲一個冒號定義。(相似DOES>,編譯運行過程已經規定好的代碼到代碼域)
         NEST  ,                編譯地址解釋程序NEST的地址,使其可被放進新定義的cfa
:   ;    ( - )           結束一個冒號定義,它編譯執行代碼UNNEST退出一層件嵌套,改變STATE的值以解釋編譯
         ?CSP             當前的參數堆棧指針與CSP的值相符嗎?若不相符則中止
         COMPILE  UNNEST             給新定義的末尾加上UNNEST,使程序的執行返回到調用者
         REVEAL         與HIDE想反操做
         [COMPILE]  [         在此外編譯,[  以結束新定義的編譯 
         ;  IMMEDIATE       分號自身必須在編譯狀態下執行,因此必須是一個當即詞。





數字文字常量:
      指的是詞典中存放運行時間代碼(LIT)的cfa的單元及跟着它的數字單元;執行是LIT把數字送到參數堆棧。LITERAL、DLITERAL、ASCII、[']……
:  ASCII      ( - char )         把輸入流中下一個字符的ASCII代碼編譯爲一個數字文字常量
        BL  WORD        取出下一個字符
        1+  C@          獲得它的ASCII代碼
        STATE  @         獲取當前系統狀態(編譯?解釋)
        IF  [COMPILE]  LITERAL          編譯狀態就把其編爲文字常數
        THEN          不然留在參數堆棧上
        ;  IMMEDIATE
:  [']   ( - )
       '  
       [COMPILE]  LITERAL
       ;  IMMEDIATE
[']  被定義成爲一個當即型的定義,所以對於 ['] 定義內的 ' 來講,所謂的輸入流中的下一個詞就是冒號定義內跟在 ['] 後面的那個詞。





數字輸出轉換:

HLD  (- add)  在數字輸出轉換過程當中,保存文本最後字符地址的變量。
:  HOLD   ( char -  )      把ASCII字符char插入輸出字符串中
        -1  HLD  +!      HLD爲指向輸出文本緩衝區的字符指針,在輸出文本緩衝區中造成輸出數字字符串。數字字符串是從最低有效位開始逆向創建,爲把一個字符插入該字符串。HLD 的內容要減一
         HLD  @       指定地址
         C!  ;        把字符串插入HLD所指定的地址字節
:  <#   ( - )        初始化數字轉換過程,要求棧中是個無符號雙字長數
       PAD                返回輸文本的緩衝區地址
       HLD  !  ;      HLD 指向 PAD ,以便字符串能在 PAD 緩衝區中創建
:  #>   ( d - addr len )         結束輸出數字轉換,並把字符串的地址和長度送入堆棧,它們正是 TYPE 命令所須要的參數
       2DROP       再也不須要堆棧中的雙字長數
       HLD  @            取出字符串首址
       PAD           取出字符串的末尾地址
       OVER  -  ;         獲得字符串的長度
:  SIGN  ( n -  )          若是棧頂項 n 是負數,則在輸出數字字符串中插入一個負號
       0<  IF       判斷棧頂是否是負數
       ASCII  -  HOLD         插入負號
       THEN  ;   
:  #   ( d1 - d2  )           轉換一位數字,並把它加到輸出數字字符串中。轉換中,d1除以基,商d2被保留在堆棧中,餘數被轉換成ASCII碼,送入輸出緩衝區
       BASE  @  MU/MOD         餘數和雙字長整數商保留在堆棧上
       ROT         把餘數移到棧頂
       9  OVER  <        果餘數大於9
       IF  7  +  THEN       加7以造成A
       ASCII  0  +  HOLD  ;     把餘數轉換成 ASCII 碼,並插入到輸出緩衝區
:  #S  ( d - 0 0 )         轉換一個雙字長整數,直至結果爲零
       BEGIN
           #       轉換一位數字
           2DUP  OR        判斷商是否爲 0 ?
       0=  UNTIL  ;         若是商爲零,退出循環;不然,繼續轉換。
eg:無符號雙字長整數 12345.  ,要求打印成:123.45  ; 命令:<#  #  #  ASCII  .  HOLD  #s  #>  TYPE


小數點的前三位整數用 #S 實現轉換。整個轉換過程是在 PAD 緩衝區內進行的。轉換完成堆棧頂部存放的是ASCII字符串的首地址和長度。

  // 當前執行到 #S ,HLD 也相應的指向3
相關文章
相關標籤/搜索