Copyright | (c) Gregor Riegler, 2015 |
---|---|
License | MIT |
Maintainer | gregor.riegler@gmail.com |
Stability | experimental |
Safe Haskell | Safe |
Language | Haskell2010 |
Sometimes it is not trivial to understand when/why the parser state changes when you have parsers that depend partly on each other. For that reason, parsec-trace instruments Parsec parsers in such a way that a tree in the parser state is managed to trace the hierarchy of successful parsers. In addition, hooks for tracing information dependent on the parser state are provided on entering/exiting a parser.
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, NoMonomorphismRestriction #-}
module UseParsecTrace where
import Text.Parsec hiding (parse)
import qualified Text.Parsec.Trace.Tree as T
-- Parsec user state with a TraceTree
data MyState = MyState {
importantInt :: Int
, trace :: T.TraceTree String }
-- Tell me how to get to the tree/modify the tree
instance T.HasTraceTree MyState String where
getTrace x = trace x
modTrace f x = let t = trace x in x { trace = f t }
-- Specify the parsing configuration (you could use the provided lenses, too)
conf :: T.TraceConfig Expression String MyState Identity
conf = let logEn :: MyState -> Identity String
logEn = logF "On Enter: "
logEx = logF "On Exit: "
logF caption = return . (caption ++ ) . show . importantInt
in
T.setTraceFunc show $ T.setLogEnter logEn $ T.setLogExit logEx T.defaultTraceConfig
-- Use the resulting function to instrument your parsers
withTrace = T.traceWith conf
data Expression = Expr1 | Expr2 | Expr3 | Expr4 deriving (Eq, Show)
parseExpr1 = withTrace $ do
string "expr1"
char ' '
try parseExpr2 <|> parseExpr3
return $ Expr1
parseExpr2 = withTrace $ do
string "expr2"
modifyState $ x -> x { importantInt = importantInt x + 1 }
return $ Expr2
parseExpr3 = withTrace $ do
string "expr3"
return Expr3
parseExpr4 = withTrace $ do
parseExpr2
T.logP $ (MyState importantInt _) -> return $ "in parseExpr4: " ++ show importantInt
char ' '
parseExpr3
modifyState $ x -> x { importantInt = importantInt x - 2 }
return Expr4
anyParser = try parseExpr1 <|> try parseExpr4 <|> try parseExpr2 <|> parseExpr3
myparserWithState :: ParsecT String MyState Identity ([Expression], MyState)
myparserWithState = do
result <- anyParser sepBy1
char ' '
s <- getState
return (result, s)
parse text = do
result <- return . runIdentity $ runPT myparserWithState (MyState 0 T.initialTraceTree) "" text
case result of
Left e -> print e
Right (_, s) -> putStrLn . T.drawTraceTree' $ s
As a result, state transitions can thus be traced more easily than by using "ad-hoc putStrLn-style", even more so in pure Parsec parsers with an underlying Identity monad.
parse "expr1 expr2 expr3 expr2 expr3"
| +- Expr1 | | | +- On Enter: 0 | | | +- Expr2 | | | | | +- On Enter: 0 | | | | | `- On Exit: 1 | | | `- On Exit: 1 | +- Expr3 | | | +- On Enter: 1 | | | `- On Exit: 1 | `- Expr4 | +- On Enter: 1 | +- Expr2 | | | +- On Enter: 1 | | | `- On Exit: 2 | +- in parseExpr4: 2 | +- Expr3 | | | +- On Enter: 2 | | | `- On Exit: 2 | `- On Exit: 0
- type TraceTree a = TreePos a
- class IsString a => HasTraceTree t a | t -> a where
- data TraceConfig e s u m
- defaultTraceConfig :: IsString s => TraceConfig e s u m
- setTraceFunc :: IsString s => (e -> s) -> TraceConfig e s u m -> TraceConfig e s u m
- setLogEnter :: IsString s => (u -> m s) -> TraceConfig a s u m -> TraceConfig a s u m
- setLogExit :: IsString s => (u -> m s) -> TraceConfig a s u m -> TraceConfig a s u m
- _traceFunc :: (Functor f, IsString s) => ((a -> s) -> f (a -> s)) -> TraceConfig a s u m -> f (TraceConfig a s u m)
- _logEnter :: (Functor f, IsString s) => (Maybe (u -> m s) -> f (Maybe (u -> m s))) -> TraceConfig a s u m -> f (TraceConfig a s u m)
- _logExit :: (Functor f, IsString s) => (Maybe (u -> m s) -> f (Maybe (u -> m s))) -> TraceConfig a s u m -> f (TraceConfig a s u m)
- traceWith :: (Monad m, IsString s, HasTraceTree u s) => TraceConfig e s u m -> ParsecT t u m e -> ParsecT t u m e
- initialTraceTree :: IsString s => TraceTree s
- logP :: (Monad m, HasTraceTree u s, IsString s) => (u -> m s) -> ParsecT t u m ()
- drawTraceTree :: HasTraceTree t a => (a -> String) -> t -> String
- drawTraceTree' :: HasTraceTree t String => t -> String
- getTraceTree :: (HasTraceTree t s, IsString s) => t -> Tree s
Documentation
HasTraceTree
class IsString a => HasTraceTree t a | t -> a where Source
An instance of HasTraceTree
somehow refers to a TraceTree
Make your Parsec user state type an instance of this class.
Trace configuration
data TraceConfig e s u m Source
e
is the value type of your parsers
s
is the IsString
instance type of the tree values
u
is the Parsec user state
m
is the underlying monad of your Parsec parsers
defaultTraceConfig :: IsString s => TraceConfig e s u m Source
Default configuration which logs nothing on entering/exiting and ignores the parser values
Manipulate this default configuration with setters as setLogEnter
or lenses as _logEnter
Setting fields of the trace configuration
:: IsString s | |
=> (e -> s) | How to produce a tree value given the result of a parser |
-> TraceConfig e s u m | |
-> TraceConfig e s u m |
:: IsString s | |
=> (u -> m s) | Logging action that is run at the start of a parser |
-> TraceConfig a s u m | |
-> TraceConfig a s u m |
:: IsString s | |
=> (u -> m s) | Logging action that is run at the end of the parser |
-> TraceConfig a s u m | |
-> TraceConfig a s u m |
Lenses for modifying a trace configuration
_traceFunc :: (Functor f, IsString s) => ((a -> s) -> f (a -> s)) -> TraceConfig a s u m -> f (TraceConfig a s u m) Source
_logEnter :: (Functor f, IsString s) => (Maybe (u -> m s) -> f (Maybe (u -> m s))) -> TraceConfig a s u m -> f (TraceConfig a s u m) Source
_logExit :: (Functor f, IsString s) => (Maybe (u -> m s) -> f (Maybe (u -> m s))) -> TraceConfig a s u m -> f (TraceConfig a s u m) Source
Provide tracing to parsers
traceWith :: (Monad m, IsString s, HasTraceTree u s) => TraceConfig e s u m -> ParsecT t u m e -> ParsecT t u m e Source
Apply this function to your TraceConfig
to get a function which adds tracing to a parser
trace = traceWith (setTraceFunc show defaultTraceConfig) myparser = trace $ string "parseSomething"
initialTraceTree :: IsString s => TraceTree s Source
The value that can be used on initialisation of the Parsec user state
Processing the user state providing the trace tree (after parsing)
drawTraceTree :: HasTraceTree t a => (a -> String) -> t -> String Source
drawTraceTree' :: HasTraceTree t String => t -> String Source
getTraceTree :: (HasTraceTree t s, IsString s) => t -> Tree s Source