【发布时间】:2017-05-01 13:56:43
【问题描述】:
我正在使用管道、attoparsec 和管道-attoparsec 来编写数据库转储文件转换器。文件的一般格式是有一个创建表命令,后跟一个可选的插入命令。除了就地转换语句之外,表定义还必须保存在内存中,直到最后进行额外处理(索引、约束等)。
这工作正常,但现在我需要允许我的一些内部解析器访问我的生产者状态,以便确定在处理来自插入命令的值时需要运行哪个解析器。
我尝试过这样的事情:
-- IO
import qualified Data.ByteString.Char8 as BS (putStrLn)
import System.Exit (ExitCode (..), exitSuccess, exitFailure)
import System.IO (hPutStrLn, stderr)
-- Pipes
import Pipes (runEffect, for, liftIO, Producer, Effect)
import Pipes.Attoparsec (parsed, ParsingError)
import Pipes.Lift (runStateP)
import Pipes.Safe (runSafeT)
import qualified Pipes.ByteString as PBS (stdin)
-- State
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State.Strict
dump' :: StateT ParserState Parser Command
dump' = fmap Create createStatements' <|> fmap Insert justData'
doStuff :: MonadIO m => Effect m (Either (ParsingError, Producer ByteString (StateT ParserState m) ()) (), ParserState)
doStuff = runStateP defaultParserState theStuff
theStuff :: MonadIO m => Effect (StateT ParserState m) (Either (ParsingError, Producer ByteString (StateT ParserState m) ()) ())
theStuff = for runParser (liftIO . BS.putStrLn <=< lift . processCommand)
runParser :: MonadIO m => Producer Command (StateT ParserState m) (Either (ParsingError, Producer ByteString (StateT ParserState m) ()) ())
runParser = do
s <- lift get
liftIO $ putStrLn "runParser"
liftIO $ putStrLn $ show s
parsed (evalStateT dump' s) PBS.stdin
processCommand :: MonadIO m => Command -> StateT ParserState m ByteString
processCommand (Create xs) = do
currentState <- get
liftIO $ putStrLn "processCommand"
liftIO $ putStrLn $ show currentState
_ <- put (currentState { constructs = xs ++ (constructs currentState)})
return $ P.firstPass $ P.transformConstructs xs
processCommand (Insert x) = return x
完整来源(包括解析器):https://github.com/cimmanon/mysqlnothx/blob/parser-state/src/Main.hs
当我运行它时,我得到的结果如下所示:
runParser
ParserState {constructs = []}
processCommand
ParserState {constructs = []}
processCommand
ParserState {constructs = [ ... ]}
processCommand
ParserState {constructs = [ ..... ]}
我希望每次 processCommand 运行时都会运行 runParser(它将从 State 中获取最新内容),但根据输出显然不是这种情况。当我在解析器中检查 State 的内容时,无论解析多少命令,它总是为空。
如何将状态从我的生产者扩展到我的解析器(转储'),以便它们共享相同的状态?如果我的 Producer 在 State 中有 4 个值,解析器也应该看到这 4 个值。
【问题讨论】:
-
你从哪里得到你的
Parser类型? -
@danidiaz 对于
pipes-autoparsec,它需要是自动解析ByteStringParser。我通过Producer的参数找出了在runParser的错误中返回的哪一个。 -
@danidiaz 来自 attoparsec (Data.Attoparsec.ByteString)。
标签: haskell haskell-pipes attoparsec