写这个编译器的目的,是为了完成编译原理课上老师布置的大作业,实际上该大作业并不是真的实现一个编译器,而我选择硬刚,是为了完成我的小愿望--手写内核,编译器和CPU。我花了整个上半学期,写完了WeiOS,为了让它支持更多的用户态程序,甚至是基本的程序开发,必须给它量身打造一个编译器。于是这个编译器被提上日程。
因为我要复习考研和专业课过多,我打消了手写词法分析和语法分析的念头,转而使用FLEX和YACC,等到有时间再完成手工的版本--我认为也不是很难,如果用递归下降的话。
全部代码都在该处 https://github.com/mynamevhinf/CMinusCompiler
词法分析
因为是精简的C语言,所以只支持基本的符号(Token),像”++”, “--”和位操作都不予考虑。另外,只支持int和void类型。于是便构成了以下符号集:
number [1-9][0-9]*
letter [a-zA-Z]
Identifier {letter}({letter}|{number})*
Newline \n
whitespace [ \t\r]+
保留字:
If else while int void return
运算和界限符号:
<= >= != == + - * / < > ; , ( ) { } [ ]
主体函数是getToken(),这个函数封装了FLEX原生的yylex函数。而yyparser也将直接调用该函数。它的主要工作是在开始第一次词的检索前,初始化相关变量。然后在每次被调用的时候,返回符号的类型给yyparser,并且把构成符号的字符串临时保存在tokenString字符数组中。所以这函数相当于什么事情都没有干。
另外注意的是注释的两个符号,我直接在词法分析处理注释了。行注释是”//”,利用FLEX自带的input()函数(如果有的话,没有就写一个)一直读到’\n’出现。然后就是段注释符”/*”和”*/”,相似的做法。
语法分析
以下是BNF格式的语法规则:
Program -> declaration_list
declaration_list -> declaration_list declaration | declaration
declaration -> var_declaration | func_declaration
type_specifier -> INT | VOID
var_declaration -> type_specifier VARIABLE ; | type_specifier VARIABLE [ NUM ] ;
func_declaration -> type_specifier VARIABLE ( params ) compound_stmt
Params -> params_list | VOID
params_list -> params_list , param | param
Param -> type_specifier VARIABLE | type_specifier VARIABLE [ ]
compound_stmt -> { local_declarations stmt_list }
local_declarations -> local_declarations var_declaration | /* empty */
stmt_list ->stmt_list stmt | stmt
Stmt -> expr_stmt | if_stmt | return_stmt | compound_stmt | iteration_stmt
expr_stmt -> expr ; | ;
if_stmt ->IF ( expr ) stmt | IF ( expr ) stmt ELSE stmt
iteration_stmt -> WHILE ( expr ) stmt
return_stmt ->RET ; | RET expr ;
Expr -> var = expr | simple_expr
Var -> VARIABLE | VARIABLE [ NUM ]
Call -> VARIABLE ( args )
Args -> arg_list | /* empty */
arg_list -> arg_list , expr | expr
simple_expr -> NUM | var | call | ( expr ) | - simple_expr
| simple_expr + simple_expr
| simple_expr - simple_expr
| simple_expr * simple_expr
| simple_expr / simple_expr
| simple_expr < simple_expr
| simple_expr > simple_expr
| simple_expr >=simple_expr
| simple_expr <= simple_expr
| simple_expr != simple_expr
| simple_expr == simple_expr
我用globl.h中的TreeNode结构来保存语法树中的每一个节点。而一些为空的转换,我打算还是用一个该结构来表示,但是类型标记为None(也许有点浪费内存).
我实现的C-还算是个比较完整的程序语言,所以很有必要生成AST(抽象语法树),那么语法树中共有几种类型的节点呢?按理说应每种语法规则对应一种类型,例如参数列表,声明语句,普通语句和表达式等都对应一个节点类型,详细可以参见NodeType枚举类型。Parser.c文件是处理与语法树相关的函数,目前来说当中几个函数还没写清楚,