c语言可以编译ce工具,实现简易的C语言编译器(part 7)

紧接着上一部分抽象语法树的内容。在这一部分,我们将利用这些定义好的节点(砖块)和抽象语法描述(水泥)搭建起完整的抽象语法树。

同词法分析实现的方式一样,我们首先定义语法分析的类:

class Parser(object):

def __init__(self, lexer):

self.lexer = lexer

# set current token to the first token taken from the input

self.current_token = self.lexer.get_next_token()

def eat(self, token_type):

# compare the current token type with the passed token type

# otherwise raise an exception.

if self.current_token.type == token_type:

self.current_token = self.lexer.get_next_token()

else:

raise Exception(f"token {token_type} is undesired in function")

通过eat这个函数,可能你已经大概猜到了我们将要实现的语法分析的原理:一个一个的获取token进行比较,要么选择吃掉它,然后获取下一个token;要么抛出错误,终止程序。这种方式也符合我们分析代码的过程,比如括号必须成对出现。通过设置期望的token,从而可以按照语法规则进行token的分析和抽象语法树的搭建。在抛出异常中,我们使用了Python语言的一个功能:F字符串。有兴趣的可以参考Python的官方指南,简单并且强大着。

其实,有了前面归纳的语法描述形式,我们完全可以写出一一对应的函数,使用上一部分定义好的节点来存储这些token,然后按照函数之间的逻辑关系再将这些节点连接起来形成新的节点,最终得到整个的抽象语法树。比如,在前面介绍四则运算表达式的描述的时候,我们已经给出了如何用节点存储这些token。接下来,我们按照声明、语句和表达式三个部分详细阐述抽象语法树构建的原理。

5.1 声明

为了使描述简明扼要,抓住问题核心,我们先暂时省略结构体和枚举类型,以及带有初始值的变量声明的描述,得到如下简化之后的声明的描述:

type_specifier : INT

| CHAR

declarator : direct_declarator

| * declarator

direct_declarator : ID

| direct_declarator ( parameter_type_list )

| direct_declarator ( )

| direct_declarator [ const_expression ]

| direct_declarator [ ]

declarator_list : declarator

| declarator_list , declarator

declaration : type_specifier declarator_list ;

在Parser类里面,我们以函数的形式定义上面出现的所有描述:

def type_specifier(self):

"""type_spec : INT

| CHAR

"""

token = self.current_token

if token.type in (INT, CHAR):

self.eat(token.type)

return BaseType(token.value)

else:

# no such type

raise Exception(f"undefined type.")

def declarator(self):

"""declarator : * direct_declarator

| direct_declarator

"""

if self.current_token.type == MUL:

self.eat(MUL)

node = self.declarator()

node.set_type(PointerType())

return node

return self.direct_declarator()

def direct_declarator(self):

"""direct_declarator : ID

| direct_declarator [ const_expr ]

| direct_declarator [ ]

| direct_declarator ( )

| direct_declarator ( parameter_type_list )

"""

node = Declaration(self.variable())

if self.current_token.type == LPAREN:

# function type

self.eat(LPAREN)

node.add_type(FunctionType(self.parameter_type_list()))

self.eat(RPAREN)

elif self.current_token.type == LBRACKET:

# array type

self.eat(LBRACKET)

node.add_type(ArrayType(self.const_expression()))

self.eat(RBRACKET)

return node

def variable(self):

"""variable : ID"""

name = self.current_token.value

self.eat(ID)

return name

def declarator_list(self):

"""declarator_list : declarator

| declarator_list, declarator

"""

nodes = VariableList()

while self.current_token.type != SEMI:

child_node = self.declarator()

nodes.add(child_node)

if self.current_token.type == SEMI:

break

self.eat(COMMA)

return nodes

def declaration(self):

"""declarations : type_specifier declarator_list SEMI

"""

nodes = DeclarationList()

type_node = self.type_specifier()

for decl_node in self.declarator_list().nodes:

decl_node.set_type(type_node)

nodes.add(decl_node)

self.eat(SEMI)

return nodes

这里用到的两个list类其实就是前面介绍的NodeList的变体,如下:

class DeclarationList(NodeList):

"""A declaration list, derived for identification"""

pass

class VariableList(NodeList):

"""A variable list, derived for identification"""

pass

先不去细究parameter_type_list和const_expression的实现方式,单从简化过的声明的描述上来看,我们是完全按照描述照葫芦画瓢来实现对应的函数,这个过程中用到了前面定义的节点,以及如何组织它们。同样地,下面介绍地语句和表达式,其实也是类似的实现形式。

5.2 语句

还是从语句的描述入手:

statement : expression_statement

| jump_statement

| iteration_statement

| selection_statement

| compound_statement

| empty_statement

compound_statement : { declaration_list }

| { declaration_list statement_list }

selection_statement : IF ( expression ) statement

| IF ( expression ) statement ELSE statement

...

对应的实现如下:

def statement(self):

