Megaparsec,使用 StateT 和 ParsecT 回溯用户状态
Megaparsec, backtracking user state with StateT and ParsecT
使用百万秒差距 5。
在this guide之后,我可以通过组合StateT
和ParsecT
(非定义类型应该是obvious/irrelevant)来实现回溯用户状态:
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 中引入的功能)并将用户状态封装在其中?
您可以尝试将 ParserT
夹在两个 State
之间,例如
type MyParser a = StateT UserState (ParsecT Dec T.Text (State UsersState)) a
并编写专用 put
和 modify
操作,在更改外部状态后,使用 put
将整个状态复制到内部 State
monad 中。
这样,即使解析失败,您也可以从内部 State
monad 获得最后一个 "state before failure"。
您可以将自定义错误组件与 observing
函数结合用于此目的(有关详细信息,请参阅 this great post):
{-# 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
returns a 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")
实际上你可能想做一些更聪明的事情(例如我想你也可以添加你在遍历堆栈时经历的整个状态堆栈)。
使用百万秒差距 5。
在this guide之后,我可以通过组合StateT
和ParsecT
(非定义类型应该是obvious/irrelevant)来实现回溯用户状态:
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 中引入的功能)并将用户状态封装在其中?
您可以尝试将 ParserT
夹在两个 State
之间,例如
type MyParser a = StateT UserState (ParsecT Dec T.Text (State UsersState)) a
并编写专用 put
和 modify
操作,在更改外部状态后,使用 put
将整个状态复制到内部 State
monad 中。
这样,即使解析失败,您也可以从内部 State
monad 获得最后一个 "state before failure"。
您可以将自定义错误组件与 observing
函数结合用于此目的(有关详细信息,请参阅 this great post):
{-# 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
returns a 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")
实际上你可能想做一些更聪明的事情(例如我想你也可以添加你在遍历堆栈时经历的整个状态堆栈)。