Разбор кавычек с помощью Haskell - PullRequest
       44

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

0 голосов
/ 13 сентября 2018

Требования взяты из спецификации языка 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 Ответ

0 голосов
/ 13 сентября 2018

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

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

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

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

"foo \"bar"

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

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

\" представляет литерал ", а \\ представляет литерал \.

...