快速解析允许转义字符的字符串?

Fast parsing of string that allows escaped characters?

我正在尝试解析一个可以包含转义字符的字符串,下面是一个示例:

import qualified Data.Text as T

exampleParser :: Parser T.Text
exampleParser = T.pack <$> many (char '\' *> escaped <|> anyChar)
  where escaped = satisfy (\c -> c `elem` ['\', '"', '[', ']'])

上面的解析器创建了一个String,然后将其打包成Text。有没有什么方法可以使用 attoparsec 提供的高效字符串处理函数来解析像上面这样的带有转义符的字符串?喜欢 stringscanrunScannertakeWhile...

解析类似 "one \"two\" \[three\]" 的内容会产生 one "two" [three].

更新:

感谢@epsilonhalbe,我能够提出一个完美满足我需求的通用解决方案;请注意,以下函数不会查找匹配的转义字符,如 [..]".."(..) 等;而且,如果它发现一个无效的转义字符,它会将 \ 视为文字字符。

takeEscapedWhile :: (Char -> Bool) -> (Char -> Bool) -> Parser Text
takeEscapedWhile isEscapable while = do
  x <- normal
  xs <- many escaped
  return $ T.concat (x:xs)
  where normal = Atto.takeWhile (\c -> c /= '\' && while c)
        escaped = do
          x <- (char '\' *> satisfy isEscapable) <|> char '\'
          xs <- normal
          return $ T.cons x xs

可以编写一些转义代码,attoparsectext - 总的来说它非常简单 - 看到你已经使用过解析器

import Data.Attoparsec.Text as AT
import qualified Data.Text as T
import Data.Text (Text)

escaped, quoted, brackted :: Parser Text
normal =  AT.takeWhile (/= '\')
escaped = do r <- normal
             rs <- many escaped'
             return $ T.concat $ r:rs
  where escaped' = do r1 <- normal
                      r2 <- quoted <|> brackted
                      return $ r1 <> r2

quoted = do string "\\""
            res <- normal
            string "\\""
            return $ "\""<>res <>"\""

brackted = do string "\["
              res <- normal
              string "\]"
              return $ "["<>res<>"]"

那么你可以用它来解析下面的测试用例

Prelude >: MyModule
Prelude MyModule> import Data.Attoparsec.Text as AT
Prelude MyModule AT> import Data.Text.IO as TIO
Prelude MyModule AT TIO>:set -XOverloadedStrings
Prelude MyModule AT TIO> TIO.putStrLn $ parseOnly escaped "test"
test
Prelude MyModule AT TIO> TIO.putStrLn $ parseOnly escaped "\\"test\\""
"test"
Prelude MyModule AT TIO> TIO.putStrLn $ parseOnly escaped "\[test\]"
[test]
Prelude MyModule AT TIO> TIO.putStrLn $ parseOnly escaped "test \\"test\\" \[test\]"
test "test" [test]

注意你必须转义 - 这就是为什么你看到 \\" 而不是 \"

此外,如果您只是解析它,它将打印转义的 Text 值,例如

Right "test \"text\" [test]"

最后一个例子。

如果您解析文件,您会在文件中写入简单的转义文本。

test.txt

I \[like\] \"Haskell\"

那么你可以

Prelude MyModule AT TIO> file <- TIO.readFile "test.txt" 
Prelude MyModule AT TIO> TIO.putStrLn $ parseOnly escaped file
I [like] "Haskell"