? ? ? ? 我們已經重新抽象描述了C語言的表達式、語句和聲明,并且進行了實現。如果大家對在實現中出現的并沒有過多解釋的Binop
和Negative
這樣的結構還有一些印象,那么這一部分,我們將解釋并且引入更多的類似的東西,讓它們幫助我們串接起詞法分析中得到的所有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
顯然,+b
和b
是完全等價的,所以我們并沒有單獨派生出一個Positive
的類,完全可以使用基類UnaryOp
替代。
- +b
- **a
對于這種嵌套的單目運算符表達式,也可以采用嵌套的方式得到具體的節點,比如使用Pointer(Pointer)
來表示。
- **a
- !a
我們這個編譯器忽略了這樣的結構,但是如果大家有興趣,完全可以再派生出一個新的類來表示。同時,記得抽象出具體的表達式描述。
- !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 跳轉語句
? ? ? ? break
和continue
并不包含附屬的信息,但本身蘊涵著信息。因此,需要具體的節點表示。而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)