C语言简单子集编译器详细设计书(第一版)
来源:互联网 发布:linux vi编辑器使用 编辑:程序博客网 时间:2024/06/10 09:58
1. 预处理器
1.1) 去除注释
逐行读取源码文件xxx.c到缓存中,凡是遇到‘\\’,就将它和后续字符清空。将缓存字符串逐行写入文件xxx.c.tmp。
2. 词法语法分析器
2.1) 处理流程
按照《C语言简单子集编译器功能设计书》第二章基本语法,生成语法树。
2.2) 语法树结构
参照scmpstx.h中所定义的数据结构。
2.3) DEBUG
将生成的语法树写入文件xxx.c.stx。
2.4) 异常处理
遇到语法、词法错误时,统一报如下异常:
Syntax error at line @1. @2
其中参数@1为行号,参数@2为详细出错信息。
2.5) 内存管理
使用动态内存的函数必须在退出函数时释放内存。
全局作用域的动态内存(标志符表和语法树)使用如下配对函数进行申请和释放:
initialize_XXX() release_XXX()
3. 语义分析、汇编码生成器
在Windows下生成MASM汇编码,在Linux下生成AT&T汇编码。
[ MASM ]
3.1) 汇编码的组织
3.1.1) 头部
使用如下固定内容:
.MODEL SMALL
.STACK 4096h
INCLUDE PrintVal.mac
3.1.2) 数据部
使用如下模式:
.DATA
A DW 0
Name_A DB ‘A’,20H,20H
B DW 0
Name_B DB ‘B’,20H,20H
3.1.3) 代码部
.CODE
CALL Main
Sub1 PROC
…
Sub1 ENDP
…
Main PROC
…
Main ENDP
END Main
3.2) 汇编码生成流程
(1) 生成头部固定代码。
(2) 生成数据部。遍历IdTable,生成所有变量的定义代码。生成变量名字符串Name_XXX。
(3) 生成代码段。遍历语法树,生成除变量定义外的代码。
3.3) 函数定义的实现
3.3.1) 局部变量和全局变量都定义在数据段,不区分。
3.3.2) 所有寄存器进入函数时入栈,出函数时出栈。
3.3.3) 退出函数前,打印出函数名、函数内所有局部变量的名字和值。
方法:遍历IdTable,将所有变量的名字和值打印。
3.4) 赋值语句
3.4.1) 生成FORMULA代码。
3.4.2) FORMULA计算结果保存到ax寄存器。
3.4.3) MOV 变量名, ax。
3.5) if语句
3.5.1) 生成BOOL_FORMULA代码。
3.5.2) BOOL_FORMULA计算结果保存在ax寄存器。
3.5.3) 维护标记列表,为if语句末尾加一个标记SIGN_XXX,其中XXX为当前标记的序号。(标记代码在3.5.5之后生成,名字在3.5.3生成)。
3.5.4) 比较ax和0,相等,则跳转到标记SIGN_XXX,不等,则继续下条语句。
3.5.5) 生成大括号中的STATEMENT_LIST代码。
3.6) for语句
3.6.1) 如果存在第一赋值语句,生成代码。
3.6.2) 维护标记列表,为for语句BOOL_FORMULA之前加一个标记SIGN_XXX,其中XXX为当前标记的序号。
3.6.3) 生成BOOL_FORMULA代码。
3.6.4) BOOL_FORMULA计算结果保存在ax寄存器。
3.6.5) 维护标记列表,为for语句末尾加一个标记SIGN_YYY,其中YYY为当前标记的序号。(标记代码在3.6.9之后生成,名字在3.6.5生成)。
3.6.6) 比较ax和0,相等,则跳转到标记SIGN_YYY,不等,则继续下条语句。
3.6.7) 生成大括号中的STATEMENT_LIST代码。
3.6.8) 如果存在第二赋值语句,生成代码。
3.6.9) 生成无条件跳转代码,跳转到SIGN_XXX。
3.7) while语句
3.7.1) 维护标记列表,为while语句BOOL_FORMULA之前加一个标记SIGN_XXX,其中XXX为当前标记的序号。
3.7.2) 生成BOOL_FORMULA代码。
3.7.3) BOOL_FORMULA计算结果保存在ax寄存器。
3.7.4) 维护标记列表,为while语句末尾加一个标记SIGN_YYY,其中YYY为当前标记的序号。(标记代码在3.7.7之后生成,名字在3.7.4生成)。
3.7.5) 比较ax和0,相等,则跳转到标记SIGN_YYY,不等,则继续下条语句。
3.7.6) 生成大括号中的STATEMENT_LIST代码。
3.7.7) 生成无条件跳转代码,跳转到SIGN_XXX。
3.8) FORMULA
利用栈进行运算。
运算级别5: + -
运算级别6: * /
运算级别7: (
运算级别0: )
3.8.1) 算法描述
由于只支持双操作数的操作符,因此比较简单。基本原理:如果当前操作符的运算级别小于或等于上个操作符的运算级别,则从栈中退出3个元素(2个操作数和1个操作符),计算出结果,如果算式还没结束,结果压栈,继续分析下个操作符。否则从栈中依次退出2个元素(1个操作符和1个操作数),与当前结果累计计算,直到栈被清空。注意记录栈中元素个数,防止越界。另外,如果当前操作符是“)”,退栈时需要将“(”一并退出,即一次退出4个元素。例外1:“( )”之间可能是单个的数字,这时退栈只2个元素,即“(”和这个数字。例外2:如果上个操作符是“(”,当前不退栈,必须等到下个操作符比当前操作符级别低再退栈计算。
例:
A+B*(C+D)
栈 操作
A ----
A+ +
A+B +
A+B* *
A+B*( (
A+B*(C (
A+B*(C+ +
A+B*(C+D +
A+B* C+D=R1
A+ B*R1=R2
---- A+R2=R3
3.9) BOOL_FORMULA
运算级别0:)
运算级别1:||
运算级别2:&&
运算级别3:== !=
运算级别4:> >= < <=
运算级别5:+ -
运算级别6:* /
运算级别7:(
3.9.1) 算法描述
具体算法与3.8.1)基本相同。
注:BOOL运算结果为真,则结果为数值1,假则为0。
3.9.2) 或运算代码模板:
; || Begin
CMP AX, 0 ;第一操作数
JNE SIGN_XXX
CMP BX, 0 ;第二操作数
JNE SIGN_XXX
MOV AX, 0
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 1
SIGN_YYY:
; || End
3.9.3) 与运算代码模板
; && Begin
CMP AX, 0 ;第一操作数
JE SIGN_XXX
CMP BX, 0 ;第二操作数
JE SIGN_XXX
MOV AX, 1
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 0
SIGN_YYY:
; && End
3.9.4) ==运算代码模板
; == Begin
CMP AX, BX ;第一操作数, 第二操作数
JE SIGN_XXX
MOV AX, 0
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 1
SIGN_YYY:
; == End
3.9.5) !=运算代码模板
; != Begin
CMP AX, BX ;第一操作数, 第二操作数
JE SIGN_XXX
MOV AX, 1
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 0
SIGN_YYY:
; != End
3.9.6) >运算代码模板
; > Begin
CMP AX, BX ;第一操作数, 第二操作数
JA SIGN_XXX
MOV AX, 0
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 1
SIGN_YYY:
; > End
3.9.7) >=运算代码模板
; >= Begin
CMP AX, BX ;第一操作数, 第二操作数
JGE SIGN_XXX
MOV AX, 0
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 1
SIGN_YYY:
; >= End
3.9.8) <运算代码模板
; < Begin
CMP AX, BX ;第一操作数, 第二操作数
JGE SIGN_XXX
MOV AX, 1
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 0
SIGN_YYY:
; < End
3.9.9) <=运算代码模板
; <= Begin
CMP AX, BX ;第一操作数, 第二操作数
JA SIGN_XXX
MOV AX, 1
JMP SIGN_YYY
SIGN_XXX:
MOV AX, 0
SIGN_YYY:
; <= End
3.10) 函数调用
代码模板
CALL XXXX ; 函数名
[ AT&T ]
未稿
4. 可执行文件生成器
4.1) 生成方法
在Windows下使用masm.exe(Microsoft (R) Macro Assembler Version 5.00)和link.exe(Microsoft (R) Overlay Linker Version 3.60)编译为可执行文件。在Linux下使用as编译。
5. 附录
5.1) MASM汇编码
.MODEL SMALL
.STACK 200h
CR equ 13
LF equ 10
TERMINATOR equ '$'
.DATA
Message DB 'Hello, World !',0AH,0DH,'$'
.CODE
Main PROC
;lea ax, Message
;mov ds, ax
mov ax, DGROUP
mov ds, ax
lea ax, Message
mov dx, ax
mov ah, 9
int 21h
mov ax, 4c00h
int 21h
Main ENDP
END main
5.2) AT&T汇编码
#hello.s
.data # 数据段声明
msg : .string "Hello, world!\\n" # 要输出的字符串
len = . - msg # 字串长度
.text # 代码段声明
.global _start # 指定入口函数
_start: # 在屏幕上显示一个字符串
movl $len, %edx # 参数三:字符串长度
movl $msg, %ecx # 参数二:要显示的字符串
movl $1, %ebx # 参数一:文件描述符(stdout)
movl $4, %eax # 系统调用号(sys_write)
int $0x80 # 调用内核功能
# 退出程序
movl $0,%ebx # 参数一:退出代码
movl $1,%eax # 系统调用号(sys_exit)
int $0x80 # 调用内核功能
- C语言简单子集编译器详细设计书(第一版)
- C语言简单子集编译器源码(第一版)
- C语言简单子集编译器功能设计书(第一版)
- C语言简单子集编译器(第一版)发布包
- 一个简单的C语言编译器
- c语言简单设计
- C语言程序(第一版) 0
- C语言程序(第一版) 1
- 车牌识别C语言源码 第一版
- 车牌识别C语言源码 第一版
- C语言递归求子集
- 大一 PROJECT REPORT#1 设计一个基于C的简单语言,并为其写编译器,汇编器,虚拟机
- 简单C语言编译器之需求说明【待完善】
- 简单C语言编译器之SLR分析【待完善】
- 简单C语言编译器之头文件【待完善】
- 【c语言基础必备】gcc编译器的简单编译
- 小白说编译原理-9-最简单minus-c语言编译器
- C语言编译器
- 【JAVA】JNI技术
- dir命令(上)
- hibernate的环境搭建以及实例
- NSSTring
- oracle 调用Java 工作实例
- C语言简单子集编译器详细设计书(第一版)
- 给Android应用开发者的十个建议
- dir命令(下)
- The reference to entity "characterEncoding" must end with the ';' delimiter
- C\C++ 获取当前路径
- 用JAVA画一颗树(Applet)
- error debug
- 10个方法助你少奋斗10年
- 字符编码笔记:ASCII,Unicode和UTF-8