PyParsing взгляды и жадные выражения - PullRequest
10 голосов
/ 11 февраля 2012

Я пишу синтаксический анализатор для языка запросов с использованием PyParsing, и я застрял (как я полагаю) в проблеме с прогнозом.Один тип предложения в запросе предназначен для разделения строк на 3 части (имя поля, оператор, значение), так что имя поля - это одно слово, оператор - это одно или несколько слов, а значение - это слово, строка в кавычках или список в скобках.эти.

Мои данные выглядят как

author is william
author is 'william shakespeare'
author is not shakespeare
author is in (william,'the bard',shakespeare)

И мой текущий синтаксический анализатор для этого предложения записывается как:

fieldname = Word(alphas)

operator = OneOrMore(Word(alphas))

single_value = Word(alphas) ^ QuotedString(quoteChar="'")
list_value = Literal("(") + Group(delimitedList(single_value)) + Literal(")")
value = single_value ^ list_value

clause = fieldname + originalTextFor(operator) + value

Очевидно, что это не удается из-за того факта, что operator элемент жадный и сожрет value, если сможет.Прочитав другие подобные вопросы и документы, я понял, что мне нужно управлять этим прогнозом с помощью NotAny или FollowedBy, но я не смог понять, как заставить это работать.

1 Ответ

12 голосов
/ 12 февраля 2012

Это хорошее место, чтобы быть парсером.Или, точнее, заставьте парсер думать так же, как вы.Спросите себя: «В« автор - это Шекспир », откуда мне знать, что« Шекспир »не является частью оператора?»Вы знаете, что «Шекспир» - это значение, потому что оно находится в конце запроса, после него больше ничего нет.Так что слова оператора - это не просто слова альфа, - это слова альфы, за которыми не следует конец строки .Теперь встроите эту логику в ваше определение operator:

operator = OneOrMore(Word(alphas) + ~FollowedBy(StringEnd()))

И я думаю, что для вас это станет лучше.

Некоторые другие советы:

  • Я использую оператор '^' только в том случае, если будет некоторая возможная неоднозначность, например, если я собираюсь проанализировать строку с числами, которые могут быть целыми или шестнадцатеричными.Если бы я использовал Word(nums) | Word(hexnums), то мог бы неправильно обработать «123ABC» как просто «123».Изменяя «|»'^', все альтернативы будут проверены и выбрано самое длинное совпадение.В моем примере парсинга десятичных или шестнадцатеричных целых чисел я мог бы получить тот же результат, перевернув альтернативы, и сначала проверив наличие Word(hexnums).В языке запросов вы не можете перепутать строку в кавычках со значением одного слова без кавычек (одно приводит к ' или ", другое - нет), поэтому нет причин использовать '^',' | 'будет достаточно.Аналогично для value = singleValue ^ listValue.

  • Добавление имен результатов к ключевым компонентам строки запроса упрощает работу с ними позже:

    clause = fieldname("fieldname") + originalTextFor(operator)("operator") + value("value")

    Теперь вы можете получить доступ к проанализированным значениям по имени, а не по позиции разбора (что может стать сложным и подверженным ошибкам, как только вы начнете усложняться с помощью дополнительных полей и т. Д.):

    queryParts = clause.parseString('author is william')

    print queryParts.fieldname

    print queryParts.operator

...