【Lua篇】靜態程式碼掃描分析(三)語法分析
阿新 • • 發佈:2021-08-14
一、語法分析
通過將詞法分析獲取的Token流按照目標語言的語法進行解析的過程,例如解析函式宣告、函式呼叫、變數宣告、各種語句等。
二、Lua語法分析
在寫語法分析程式前,先需要了解Lua的語句和語法,然後進行逐個解析。
下面簡單列舉一下Lua的語句:
1.函式定義
1 -- 普通的函式宣告 2 function demo1() 3 -- <函式體> 4 end 5 -- 類方法 6 function t.demo2(self) 7 -- <函式體> 8 end 9 -- 方法呼叫中的匿名函式(其實還有很多) 10 some_method_call(arg1, function() <callback> end)
2.函式呼叫
1 -- 普通的函式呼叫 2 print("Hello Lua") 3 -- 模組方法 4 math.random(1,2) 5 -- 類方法 6 obj:set_id(xx)
3. 賦值語句
1 -- 帶local區域性變數宣告賦值 2 local a = 1 3 -- 多變數 4 local a,b,c = 2,3 5 -- 全域性的變數 6 g_a = 3 7 -- 賦值可以是函式、函式呼叫、表等 8 g_c,g_b = {}, function() end
4.條件分支
1 -- if條件分支 2 if a > 1then 3 elseif a > 2 then 4 else 5 end
5. for迴圈
1 -- for迴圈 2 for i=1,10,2 do 3 end
6. for迭代器
1 -- 迭代包含in pairs 和 in ipairs 2 for k,v in pairs(tb) do 3 end
7. while迴圈
1 -- while迴圈,條件可以很複雜 2 while a >1 and b > 1 do 3 end
8. repeat until 迴圈
1 repeat 2 until a > 1
上面僅僅簡單了進行了舉例,實際上專案中的寫法是千奇百怪的,因此需要確保包含所有的語法規則。在進行語法解析的時候,首先進行程式碼塊的解析,可以把一個檔案看作整個程式碼塊,裡面包含各種的語句。然後其實是逐語句解析,先取出一個Token,判斷這個Token的型別,然後決定更細規則的解析,就像下面程式碼列舉的過程一樣。
1. 語法解析的入口,呼叫block解析。
1 def parse(self): 2 self._TokenNum = self.mTokens 3 block = self.on_block_parse() 4 return block
2. 對block解析的時候,實際是迴圈呼叫逐語句解析,直到匹配到語句塊結束標記。
1 def on_block_parse(self): 2 token = self.get_token() 3 statements = [] 4 while token is not None: 5 # until,else,elseif,end 6 if self.is_end_token(token): 7 self.put_token() 8 break 9 # 語句解析 10 statement = self.on_statement_parse(token) 11 if not statement: 12 break 13 statements.append(statement) 14 token = self.get_token() 15 return Block(statements)
3. 逐語句解析就是獲取Token的型別,並呼叫對應型別的解析方法。
1 def on_statement_parse(self, token): 2 # 函式 3 if token.tokenType == T.FUNCTION: 4 return self.on_function_parse(token) 5 # for .. in .. do .. end 6 elif token.tokenType == T.FOR: 7 return self.on_for_parse(token) 8 # while .. do .. end 9 elif token.tokenType == T.WHILE: 10 return self.on_while_parse(token) 11 # repeat ... until xx 12 elif token.tokenType == T.REPEAT: 13 return self.on_repeat_parse(token) 14 # if .. elseif ... else .. end 15 elif token.tokenType == T.IF: 16 return self.on_if_parse(token) 17 # do ... end 18 elif token.tokenType == T.DO: 19 return self.on_do_parse(token) 20 # local xx 21 elif token.tokenType == T.LOCAL: 22 return self.on_local_parse(token) 23 # return xxx 24 elif token.tokenType == T.RETURN: 25 return self.on_return_parse(token) 26 else: 27 return self.on_expression_parse(token)
4. 列舉一下while迴圈的解析,其中會呼叫表示式解析while的條件,呼叫block解析while的語句體,然後檢查是否有end。基本上其他的語句也是按照這樣的方式進行分析與解析。
1 def on_while_parse(self, token): 2 """ while 迴圈 """ 3 # while <condition> do 4 exp = self.on_expression_parse(self.get_token()) 5 if not exp: 6 self.print_syntax_error(token) 7 return None 8 next_token = self.get_token() 9 if next_token.text != 'do': 10 self.print_syntax_error(next_token) 11 return 12 body = self.on_block_parse() 13 next_token = self.get_token() 14 if next_token.text != 'end': 15 self.print_syntax_error(next_token) 16 return None 17 while_statement = LNodeWhile(token.lineno, exp, body) 18 return while_statement
三、總結
通過上面的簡單介紹,不知可能上手寫一寫呢?將所有的語句都解析完成後,接下來就是進行靜態程式碼掃描了。如果是使用C或者C++寫的話,完全可以從Lua的原始碼中提取上面的詞法和語法解析內容,而直接進行程式碼規則掃描檢查的編寫。
文章來自我的公眾號,大家如果有興趣可以關注,具體掃描關注下圖。