是我的解析器懒惰?

时间:2015-05-16 09:58:00

标签: parsing haskell lazy-evaluation ghci

我正在玩Hutton和Meijer(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]]),[])]

所以我看到下一个解析步骤仍然是一个thunk(_t5)。但是输入流现在是:[] - 它似乎已被完全消耗。

它去了哪里?我应该从中推断出什么?我想知道我的解析器是否完全懒惰......

编辑:请求的自包含代码:

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)

1 个答案:

答案 0 :(得分:1)

问题可能出在你对#34; +++&#34;的定义中,除了第一个解析之外,它会丢弃所有。 &#34;案例&#34;声明严格;它强制解析器找到p +++ q的第一个完整解析,如果你再做一些跟踪,你可能会发现这必须扫描到文本的末尾才能确定有效的解析是什么。 &#34; sepBy&#34;和&#34;很多&#34;可能会有这个问题,因为他们使用&#34; +++&#34;允许空解析。

为什么不说'&#34;(+++)= mplus&#34; ?