在上一节中,我们实现了比较运算符的计算,现在,我们可以容易地实现IF条件语句。
首先我们看IF语句的SysY语言的定义:
语句: Stmt → LVal ‘=’ Exp ‘;’ | ‘if’ '( Cond ‘)’ Stmt [ ‘else’ Stmt ]
修改词法分析
首先,我们要增加新的单词类型,表示"{","}","(",")",“if”,"else"六种符号。
// 单词类型
enum
{
T_EOF,
T_ADD, T_SUB, T_MUL, T_DIV, T_MOD, T_EQ, T_NE, T_LT, T_GT, T_LE, T_GE,
T_INT, T_SEM, T_PRINT, T_EQU, T_IDENT, T_KEYINT, T_IF, T_ELSE,
T_LBRACE, T_RBRACE, T_LPAREN, T_RPAREN
};
然后,我们要修改扫描函数,使之能识别到新的单词类型。
// 加入"if"和"else"的匹配
int match_keyword(char *s)
{
switch (*s)
{
case 'e': if(!strcmp(s, "else")) return (T_ELSE);
break;
case 'i': if(!strcmp(s, "if")) return (T_IF);
if(!strcmp(s, "int")) return (T_KEYINT);
break;
case 'p': if(!strcmp(s, "print")) return (T_PRINT);
break;
}
return 0;
}
// scan()函数中加入解析"{","}","(",")"
case '{': t->token = T_LBRACE; break;
case '}': t->token = T_RBRACE; break;
case '(': t->token = T_LPAREN; break;
case ')': t->token = T_RPAREN; break;
同时添加新的单词匹配函数:
// 检查当前单词是否为"{",并获取下一个单词
void lbrace()
{
match(T_LBRACE, "{");
}
// 检查当前单词是否为"}",并获取下一个单词
void rbrace()
{
match(T_RBRACE, "}");
}
// 检查当前单词是否为"(",并获取下一个单词
void lparen()
{
match(T_LPAREN, "(");
}
// 检查当前单词是否为")",并获取下一个单词
void rparen()
{
match(T_RPAREN, ")");
}
修改语法分析
为了后面的代码更容易实现,我暂时强制规定语法:
编译单元: CompUnit → Block
语句块: Block → ‘{’ { BlockItem } ‘}’
语句块项: BlockItem → Stmt
这些规则的目的是强制代码以{Stmt}
的语句块形式书写,目的是为了避免复合语句的else的歧义性。由于这是一个正在进行的项目,因此我会新增一些规则,同时在后面会撤消这些规则并将其重构。后面的章节中,我会使{Stmt}
为可选。
分析复合语句块
将原来分析句子的statements()
函数改为分析语句块的Block_statement()
函数
// 分析语句块并返回其AST
struct ASTnode *Block_statement()
{
struct ASTnode *left = NULL;
struct ASTnode *tree;
// 匹配"{"
lbrace();
while (1)
{
switch (Token.token)
{
case T_PRINT: tree = print_statement(); break;
case T_KEYINT: var_declaration(); tree = NULL; break;
case T_IDENT: tree = assignment_statement(); break;
case T_IF: tree = if_statement(); break;
case T_RBRACE: rbrace(); return (left); //遇到"}"时,返回AST树
default: fprintf(stderr, "Syntax error, token:%d on line %d\n", Token.token, Line);
exit(1);
}
// 对于每个新树,如果左子树为空,则将其保存在左子树中,
// 否则将左子树和新树合并
if (tree)
{
if (left == NULL) left = tree;
else left = mkastnode(A_GLUE