【问题标题】:pyparsing to parse a python function call in its most general formpyparsing 以最一般的形式解析 python 函数调用
【发布时间】:2013-01-23 09:24:17
【问题描述】:

我想使用优秀的 pyparsing 包来解析最通用形式的 python 函数调用。我读过一篇文章,here 有点用,但还不够笼统。

我想解析以下表达式:

f(arg1,arg2,arg3,...,kw1=var1,kw2=var2,kw3=var3,...)

在哪里

  1. arg1,arg2,arg3 ...是任何类型的有效python对象(整数、实数、列表、字典、函数、变量名...)
  2. kw1、kw2、kw3 ... 是有效的 Python 关键字名称
  3. var1,var2,var3 是有效的 Python 对象

我想知道是否可以为这样的通用模板定义语法。我可能问得太多了......你有什么想法吗?

非常感谢您的帮助

埃里克

【问题讨论】:

  • 当然有可能,在 pyparsing 示例中有一个完整的 python grammar implementation(虽然它看起来很神秘,但可能会有所帮助)。
  • 取决于你用它做什么 - 另一种选择是使用 Python 的标准库来解析它:参见 ast.parse 和相关的 NodeVisitor 和 NodeTransformer 类。
  • 这不是最通用的形式。最通用的形式是f(arg1, arg2, arg3, ..., kwarg1=val1, kwarg2=val2, ..., *args, **kwargs)。
  • 你说得对 Bakuriu,谢谢你的评论。

标签: python pyparsing


【解决方案1】:

就这些了吗?让我们从一个简单的非正式 BNF 开始:

func_call ::= identifier '(' func_arg [',' func_arg]... ')'
func_arg ::= named_arg | arg_expr
named_arg ::= identifier '=' arg_expr
arg_expr ::= identifier | real | integer | dict_literal | list_literal | tuple_literal | func_call
identifier ::= (alpha|'_') (alpha|num|'_')*
alpha ::= some letter 'a'..'z' 'A'..'Z'
num ::= some digit '0'..'9'

翻译成pyparsing,自下而上工作:

identifier = Word(alphas+'_', alphanums+'_')

# definitions of real, integer, dict_literal, list_literal, tuple_literal go here
# see further text below

# define a placeholder for func_call - we don't have it yet, but we need it now
func_call = Forward()

string = pp.quotedString | pp.unicodeString

arg_expr = identifier | real | integer | string | dict_literal | list_literal | tuple_literal | func_call

named_arg = identifier + '=' + arg_expr

# to define func_arg, must first see if it is a named_arg
# why do you think this is?
func_arg = named_arg | arg_expr

# now define func_call using '<<' instead of '=', to "inject" the definition 
# into the previously declared Forward
#
# Group each arg to keep its set of tokens separate, otherwise you just get one
# continuous list of parsed strings, which is almost as worthless the original
# string
func_call << identifier + '(' + delimitedList(Group(func_arg)) + ')'

那些 arg_expr 元素可能需要一段时间才能完成,但幸运的是,您可以将它们从 pyparsing wiki 的示例页面中删除:http://pyparsing.wikispaces.com/file/view/parsePythonValue.py

from parsePythonValue import (integer, real, dictStr as dict_literal, 
                              listStr as list_literal, tupleStr as tuple_literal)

您仍然可以使用*list_of_args 或**dict_of_named_args 表示法传递参数。扩展 arg_expr 以支持这些:

deref_list = '*' + (identifier | list_literal | tuple_literal)
deref_dict = '**' + (identifier | dict_literal)

arg_expr = identifier | real | integer | dict_literal | list_literal | tuple_literal | func_call | deref_list | deref_dict

现在为自己编写一些测试用例 - 从简单开始逐步复杂化:

sin(30)
sin(a)
hypot(a,b)
len([1,2,3])
max(*list_of_vals)

需要添加到 arg_expr 的其他参数类型(留作 OP 的进一步练习):

  • 索引参数:dictval['a']divmod(10,3)[0]range(10)[::2]

  • 对象属性引用:a.b.c

  • 算术表达式:sin(30), sin(a+2*b)

  • 比较表达式:sin(a+2*b) &gt; 0.510 &lt; a &lt; 20

  • 布尔表达式:a or b and not (d or c and b)

  • lambda 表达式:lambda x : sin(x+math.pi/2)

  • 列表理解

  • 生成器表达式

【讨论】:

  • 我尝试了您提供的一些示例,但定义的语法无法解析len([1,2,3]) 字符串。它给出了一个TypeError: object of type 'int' has no len(),因为[1,2,3] 列表连接到123 个整数。我试图找出原因。我认为这可能是由于用于定义 listStr、tupleStr 和 dictStr 的 Optional(Suppress(",")) 但删除它们仍然会产生相同的错误。
  • 我不明白为什么 [1,2,3] 连接成一个整数 - listStr 应该解析并将其转换为 3 元素列表 [1,2,3]。
  • 这也是我困惑的地方,保罗。如果我这样做:type(eval(func_call.transformString('[1,2,3]'))) 我得到一个 &lt;type 'list'&gt; 但是当我这样做时 func_call.transformString('len([1,2,3])') 我得到 len(123) 而不是 len([1,2,3]) 符合预期。我会尝试进一步挖掘这个。如果在此期间,你能找到问题,你很高兴!!!
  • Pyparsing 不再托管在 wikispaces.com 上。转至github.com/pyparsing/pyparsing
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 1970-01-01
相关资源
最近更新 更多