Parsec分离器/终止器

时间:2014-11-27 10:54:36

标签: haskell parsec

显然我太笨了,无法弄明白......

考虑以下字符串:

foobar(123, 456, 789)

我正在努力解决如何解析这个问题。特别是,

call = do
  cs <- many1 letter
  char '('
  as <- many argument
  return (cs, as)

argument = manyTill anyChar (char ',' <|> char ')')

这很有效,直到我在输入字符串的末尾添加内容,此时它会尝试将这些内容解析为下一个参数,并且当它不以逗号或括号结尾时会感到沮丧。

从根本上说,问题是逗号是分隔符,而括号是终结符。 Parsec似乎没有为此提供组合。

只是为了让事情变得更有趣,输入字符串也可以是

foobar(123, 456, ...

表示邮件不完整。似乎无法解析具有两个可能的终结符的序列,并且知道找到了哪个。 (我实际上想知道参数列表是完整还是不完整。)

有人能弄明白我是怎么爬出来的吗?

1 个答案:

答案 0 :(得分:3)

您应该从函数参数的允许字符中排除您的分隔符/终结符字符。此外,您可以使用betweensepBy使分隔符和终结符之间的区别更加清晰:

call = do
  cs <- many1 letter
  as <- between (char '(') (char ')')
      $ sepBy (many1 (noneOf ",)")) (char ',')
  return (cs, as)

然而,这可能仍然不是你想要的,因为它没有正确处理空白。您应该查看Text.Parsec.Token以获得更强大的方法来执行此操作。

修改

随着...的加入,它确实变得有点奇怪,我不认为它很适合任何 预定义的组合器,所以我们必须自己做。

让我们为结果定义一个类型:

data Args = String :. Args | Nil | Dots
  deriving Show

infixr 5 :.

这就像一个列表,但它有两种不同的&#34;空列表&#34;区分...案例。当然,您也可以使用([String], Bool)作为结果类型,但我会将其作为练习。以下假设我们有

import Control.Applicative ((<$>), (<*>), (<$), (*>))

解析器变为:

call = do
  cs <- many1 letter
  char '('
  as <- args
  return (cs, as)

args = do
      (:.) <$> arg <*> argcont
  <|> Dots <$ string "..."

arg = many1 (noneOf ".,)")

argcont =
      Nil <$ char ')'
  <|> char ',' *> args

除了空白之外,它处理一切都很好,我的原始建议是查看令牌解析器。

让我们测试一下:

GHCi> parseTest call "foobar(foo,bar,baz)"
("foobar","foo" :. ("bar" :. ("baz" :. Nil)))
GHCi> parseTest call "foobar(1,2,..."
("foobar","1" :. ("2" :. Dots))
GHCi> parseTest ((,) <$> call <*> call) "foo(1)bar(2,...)"
(("foo","1" :. Nil),("bar","2" :. Dots))