2016-08-22 1 views
2

Я работал над «Напишите себе схему через 48 часов», чтобы узнать Haskell, и я столкнулся с проблемой, которую я действительно не понимаю. Это вопрос 2 из упражнений внизу this section.Как выразить логику разбора в Parsec ParserT monad

Задача состоит в том, чтобы переписать

import Text.ParserCombinators.Parsec 
parseString :: Parser LispVal 
parseString = do 
       char '"' 
       x <- many (noneOf "\"") 
       char '"' 
       return $ String x 

таким образом, что кавычки, которые должным образом экранированы (например, в «Эта фраза \» нонсенс ") быть принятым анализатором.

В императивном языке Я мог бы написать что-то вроде этого (примерно вещий псевдокоде):

def parseString(input): 
    if input[0] != "\"" or input[len(input)-1] != "\"": 
    return error 
    input = input[1:len(input) - 1] # slice off quotation marks 
    output = "" # This is the 'zero' that accumulates over the following loop 
    # If there is a '"' in our string we want to make sure the previous char 
    # was '\' 

    for n in range(len(input)): 
    if input[n] == "\"": 
     try: 
     if input[n - 1] != "\\": 
      return error 
     catch IndexOutOfBoundsError: 
     return error 
    output += input[n] 
    return output 

Я смотрел на docs for Parsec, и я просто не могу понять, как решить эту проблему как монадическое выражение.

Я получил это:

parseString :: Parser LispVal 
parseString = do 
       char '"' 
       regular <- try $ many (noneOf "\"\\") 
       quote <- string "\\\"" 
       char '"' 
       return $ String $ regular ++ quote 

Но это работает только для одной кавычки, и он должен быть в самом конце строки - я не могу думать о функциональной экспрессии, которая делает работайте с моими петлями и if-statements в императивном псевдокоде.

Я ценю, что вы не торопитесь, чтобы прочитать это и дать мне совет.

ответ

3

Попробуйте что-то вроде этого:

dq :: Char 
dq = '"' 

parseString :: Parser Val 
parseString = do 
    _ <- char dq 
    x <- many ((char '\\' >> escapes) <|> noneOf [dq]) 
    _ <- char dq 
    return $ String x 
    where 
     escapes = dq <$ char dq 
      <|> '\n' <$ char 'n' 
      <|> '\r' <$ char 'r' 
      <|> '\t' <$ char 't' 
      <|> '\\' <$ char '\\' 
+1

В сторону: 'х >> вернуться a' является' а <$ x'. – user2407038

+0

О, спасибо. Я обновлю пример! –

+0

Спасибо! Я работал над сигнатурами типов и документацией, чтобы понять, почему 'x >> return a' является' a <$ x'. Есть ли причина, почему последнее выражение предпочтительнее первого здесь? И есть ли причина, по которой '_ <- char dq' предпочитается над' char dq'? (отредактировано) – lachrimae

2

Решение состоит в том, чтобы определить строковый литерал как стартовую цитату + много действительных символов + конечную цитату, где «действительный символ» является либо escape-последовательностью, либо некакой.

Так происходит смена одна линия parseString:

parseString = do char '"' 
       x <- many validChar 
       char '"' 
       return $ String x 

, и мы добавим определения:

validChar = try escapeSequence <|> satisfy (/= '"') 
escapeSequence = do { char '\\'; anyChar } 

escapeSequence может быть уточнена, чтобы ограниченный набор управляющих последовательностей.

Смежные вопросы