"""statement : expression_statement

| jump_statement

| iteration_statement

| selection_statement

| compound_statement

| empty_statement

"""

if self.current_token.type == BEGIN:

node = self.compound_statement()

elif self.current_token.type == IF:

node = self.selection_statement()

elif self.current_token.type in (RETURN, BREAK, CONTINUE):

node = self.jump_statement()

elif self.current_token.type in (FOR, WHILE):

node = self.iteration_statement()

elif self.current_token.type == SEMI:

node = None

else:

node = self.expression_statement()

return node

def compound_statement(self):

"""compound_statement : { declaration_list }

| { declaration_list statement_list }

"""

self.eat(BEGIN)

declaration_nodes = DeclarationList()

statement_nodes = StatementsList()

while True:

while self.current_token.type in (INT, CHAR):

for child in self.declaration().nodes:

declaration_nodes.add(child)

if self.current_token.type == END:

break

statement_nodes.add(self.statement())

self.eat(END)

node = CompoundStatement(declaration_nodes, statement_nodes)

return node

def selection_statement(self):

"""if_statement : IF ( expression ) statement { ELSE statement }

"""

self.eat(IF)

self.eat(LPAREN)

expr_node = self.expression()

self.eat(RPAREN)

then_node = self.statement()

else_node = None

if self.current_token.type == ELSE:

self.eat(ELSE)

else_node = self.statement()

return IfStatement(expr_node, then_node, else_node)

def iteration_statement(self):

"""for_statement : FOR ( expression_statement expression_statement expression ) statement

"""

...

def jump_statement(self):

"""jump_statement : CONTINUE ;

| BREAK ;

| RETURN expression ;

"""

...

def expression_statement(self):

"""expression_statement: expression ;

"""

...

因为函数的定义和实现完全按照描述来做的,没有什么难度。剩下的部分亦可按照这样的方式进行,这里就不一一介绍了。

5.3 表达式

前面介绍表达式的时候,已经给出了四则运算表达式的实现,这里主要分析逻辑和赋值运算。首先,重温一下它们的表述方式:

relation_expression : additive_expression

| relation_expression < additive_expression

| relation_expression <= additive_expression

| relation_expression > additive_expression

| relation_expression >= additive_expression

equality_expression : relation_expression

| equality_expression == relation_expression

| equality_expression != relation_expression

expression : equality_expression

| equality_expression = expression

它们的实现亦可按照这样的表示方法进行:

def relation_expression(self):

"""relation_expression : additive_expression

| relation_expression (< | >| <=| >=) additive_expression

"""

node = self.additive_expression()

while self.current_token.type in (RELT, REGT, RELEQ, REGEQ):

token = self.current_token

self.eat(token.type)

node = BinOp(left=node, op=token.value, right=self.additive_expression())

return node

def equality_expression(self):

"""equality_expression : relation_expression

| equality_expression (== | !=) relation_expression

"""

node = self.relation_expression()

while self.current_token.type in (REEQ, RENEQ):

token = self.current_token

self.eat(token.type)

node = BinOp(left=node, op=token.value, right=self.relation_expression())

return node

def assignment_expression(self):

"""assignment_expression : equality_expression

| unary_expression = assignment_expression

"""

node = self. equality_expression()

token = self.current_token

if token.type == ASSIGN:

self.eat(ASSIGN)

right_node = self.assignment_expression()

node = BinOp(left=node, op=token.value, right=right_node)

return node

这里有一个细节,不知道大家有没有注意到逻辑和赋值表达式实现的区别:我们都用了BinOp去存储二者之间的逻辑或者赋值关系,但是在赋值表达式中,我们先递归后存储;而逻辑表达式则先存储再递归。这样的区别就是按照前面我们介绍的左结合和右结合的概念实现上的区别,以便符合C语言语法的要求。

5.4 函数定义

再来看一看函数定义的抽象描述:

function_definition : type_specifier declarator compound_statement

对应地,实现如下:

def function_definition(self):

"""function_definition : type_specifier declarator compound_statement

"""

type_node = self.type_specifier()

decl_node.set_type(type_node)

node = FunctionDefn(decl_node, self.compound_statement())

return node

5.5 实例

有了这些描述对应的实现,我们定义下面的类:

class TranslationUnit(NodeList):

"""An unit list, derived for identification"""

pass

作为一个根节点使用。为了对上面介绍的实现细节有一个直观的感受,对于下面的C代码:

int a, b;

int main()

{

a = 1;

if (a > 0)

b = 2;

return 0;

}

用Graphviz库简单绘制创建的节点,可以得到下面的抽象语法树图示:

7afa69264ce1

图5-1 抽象语法树

至此,我们已经一步一步地分析和实现了语法分析的过程。这里面,还有很多上一部分定义的抽象描述没有具体实现。但是,相信经过这部分的分析,大家可以试着补充完整,从而更好地理解语法分析这部分的内容。下一部分,将开启新的篇章,敬请期待。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值