16

我正在用 Parsec 编写我的第一个程序。我想解析 MySQL 模式转储,并想想出一种很好的方法来解析代表某些关键字的字符串以不区分大小写的方式。这是一些代码,显示了我用来解析“CREATE”或“create”的方法。有一个更好的方法吗?最好不要求助于 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
4

5 回答 5

20

您可以使用字符解析器构建不区分大小写的解析器。

-- 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 ++ "\""
于 2012-10-17T16:24:41.380 回答
8

重复我在评论中所说的话,因为这显然很有帮助:

这里简单的大锤解决方案是toLower在运行解析器之前简单地映射整个输入,然后以小写形式进行所有关键字匹配。

如果您正在解析某些地方需要不区分大小写而在其他地方需要区分大小写的内容,或者您​​出于美观原因关心保留大小写,这会带来明显的困难。例如,虽然 HTML 标签不区分大小写,但在解析整个网页时将其转换为小写可能是不可取的。即使在编译不区分大小写的编程语言时,转换标识符也可能很烦人,因为任何生成的错误消息都与程序员编写的内容不匹配。

于 2012-10-17T15:43:37.343 回答
3

不,Parsec 不能以干净的方式做到这一点。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 的作者之一。

于 2015-09-25T17:12:13.553 回答
0

toLower考虑使用caseStringfrom 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:我知道这是一个古老的问题,我只是想我会添加这个,因为在我搜索类似问题时出现了这个问题

于 2015-05-24T00:16:16.327 回答
0

这个目的有一个包名 parsec-extra。你需要安装这个包然后使用' caseInsensitiveString '解析器。

 :m Text.Parsec
 :m +Text.Parsec.Extra

*> parseTest   (caseInsensitiveString  "values")   "vaLUES"
"values"

*> parseTest   (caseInsensitiveString  "values")   "VAlues"
"values"

包链接在这里: https ://hackage.haskell.org/package/parsec-extra

于 2018-09-22T04:34:49.313 回答