Я пытаюсь создать синтаксический анализатор, который анализирует различные виды выражений, состоящих из строк Verilog и строк в кавычках. Чтобы заставить это работать, я использую конструкцию MatchFirst. Одна икота, с которой я сталкиваюсь, заключается в том, что я не знаю, как создать слово, которое не соответствует, если за ним следуют определенные символы.
Краткая версия проблемы
Предположим, мне нужно слово, которое может принимать символы «А» и «В», но не в том случае, если за ними следует любая другая буква. Таким образом, они должны совпадать:
A
AB
BA
BAABBABABABA
Но это не должно совпадать: BABC
В настоящее время синтаксический анализатор в конечном итоге частично совпадает, что искажает результат.
Длинная версия проблемы
Этот вопрос связан с предыдущим вопросом, который я задал: python pyparsing ^ vs | ключевые слова
Ниже приведен тестовый пример python3, иллюстрирующий проблему. ПРИМЕЧАНИЕ. Если бы мне пришлось изменить синтаксический анализатор с использования конструкции MatchFirst на ИЛИ, тестовый пример прошел бы успешно. т.е. 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?
Прецедент
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
Итак, здесь тестовая строка интерпретируется как число Verilog 'HA
, равное 10, вместо строки в кавычках: 'HARDWARE'
Я пробовал возиться с аргументом ключевого слова asKeyword
, но мне не повезло с этим.
РЕДАКТИРОВАТЬ
Основываясь на помощи Пола, я добавил дополнительные проверки в тестовый пример для дальнейшего уточнения решения. Я воспользовался предложением Пола добавить asKeyword=True
в определение для 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
, похоже, также испортит синтаксический анализ чисел, в отличие от строк в кавычках.