用Scheme写一个Scheme编译器(二):立即数
在上一篇博客中,我介绍了这个Scheme编译器的主要框架,以及运行它编译的程序的运行时系统。这篇博客中,我们将要尝试去表示一系列在Scheme中的值,也就是立即数。
首先我们要解释什么是立即数,立即数就是直接存储在一个字节或几个字节中的值,包括定点数,布尔值,字符,空表等。这些不同类型的立即数在我们人看来是完全不同的,但是在计算机的表示之中,他们都是一系列相同的0/1串,所以如何去区分他们呢?
我们通过为不同类型的值设立不同的掩码和标识来区分这些不同的值:
对于定点数,我们使用最底两位为掩码,标识为0x00,
布尔值,我们使用一个字节来表示它们,分别把#f定义为0x6f,#t定义为0x2f,
空表我们使用0x3f来标识,
字符我们使用两个字节来存放它们的值,一个字节是掩码,一个字节指明该字符的值,字符的掩码是0xff,字符的标识为0x0f。
1 添加与立即数相关的函数
定义完这些以后,我们需要添加这些定义到我们的Scheme编译程序之中:
(define fxshift 2) (define fxmask #x03) (define fxtag #x00) (define bool_t #x2f) (define bool_f #x6f) (define list_nil #x3f) (define charshift 8) (define charmask #xff) (define chartag #x0f)
然后是立即数的判别和求值:
(define (immediate-value? expr) (or (fixnum? expr) (boolean? expr) (null? expr) (char? expr))) (define (immediate-value-rep expr) (cond ((fixnum? expr) (arithmetic-shift expr fxshift)) ((boolean? expr) (if expr bool_t bool_f)) ((null? expr) list_nil) ((char? expr) (bitwise-ior (arithmetic-shift (char->integer expr) charshift) chartag))))
对于定点数,我们只需要对它原始值进行两位的算数移位,对于字符,除了对它进行算术移位外,我们还需要将其与我们的标识进行或运算。
2 修改编译主程序
以上工作做好之后,我们要先对我们开始时的编译程序做一个简单的调整:
1 (define (compile-program expr) 2 3 (unless (immediate-value? expr) 4 5 (error 'compile-program "immediate-value?")) 6 7 (emit " .text") 8 9 (emit " .global _scheme_entry") 10 11 (emit " .def _scheme_entry; .scl 2; .type 32; .endef") 12 13 (emit "_scheme_entry:") 14 15 (emit "LFB0:") 16 17 (emit " .cfi_startproc") 18 19 (emit " pushl %ebp") 20 21 (emit " .cfi_def_cfa_offset 8") 22 23 (emit " .cfi_offset 5, -8") 24 25 (emit " movl %esp, %ebp") 26 27 (emit " .cfi_def_cfa_register 5") 28 29 (emit " movl $~a, %eax" (immediate-value-rep expr)) 30 31 (emit " popl %ebp") 32 33 (emit " .cfi_restore 5") 34 35 (emit " .cfi_def_cfa 4, 4") 36 37 (emit " ret") 38 39 (emit " .cfi_endproc") 40 41 (emit "LFE0:"))
在原先是 (emit " movl $~a, %eax" x)的地方,我们将其改为 (emit " movl $~a, %eax" (immediate-value-rep expr)),immediate-value-rep就是我们对立即数进行编码的地方
3 修改运行时程序
然后,我们还需要对C的运行时程序进行调整,让其能够进行解码工作:
#include<stdio.h> #define fxshift 2 #define fxmask 0x03 #define fxtag 0x00 #define bool_f 0x2f #define bool_t 0x6f #define list_nil 0x3f #define word_size 4 #define charshift 8 #define charmask 0x0f #define chartag 0x0f int scheme_entry(); int main(int argc, char** argv) { int val = scheme_entry(); printf("%d",val); if((val&fxmask) == fxtag) { printf("%d", val >> fxshift); } else if(val == bool_f) { printf("#f"); } else if(val == bool_t) { printf("#t"); } else if(val == list_nil) { printf("'()"); } else if((val & charmask) == chartag) { printf("%c",val>>charshift); } return 0; }
这就是今天的内容,我们获得了一个能够编译立即数的程序!