Megaparsec,用StateT和ParsecT回溯用户状态

使用Megaparsec 5.遵循本指南,我可以通过组合StateTParsecT (非定义类型应该是显而易见的/不相关的)来实现反向跟踪用户状态:

type MyParser a = StateT UserState (ParsecT Dec T.Text Identity) a

如果我运行解析器p :: MyParser a ,就像这样:

parsed = runParser (runStateT p initialUserState) "" input

parsed的类型是:

Either (ParseError Char Dec) (a, UserState)

这意味着,如果出现错误,用户状态将丢失。

这两种情况下都有办法吗?

编辑:我可能,如果有错误,使用自定义错误组件而不是Dec(5.0中引入的功能)并封装用户状态?


您可以将自定义错误组件与observing功能结合起来用于此目的(请参阅以下伟大的帖子以获取更多信息):

{-# LANGUAGE RecordWildCards #-}

module Main where

import Text.Megaparsec
import qualified Data.Set as Set
import Control.Monad.State.Lazy

data MyState = MyState Int deriving (Ord, Eq, Show)
data MyErrorComponent = MyErrorComponent (Maybe MyState) deriving (Ord, Eq, Show)

instance ErrorComponent MyErrorComponent where
    representFail _ = MyErrorComponent Nothing 
    representIndentation _ _ _= MyErrorComponent Nothing 

type Parser = StateT MyState (Parsec MyErrorComponent String)

trackState :: Parser a -> Parser a
trackState parser = do
    result <- observing parser -- run parser but don't fail right away
    case result of
        Right x -> return x -- if it succeeds we're done here
        Left ParseError {..} -> do
            state <- get -- read the current state to add it to the error component
            failure errorUnexpected errorExpected $
                if Set.null errorCustom then Set.singleton (MyErrorComponent $ Just state) else errorCustom

在上面的剪切中, observing函数有点像捕获解析错误的try / catch块,然后读取当前状态并将其添加到自定义错误组件。 当runParser返回一个ParseError时,返回自定义错误组件。

以下演示了如何使用此功能:

a = trackState $ do
    put (MyState 6)
    string "foo"

b = trackState $ do
    put (MyState 5)
    a

main = putStrLn (show $ runParser (runStateT b (MyState 0)) "" "bar") 

实际上,你可能想要做更巧妙的事情(例如我想你也可以在遍历堆栈时添加整个堆栈状态)。


你可以尝试在两个State之间夹ParserT ,比如

type MyParser a = StateT UserState (ParsecT Dec T.Text (State UsersState)) a

并编写专用的putmodify操作,在改变外部状态后,使用put将整个状态复制到内部State monad中。

这样,即使解析失败,您也可以从内部State monad获得最后一个“失败前的状态”。

链接地址: http://www.djcxy.com/p/36491.html

上一篇: Megaparsec, backtracking user state with StateT and ParsecT

下一篇: No coverage report for inlined Kotlin methods