用Haskell解析引号

Vey*_*Vey 4 parsing haskell parsec megaparsec

这些要求来自DOT语言规范,更确切地说,我正在尝试解析[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\""
Run Code Online (Sandbox Code Playgroud)

"foo "即使我想要,上面的代码在第二次失败时返回foo "bar

我不明白为什么.我认为这megaparsec会反复应用,inner直到它解析决赛".但它只是重复应用nonEscape解析器并且第一次失败并且它使用escape,然后它似乎跳过内部字符串的其余部分并且继续移动到最终引号.

mel*_*ene 7

您的输入文本是"foo "bar",不包含任何转义引号.它被解析为完整的ID "foo "(后跟bar",被忽略).

如果要确保解析器消耗所有可用输入,可以使用

parseTest (identP <* eof) "..."
Run Code Online (Sandbox Code Playgroud)

如果你想向解析器提供带有转义引用的ID,就像这样......

"foo \"bar"
Run Code Online (Sandbox Code Playgroud)

...然后你需要转义所有特殊字符以将它们嵌入Haskell源代码中:

main = parseTest identP "\"foo \\\"bar\""
Run Code Online (Sandbox Code Playgroud)

\"代表文字"\\代表文字\.