实现简易的C语言编译器(part 6)

        我们已经重新抽象描述了C语言的表达式、语句和声明,并且进行了实现。如果大家对在实现中出现的并没有过多解释的BinopNegative这样的结构还有一些印象,那么这一部分,我们将解释并且引入更多的类似的东西,让它们帮助我们串接起词法分析中得到的所有token。这就是语法分析第二步的内容:构建抽象语法树(Abstract Syntax Tree, AST)。
        概念性的东西这里就略过了,只需要记住这是一种层级表示关系,由众多的节点组成,节点之间通过父节点产生关联,包含了整个源代码的结构信息。我们将要逐步介绍的所有过程,比如语义分析和产生汇编代码,都将基于这棵树进行的。
        为了充分利用面向对象编程语言的特点,实现多态操作,首先定义一个模版节点:

class AST(object):
    """the base AST node"""
    pass

        剩下的工作则是从这个节点派生出具有具体行为内容的节点。我们仍然按照表达式、语句和声明的分类方法具体讨论。

4.1 表达式(Expression)

        回顾我们之前分析的表达式的描述,可以将它们简单地总结为两类:单目运算符和双目运算符的表达式。

4.1.1 单目运算符

        类似于包含取地址符(&)和解除引用符(*)这样的表达式。它们由单目运算符和表达式组合而成。首先,定义一个针对该表达式的通用基类:

class UnaryOp(AST):
    """any generic unary operator"""
    def __init__(self, node):
        self.expr = node

表达式有了,如何知道具体的单目运算符呢?答案是:通过单独为它们创建具体的节点来区别对待不同的运算符,例如:

class Pointer(UnaryOp):
    """a pointer refer, e.g. '*a'"""
    pass

class AddrOf(UnaryOp):
    """an address-of operator, e.g. '&a'"""
    pass

class Negative(UnaryOp):
    """A negative unary operator, e.g. '-5'."""
    pass

        我们再考虑两个特殊情况:

    • +b
      显然,+bb是完全等价的,所以我们并没有单独派生出一个Positive的类,完全可以使用基类UnaryOp替代。
    • **a
      对于这种嵌套的单目运算符表达式,也可以采用嵌套的方式得到具体的节点,比如使用Pointer(Pointer)来表示。
    • !a
      我们这个编译器忽略了这样的结构,但是如果大家有兴趣,完全可以再派生出一个新的类来表示。同时,记得抽象出具体的表达式描述。

4.1.2 双目运算符

        典型地,所有的四则运算、逻辑和条件操作都是双目运算。与前面介绍的单目表达式节点定义策略不同,由于双目运算符众多,如果针对具体运算符派生节点,节点类将非常多。事实上,这些双目运算符操作都非常类似,与单目运算符大不同。因此,直接将表达式中的运算符存在节点之中,这样的节点可以定义为:

class BinOp(AST):
    """any binary operator, (+, -, *, /)"""
    def __init__(self, left, op, right):
        self.left = left
        self.op = op
        self.right = right

赋值操作也是一个双目运算符,完全可以采用上面节点来表示。

4.1.3 特殊运算符

        除了单目和双目运算符,C语言中有且仅有一个三目运算符:?:,但我们这里不作考虑,因为它完全可以用条件语句来替代。这里重点考虑几个特殊的运算符。在结构体中,我们通常用.或者->来指示具体的成员变量,虽然也可以当成一种双目操作,但是含义上仍然与其不同,定义新的一个节点为:

class StructOp(AST):
    """any binary operator for struct, ., ->"""
    TO_OPS = ['.', '->']

    def __init__(self, left, op, right):
        self.parent = left
        self.op = op
        self.member = right

4.2 语句(Statement)

        对于语句,我们重点突出它们的结构特征,仍然按照前面的分类进行讨论。

4.2.1 空语句

        单独;组成的语句,虽然没有实际意义,但具有特殊的含义。定义如下:

class EmptyStatement(AST):
    """empty statement"""
    pass

4.2.2 选择语句

        判断条件和具体的执行内容就是我们关心的。于是,可以定义为:

class IfStatement(AST):
    """if else statement"""
    def __init__(self, expr, then_stmt, else_stmt):
        self.expr = expr
        self.then_stmt = then_stmt
        self.else_stmt = else_stmt

4.2.3 跳转语句

        breakcontinue并不包含附属的信息,但本身蕴涵着信息。因此,需要具体的节点表示。而return可能存在返回值,于是,它们可以表示为:

class BreakStatement(AST):
    """A break statement"""
    pass

class ContinueStatement(AST):
    """A continue statement"""
    pass

class ReturnStatement(AST):
    """return statement"""
    def __init__(self, expr=None):
        self.expr = expr if expr is not None else EmptyStatement()

4.2.4 迭代语句

        和选择语句类似,可以表示为:

class WhileLoop(AST):
    """while loop"""
    def __init__(self, expr, stmt):
        self.expr = expr
        self.stmt = stmt

class ForLoop(AST):
    """for loop"""
    def __init__(self, begin_stmt, expr, end_stmt, stmt):
        self.begin_stmt = begin_stmt
        self.end_stmt = end_stmt
        self.expr = expr
        self.stmt = stmt

4.2.5 复合语句

        复合语句的大括号中包含着由一系列的声明和语句组成的集合。对于这样的集合,其实就是我们前面的描述中出现的各种xxx_list,比如:

statement_list : statement 
               | statement_list statement

我们将这些集合统一表示为一个list模板节点,抽象描述中介绍的所有list都从这个节点派生:

