Pyparsing - двусмысленность правила
Я пишу грамматику Pyparsing для преобразования креольской разметки в HTML. Я застрял, потому что есть небольшой конфликт, пытающийся разобрать эти две конструкции:
Ссылка на изображение: {{image.jpg|title}}
Игнорировать форматирование: {{{text}}}
Я анализирую ссылку на изображение следующим образом (обратите внимание, что это прекрасно преобразуется):
def parse_image(s, l, t):
try:
link, title = t[0].split("|")
except ValueError:
raise ParseFatalException(s,l,"invalid image link reference: " + t[0])
return '<img src="{0}" alt="{1}" />'.format(link, title)
image = QuotedString("{{", endQuoteChar="}}")
image.setParseAction(parse_image)
Затем я написал правило, чтобы при обнаружении {{{text}}} просто возвращать то, что находится между открывающей и закрывающей скобками, не форматируя его:
n = QuotedString("{{{", endQuoteChar="}}}")
n.setParseAction(lambda x: x[0])
Тем не менее, когда я пытаюсь запустить следующий тестовый пример:
text = italic | bold | hr | newline | image | n
print text.transformString("{{{ //ignore formatting// }}}")
Я получаю следующую трассировку стека:
Traceback (most recent call last):
File "C:\Users\User\py\kreyol\parser.py", line 36, in <module>
print text.transformString("{{{ //ignore formatting// }}}")
File "C:\Python27\lib\site-packages\pyparsing.py", line 1210, in transformString
raise exc
pyparsing.ParseFatalException: invalid image link reference: { //ignore formatting// (at char 0), (line:1, col:1)
Из того, что я понимаю, парсер встречает {{first и пытается проанализировать текст как изображение вместо текста без форматирования. Как я могу решить эту двусмысленность?
1 ответ
Проблема с этим выражением:
text = italic | bold | hr | newline | image | n
Pyparsing работает строго слева направо, без заглядывания. Используя '|' операторы, вы создаете pyparsing выражение MatchFirst, которое будет соответствовать первому совпадению всех альтернатив, даже если последующее совпадение лучше.
Вы можете изменить оценку, чтобы использовать "самое длинное совпадение", используя вместо этого оператор "^":
text = italic ^ bold ^ hr ^ newline ^ image ^ n
Это может привести к снижению производительности, поскольку каждое выражение проверяется, даже если нет возможности лучшего соответствия.
Более простое решение - просто изменить порядок выражений в вашем списке альтернатив: test for n
до image
:
text = italic | bold | hr | newline | n | image
Теперь, оценивая альтернативы, он будет искать ведущих {{{
из n
перед ведущим {{
из image
,
Это часто возникает, когда люди определяют числовые термины и случайно определяют что-то вроде:
integer = Word(nums)
realnumber = Combine(Word(nums) + '.' + Word(nums))
number = integer | realnumber
В этом случае, number
никогда не будет соответствовать realnumber
, так как первая целая часть числа будет проанализирована как целое число. Исправление, как и в вашем случае, заключается в том, чтобы либо использовать оператор "^", либо просто изменить порядок:
number = realnumber | integer