實現簡易的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)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373