class NodeList(AST):
    """A list of nodes"""
    def __init__(self, node=None):
        self.nodes = []
        if node is not None:
            self.nodes.append(node)

    def add(self, node):
        self.nodes.append(node)

那么这里用到的declaration_list, statement_list就可以照猫画虎了:

class statement_list(NodeList):
    """A list of nodes of statement"""
    pass

class declaration_list(NodeList):
    """A list of nodes of declaration"""
    pass

没有具体对应的操作,只是方便我们记忆和处理。进而,符合语句就可以表示为:

class CompoundStatement(AST):
    """A compound statement, e.g. '{ int i; i += 1; }'."""
    def __init__(self, declaration_list, statement_list):
        self.declarations = declaration_list
        self.statements = statement_list

初始化复合语句时,将使用声明和语句的集合。

4.2.6 表达式语句

        表达式语句由表达式和分号组成,可表示为:

class ExpressionStatement(AST):
    """A expression statement"""
    def __init__(self, expr):
        self.expr = expr

4.3 声明(Declaration)

        声明,简单地理解就是由类型符和变量名组成。

4.3.1 类型符

        为了表示数据的类型,我们仿照单目运算符的策略,定义一个模板类型的类:

class Type(AST):
    """assign a type node to variable"""
    pass

通过派生具体的数据类型,如int, char,就可以表示对应的类型。但是,由于指针类型的存在,比如:struct Point* point; int* array[3]; int** a;这样的声明变量的方式,必须对这个模板类做出一些调整,使得它能够很好地表示这种嵌套的数据类型。为此,我们为这个模板类型增加一个child的成员变量,进而表示如下:

class Type(AST):
    """assign a type node to variable"""
    def __init__(self, child=None):
        self.child = child

    def set_base_type(self, type):
        if self.child is None:
            self.child = type
        else:
            self.child.set_base_type(type)

成员函数child的存在,方便了我们嵌套地包含更多的数据类型。对于int** a;,就可以表示为:Type(Type(Type)) -> * ( * ( int ))。其中,括号内的类型就是外层类型的child。此时,通过派生出的具体的类型,就可以逐层地描述变量。具体地,我们可以派生出如下的类型:

  • 基本数据类型
class BaseType(Type):
    """A base type representing ints, chars, etc..."""
    def __init__(self, type_str, child=None):
        Type.__init__(self, child)
        self.type_str = type_str
  • 指针类型
class PointerType(Type):
    """A type representing a pointer to another (nested) type."""
    def __init__(self, child=None):
        Type.__init__(self, child)
  • 结构体类型
class StructType(Type):
    """A type represent a struct"""
    def __init__(self, struct_name, expr_list=None, child=None):
        Type.__init__(self, child)
        self.name = struct_name
        self.exprs = expr_list
  • 枚举类型
class EnumType(Type):
    """A type represent an enum"""
    def __init__(self, enum_name=None, expr_list=None, child=None):
        Type.__init__(self, child)
        self.name = enum_name
        self.exprs = expr_list

4.3.2 声明

        有了类型符,加上变量的名字,就构成了广义的声明,可以表示为:

class Declaration(AST):
    """A node representing a declaration of a function or variable."""
    def __init__(self, name, type=None):
        self.type = type
        self.name = name

    def set_type(self, type):
        if self.type is None:
            self.type = type
        else:
            self.type.set_base_type(type)

        但是,变量不单单是名字而已,还可以有其它的类型,比如还可以是数组或者函数:

direct_declarator : ID
                  | direct_declarator ( parameter_type_list ) 
                  | direct_declarator ( )
                  | direct_declarator [ const_expression ] 
                  | direct_declarator [ ]

因此,为了能够表示这两种后缀形式的变量,在声明中定义下面的方法来添加这些后缀,并且是以上面定义的类型符来表示的:

    def add_type(self, type):
        type.set_base_type(self.type)
        self.type = type

这样,就需要对应地添加数组或者函数类型:

class ArrayType(Type):
    """A type representing an array, e.g. a[], a[5]"""
    def __init__(self, index, child=None):
        Type.__init__(self, child)
        self.index = index

class FunctionType(Type):
    """A type representing a function"""
    def __init__(self, parms=None, child=None):
        Type.__init__(self, child)
        self.parms = parms

    def get_return_type(self):
        """Returns the return type of the function."""
        return self.child

虽然没有数组类型或者函数类型这样一种说法,但是将变量前(类型符)后(数组或函数)都当成一种类型,方便我们可以灵活地处理声明。

4.3.3 函数定义

        有了前面关于类型与变量的定义和理解,我们可以单独用一个类来表示函数的定义:

class FunctionDefn(AST):
    """A node representing a function definition"""
    def __init__(self, declaration, body):
        self.name = declaration.name
        self.function = declaration.type
        self.return_type = declaration.type.child
        self.body = body

这里就可以看出,函数类型的返回值也是一个类型,我们就可以从声明中直接获得,简化了编程的过程。

        准备工作终于做好,下一部分,我们将解决如何用这些抽象出的具体的AST节点组成整个语法结构的高楼,也就是语法树。

实现简易的C语言编译器(part 0)
实现简易的C语言编译器(part 1)
实现简易的C语言编译器(part 2)
实现简易的C语言编译器(part 3)
实现简易的C语言编译器(part 4)
实现简易的C语言编译器(part 5)
实现简易的C语言编译器(part 5)
实现简易的C语言编译器(part 7)
实现简易的C语言编译器(part 8)
实现简易的C语言编译器(part 9)
实现简易的C语言编译器(part 10)
实现简易的C语言编译器(part 11)

你可能感兴趣的:(实现简易的C语言编译器(part 6))