【问题标题】:Passing State from a Producer to a Parser将状态从生产者传递给解析器
【发布时间】: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,它需要是自动解析ByteString Parser。我通过Producer 的参数找出了在runParser 的错误中返回的哪一个。
  • @danidiaz 来自 attoparsec (Data.Attoparsec.ByteString)。

标签: haskell haskell-pipes attoparsec


【解决方案1】:

我希望每次processCommand 运行时都会运行runParser(它将从State 获取最新内容),但显然不是这样。

你的主要作用是for runParser (liftIO . BS.putStrLn &lt;=&lt; lift . processCommand)。要了解这种效果,您需要了解for 的作用:

(for p body) 循环遍历 p 将每个 yield 替换为 body

"Loops over p" 是准确的,如果有点混乱的话。它不会为p 生成的每个值运行一次p;那会爆炸的!而forp 中的每个yield 替换为body。通过将yield 替换为body,它会为每个yielded 值运行一次body。为每个生成的值运行一次正文类似于在其他语言中,for 循环 over 列表为列表中的每个值运行一次正文。

你的runParser

runParser = do
    s <- lift get
    liftIO $ putStrLn "runParser"
    liftIO $ putStrLn $ show s
    parsed (evalStateT dump' s) PBS.stdin

它读取状态,输出状态,并从stdin 生成Commands parsed。 Pipes-autoparsec 的 parsed 解析源代码,yields 为每个完全成功解析的值解析一次。然后您的forparsedyields 中的每一个替换为liftIO . BS.putStrLn &lt;=&lt; lift . processCommand。完整的效果运行runParser 一次processCommand 每个yield 一次,这是您在输出中观察到的。

【讨论】:

  • 这确实解释了我所看到的行为,但是否有可能实现我正在寻找的行为?如果需要,我准备重写我的解析器以改变工作方式,但我希望为自己节省几个小时的工作时间。
  • @cimmanon 从你的问题中我不确定你在寻找什么行为。也许更容易提出两个问题,“为什么我的代码会这样做”?关注现有代码的描述 - 就像这个一样,以及“我怎样才能让它做 X”?专注于你想要完成的 X 的描述。
  • @cimmanon 但这里有一个提示:如果你想在解析完所有 Commands 之后在最后做其他事情,请尝试在 runParser 的末尾添加一些行(比如s' &lt;- lift get; liftIO $ putStrLn "runParser end"; liftIO $ putStrLn $ show s') 看看这是不是你想要的。或者在theStuffdoStuff 的末尾,如果这样更有意义的话。
  • 它是插入分支下游的解析器,需要访问到目前为止解析的表定义,以便我可以为每种列类型运行正确的解析器(几乎可以保证创建表,然后插入对于该表,但我正在捕获的其他创建语句可能出现在类似索引之间)。生成我正在解析的转储的数据库产生无效的日期和二进制类型的格式错误......它们看起来与普通的带引号的字符串没有区别。
  • 如果你不知道我是否在问“为什么 X 这样做?”或“我该如何做 X?”,您应该要求澄清而不是发布答案。因为我的问题从来不是“为什么 X 这样做?”,所以我已经可以根据输出推断行为。
猜你喜欢
  • 2019-03-01
  • 2020-10-21
  • 2019-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-08
  • 1970-01-01
  • 2018-08-25
相关资源
最近更新 更多