【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的源码中提取上面的词法和语法解析内容,而直接进行代码规则扫描检查的编写。

    文章来自我的公众号,大家如果有兴趣可以关注,具体扫描关注下图。

posted @ 2021-08-14 08:59  爆走de萝卜  阅读(420)  评论(0编辑  收藏  举报