2011-12-28 18 views
3

我想在Python中創建一個正則表達式,它將匹配Python源代碼中的一行並返回函數調用列表。與Python函數調用匹配的正則表達式

典型的線路是這樣的:

something = a.b.method(time.time(), var=1) + q.y(x.m()) 

,其結果應該是:

["a.b.method()", "time.time()", "q.y()", "x.m()"] 

我這裏有兩個問題:

  1. 創建正確的模式
  2. 捕獲組重疊

謝謝大家幫忙

+0

那麼解析字符串和註釋怎麼樣? – Qtax 2011-12-28 16:35:51

+2

python不是一種常規的語言,所以你不能用正則表達式。 – 2011-12-28 16:43:05

+0

@DouglasLeeder,正則表達式不正規。除非我們在這裏討論形式語言理論。 ;-) – Qtax 2011-12-28 16:46:13

回答

9

我不認爲正則表達式是最好的方法。考慮ast module代替,例如:

class ParseCall(ast.NodeVisitor): 
    def __init__(self): 
     self.ls = [] 
    def visit_Attribute(self, node): 
     ast.NodeVisitor.generic_visit(self, node) 
     self.ls.append(node.attr) 
    def visit_Name(self, node): 
     self.ls.append(node.id) 


class FindFuncs(ast.NodeVisitor): 
    def visit_Call(self, node): 
     p = ParseCall() 
     p.visit(node.func) 
     print ".".join(p.ls) 
     ast.NodeVisitor.generic_visit(self, node) 


code = 'something = a.b.method(foo() + xtime.time(), var=1) + q.y(x.m())' 
tree = ast.parse(code) 
FindFuncs().visit(tree) 

結果

a.b.method 
foo 
xtime.time 
q.y 
x.m 
+0

「ast」模塊上的+1教程!很高興知道它提供的東西比'literal_eval'更有用:) :) – 2011-12-28 19:41:33

+0

事實上,除非我錯了,否則基於正則表達式的方法註定要失敗。 Python語言基於上下文無關語法,並且(除非我錯了),CFG比正則表達式更具表現力(謝謝[Chomsky Hierarchy](http://en.wikipedia.org/wiki/) Chomsky_hierarchy) – 2012-09-10 17:22:59

+2

@AdamParkin:[這個問題]的一些答案(http://stackoverflow.com/questions/11306641/what-kind-of-formal-languages-can-modern-regex-engines-parse)可能是有趣的你 – georg 2012-09-10 18:00:22

2
/([.a-zA-Z]+)\(/g 

應該匹配方法名;因爲你有一些嵌套後你必須添加parens。

+0

'foo(「bar(a,b)」)'會爲該正則表達式錯誤地返回'bar'。 – 2011-12-28 16:42:37

+0

@DouglasLeeder公平點。 – Mathletics 2011-12-28 16:49:17

+0

@DouglasLeeder它看起來不錯,但[this](http://pastebin.com/7dKpRh5B)Python代碼不能打印預期的內容。 – xralf 2011-12-28 17:35:51

0

我真的不知道Python,但我可以想像,使這項工作正常涉及一些併發症,如:

  • 評論
  • 表達式返回一個對象的

但是對於您的示例,像這樣的表達式有效:

(?:\w+\.)+\w+\(
4
$ python3 
>>> import re 
>>> from itertools import chain 
>>> def fun(s, r): 
...  t = re.sub(r'\([^()]+\)', '()', s) 
...  m = re.findall(r'[\w.]+\(\)', t) 
...  t = re.sub(r'[\w.]+\(\)', '', t) 
...  if m==r: 
...   return 
...  for i in chain(m, fun(t, m)): 
...   yield i 
... 
>>> list(fun('something = a.b.method(time.time(), var=1) + q.y(x.m())', [])) 
['time.time()', 'x.m()', 'a.b.method()', 'q.y()'] 
0


我爲你證明這一點的例子是在Python3可行

 
    import re 


    def parse_func_with_params(inp): 
     func_params_limiter = "," 
     func_current_param = func_params_adder = "\s*([a-z-A-Z]+)\s*" 

     try: 
      func_name = "([a-z-A-Z]+)\s*" 
      p = re.compile(func_name + "\(" + func_current_param + "\)") 
      print(p.match(inp).groups()) 
     except: 
      while 1: 
       func_current_param += func_params_limiter + func_params_adder 
       try: 
        func_name = "([a-z-A-Z]+)\s*" 
        p = re.compile(func_name + "\(" + func_current_param + "\)") 
        print(p.match(inp).groups()) 
        break 
       except: 
        pass 

命令線路輸入put:animalFunc
輸出:('animalFunc','lion','tiger','giraffe','singe')

正如你所看到的,函數名永遠是第一個在列表中,其餘的是通過的參數名稱