【编译原理】TINY编译器学习(一)

来源:互联网 发布:iis中将端口改为81 编辑:程序博客网 时间:2024/04/30 07:51
《编译原理及实践》中附带的TINY编译器,代码仅有几千行,
这点代码就实现了一个完整的编译器,及对应的目标代码运行程序,接下来会用一段时间研究下这个代码。

1. 源代码来源
Google “loucomp” 下载即可。

2. 源码结构
globals.h    main.c
util.h          util.c
scan.h        scan.c
parse.h      parse.c
symtab.h    symtab.c
analyze.h   analyze.c
code.h        code.c
cgen.h        cgen.c
其中scan、parse、analyze和cgen文件分别对应于代码扫描程序、分析程序、语义分析程序和代码生成器各阶段。

3. 关键函数
syntaxTree = parse( );
对应于词法分析及语法树生成。
buildSymtab(syntaxTree);
建立符号表
typeCheck(syntaxTree);
类型检测
codeGen(syntaxTree, codefile);
目标代码生成

同时工程采用了三个编译宏,可以分阶段进行控制编译器输出
NOPARSE      创建只扫描的编译器
NOANALYZE  创建只分析和扫描的编译器
NOCODE       创建执行语义分析但不生成代码的编译器


4. 使用方法
编译此工程生成tiny.exe程序,可以通过 tiny.ext sample.tny可以对源代码sample.tny进行编译,生成sample.tm可执行文件。
tm文件只有在特定的虚拟机上才能运行,对应的虚拟机是tm.exe。
编译tm.c可以生成tm.exe程序,运行tm.exe sample.tm就可以执行最终的程序。

5. 目标代码直观印象
5.1 待翻译的源代码:
a[index] = 6
5.2 普通的汇编代码:
MOV R0, index ;; value of index -> R0
MUL R0, 2       ;; double value in R0,乘以2是因为数组类型占两个字节
MOV R1, &a    ;; address of a -> R1
ADD R1, R0     ;; add R0 to R1
MOV *R1, 6    ;; constant 6 -> address in R1
5.3 TINY目标代码:
LDC 1, 0 ( 0 )  ;; load 0 into reg 1
LD 0, 68 ( 1 )  ;; load val at (68+R1) into R0; 假设index 在存储器地址68中,注意这里load的是地址里的值,不是地址,注意和LDA的区别。
LDC 1, 2 ( 0 )  ;; load 2 into reg 1
MUL 0, 1, 0     ;; put R1 * R0 into R0
LDC 1, 0 ( 0 )  ;; load 0 into reg 1
LDA 1, 20 ( 1 ) ;; load 20 + R1 into R0; 假设a在存储器地址20,注意这里load的是地址,不是地址里存的值,注意和LD的区别。
ADD 0, 1, 0     ;; put R1 + R0 into R0
LDC 1, 6 ( 0 )  ;; load 6 into reg 1
ST 1, 0 ( 0 )    ;; 将R1中存的6放到最终数组的地址中。
注意:LDC是“装入常量”,LD是“由存储器装入”,而LDA是“装入地址”

6. sample.tny
{ Sample program
  in TINY language -
  computes factorial
}
read x; { input an integer }
if 0 < x then { don't compute if x <= 0 }
  fact := 1;
  repeat
    fact := fact * x;
    x := x - 1
  until x = 0;
  write fact  { output factorial of x }
end
这段程序使用TINY语言实现了一个阶乘计算程序,其中{}用于注释,repeat-until类似于pascal语言。

7. 关于tm程序
大致浏览了下tm.c的源代码,其实现就是将TINY目标语言翻译成对应的c语言实现,相当于使用c语言实现了tm虚拟机。
0 0
原创粉丝点击