Разбор кавычек с помощью 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\""

\" представляет собой буквальный " а также \\ представляет собой буквальный \,

Другие вопросы по тегам