python - PyParsing - 语法元素围绕其他元素拆分
问题描述
我正在移动一个工具(不是我写的)来使用 PyParsing。我正在更新语法以使其更有意义,但也希望向后兼容。语法包括被另一个元素分割的元素,我需要“动词”(包装元素)和“值”(包装元素)。(不,这些不是球体,尽管它们看起来像它们 - 这很令人困惑,也是我改变它的部分原因)。
*thing* # verb: contains value: thing
*thing # verb: starts_with value: thing
thing* # verb: ends_with value: thing
!*thing* # verb: not_contains value: thing
我很难理解如何解析*thing*
“动词”围绕“值”的位置。这些元素也在一个分隔列表中,尽管这部分我很好。
我将解析的完整示例:
command *thing*, !*other_thing*, *third_thing
我试过的:
import pyparsing as pp
command = pp.Keyword("command").setResultsName("command")
value = pp.Word(pp.alphanums + "_").setResultsName("value", listAllMatches=True)
contains = ("*" + value + "*").setResultsName("verb", listAllMatches=True)
not_contains = ("!*" + value + "*").setResultsName("verb", listAllMatches=True)
starts_with = ("*" + value).setResultsName("verb", listAllMatches=True)
verbs_and_values = (
contains
| not_contains
| starts_with
)
directive = pp.Group(command + pp.delimitedList(verbs_and_values, delim=","))
example = "command *thing*, !*other_thing*, *third_thing"
result = directive.parseString(example)
print result.dump()
这让我得到了所有的价值,但动词是整个事情(即['*', 'thing', '*']
)。我尝试使用类似于以下的 parseAction 调整动词:
def process_verb(tokens):
if tokens[0] == '*' and tokens[-1] == '*':
return "contains"
# handle other verbs...
效果很好,但它破坏了价值观......
解决方案
我看到您正在使用结果名称listAllMatches=True
来捕获 delimitedList 中的多个解析值。这对于简单的数据结构是可以的,但是一旦您想为给定值存储多个值,那么您将需要开始使用 Group 或解析操作类。
作为一般做法,我避免在低级表达式中使用结果名称,而是在使用 '+' 和 '|' 组合高级表达式时添加它们 运营商。我也主要使用expr("name")
表单而不是expr.setResultsName("name")
表单来设置结果名称。
这是使用组的代码的修改版本:
command = pp.Keyword("command")
value = pp.Word(pp.alphanums + "_")
contains = pp.Group("*" + value("value") + "*")
not_contains = pp.Group("!*" + value("value") + "*")
starts_with = pp.Group("*" + value("value"))
我还为命令和动词列表添加了结果名称directive
:
directive = pp.Group(command("command")
+ pp.Group(pp.delimitedList(verbs_and_values,
delim=","))("verbs"))
现在这些表达式被包装在组中,没有必要使用listAllMatches=True
,因为现在每个值都保存在自己单独的组中。
解析结果现在如下所示:
[['command', ['*', 'thing', '*'], ['!*', 'other_thing', '*'], ['*', 'third_thing']]]
[0]:
['command', ['*', 'thing', '*'], ['!*', 'other_thing', '*'], ['*', 'third_thing']]
- command: 'command'
- verbs: [['*', 'thing', '*'], ['!*', 'other_thing', '*'], ['*', 'third_thing']]
[0]:
['*', 'thing', '*']
- value: 'thing'
[1]:
['!*', 'other_thing', '*']
- value: 'other_thing'
[2]:
['*', 'third_thing']
- value: 'third_thing'
您在使用解析操作来添加有关动词类型的信息的正确轨道上,但不是返回该值,而是希望将动词的类型添加为另一个命名结果。
def add_type_parse_action(verb_type):
def pa(s, l, t):
t[0]["type"] = verb_type
return pa
contains.addParseAction(add_type_parse_action("contains"))
not_contains.addParseAction(add_type_parse_action("not_contains"))
starts_with.addParseAction(add_type_parse_action("starts_with"))
添加解析操作后,您将获得以下结果:
[['command', ['*', 'thing', '*'], ['!*', 'other_thing', '*'], ['*', 'third_thing']]]
[0]:
['command', ['*', 'thing', '*'], ['!*', 'other_thing', '*'], ['*', 'third_thing']]
- command: 'command'
- verbs: [['*', 'thing', '*'], ['!*', 'other_thing', '*'], ['*', 'third_thing']]
[0]:
['*', 'thing', '*']
- type: 'contains'
- value: 'thing'
[1]:
['!*', 'other_thing', '*']
- type: 'not_contains'
- value: 'other_thing'
[2]:
['*', 'third_thing']
- type: 'starts_with'
- value: 'third_thing'
您还可以定义类来为您的结果提供结构。由于类被“调用”就好像它是一个解析操作,Python 将使用解析的标记构造一个类实例:
class VerbBase:
def __init__(self, tokens):
self.tokens = tokens[0]
@property
def value(self):
return self.tokens.value
def __repr__(self):
return "{}(value={!r})".format(type(self).__name__, self.value)
class Contains(VerbBase): pass
class NotContains(VerbBase): pass
class StartsWith(VerbBase): pass
contains.addParseAction(Contains)
not_contains.addParseAction(NotContains)
starts_with.addParseAction(StartsWith)
result = directive.parseString(example)
print(result.dump())
现在结果在对象实例中,其类型表明使用了哪种动词:
[['command', [Contains(value='thing'), NotContains(value='other_thing'), StartsWith(value='third_thing')]]]
[0]:
['command', [Contains(value='thing'), NotContains(value='other_thing'), StartsWith(value='third_thing')]]
- command: 'command'
- verbs: [Contains(value='thing'), NotContains(value='other_thing'), StartsWith(value='third_thing')]
注意:在整个问题中,您将命令后面的项目称为“动词”,我在此答案中保留了该名称,以便更容易与您的初始尝试进行比较。但通常,“动词”会指代一些动作,比如指令中的“命令”,而后面的项目更像是“限定词”或“参数”。名称在编码时很重要,不仅在与他人交流时,甚至在形成您自己的代码正在做什么的心理概念时。对我来说,这里的“动词”,通常是句子中的动作,更像是命令,而下面的部分我称之为“限定词”、“参数”或“主语”。
推荐阅读
- python - Django 框架 - 填充模型
- redis - 在 Redis 中查询频率统计信息?
- python - 删除一个词,除非它是另一个词的一部分
- docker - 使用 MinGW 编译的 Qt5 构建 Docker 映像在从“docker:latest”映像运行的容器中工作,但在 GitLab CI 中失败
- php - simplexml_load_string 不接受单个 XML 标记
- azure - PowerShell Azure 函数在执行时停止,没有错误消息
- grep - 仅使用“grep -v”完全匹配删除单词
- android - Android MY_PACKAGE_REPLACED 未在发布模式下调用
- perl - 无法使用 cpan 安装 perl 模块
- perl - 如何使用 Applescript 或 Perl 查询 Exchange 全局地址列表?