2016-09-22 72 views
0

我想要一个Python函数,它将字符串命令转换为AST(抽象语法树)。将简单命令转换为python中的AST

该命令的语法如下:

commandName(3, "hello", 5.0, x::int) 

命令可以接受任何数量的逗号分隔值的,可以是任一

  1. 整数
  2. 字符串
  3. 浮标
  4. 类型

假设函数被调用convert_to_ast,然后

convert_to_ast('commandName(3, "hello", 5.0, x::int)') 

应该产生以下AST:

{ 
    'type': 'command', 
    'name': 'commandName', 
    'args': [{ 
    'type': 'int', 
    'value': 3 
    }, { 
    'type': 'str', 
    'value': 'Hello' 
    }, { 
    'type': 'float', 
    'value': 5.0 
    }, { 
    'type': 'var', 
    'kind': 'int', 
    'name': 'x 
    }] 
+0

我要澄清:元组显示为一个字符串,是一个较大的字符串,通常是一个命令如“加(1的一部分,”你好“,3)' – TheSeamau5

+0

我认为唯一剩下的就是问什么是_rules_来决定什么是事情。例如说“int”是“类型”的规则是什么?它是否与Python标识符一样解析?和命令只是后面有()的标识符?在这种情况下,你仍然可以使用'AST'来解析这个(这很有趣)。 – mgilson

+0

好问题。我刚刚更新我的示例稍微复杂一些。我用“变量”替换“类型”。你会看到现在的语法不再是Python了。重点是我稍后将添加此命令可以接受的不同类型的数据,并且其语法不应与Python的语法绑定。 – TheSeamau5

回答

5

好像你可以只是评估字符串,然后从那里摘掉类型:

>>> items = ast.literal_eval('(404.5, "Hello", 5)') 
>>> [{'type': type(item).__name__, 'value': item} for item in items] 
[{'type': 'float', 'value': 404.5}, {'type': 'str', 'value': 'Hello'}, {'type': 'int', 'value': 5}] 

当然,如果你想做更多有趣的事情,你可以访问AST di rectly:

>>> ast.dump(ast.parse('(404.5, "Hello", 5)')) 
"Module(body=[Expr(value=Tuple(elts=[Num(n=404.5), Str(s='Hello'), Num(n=5)], ctx=Load()))])" 
>>> ast.parse('(404.5, "Hello", 5)').body[0].value.elts 
[<_ast.Num object at 0x107fa1250>, <_ast.Str object at 0x107fa1290>, <_ast.Num object at 0x107fa12d0>] 

对于除解析一个元组(如你加入到这个问题),我们仍然可以使用Python的AST解析这个更一般的事情(只要你的语法有效的Python) 。在这种情况下,我们将创建一个ast.NodeVisitor,它将提取我们在访问我们关心的Python AST的每个节点时的信息。在这种情况下,我们关心CallNumStrName节点:

import ast 

class Parser(ast.NodeVisitor): 

    def __init__(self): 
     self.calls = [] 
     self.current_command = None 

    def visit_Call(self, node): 
     name = node.func.id 
     self.current_command = { 
      'type': 'command', 
      'name': name, 
      'args': [] 
     } 
     self.calls.append(self.current_command) 
     for arg in node.args: 
      self.visit(arg) 
     self.current_command = None 

    def visit_Num(self, node): 
     if not self.current_command: 
      return 
     args = self.current_command['args'] 
     arg = { 
      'type': type(node.n).__name__, 
      'value': node.n 
     } 
     args.append(arg) 

    def visit_Str(self, node): 
     if not self.current_command: 
      return 
     args = self.current_command['args'] 
     arg = { 
      'type': 'str', 
      'value': node.s 
     } 
     args.append(arg) 

    def visit_Name(self, node): 
     if not self.current_command: 
      return 
     args = self.current_command['args'] 
     arg = { 
      'type': 'type', 
      'kind': node.id 
     } 
     args.append(arg) 


S = 'commandName(3, "hello", 5.0, int)' 

tree = ast.parse(S) 
p = Parser() 
p.visit(tree) 
print p.calls 
+0

谢谢你的回答。这将是完全孤立的元组。我已更新我的问题以澄清细节。基本上,元组通常是一些较大字符串的一部分,通常作为命令的参数。此外,元组中的所有类型都不是Python类型。我只说了Python类型来保持简单的问题。 在任何情况下,有关更多详细信息,请参阅我在问题中的上述编辑。再次,非常感谢您的答复。 – TheSeamau5

+0

@ TheSeamau5 - 不幸的是,这仍然不是那么清楚......看起来你只想在结果中使用python文字,并且没有''的条目?这似乎是一个解析问题,其中很多东西是未定义的,确实是一个非常困难的问题:-) – mgilson

+0

我很抱歉。我把''放在那里说我正在寻找一种解决方案,即使对于非Python类型的元素也是如此。无论那种类型是什么,我都会通过解析来解决它。 – TheSeamau5