Разбор кавычек с помощью haskell

Требования взяты из Спецификация языка DOT, точнее, я пытаюсь разобрать атрибут [ID], который может быть, например,

any double-quoted string ("...") possibly containing escaped quotes (\")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, он затем, кажется, пропускает оставшуюся часть внутренней строки и просто переходит к заключительным кавычкам.

>>= return . можно заменить на <$>: identP = pack <$> identPQuoted
melpomene 13.09.2018 20:34

Блок do в identPQuoted можно записать как char '"' *> (concat <$> many inner) <* char '"'.

melpomene 13.09.2018 20:37

Можете выложить минимальный воспроизводимый пример? Я бы сам хотел попробовать.

melpomene 13.09.2018 20:37

Требование сформулировано довольно неудачно. Можете ли вы показать настоящую грамматику вашего языка ввода? Также попробуйте reads.

n. 1.8e9-where's-my-share m. 13.09.2018 20:48

Я добавил пример @melpomene и обновил требования

Vey 13.09.2018 21:26

Список символов, таких как ['\\', '\"', '0', 'n', 'r', 'v', 't', 'b', 'f'], можно записать более удобно и компактно как литерал String, например "\\\"0nrvtbf", поскольку type String = [Char]

Jon Purdy 14.09.2018 04:10

Предупреждение: список символов в nonEscape представляет собой список одиночные персонажи, то есть \n - это буквальный символ новой строки, \0 - это нулевой символ (который вы никогда не увидите в текстовом файле). Наверное, не то, что вы имели в виду.

luqui 14.09.2018 09:49

@luqui Мне нравится эта часть. В нем говорится, что строки в двойных кавычках не могут содержать буквальные управляющие символы (например, NUL, перевод строки и т. д.).

melpomene 14.09.2018 14:35
4
8
522
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш вводимый текст - "foo "bar", он не содержит экранированных кавычек. Он анализируется как полный идентификатор "foo " (за ним следует bar", который игнорируется).

Если вы хотите убедиться, что ваш парсер использует весь доступный ввод, вы можете использовать

parseTest (identP <* eof) "..."

Если вы хотите предоставить синтаксическому анализатору идентификатор с экранированной кавычкой, например ...

"foo \"bar"

... тогда вам нужно экранировать все специальные символы, чтобы встроить их в исходный код Haskell:

main = parseTest identP "\"foo \\\"bar\""

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

Спасибо! Оглядываясь назад, немного смущает.

Vey 13.09.2018 21:37

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