通过匹配括号评估字符串
Evaluate string by matching parentheses
以编程方式翻译像
这样的字符串的最佳方式是什么
"((abc&(def|ghi))|jkl)&mno"
执行为:
if ((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno'):
return True
我觉得一定有一种简单的方法可以实现这一点,但我无法理解它。
好吧,如果您的字符串不比您显示的更复杂(例如仅由那些符号加上 letters/numbers 组成),您可以根据需要用一些简单的正则表达式替换匹配来解析它。之后,您可以使用 eval()
到 运行 作为 python 代码。
例如:
import re
def func(x):
# just an example...
return True
s = "((abc&(def|ghi))|jkl)&mno"
s = re.sub(r'(\w+)', r"func('')", s)
s = s.replace('&', ' and ')
s = s.replace('|', ' or ')
print(s)
print(eval(s))
输出:
((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno')
True
这是一个有趣的小问题,解决方案有很多层。
首先,鉴于此示例,您需要一个基本的中缀符号解析器。在 pyparsing 中,有一个内置的辅助方法 infixNotation
。几个 pyparsing 示例展示了如何使用 infixNotation
解析布尔表达式。这是一个将解析您的示例表达式的解析器:
import pyparsing as pp
term = pp.Word(pp.alphas)
AND = pp.Literal("&")
OR = pp.Literal("|")
expr = pp.infixNotation(term,
[
(AND, 2, pp.opAssoc.LEFT,),
(OR, 2, pp.opAssoc.LEFT,),
])
print(expr.parseString(sample).asList())
对于您的示例,这将打印:
[[[['abc', '&', ['def', '|', 'ghi']], '|', 'jkl'], '&', 'mno']]
你可以看到我们不仅捕获了表达式,还捕获了括号分组。
我们可以开始通过添加解析操作来转换为您想要的输出。这些是 pyparsing 将调用的 parse-time 回调,以用不同的值替换已解析的标记(不需要是字符串,可以是用于评估的 AST 节点 - 但在这种情况下我们将 return a修改后的字符串)。
AND.addParseAction(lambda: " and ")
OR.addParseAction(lambda: " or ")
term.addParseAction(lambda t: "func('{}')".format(t[0]))
expr.addParseAction(lambda t: "({})".format(''.join(t[0])))
解析操作可以是具有各种签名的方法:
function()
function(tokens)
function(location, tokens)
function(input_string, location, tokens)
对于AND和OR,我们只需要将解析后的运算符替换为对应的“and”和“or”关键字即可。对于已解析的变量项,我们想将“xxx”更改为“func(xxx)”,因此我们编写了一个解析操作来获取已解析的标记,以及returns 修改后的字符串。
expr
的解析操作很有趣,因为它所做的只是获取解析的内容,使用 ''.join()
加入它们,然后将其包装在 ()
中。由于 expr
实际上是一个递归表达式,我们将看到它在解析的嵌套列表的每一层都对 () 进行了适当的包装。
添加这些解析操作后,我们可以尝试再次调用 parseString()
,现在给出:
["(((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno'))"]
越来越近了!
要将格式设置为所需的 if
语句,我们可以使用另一个解析操作。但是我们不能将此解析操作直接附加到 expr
,因为我们看到 expr
(及其关联的解析操作)将在所有嵌套级别进行解析。因此,我们可以创建一个 expr 的“外部”版本,它只是一个 expr 的容器表达式:
outer_expr = pp.Group(expr)
解析操作与我们在 expr
中看到的类似,其中我们 return 使用输入标记的新字符串:
def format_expression(tokens):
return "if {}:\n return True".format(''.join(tokens[0]))
outer_expr.addParseAction(format_expression)
现在我们使用outer_expr
解析输入字符串:
print(outer_expr.parseString(sample)[0])
获得:
if (((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno')):
return True
(此值可能有一组额外的 (),如果需要,可以在 outer_expr
的解析操作中将其删除。)
解析器的完成版本(取消注释中间打印语句以查看解析器功能的进展):
sample = "((abc&(def|ghi))|jkl)&mno"
import pyparsing as pp
term = pp.Word(pp.alphas)
AND = pp.Literal("&")
OR = pp.Literal("|")
expr = pp.infixNotation(term,
[
(AND, 2, pp.opAssoc.LEFT,),
(OR, 2, pp.opAssoc.LEFT,),
])
# print(expr.parseString(sample).asList())
AND.addParseAction(lambda: " and ")
OR.addParseAction(lambda: " or ")
term.addParseAction(lambda t: "func('{}')".format(t[0]))
expr.addParseAction(lambda t: "({})".format(''.join(t[0])))
# print(expr.parseString(sample).asList())
def format_expression(tokens):
return "if {}:\n return True".format(''.join(tokens[0]))
outer_expr = pp.Group(expr).addParseAction(format_expression)
print(outer_expr.parseString(sample)[0])
以编程方式翻译像
这样的字符串的最佳方式是什么"((abc&(def|ghi))|jkl)&mno"
执行为:
if ((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno'):
return True
我觉得一定有一种简单的方法可以实现这一点,但我无法理解它。
好吧,如果您的字符串不比您显示的更复杂(例如仅由那些符号加上 letters/numbers 组成),您可以根据需要用一些简单的正则表达式替换匹配来解析它。之后,您可以使用 eval()
到 运行 作为 python 代码。
例如:
import re
def func(x):
# just an example...
return True
s = "((abc&(def|ghi))|jkl)&mno"
s = re.sub(r'(\w+)', r"func('')", s)
s = s.replace('&', ' and ')
s = s.replace('|', ' or ')
print(s)
print(eval(s))
输出:
((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno')
True
这是一个有趣的小问题,解决方案有很多层。
首先,鉴于此示例,您需要一个基本的中缀符号解析器。在 pyparsing 中,有一个内置的辅助方法 infixNotation
。几个 pyparsing 示例展示了如何使用 infixNotation
解析布尔表达式。这是一个将解析您的示例表达式的解析器:
import pyparsing as pp
term = pp.Word(pp.alphas)
AND = pp.Literal("&")
OR = pp.Literal("|")
expr = pp.infixNotation(term,
[
(AND, 2, pp.opAssoc.LEFT,),
(OR, 2, pp.opAssoc.LEFT,),
])
print(expr.parseString(sample).asList())
对于您的示例,这将打印:
[[[['abc', '&', ['def', '|', 'ghi']], '|', 'jkl'], '&', 'mno']]
你可以看到我们不仅捕获了表达式,还捕获了括号分组。
我们可以开始通过添加解析操作来转换为您想要的输出。这些是 pyparsing 将调用的 parse-time 回调,以用不同的值替换已解析的标记(不需要是字符串,可以是用于评估的 AST 节点 - 但在这种情况下我们将 return a修改后的字符串)。
AND.addParseAction(lambda: " and ")
OR.addParseAction(lambda: " or ")
term.addParseAction(lambda t: "func('{}')".format(t[0]))
expr.addParseAction(lambda t: "({})".format(''.join(t[0])))
解析操作可以是具有各种签名的方法:
function()
function(tokens)
function(location, tokens)
function(input_string, location, tokens)
对于AND和OR,我们只需要将解析后的运算符替换为对应的“and”和“or”关键字即可。对于已解析的变量项,我们想将“xxx”更改为“func(xxx)”,因此我们编写了一个解析操作来获取已解析的标记,以及returns 修改后的字符串。
expr
的解析操作很有趣,因为它所做的只是获取解析的内容,使用 ''.join()
加入它们,然后将其包装在 ()
中。由于 expr
实际上是一个递归表达式,我们将看到它在解析的嵌套列表的每一层都对 () 进行了适当的包装。
添加这些解析操作后,我们可以尝试再次调用 parseString()
,现在给出:
["(((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno'))"]
越来越近了!
要将格式设置为所需的 if
语句,我们可以使用另一个解析操作。但是我们不能将此解析操作直接附加到 expr
,因为我们看到 expr
(及其关联的解析操作)将在所有嵌套级别进行解析。因此,我们可以创建一个 expr 的“外部”版本,它只是一个 expr 的容器表达式:
outer_expr = pp.Group(expr)
解析操作与我们在 expr
中看到的类似,其中我们 return 使用输入标记的新字符串:
def format_expression(tokens):
return "if {}:\n return True".format(''.join(tokens[0]))
outer_expr.addParseAction(format_expression)
现在我们使用outer_expr
解析输入字符串:
print(outer_expr.parseString(sample)[0])
获得:
if (((func('abc') and (func('def') or func('ghi'))) or func('jkl')) and func('mno')):
return True
(此值可能有一组额外的 (),如果需要,可以在 outer_expr
的解析操作中将其删除。)
解析器的完成版本(取消注释中间打印语句以查看解析器功能的进展):
sample = "((abc&(def|ghi))|jkl)&mno"
import pyparsing as pp
term = pp.Word(pp.alphas)
AND = pp.Literal("&")
OR = pp.Literal("|")
expr = pp.infixNotation(term,
[
(AND, 2, pp.opAssoc.LEFT,),
(OR, 2, pp.opAssoc.LEFT,),
])
# print(expr.parseString(sample).asList())
AND.addParseAction(lambda: " and ")
OR.addParseAction(lambda: " or ")
term.addParseAction(lambda t: "func('{}')".format(t[0]))
expr.addParseAction(lambda t: "({})".format(''.join(t[0])))
# print(expr.parseString(sample).asList())
def format_expression(tokens):
return "if {}:\n return True".format(''.join(tokens[0]))
outer_expr = pp.Group(expr).addParseAction(format_expression)
print(outer_expr.parseString(sample)[0])