Какой самый чистый способ делать безразличный синтаксический разбор с Text.Combinators.Parsec? - программирование
Подтвердить что ты не робот

Какой самый чистый способ делать безразличный синтаксический разбор с 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
4b9b3361

Ответ 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: Я знаю, что это древний вопрос, я просто подумал, что добавлю это, поскольку этот вопрос возник, когда я искал аналогичную проблему.