ucc编译器(语法解析)
Posted 费晓行
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ucc编译器(语法解析)相关的知识,希望对你有一定的参考价值。
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
做完词法分析,后面紧接着就是语法分析。对于一个编程语言而言,语法解析才是语言和语言之间最大的区别。c语言有自己的语法,cpp也有cpp的语法,所以我们说学习一门新的语言,其实主要工作就是学习它的语法。
语法分析有自顶向下和自底向上两种。对于编译器手动实现来说,自顶向下其实是比较好处理的,因为大部分工作都可以用递归的方法来处理,只要不出现左递归语法的情况。
此外,目前而言,实现一门新的语言,已经不再需要自己从头到尾编写全部代码。完全可以用bison、yacc这样的工具帮助我们来完成代码的编写,这样也是可以的。毕竟,所谓语法分析,最终都是为了构建一个语法树。
1、c语言的主要三种语法形式
a,declaration语法
int a;
char b;
float c;
b,expression语法
a = 1;
b = 2;
c = a + b;
c,statement语法
{
// other code
if(expression)
{
// ...
}
else
{
// ...
}
}
2、语法解析文件
decl.c
https://github.com/nobled/ucc/blob/master/ucl/decl.c
expr.c
https://github.com/nobled/ucc/blob/master/ucl/expr.c
stmt.c
https://github.com/nobled/ucc/blob/master/ucl/stmt.c
3、语法树打印
dumpast.c
https://github.com/nobled/ucc/blob/master/ucl/dumpast.c
入口函数是DumpTranslationUnit
void DumpTranslationUnit(AstTranslationUnit transUnit)
{
AstNode p;
ASTFile = CreateOutput(Input.filename, ".ast");
p = transUnit->extDecls;
while (p)
{
if (p->kind == NK_Function)
{
DumpFunction((AstFunction)p);
}
p = p->next;
}
fclose(ASTFile);
}
相关数据结构,
struct astStatement
{
AST_STATEMENT_COMMON
};
typedef struct astLoopStatement
{
AST_LOOP_STATEMENT_COMMON
} *AstLoopStatement;
typedef struct astExpressionStatement
{
AST_STATEMENT_COMMON
AstExpression expr;
} *AstExpressionStatement;
typedef struct astLabelStatement
{
AST_STATEMENT_COMMON
char *id;
AstStatement stmt;
Label label;
} *AstLabelStatement;
typedef struct astCaseStatement
{
AST_STATEMENT_COMMON
AstExpression expr;
AstStatement stmt;
struct astCaseStatement *nextCase;
BBlock respBB;
} *AstCaseStatement;
typedef struct astDefaultStatement
{
AST_STATEMENT_COMMON
AstStatement stmt;
BBlock respBB;
} *AstDefaultStatement;
4、不失一般性,我们以分析stmt入手
4.1 stmt分析总入口
static AstStatement ParseStatement(void)
{
switch (CurrentToken)
{
case TK_ID:
return ParseLabelStatement();
case TK_CASE:
return ParseCaseStatement();
case TK_DEFAULT:
return ParseDefaultStatement();
case TK_IF:
return ParseIfStatement();
case TK_SWITCH:
return ParseSwitchStatement();
case TK_WHILE:
return ParseWhileStatement();
case TK_DO:
return ParseDoStatement();
case TK_FOR:
return ParseForStatement();
case TK_GOTO:
return ParseGotoStatement();
case TK_CONTINUE:
return ParseContinueStatement();
case TK_BREAK:
return ParseBreakStatement();
case TK_RETURN:
return ParseReturnStatement();
case TK_LBRACE:
return ParseCompoundStatement();
default:
return ParseExpressionStatement();
}
}
4.2 if statement解析
/**
* if-statement:
* if ( expression ) statement
* if ( epxression ) statement else statement
*/
static AstStatement ParseIfStatement(void)
{
AstIfStatement ifStmt;
CREATE_AST_NODE(ifStmt, IfStatement);
NEXT_TOKEN;
Expect(TK_LPAREN);
ifStmt->expr = ParseExpression();
Expect(TK_RPAREN);
ifStmt->thenStmt = ParseStatement();
if (CurrentToken == TK_ELSE)
{
NEXT_TOKEN;
ifStmt->elseStmt = ParseStatement();
}
return (AstStatement)ifStmt;
}
4.3 for statement解析
/**
* for-statement:
* for ( [expression] ; [expression] ; [expression] ) statement
*/
static AstStatement ParseForStatement()
{
AstForStatement forStmt;
CREATE_AST_NODE(forStmt, ForStatement);
NEXT_TOKEN;
Expect(TK_LPAREN);
if (CurrentToken != TK_SEMICOLON)
{
forStmt->initExpr = ParseExpression();
}
Expect(TK_SEMICOLON);
if (CurrentToken != TK_SEMICOLON)
{
forStmt->expr = ParseExpression();
}
Expect(TK_SEMICOLON);
if (CurrentToken != TK_RPAREN)
{
forStmt->incrExpr = ParseExpression();
}
Expect(TK_RPAREN);
forStmt->stmt = ParseStatement();
return (AstStatement)forStmt;
}
4.4 switch statement解析
/**
* switch-statement:
* switch ( expression ) statement
*/
static AstStatement ParseSwitchStatement(void)
{
AstSwitchStatement swtchStmt;
CREATE_AST_NODE(swtchStmt, SwitchStatement);
NEXT_TOKEN;
Expect(TK_LPAREN);
swtchStmt->expr = ParseExpression();
Expect(TK_RPAREN);
swtchStmt->stmt = ParseStatement();
return (AstStatement)swtchStmt;
}
4.5 break statement解析
/**
* break-statement:
* break ;
*/
static AstStatement ParseBreakStatement(void)
{
AstBreakStatement brkStmt;
CREATE_AST_NODE(brkStmt, BreakStatement);
NEXT_TOKEN;
Expect(TK_SEMICOLON);
return (AstStatement)brkStmt;
}
这部分可能和大家理解的不一样,case、break、continue、return、goto其实都是作为独立的一个statement处理的。
4.6 和declaration有连接的一个statement,
/**
* compound-statement:
* { [declaration-list] [statement-list] }
* declaration-list:
* declaration
* declaration-list declaration
* statement-list:
* statement
* statement-list statement
*/
AstStatement ParseCompoundStatement(void)
{
AstCompoundStatement compStmt;
AstNode *tail;
Level++;
CREATE_AST_NODE(compStmt, CompoundStatement);
NEXT_TOKEN;
tail = &compStmt->decls;
while (CurrentTokenIn(FIRST_Declaration))
{
if (CurrentToken == TK_ID && ! IsTypeName(CurrentToken))
break;
*tail = (AstNode)ParseDeclaration();
tail = &(*tail)->next;
}
tail = &compStmt->stmts;
while (CurrentToken != TK_RBRACE && CurrentToken != TK_END)
{
*tail = (AstNode)ParseStatement();
tail = &(*tail)->next;
if (CurrentToken == TK_RBRACE)
break;
SkipTo(FIRST_Statement, "the beginning of a statement");
}
Expect(TK_RBRACE);
PostCheckTypedef();
Level--;
return (AstStatement)compStmt;
}
这个statement叫compound statement,可以看成是一个大杂烩statment。它最大的一个特点,也就是区别于其他statement的地方,它实现了和declaration之间的衔接,这是很重要的。
4.7 和expression之间的衔接
/**
* expression-statement:
* [expression] ;
*/
static AstStatement ParseExpressionStatement(void)
{
AstExpressionStatement exprStmt;
CREATE_AST_NODE(exprStmt, ExpressionStatement);
if (CurrentToken != TK_SEMICOLON)
{
exprStmt->expr = ParseExpression();
}
Expect(TK_SEMICOLON);
return (AstStatement)exprStmt;
}
如果什么statement都不是,那么它只能是expression-statement了。也就是说,这个时候编译器就要调用ParseExpression函数做进一步的解析了。
4.8 总结
当然不管是哪一种statement,目的都是为了要构建一个abstract syntax tree,也就是抽象语法树。这个过程其实可能遇到不断地嵌套处理地,比如statement -> ifstatement -> statement -> forstatement-> ......,就这样一直递归调用下去。这部分本来就是被允许的。等到解析完成后,一个抽象语法树其实就可以被构建出来了。
一般的高校,作业实践的部分基本上到这就结束了。但是对编译器来说,现在只是完成了前端解析而已。
以上是关于ucc编译器(语法解析)的主要内容,如果未能解决你的问题,请参考以下文章