0
点赞
收藏
分享

微信扫一扫

ucc编译器(语法解析)

四月Ren间 2022-11-23 阅读 53



 

    做完词法分析,后面紧接着就是语法分析。对于一个编程语言而言,语法解析才是语言和语言之间最大的区别。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-> ......,就这样一直递归调用下去。这部分本来就是被允许的。等到解析完成后,一个抽象语法树其实就可以被构建出来了。

 

    一般的高校,作业实践的部分基本上到这就结束了。但是对编译器来说,现在只是完成了前端解析而已。

 

 

举报

相关推荐

0 条评论