我无法弄清楚如何调试用Parsec编写的解析器。最奇怪的是执行的顺序。我有以下代码:
import Data.Text (pack)
import Debug.Trace
import Text.Parsec.Text (Parser)
import Text.Parsec
material :: Parser MTL
material = do
name <- many finishLine >> string "newmtl" >> whitespaces >> manyTill anyChar (try finishLine)
illumCmds <- parseIllum
texMapCmds <- parseTexMaps
let mtl = constructMaterial name illumCmds texMapCmds
traceShow mtl $ return ()
return mtl
parseMTL :: FilePath -> IO [MTL]
parseMTL filepath = do
s <- readFile filepath
case parse (many material) filepath (pack s) of
Left x -> error $ show x
Right x -> return x
loadMTL :: FilePath -> IO ()
loadMTL fp = do
putStrLn $ "Start loading MTL: " ++ show fp
parseMTL fp >>= print
putStrLn $ "Finished."
在我的计划中,我在文件上调用了loadMTL
,并且我收到了以下错误:
lobjview.exe: Text.ParserCombinators.Parsec.Prim.many:
combinator 'many' is applied to a parser that accepts an empty string.
所以我认为在我的程序中的某处有一个错误,我希望通过使用trace
我可以缩小它(其中显示thunk将强制它被评估,它将解决所有先前解析直到调用trace
)。但是,我得到了一些我不太了解的行为。我的输出如下:
... other stuff ...
MTL { ... everything looks good here ... }
... other stuff ...
Loading MTL: filepath.mtl
lobjview.exe: Text.ParserCombinators.Parsec.Prim.many:
combinator 'many' is applied to a parser that accepts an empty string.
所以我的第一个问题是:
trace
会在IO
monad的输出之前显示?我决定分析我的代码并使用+RTS -xc
运行它以查看此错误来自的堆栈跟踪。最好的猜测是:
Text.Parsec.Prim.CAF
--> evaluated by: Lambency.Loaders.MTLLoader.whitespace,
called from Lambency.Loaders.MTLLoader.finishLine.comment,
called from Lambency.Loaders.MTLLoader.finishLine.endMarker,
called from Lambency.Loaders.MTLLoader.finishLine,
这对应于此代码:
whitespace :: Parser Char
whitespace = tab <|> char ' '
whitespaces :: Parser [Char]
whitespaces = many whitespace
finishLine :: Parser ()
finishLine = many (whitespace <|> comment) >> endMarker >> return ()
where
endMarker = endOfLine
comment :: Parser Char
comment = char '#' >> manyTill anyChar (try $ lookAhead endMarker) >> return '_'
所以我的第二个问题是:
我的代码链接可以在这里找到:
https://github.com/Mokosha/Lambency/blob/MTLLoader/lib/Lambency/Loaders/MTLLoader.hs#L555
这段代码正如我所期望的那样运行,但我主要是通过猜测来达到这个解决方案。我仍然喜欢原始问题的答案。此代码与问题代码之间的主要区别是:
material = do
已更改为material = try $ do
many material
已更改为many material >>= (\x -> many finishLine >> eof >> return x)
也许这可以首先解释造成问题的原因。此代码解析的数据文件示例如下:
# Default material file. Created by Morgan McGuire and released into
# the Public Domain on July 16, 2011.
#
# http://graphics.cs.williams.edu/data
newmtl default
Ns 10.0000
Ni 1.5000
Tr 0 0
illum 2
Ka 1 1 1
Kd 1 1 1
Ks 0.2 0.2 0.2
Ke 0 0 0
map_Kd default.png