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
Другие вопросы по тегам