Какой самый чистый способ делать безразличный синтаксический разбор с Text.Combinators.Parsec?
Я пишу свою первую программу с Parsec. Я хочу разбирать дампы схемы MySQL и хотел бы придумать хороший способ анализировать строки, представляющие определенные ключевые слова, в режиме без учета регистра. Вот некоторый код, показывающий подход, который я использую, чтобы разобрать "СОЗДАТЬ" или "создать". Есть лучший способ сделать это? Ответ, который не прибегает к buildExpressionParser, был бы лучшим. Я принимаю детские шаги здесь.
p_create_t :: GenParser Char st Statement
p_create_t = do
x <- (string "CREATE" <|> string "create")
xs <- manyTill anyChar (char ';')
return $ CreateTable (x ++ xs) [] -- refine later
Ответы
Ответ 1
Вы можете построить анализатор, нечувствительный к регистру, из парсеров символов.
-- Match the lowercase or uppercase form of 'c'
caseInsensitiveChar c = char (toLower c) <|> char (toUpper c)
-- Match the string 's', accepting either lowercase or uppercase form of each character
caseInsensitiveString s = try (mapM caseInsensitiveChar s) <?> "\"" ++ s ++ "\""
Ответ 2
Повторяя то, что я сказал в комментарии, поскольку это было очевидно полезно:
Простое решение кувалды здесь состоит в том, чтобы просто сопоставить toLower
по всему вводу перед запуском синтаксического анализатора, а затем выполнить все сопоставление ключевых слов в нижнем регистре.
Это создает очевидные трудности, если вы разбираете то, что должно быть нечувствительным к регистру в некоторых местах и чувствительным к регистру в других, или если вы заботитесь о сохранении дела по косметическим соображениям. Например, хотя теги HTML не чувствительны к регистру, преобразование всей веб-страницы в нижний регистр при ее разборке, вероятно, будет нежелательным. Даже при компиляции языка программирования, нечувствительного к регистру, преобразование идентификаторов может быть раздражающим, поскольку любые результирующие сообщения об ошибках не соответствуют тому, что написал программист.
Ответ 3
Нет, Парсек не может сделать это чистым способом. string
реализуется поверх
примитивный комбинатор tokens
, который жестко закодирован для использования теста равенства
(==)
. Это немного проще разбирать нечувствительный к регистру характер, но вы
возможно, больше.
Однако существует современная вилка Parsec, называемая
Megaparsec, который имеет
встроенные решения для всего, что вы можете пожелать:
λ> parseTest (char' 'a') "b"
parse error at line 1, column 1:
unexpected 'b'
expecting 'A' or 'a'
λ> parseTest (string' "foo") "Foo"
"Foo"
λ> parseTest (string' "foo") "FOO"
"FOO"
λ> parseTest (string' "foo") "fo!"
parse error at line 1, column 1:
unexpected "fo!"
expecting "foo"
Обратите внимание на последнее сообщение об ошибке, это лучше, чем вы можете получить синтаксический анализ
символов один за другим (особенно полезно в вашем конкретном случае). string'
реализуется так же, как Parsec string
, но использует регистр без учета регистра
сравнение с сравнениями символов. Существуют также oneOf'
и noneOf'
, что
могут быть полезны в некоторых случаях.
Раскрытие информации: Я один из авторов Megaparsec.
Ответ 4
Вместо того, чтобы отображать весь ввод с помощью toLower
, используйте caseString
из Text.ParserCombinators.Parsec.Rfc2234
(из пакета hsemail)
Text.ParsecCombinators.Parsec.Rfc2234
p_create_t :: GenParser Char st Statement
p_create_t = do
x <- (caseString "create")
xs <- manyTill anyChar (char ';')
return $ CreateTable (x ++ xs) [] -- refine later
Итак, теперь x
будет использовать любой случай-вариант во входе без изменения вашего ввода.
ps: Я знаю, что это древний вопрос, я просто подумал, что добавлю это, поскольку этот вопрос возник, когда я искал аналогичную проблему.