org-parser-0.1.0.0: src/Org/Parser/MarkupContexts.hs
{- | This module used to define a "subparsing" monad but this was later
absorbed into OrgState. Maybe I should move its contents elsewhere.
-}
module Org.Parser.MarkupContexts where
import Data.Set (notMember)
import Data.Text qualified as T
import Org.Parser.Common
import Org.Parser.Definitions
skipManyTill' ::
forall skip end m.
MonadParser m =>
m skip ->
m end ->
m (Text, end)
skipManyTill' skip end = try $ do
o0 <- getOffset
s0 <- getInput
-- note: skipManyTill tries end parser first
(o1, final) <- skipManyTill skip (liftA2 (,) getOffset end)
pure (T.take (o1 - o0) s0, final)
{-# INLINEABLE skipManyTill' #-}
findSkipping ::
forall end.
(Char -> Bool) ->
OrgParser end ->
OrgParser (Text, end)
findSkipping skip = skipManyTill' toSkip
where
toSkip = anySingle *> takeWhileP Nothing skip
{-# INLINEABLE findSkipping #-}
withContext__ ::
forall a end skip.
OrgParser skip ->
OrgParser end ->
OrgParser a ->
OrgParser (a, end, Text)
withContext__ skip end p = try do
clearLastChar
st <- getFullState
(str, final) <- skipManyTill' skip end
guard (not $ T.null str)
(,final,str) <$> parseFromText st str p
{-# INLINEABLE withContext__ #-}
withContext_ ::
forall a end skip.
OrgParser skip ->
OrgParser end ->
OrgParser a ->
OrgParser (a, end)
withContext_ skip end p =
withContext__ skip end p <&> \(x, y, _) -> (x, y)
{-# INLINEABLE withContext_ #-}
withContext ::
forall a end skip.
OrgParser skip ->
OrgParser end ->
OrgParser a ->
OrgParser a
withContext skip end = fmap fst . withContext_ skip end
{-# INLINEABLE withContext #-}
withMContext ::
forall a b.
(Char -> Bool) ->
(Char -> Bool) ->
OrgParser b ->
OrgParser a ->
OrgParser a
withMContext allowed skip end p = try do
clearLastChar
st <- getFullState
prelim <- takeWhileP Nothing \c -> skip c && allowed c
((prelim <>) -> str, _) <- skipManyTill' toSkip end
guard (not $ T.null str)
parseFromText st str p
where
toSkip = satisfy allowed *> takeWhileP Nothing \c -> skip c && allowed c
{-# INLINEABLE withMContext #-}
withBalancedContext ::
Char ->
Char ->
-- | Allowed
(Char -> Bool) ->
OrgParser a ->
OrgParser a
withBalancedContext lchar rchar allowed p = try do
_ <- char lchar
let skip :: StateT Int OrgParser ()
skip = do
_ <-
takeWhileP
(Just "insides of markup")
(\c -> allowed c && c /= lchar && c /= rchar)
c <- lookAhead (satisfy allowed) <?> "balanced delimiters"
when (c == lchar) $ modify (+ 1)
when (c == rchar) $ modify (subtract 1)
get >>= \case
balance
| balance < 0 -> fail "unbalaced delimiters"
| balance == 0 -> pure ()
| otherwise -> void anySingle
end = try do
guard . (== 0) =<< get
_ <- char rchar
lift $ putLastChar rchar
st <- getFullState
(str, _) <- evalStateT (skipManyTill' skip end) 1
parseFromText st str p
{- | Parse inside a "context": text that is not captured by the parser `elems`
gets converted to the type `k` via the function `f`.
-}
markupContext ::
Monoid k =>
(Text -> k) ->
Marked OrgParser k ->
OrgParser k
markupContext f elems = go
where
go = try $ do
let specials :: Set Char = fromList $ getMarks elems
str <-
optional $
takeWhile1P
Nothing
(`notMember` specials)
-- traceM $ "consumed: " ++ show str
let self = maybe mempty f str
setLastChar (T.last <$> str)
(self <>) <$> (finishSelf <|> anotherEl <|> nextChar)
where
finishSelf = eof $> mempty
anotherEl = try $ do
el <- getParser elems
rest <- go
pure $ el <> rest
nextChar = try $ do
c <- anySingle
-- traceM $ "parsed char: " ++ show c
putLastChar c
rest <- go
pure $ f (one c) <> rest