1. 程式人生 > 其它 >【Lua篇】靜態程式碼掃描分析(三)語法分析

【Lua篇】靜態程式碼掃描分析(三)語法分析

一、語法分析

通過將詞法分析獲取的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 > 1
then 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的原始碼中提取上面的詞法和語法解析內容,而直接進行程式碼規則掃描檢查的編寫。

文章來自我的公眾號,大家如果有興趣可以關注,具體掃描關注下圖。