7

使用 Megaparsec 5。按照本指南StateT,我可以通过组合和来实现回溯用户状态ParsecT(未定义的类型应该是显而易见的/不相关的):

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 中引入的功能)并将用户状态封装在那里?

4

2 回答 2

2

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") 

实际上,您可能想要做一些更聪明的事情(例如,我想您还可以添加遍历堆栈时经历的整个状态堆栈)。

于 2016-12-19T17:56:12.217 回答
1

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

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

并编写特殊用途putmodify操作,在更改外部状态后,使用 . 将整个状态复制到内部Statemonad 中put

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

于 2016-10-01T08:34:39.567 回答