Где мне провести черту между лексером и парсером? - PullRequest
11 голосов
/ 19 марта 2011

Я пишу лексер для протокола IMAP в образовательных целях, и я озадачен тем, где я должен провести черту между лексером и парсером. Возьмите этот пример ответа сервера IMAP:

* FLAGS (\Answered \Deleted)

Этот ответ определяется в формальном синтаксисе следующим образом:

mailbox-data   = "FLAGS" SP flag-list
flag-list      = "(" [flag *(SP flag)] ")"
flag           = "\Answered" / "\Deleted"

Поскольку они указываются в виде строковых литералов (или «терминальных» токенов), для лексера было бы правильнее выдавать уникальный токен для каждого, например:

(TknAnsweredFlag)
(TknSpace)
(TknDeletedFlag)

Или было бы так же правильно испускать что-то вроде этого:

(TknBackSlash)
(TknString "Answered")
(TknSpace)
(TknBackSlash)
(TknString "Deleted")

Я путаюсь с тем, что предыдущий метод мог бы усложнить лексер - если бы \Answered имел два значения в двух разных контекстах, лексер не испустил бы правильный токен. В качестве надуманного примера (такой ситуации не будет, потому что адреса электронной почты заключены в кавычки), как лексер будет иметь дело с адресом электронной почты, таким как \ Anspted@googlemail.com? Или формальный синтаксис предназначен для того, чтобы никогда не допустить возникновения такой неоднозначности?

Ответы [ 3 ]

7 голосов
/ 19 марта 2011

Как правило, вы не хотите, чтобы лексический синтаксис распространялся в грамматике, потому что это просто детали.Например, лексер для языка программирования, такого как C, наверняка распознает числа, но вообще неуместно производить токены HEXNUMBER и DECIMALNUMBER, потому что это не важно для грамматики.

Я думаю, что вы хотитеэто самые абстрактные токены, которые позволяют вашей грамматике различать интересующие вас случаи относительно вашей цели.Вы можете быть посредником в этом путанице, вызванной в одной части грамматики, в выборе, который вы можете сделать в других частях.

Если ваша цель - просто прочитать значения флага, то на самом деле вам не нужночтобы различать их, достаточно использовать TknFlag без связанного содержимого.

Если ваша цель - обрабатывать значения флагов по отдельности, вам необходимо знать, есть ли у вас указания ANSWERED и / или DELETED.Как они пишутся лексически, не имеет значения;так что я бы пошел с вашим решением TknAnsptedFlag.Я бы выбросил TknSpace, потому что в любой последовательности флагов должны быть промежуточные пробелы (ваша спецификация так скажет), поэтому я бы попытался исключить использование любых механизмов подавления пробелов, которые вы предлагаете. Lxer

ИногдаЯ сталкиваюсь с ситуациями, когда существуют десятки подобных флагов.Тогда ваша грамматика начинает загромождаться, если у вас есть токен для каждого.Если грамматике не нужно знать конкретные флаги, то у вас должен быть TknFlag со связанным строковым значением.Если грамматика нуждается в небольшом подмножестве флагов для различения, но большинство из них - нет, то вам следует пойти на компромисс: иметь отдельные токены для тех флагов, которые имеют значение для грамматики, и перехватить весь TknFlag со связанной строкой для остальных.

Что касается сложности наличия двух разных интерпретаций: это один из тех компромиссов.Если у вас есть такая проблема, то ваши токены должны быть достаточно точными в обоих местах, где они нужны в грамматике, чтобы вы могли различать.Если «\» относится к токену где-то еще в грамматике, вы, безусловно, можете создать и TknBackSlash, и TknAnspted.Однако, если способ обработки чего-либо в одной части грамматики отличается от другого, вы часто можете обойти это, используя управляемый режимом лексер.Думайте о модах как о конечном автомате, у каждого из которых есть связанный (суб) лексер.Переходы между режимами инициируются токенами, которые являются сигналами (у вас должен быть токен FLAGS; это точно такой сигнал, что вы собираетесь собирать значения флагов).В режиме вы можете создавать токены, которые другие режимы не производят;таким образом, в одном режиме вы можете создавать жетоны "\", но в режиме вашего флага вам это не нужно.Поддержка режима довольно распространена в лексерах, потому что эта проблема встречается чаще, чем вы могли ожидать.Для примера см. Документацию Flex.

Тот факт, что вы задаете вопрос, показывает, что вы на правильном пути, чтобы сделать правильный выбор.Вам необходимо сбалансировать цель обслуживания по минимизации токенов (технически вы можете анализировать использование токена для любого символа ASCII!) С фундаментальной потребностью достаточно хорошо различать для ваших нужд.После того, как вы построите дюжину грамматик, этот компромисс покажется простым, но я думаю, что предоставленные мною эмпирические правила довольно хороши.

1 голос
/ 19 марта 2011

Сначала я придумал бы CFG, и любые терминалы, которые ему нужны для работы, должны распознавать лексеры;в противном случае вы просто угадываете правильный способ токенизации строки.

0 голосов
/ 19 марта 2011

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

...