显然我太笨了,无法弄明白......
考虑以下字符串:
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, ...
表示邮件不完整。似乎无法解析具有两个可能的终结符的序列,并且知道找到了哪个。 (我实际上想知道参数列表是完整还是不完整。)
有人能弄明白我是怎么爬出来的吗?
答案 0 :(得分:3)
您应该从函数参数的允许字符中排除您的分隔符/终结符字符。此外,您可以使用between
和sepBy
使分隔符和终结符之间的区别更加清晰:
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))