1 {-# LANGUAGE ConstraintKinds #-}
2 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE Rank2Types #-}
5 {-# LANGUAGE TypeFamilies #-}
6 module Hdoc.TCT.Read.Cell where
8 import Control.Applicative (Applicative(..))
9 import Control.Monad (Monad(..))
10 import Control.Monad.Trans.Class (MonadTrans(..))
11 import Data.Char (Char)
12 import Data.Either (Either(..))
14 import Data.Function (($))
15 import Data.Functor ((<$>))
16 import Data.List.NonEmpty (NonEmpty(..))
17 import Data.Maybe (Maybe(..))
19 import Data.Proxy (Proxy(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.String (IsString)
22 import Data.Tuple (snd)
23 import qualified Control.Monad.Trans.Reader as R
24 import qualified Data.Set as Set
25 import qualified Data.Text.Lazy as TL
26 import qualified Text.Megaparsec as P
31 -- | Convenient alias.
34 P.ParsecT e s (R.Reader [Span]) a
41 , IsString (P.Tokens s)
42 , P.ShowErrorComponent e
45 -- | Like 'P.satisfy' but with a predicate returning 'Maybe' instead of 'Bool'.
46 p_satisfyMaybe :: P.MonadParsec e s m => (P.Token s -> Maybe a) -> m a
47 p_satisfyMaybe f = check `P.token` Nothing
52 Nothing -> Left (Just $ P.Tokens $ c:|[], Set.empty)
54 p_Position :: Parser e s Pos
56 pos :| _ <- P.statePos <$> P.getParserState
57 return $ Pos (P.unPos $ P.sourceLine pos) (P.unPos $ P.sourceColumn pos)
59 p_Cell :: Parser e s a -> Parser e s (Cell a)
62 b :| _ <- P.statePos <$> P.getParserState
64 span_end <- p_Position
65 let span_begin = Pos (P.unPos $ P.sourceLine b) (P.unPos $ P.sourceColumn b)
66 let span = Span {span_file=P.sourceName b, span_begin, span_end}
67 return $ Cell (span:|path) a
69 -- | Wrapper around |P.runParserT'|
70 -- to use given 'Cell' as starting position.
72 Parsable e StreamCell a =>
73 Parser e StreamCell a ->
75 Either (P.ParseError (P.Token StreamCell) e) a
76 runParserOnCell p (Cell (Span inp bp _ep :| path) s) =
78 (`R.runReader` path) $
79 P.runParserT' (p <* P.eof)
81 { P.stateInput = StreamCell s
82 , P.statePos = pure $ P.SourcePos inp (P.mkPos $ pos_line bp) indent
83 , P.stateTabWidth = indent
84 , P.stateTokensProcessed = 0
86 where indent = P.mkPos $ pos_column bp
88 -- * Type 'StreamCell'
89 -- | Wrap 'TL.Text' to have a 'P.Stream' instance
90 -- whose 'P.advance1' method abuses the tab width state
91 -- to instead pass the line indent.
92 -- This in order to report correct 'P.SourcePos'
93 -- when parsing a 'Cell' containing newlines.
94 newtype StreamCell = StreamCell { unStreamCell :: TL.Text }
95 deriving (IsString,Eq,Ord)
96 instance P.Stream StreamCell where
97 type Token StreamCell = Char
98 type Tokens StreamCell = TL.Text
99 take1_ (StreamCell t) = (StreamCell <$>) <$> P.take1_ t
100 takeN_ n (StreamCell t) = (StreamCell <$>) <$> P.takeN_ n t
101 takeWhile_ f (StreamCell t) = StreamCell <$> P.takeWhile_ f t
102 tokensToChunk _s = P.tokensToChunk (Proxy::Proxy TL.Text)
103 chunkToTokens _s = P.chunkToTokens (Proxy::Proxy TL.Text)
104 chunkLength _s = P.chunkLength (Proxy::Proxy TL.Text)
105 advance1 _s indent (P.SourcePos n line col) c =
107 '\n' -> P.SourcePos n (line <> P.pos1) indent
108 _ -> P.SourcePos n line (col <> P.pos1)
109 advanceN s indent = TL.foldl' (P.advance1 s indent)