Разбор кавычек с помощью Haskell
Требования взяты из спецификации языка 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\""
Приведенный выше код не выполняется на втором с возвратами "foo "
хотя я хочу foo "bar
Я не понимаю почему. я думал так megaparsec
будет неоднократно применять inner
пока не разбирает финал "
, Но это только неоднократно применяется nonEscape
Парсер и первый раз, когда не удается, и он использует escape
затем он пропускает оставшуюся внутреннюю строку и просто переходит к последним кавычкам.
1 ответ
Ваш вводимый текст "foo "bar"
, который не содержит экранированных кавычек. Он анализируется как полный идентификатор "foo "
(с последующим bar"
, который игнорируется).
Если вы хотите убедиться, что ваш синтаксический анализатор использует все доступные входные данные, вы можете использовать
parseTest (identP <* eof) "..."
Если вы хотите предоставить парсеру идентификатор с экранированной кавычкой, вот так...
"foo \"bar"
... тогда вам нужно экранировать все специальные символы, чтобы встроить их в исходный код на Haskell:
main = parseTest identP "\"foo \\\"bar\""
\"
представляет собой буквальный "
а также \\
представляет собой буквальный \
,