Attoparsec hangs



我目前正在解决AOC第四项任务,其中有以下输入格式,一行用逗号分隔的数字,然后是5x5矩阵:

27,14,70,7,85,66,65
31 23 52 26  8
27 89 37 80 46
97 19 63 34 79
13 59 45 12 73
42 25 22  6 39
27 71 24  3  0
79 42 32 72 62
99 52 11 92 33
38 22 16 44 39
35 26 76 49 58
27 71 24 ...

我有以下解析器:

{-# LANGUAGE TupleSections #-}
module Lib4Parse (parseAll) where
import Control.Applicative
import Control.Monad
import Data.Attoparsec.ByteString.Char8 (Parser, Result, char, count, decimal, eitherResult, endOfInput, endOfLine, isDigit, isEndOfLine, isSpace, many', many1, option, parse, parseOnly, parseTest, scientific, sepBy, signed, skipMany, skipMany1, skipSpace, skipWhile, space, takeTill)
import qualified Data.ByteString as B
newtype BoardEntry = MkBoardEntry (Bool, Int) deriving (Show, Eq)
newtype Board = MkBoard [[BoardEntry]] deriving (Show, Eq)
parseAll :: B.ByteString -> Maybe ([Int], [Board])
parseAll input =
either (const Nothing) Just $
parseOnly ((,) <$> inputNumbers <*> boards) input
inputNumbers :: Parser [Int]
inputNumbers = decimal `sepBy` char ','
boardEntry :: Parser [BoardEntry]
boardEntry = map (x -> MkBoardEntry (False, x)) <$> (decimal `sepBy` many1 (char ' '))
board :: Parser Board
board =
MkBoard <$> count 5 (boardEntry <* (skipWhile isSpace <|> endOfInput))
boards :: Parser [Board]
boards = takeTill isDigit >> many board

问题是,当我在boards函数中使用many时,它会挂起(当调用parseAll函数时(。但是,如果董事会的功能看起来像这样,它就会起作用:

boards :: Parser [Board]
boards = takeTill isDigit >> count 5 board

然而,我想解析所有的板,而不仅仅是指定数量的板。

问题

这是一个什么问题,如何解决?

如何调试这样的解析器?-我无法调试它,因为我不知道解析器在做什么。调试这样的解析器有什么技巧和窍门吗?

根据@Carl的评论,问题是board可以匹配空输入,所以many board在到达输入末尾时试图解析无限多的空板。具体地说,一个board匹配五个boardEntry,但boardEntry可以匹配零个数字(即空字符串(。如果所有行都应该那么长,您可以要求它匹配五个数字,也可以将sepBy升级为sepBy1,以确保至少匹配一个数字:

boardEntry :: Parser [BoardEntry]
boardEntry = map (x -> MkBoardEntry (False, x)) <$> (decimal `sepBy1` many1 (char ' '))

我调试解析器的常用方法是孤立地运行小部件:

> :set -XOverloadedStrings
> parseOnly boardEntry "1 2 3 4 5n"
Right [MkBoardEntry (False,1),...]

在这种情况下,这并没有起到多大作用。我碰巧在boardEntry中发现了这个问题。

最新更新