module Text.MMark.Parser.Internal
(
BParser
, runBParser
, isNakedAllowed
, refLevel
, subEnv
, registerReference
, registerFootnote
, IParser
, runIParser
, disallowEmpty
, isEmptyAllowed
, disallowLinks
, isLinksAllowed
, disallowImages
, isImagesAllowed
, isLastSpace
, isLastOther
, lastSpace
, lastOther
, lookupReference
, lookupFootnote
, Isp (..)
, Defs
, MMarkErr (..) )
where
import Control.Applicative
import Control.Monad.State.Strict
import Data.Default.Class
import Data.Function ((&))
import Data.HashMap.Strict (HashMap)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Ratio ((%))
import Data.Text (Text)
import Data.Text.Metrics (damerauLevenshteinNorm)
import Lens.Micro (Lens', (^.), (.~), set, over, to)
import Lens.Micro.Extras (view)
import Text.MMark.Internal
import Text.MMark.Parser.Internal.Type
import Text.Megaparsec hiding (State)
import Text.URI (URI)
import qualified Data.HashMap.Strict as HM
import qualified Text.Megaparsec as M
newtype BParser a = BParser
{ unBParser :: ParsecT MMarkErr Text (State BlockState) a }
deriving ( Functor
, Applicative
, Alternative
, Monad
, MonadPlus
, MonadParsec MMarkErr Text )
runBParser
:: BParser a
-> FilePath
-> Text
-> Either (NonEmpty (ParseError Char MMarkErr)) (a, Defs)
runBParser (BParser p) file input =
case runState (runParserT p file input) def of
(Left err, _) -> Left (err :| [])
(Right x, st) -> Right (x, st ^. bstDefs)
isNakedAllowed :: BParser Bool
isNakedAllowed = BParser $ gets (^. bstAllowNaked)
refLevel :: BParser Pos
refLevel = BParser $ gets (^. bstRefLevel)
subEnv
:: Bool
-> Pos
-> BParser a
-> BParser a
subEnv allowNaked rlevel
= BParser
. locally bstAllowNaked allowNaked
. locally bstRefLevel rlevel
. unBParser
registerReference
:: Text
-> (URI, Maybe Text)
-> BParser Bool
registerReference = registerGeneric referenceDefs
registerFootnote
:: Text
-> NonEmpty Inline
-> BParser Bool
registerFootnote = registerGeneric footnoteDefs
registerGeneric
:: Lens' Defs (HashMap DefLabel a)
-> Text
-> a
-> BParser Bool
registerGeneric l name a = BParser $ do
let dlabel = mkDefLabel name
defs <- gets (^. bstDefs . l)
if HM.member dlabel defs
then return True
else do
modify' $ over (bstDefs . l) (HM.insert dlabel a)
return False
newtype IParser a = IParser
{ unIParser :: StateT InlineState (Parsec MMarkErr Text) a }
deriving ( Functor
, Applicative
, Alternative
, Monad
, MonadPlus
, MonadParsec MMarkErr Text )
runIParser
:: Defs
-> IParser a
-> Isp
-> Either (ParseError Char MMarkErr) a
runIParser _ _ (IspError err) = Left err
runIParser defs (IParser p) (IspSpan startPos input) =
snd (runParser' (evalStateT p ist) pst)
where
ist = def & istDefs .~ defs
pst = M.State
{ stateInput = input
, statePos = startPos :| []
, stateTokensProcessed = 0
, stateTabWidth = mkPos 4
}
disallowEmpty :: IParser a -> IParser a
disallowEmpty = IParser . locally istAllowEmpty False . unIParser
isEmptyAllowed :: IParser Bool
isEmptyAllowed = IParser . gets $ view istAllowEmpty
disallowLinks :: IParser a -> IParser a
disallowLinks = IParser . locally istAllowLinks False . unIParser
isLinksAllowed :: IParser Bool
isLinksAllowed = IParser . gets $ view istAllowLinks
disallowImages :: IParser a -> IParser a
disallowImages = IParser . locally istAllowImages False . unIParser
isImagesAllowed :: IParser Bool
isImagesAllowed = IParser . gets $ view istAllowImages
isLastSpace :: IParser Bool
isLastSpace = IParser . gets $ view (istLastChar . to (== SpaceChar))
isLastOther :: IParser Bool
isLastOther = IParser . gets $ view (istLastChar . to (== OtherChar))
lastSpace :: IParser ()
lastSpace = IParser . modify' $ set istLastChar SpaceChar
lastOther :: IParser ()
lastOther = IParser . modify' $ set istLastChar OtherChar
lookupReference
:: Text
-> IParser (Either [Text] (URI, Maybe Text))
lookupReference = lookupGeneric referenceDefs
lookupFootnote
:: Text
-> IParser (Either [Text] (NonEmpty Inline))
lookupFootnote = lookupGeneric footnoteDefs
lookupGeneric
:: Lens' Defs (HashMap DefLabel a)
-> Text
-> IParser (Either [Text] a)
lookupGeneric l name = IParser $ do
let dlabel = mkDefLabel name
defs <- gets (view (istDefs . l))
case HM.lookup dlabel defs of
Nothing -> return . Left $ closeNames dlabel (HM.keys defs)
Just x -> return (Right x)
closeNames :: DefLabel -> [DefLabel] -> [Text]
closeNames r'
= filter (\x -> damerauLevenshteinNorm r x >= (2 % 3))
. map unDefLabel
where
r = unDefLabel r'
locally :: MonadState s m => Lens' s a -> a -> m b -> m b
locally l x m = do
y <- gets (^. l)
modify' (set l x)
r <- m
modify' (set l y)
return r