我正在尝试创建一个解析器,解析由verilog字符串和引号字符串组成的不同类型的表达式。为了使其正常工作,我使用了MatchFirst构造。我遇到的一个问题是,我不知道如何创建一个不匹配的单词,如果后面跟着某些字符。
这个问题的简短版本
让我们假设,我想要一个词,可以接受字符'A‘和'B’,但如果他们后面有任何其他字母。所以这些应该是匹配的:
A
AB
BA
BAABBABABABA但这不应该匹配:BABC
当前,解析器以部分匹配结束,这将导致结果混乱。
这个问题的长篇版本
这个问题与我之前问过的一个问题有关:python pyparsing "^" vs "|" keywords
下面是一个说明问题的python3测试案例。注意到如果我必须将解析器从使用MatchFirst构造更改为OR,则测试用例通过。也就是说,parser = (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal")) ^ pp.quotedString而不是parser = (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal")) | pp.quotedString,但是这也构成了一个更复杂的解析器的一部分,而且(我认为)我需要优先级来使它工作。
因此,最终的问题是,我如何才能让这场比赛不依赖于OR的“最长”匹配选择性?
TestCase
import unittest
import pyparsing as pp
def _get_verilog_num_parse():
"""Get a parser that can read a verilog number
return: Parser for verilog numbers
rtype: PyParsing parser object
See this link where I got help with geting this parser to work:
https://stackoverflow.com/questions/34258011/python-pyparsing-vs-keywords
"""
apos = pp.Suppress(pp.Literal("'"))
size_num = pp.Word(pp.nums+'_' ).setParseAction(lambda x:int(x[0].replace('_', ''),10))
#dec_num = pp.Word(pp.nums+'_' , asKeyword=True).setParseAction(lambda x:int(x[0].replace('_', ''),10))
dec_num = pp.Word(pp.nums+'_' ).setParseAction(lambda x:int(x[0].replace('_', ''),10))
hex_num = pp.Word(pp.hexnums+'_', asKeyword=True).setParseAction(lambda x:int(x[0].replace('_', ''),16))
bin_num = pp.Word('01'+'_', asKeyword=True).setParseAction(lambda x:int(x[0].replace('_', ''),2))
size = pp.Optional(size_num).setResultsName('size')
def size_mask(parser):
size = parser.get('size')
if size is not None:
return parser['value'] & ((1<<size) -1)
else:
return parser['value']
radix_int = pp.ungroup(pp.CaselessLiteral('d').suppress() + dec_num |
pp.CaselessLiteral('h').suppress() + hex_num |
pp.CaselessLiteral('b').suppress() + bin_num)
#print(radix_int)
return (size + apos + radix_int('value')).addParseAction(size_mask)
class test_PyParsing(unittest.TestCase):
'''Check that the Expression Parser works with the expressions
defined in this test'''
def test_or(self):
"""Check basic expressions not involving referenced parameters"""
expressions_to_test = [
("8'd255",255),
("'d255",255),
("12'h200",0x200),
("'blah'","'blah'"),
("'HARDWARE'","'HARDWARE'"),
("'HA'","'HA'"),
("'b101010'","'b101010'"),
("'d1010'","'d1010'"),
("'1010'","'1010'"),
]
parser = (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal")) | pp.quotedString
for expr,expected in expressions_to_test:
result = parser.parseString(expr)
#print("result: {}, val: {}".format(result, result[0]))
self.assertEqual(expected,result[0], "test_string: {}, expected: {}, result: {}".format(expr, expected, result[0]))结果
self.assertEqual(expected,result[0], "test_string: {}, expected: {}, result: {}".format(expr, expected, result[0]))
AssertionError: "'HARDWARE'" != 10 : test_string: 'HARDWARE', expected: 'HARDWARE', result: 10因此,在这里,teststring被解释为verilog数字'HA,它是10,而不是引用的字符串:'HARDWARE'
我尝试过使用asKeyword关键字参数,但我在这方面没有任何进展。
编辑
到目前为止,基于Paul的帮助,我在测试用例中添加了额外的检查,以进一步完善解决方案。我使用了Paul的建议,将asKeyword=True添加到for hex_num的定义中,这解决了我最初的问题,然后将它添加到bin_num的定义中,这也满足了添加的检查要求:
("'b101010'","'b101010'"),
("'d1010'","'d1010'"),然后,我又添加了2张支票:
("'d1010'","'d1010'"),
("'1010'","'1010'"),然后,测试用例失败,结果如下:
self.assertEqual(expected,result[0], "test_string: {}, expected: {}, result: {}".format(expr, expected, result[0]))
AssertionError: "'d1010'" != 1010 : test_string: 'd1010', expected: 'd1010', result: 1010要尝试的逻辑是为asKeyword=True的定义添加dec_num。但这导致了一个奇怪的错误:
result = parser.parseString(expr)
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 1125, in parseString
raise exc
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 1115, in parseString
loc, tokens = self._parse( instring, 0 )
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 989, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 2497, in parseImpl
raise maxException
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 2483, in parseImpl
ret = e._parse( instring, loc, doActions )
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 989, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 2440, in parseImpl
raise maxException
pyparsing.ParseException: Expected W:(0123...) (at char 3), (line:1, col:4)备注
添加asKeyword=True似乎也会使数字解析工作变得混乱,而不是引用字符串。
发布于 2016-01-02 17:50:18
asKeyword参数用'\b'括号内的正则表达式。我认为你添加的excludeChars论点把事情搞砸了。只需将hex_num定义为:
hex_num = pp.Word(pp.hexnums+'_', asKeyword=True).setParseAction(
lambda x:int(x[0].replace('_', ''),16))当我运行您的测试代码时,这是可行的。(我认为hexnums是3种数字中唯一一种需要这样做的数字,因为十进制和二进制对尾随字母没有任何歧义。)
FYI - excludeChars被添加到Word中,以简化定义“除‘:’以外的所有可打印的东西”或“除‘q’以外的所有在幻影中的东西”的字符组。(https://pythonhosted.org/pyparsing/pyparsing.Word-class.html)
编辑
我认为问题的一部分是,我们需要查看单个表达式中的前缀h/d/b字符和数字字符,以便对数字字符做正确的处理。我们希望在数字之后强制中断,而不是在前面的前缀和数字之间。恐怕最好的方法就是使用Regex。下面是一组表达式,它将前缀和数字组合成一个等价的正则表达式,并添加了尾随但不领先的单词中断:
make_num_expr = lambda prefix,numeric_chars,radix: pp.Regex(r"[%s%s](?P<num>[%s_]+)\b" %
(prefix,prefix.upper(),numeric_chars)).setParseAction(
lambda x: int(x.num.replace('_',''), radix))
dec_num = make_num_expr('d', pp.nums, 10).setName("dec_num")
hex_num = make_num_expr('h', pp.hexnums, 16).setName("hex_num")
bin_num = make_num_expr('b', '01', 2).setName("bin_num")
radix_int = (dec_num | hex_num | bin_num).setName("radix_int")注意Regex的数值字段使用了命名组num。我还添加了setName调用,这些调用现在更重要了,因为Or和MatchFirst (正确地)枚举了所有选项( i11n )它们的异常消息。
编辑(2)
刚刚注意到我们在'HA'上失败了,我认为如果您更改解析器替代方案的顺序,就可以解决这个问题:
parser = pp.quotedString | (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal"))https://stackoverflow.com/questions/34565305
复制相似问题