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程序,当相同的标签被用来
指定两个不同的位置时,让汇编器能报一个错误。