需求取自DOT language specification,更准确地说,我正在尝试解析[ID]
属性,例如,
任何可能包含转义引号(\“)1的双引号字符串(” ...“);
以下应该是一个最小的示例。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Text.Megaparsec
import Text.Megaparsec.Char
import Data.Void
import Data.Char
import Data.Text hiding ( map
, all
, concat
)
type Parser = Parsec Void Text
escape :: Parser String
escape = do
d <- char '\\'
c <- oneOf ['\\', '\"', '0', 'n', 'r', 'v', 't', 'b', 'f']
return [d, c]
nonEscape :: Parser Char
nonEscape = noneOf ['\\', '\"', '\0', '\n', '\r', '\v', '\t', '\b', '\f']
identPQuoted :: Parser String
identPQuoted =
let inner = fmap return (try nonEscape) <|> escape
in do
char '"'
strings <- many inner
char '"'
return $ concat strings
identP :: Parser Text
identP = identPQuoted >>= return . pack
main = parseTest identP "\"foo \"bar\""
上面的代码第二次失败,并返回"foo "
,即使我想要foo "bar
我不明白为什么。我认为megaparsec
将反复应用inner
直到解析最后的"
。但是它仅重复应用nonEscape
解析器,并且第一次失败,并且使用escape
,然后它似乎跳过了内部字符串的其余部分,而直接移至最终引号。>
答案 0 :(得分:6)
您输入的文本为"foo "bar"
,其中不包含任何转义的引号。它被解析为"foo "
的完整ID(后跟bar"
,将被忽略)。
如果要确保解析器使用所有可用输入,可以使用
parseTest (identP <* eof) "..."
如果要向解析器提供带有转义引号的ID,例如...
"foo \"bar"
...那么您需要转义所有特殊字符以将它们嵌入Haskell源代码:
main = parseTest identP "\"foo \\\"bar\""
\"
代表文字"
,而\\
代表文字\
。