Parsec计算中的执行顺序

时间:2014-11-26 04:50:56

标签: haskell parsec

我无法弄清楚如何调试用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

这段代码正如我所期望的那样运行,但我主要是通过猜测来达到这个解决方案。我仍然喜欢原始问题的答案。此代码与问题代码之间的主要区别是:

  1. material = do已更改为material = try $ do
  2. many material已更改为many material >>= (\x -> many finishLine >> eof >> return x)
  3. 也许这可以首先解释造成问题的原因。此代码解析的数据文件示例如下:

    # 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
    

0 个答案:

没有答案