1. 程式人生 > >5.2.2 彙編器

5.2.2 彙編器

5.2.2 彙編器
彙編器為了一個機器,把控制器表示式的序列轉換成了相應的機器指令的列表,
任何一個指令都有它的執行程式。進而,彙編器更像是在第四章中我們學習的直譯器
這有一個輸入的語言(在這個例子中,是暫存器機器的語言)我們為了語言中的任何
一種表示式,必須執行一個合適的動作。

為了任何一個指令生成一個執行程式的技術正如我們在4.1.7部分中使用的,為了加速直譯器的執行,
我們通過把分析與執行時的執行分離出來。正如 我們在第四章中所看到的那樣,scheme的表示式
的更有用的分析能被執行,而不用知道變數的實際的值。這裡與之相似的是,暫存器機器的語言的表示式
的更有用的分析能被執行而不用知道機器的暫存器中的實際的值。例如,我們能通過對暫存器物件的指向
來代替對暫存器的引用,我們能通過標籤的目標的指令序列中的位置的指向來代替對標籤的引用。

在生成指令執行程式之前,彙編器必須知道所有的標籤指向哪裡,所在在開始時,通過掃描控制器的文字,
把標籤從指令中分享出來。如掃描文字,它組裝了指令的列表與把任何的標籤與指向列表的指標關聯的表格。
然後,彙編器通過為任何的指令新增上執行程式,而更新的指令的列表。

程式assemble是彙編器的主入口。它以控制器的文字和機器模型為實際引數,返回了指令序列,
它被儲存在模型中。程式assemble 呼叫 extract-labels 來從提供的控制器文字中,構建初始化
的指令列表和標籤表格。extract-labels的第二個實際引數是一個程式,它被呼叫為了處理這些結果:
這個程式使用update-insts!來生成指令執行程式和把它們插入到指令列表中,並且返回修改後的列表。

(define  (assemble   controller-text  machine)
    (extract-labels  controller-text 
           (lambda  (insts  labels)  
                      (update-insts!   insts  labels  machine)
                      insts))
)

extract-labels以一個列表text 為引數(控制器指令表示式的序列)和一個 receive程式為引數。
receive程式被呼叫時帶著兩個引數:1指令的資料結構insts的列表,任何一個insts都包含著
一個指令,2一個表格叫做labels,它從文字中關聯任何一個標籤和它在insts的列表中的位置。

(define  (extract-labels  text receive)
     (if   (null?  text)  
           (receive  '()  '())  
           (extract-labels  (cdr  text)  
                               (lambda  (insts  labels) 
                                    (let   ((next-insts  (car text))) 
                                           (if   (symbol?  next-inst) 
                                                 (receive  insts 
                                                               (cons  (make-label-entry  next-inst  insts) 
                                                                          labels)) 
                                                 (receive  (cons  (make-instruction  next-inst) 
                                                                          insts) 
                                                               labels))))))
)

extract-labels工作,通過順序性的掃描文字的元素,累加到insts 和labels中。如果
一個元素是一個符號(因此是一個標籤)一個合適的入口被新增到labels表格中。否則,
元素被累加到insts的列表中。

update-insts!修改了指令列表,它的初始化時僅包含了指令的文字,為了包括相應的
執行程式:

(define  (update-insts!  inst  labels  machine) 
 (let   ((pc  (get-register machine  'pc))
         (flag  (get-register  machine  'flag))
         (stack  (machine  'stack))
         (ops   (machine  'operations)))  
        (for-each  (lambda  (inst)  
                                        (set-instruction-execution-proc!   inst 
                                                 (make-execution-procedure   (instruction-text  list) 
                                                           labels  machine  pc flag  stack ops)))
                        insts
        )
 )
)

機器指令的資料結構簡化了資料對指令文字和它對應的執行程式。
執行程式不是可用的,當extract-labels組裝指令時,並且在稍後被
update-insts!新增時。

(define  (make-instruction  text) 
    (cons  text  '()))

(define  (instruction-text  inst)
   (car  inst)
)

(define  (instruction-execution-proc  inst)
    (cdr inst)
)

(define  (set-instruction-proc!  inst  proc) 
(set-cdr!   inst    proc))

這個指令的文字是不能被我們的模擬器使用的,但是能手工地進行除錯
(見練習5.16)

標籤表格的元素是如下的資料對:

(define  (make-label-entry  label-name  insts) 
 (cons  label-name  insts))

在表格中入口的查詢如下:

(define  (lookup-label  labels  label-name) 
(let  ((val  (assoc  label-name  labels))) 
       (if  val  (cdr val) 
      (error  "Undefined  label  --ASSEMBLE" label-name))))

練習5.8   如下的暫存器程式碼是混亂的,因為標籤被定義了很多次:

start
      (goto  (label  here))
here
      (assign  a  (const 3))
      (goto  (label  there))
here
      (assign  a  (const 4))
      (goto  (label  there))
there

在如上的模擬器中,當控制到達了there? 暫存器a 的值是什麼內容?  修改
extract-labels程式,當相同的標籤被用來
指定兩個不同的位置時,讓彙編器能報一個錯誤。