zl程序教程

您现在的位置是:首页 >  工具

当前栏目

ucc编译器(语法解析)

编译器 解析 语法
2023-09-27 14:27:10 时间

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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-> ......,就这样一直递归调用下去。这部分本来就是被允许的。等到解析完成后,一个抽象语法树其实就可以被构建出来了。

 

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