pyparsing 正则表达式作为单词
我正在创建一个语法解析器,用来对通过点号表示法识别的对象执行简单操作,类似这样的:
DISABLE ALL;
ENABLE A.1 B.1.1 C
但是在DISABLE ALL
中,关键字ALL
被匹配为3个Regex(r'[a-zA-Z]') => 'A', 'L', 'L'
,我用这个来匹配参数。
我该如何使用正则表达式来构建一个单词?据我所知,我无法通过单词获取A.1.1
请看下面的例子
import pyparsing as pp
def toggle_item_action(s, loc, tokens):
'enable / disable a sequence of items'
action = True if tokens[0].lower() == "enable" else False
for token in tokens[1:]:
print "it[%s].active = %s" % (token, action)
def toggle_all_items_action(s, loc, tokens):
'enable / disable ALL items'
action = True if tokens[0].lower() == "enable" else False
print "it.enable_all(%s)" % action
expr_separator = pp.Suppress(';')
#match A
area = pp.Regex(r'[a-zA-Z]')
#match A.1
category = pp.Regex(r'[a-zA-Z]\.\d{1,2}')
#match A.1.1
criteria = pp.Regex(r'[a-zA-Z]\.\d{1,2}\.\d{1,2}')
#match any of the above
item = area ^ category ^ criteria
#keyword to perform action on ALL items
all_ = pp.CaselessLiteral("all")
#actions
enable = pp.CaselessKeyword('enable')
disable = pp.CaselessKeyword('disable')
toggle = enable | disable
#toggle item expression
toggle_item = (toggle + item + pp.ZeroOrMore(item)
).setParseAction(toggle_item_action)
#toggle ALL items expression
toggle_all_items = (toggle + all_).setParseAction(toggle_all_items_action)
#swapping order to `toggle_all_items ^ toggle_item` works
#but seems to weak to me and error prone for future maintenance
expr = toggle_item ^ toggle_all_items
#expr = toggle_all_items ^ toggle_item
more = expr + pp.ZeroOrMore(expr_separator + expr)
more.parseString("""
ENABLE A.1 B.1.1;
DISABLE ALL
""", parseAll=True)
1 个回答
4
这是问题吗?
#match any of the above
item = area ^ category ^ criteria
#keyword to perform action on ALL items
all_ = pp.CaselessLiteral("all")
应该是:
#keyword to perform action on ALL items
all_ = pp.CaselessLiteral("all")
#match any of the above
item = area ^ category ^ criteria ^ all_
编辑 - 如果你感兴趣的话...
你的正则表达式(regex)看起来很相似,我想看看能不能把它们合并成一个。这里有一段代码,可以用一个正则表达式来解析你提到的三种带点的表示法,然后通过解析的动作来判断你得到了哪种类型:
import pyparsing as pp
dotted_notation = pp.Regex(r'[a-zA-Z](\.\d{1,2}(\.\d{1,2})?)?')
def name_notation_type(tokens):
name = {
0 : "area",
1 : "category",
2 : "criteria"}[tokens[0].count('.')]
# assign results name to results -
tokens[name] = tokens[0]
dotted_notation.setParseAction(name_notation_type)
# test each individually
tests = "A A.1 A.2.2".split()
for t in tests:
print t
val = dotted_notation.parseString(t)
print val.dump()
print val[0], 'is a', val.getName()
print
# test all at once
tests = "A A.1 A.2.2"
val = pp.OneOrMore(dotted_notation).parseString(tests)
print val.dump()
输出:
A
['A']
- area: A
A is a area
A.1
['A.1']
- category: A.1
A.1 is a category
A.2.2
['A.2.2']
- criteria: A.2.2
A.2.2 is a criteria
['A', 'A.1', 'A.2.2']
- area: A
- category: A.1
- criteria: A.2.2
编辑2 - 我明白原来的问题了...
让你困惑的是pyparsing的隐式空白跳过。pyparsing会跳过定义的标记之间的空白,但反过来就不一定了——pyparsing并不要求不同的解析表达式之间有空白。所以在你没有“_”的版本中,“ALL”看起来像是三个部分,“A”、“L”和“L”。这不仅适用于正则表达式,也适用于几乎所有的pyparsing类。看看pyparsing的WordEnd类是否能帮助你解决这个问题。
编辑3 - 那也许可以这样做...
toggle_item = (toggle + pp.OneOrMore(item)).setParseAction(toggle_item_action)
toggle_all = (toggle + all_).setParseAction(toggle_all_action)
toggle_directive = toggle_all | toggle_item
根据你命令的格式,解析器首先需要检查“ALL”是否被切换,然后再寻找单独的区域等等。如果你需要支持像“ENABLE A.1 ALL”这样的输入,那么可以使用负向前瞻来处理item
:item = ~all_ + (area ^ 等等...)
。(另外,我还把item + pp.ZeroOrMore(item)
替换成了pp.OneOrMore(item)
。)