? ? ? ? 緊接著上一部分抽象語法樹的內容。在這一部分,我們將利用這些定義好的節點(磚塊)和抽象語法描述(水泥)搭建起完整的抽象語法樹。
? ? ? ? 同詞法分析實現的方式一樣,我們首先定義語法分析的類:
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庫簡單繪制創建的節點,可以得到下面的抽象語法樹圖示:
? ? ? ? 至此,我們已經一步一步地分析和實現了語法分析的過程。這里面,還有很多上一部分定義的抽象描述沒有具體實現。但是,相信經過這部分的分析,大家可以試著補充完整,從而更好地理解語法分析這部分的內容。下一部分,將開啟新的篇章,敬請期待。
實現簡易的C語言編譯器(part 0)
實現簡易的C語言編譯器(part 1)
實現簡易的C語言編譯器(part 2)
實現簡易的C語言編譯器(part 3)
實現簡易的C語言編譯器(part 4)
實現簡易的C語言編譯器(part 5)
實現簡易的C語言編譯器(part 6)
實現簡易的C語言編譯器(part 8)
實現簡易的C語言編譯器(part 9)
實現簡易的C語言編譯器(part 10)
實現簡易的C語言編譯器(part 11)