2015-05-16 3 views
4

Я играю с функциональной жемчужиной Хаттон и Мейер (https://www.cs.nott.ac.uk/~gmh/pearl.pdf). С примитивных функций, определенных в нем, я сделал очень простой CSV парсер:мой парсер ленивый?

csvFile :: Parser [[String]] 
csvFile = record `sepBy` char '\n' 

record :: Parser [String] 
record = (quotedField +++ unquotedField) `sepBy` char ';' 

unquotedField :: Parser String 
unquotedField = many (sat (not . (`elem` ";\n"))) 

quotedField :: Parser String 
quotedField = do 
    char '"' 
    xs <- many (sat (not . (== '"'))) 
    char '"' 
    ys <- do { zs <- quotedField; return ('"':zs) } +++ return [] 
    return (xs++ys) 

Я пытался получить ощущение того, что было на самом деле оценивали, когда я называю этот анализатор. Таким образом, в GHCI:

*Main> let tst = "123;123;123\n123;\"123;123\";124\n124;122;125" 
*Main> let psd = parse csvFile tst 
*Main> let x = head . fst . head $ psd 
*Main> x 
["123","123","123"] 
*Main> :p psd 
psd = [(["123","123","123"] : (_t5::[[String]]),[])] 

Так я вижу, что следующий шаг разборе еще преобразователь (_t5). Однако входной поток теперь: [], поскольку он, кажется, полностью потребляется.

Куда он делся? Что я могу извлечь из этого? Я интересно, если мой парсер лень вообще ...

Edit: автономный код с просьбой:

import Control.Monad 
import Data.Char 

newtype Parser a = Parser (String -> [(a, String)]) 

parse :: (Parser a) -> (String -> [(a, String)]) 
parse (Parser p) = p 

instance Monad Parser where 
    return a = Parser (\cs -> [(a, cs)]) 
    p >>= f = Parser (\cs -> concat [parse (f a) cs' | (a, cs') <- parse p cs]) 

instance MonadPlus Parser where 
    mzero = Parser(\cs -> []) 
    mplus p q = Parser (\cs -> (parse p cs) ++ (parse q cs)) 

(+++) :: Parser a -> Parser a -> Parser a 
p +++ q = Parser (\cs -> case (parse (p `mplus` q) cs) of 
           [] -> [] 
           (x:xs) -> [x]) 

item :: Parser Char 
item = Parser (\cs -> case cs of 
         (c:nc) -> [(c, nc)] 
         _ -> []) 

sat :: (Char -> Bool) -> Parser Char 
sat f = do { c <- item ; if f c then return c else mzero } 

char :: Char -> Parser Char 
char c = sat (c ==) 

many :: Parser a -> Parser [a] 
many p = many1 p +++ (return []) 

many1 :: Parser a -> Parser [a] 
many1 p = do {t <- p; ts <- many p; return (t:ts) } 

sepBy :: Parser a -> Parser b -> Parser [a] 
p `sepBy` sep = sepBy1 p sep +++ do {x <- p; return [x]} 

sepBy1 :: Parser a -> Parser b -> Parser [a] 
p `sepBy1` sep = do { x <- p; sep; xs <- p `sepBy` sep; return (x:xs)} 

csvFile :: Parser [[String]] 
csvFile = record `sepBy` char '\n' 

record :: Parser [String] 
record = (quotedField +++ unquotedField) `sepBy` char ';' 

unquotedField :: Parser String 
unquotedField = many (sat (not . (`elem` ";\n"))) 

quotedField :: Parser String 
quotedField = do 
    char '"' 
    xs <- many (sat (not . (== '"'))) 
    char '"' 
    ys <- do { zs <- quotedField; return ('"':zs) } +++ return [] 
    return (xs++ys) 
+3

было бы неплохо, если бы вы могли ссылаться на автономную, исполняемую версию кода. –

+2

Я подозреваю, что проблема может заключаться в вашем определении «+++», которое отбрасывает все, кроме первого синтаксического анализа. Утверждение «case» строгое; он заставляет синтаксический анализ находить первый полный анализ p ++ + q, и если вы выполните еще несколько трассировок, вы можете обнаружить, что это должно сканировать до конца текста, чтобы определить, что такое действительный синтаксический анализ. «sepBy» и «many», вероятно, будут иметь эту проблему, поскольку они используют «+++», чтобы разрешить пустой синтаксический анализ. Почему бы вам не сказать «(+++) = mplus»? –

+0

@PaulJohnson: (+++) - детерминированная версия mplus. Но вы были правы, я знаю, что получаю: * Main>: p psd psd = (["123"; 1456 "," 123 "," 122 "]: (_t1 :: [[String]]), []) : (_t2 :: [([[String]], String)]). Но это не объясняет, почему ghci показывал поток и потребленный поток, или делает это? – papagaga

ответ

0

Проблема может заключаться в вашем определении «+++», которая проливает прочь все, кроме первого разбора. Утверждение «case» строгое; он заставляет синтаксический анализ находить первый полный анализ p ++ + q, и если вы выполните еще несколько трассировок, вы можете обнаружить, что это должно сканировать до конца текста, чтобы определить, что такое действительный синтаксический анализ. «sepBy» и «many», вероятно, будут иметь эту проблему, поскольку они используют «+++», чтобы разрешить пустой синтаксический анализ.

Почему бы вам не сказать «(+++) = mplus»?

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