module Text.SmallCaps.PrintableParser where
import Prelude hiding ( head, tail, null )
import Text.Parsec ( runParser, try, oneOf, anyChar, many, many1, lower, upper, string, getState, modifyState )
import qualified Text.Parsec as P ( space, newline )
import Text.Parsec.Text ( GenParser )
import Data.Text ( Text, singleton, pack, unpack, intercalate )
import Control.Monad ( msum )
import Text.SmallCaps.Config ( Config (..), StopState (..), ParserState (..), SubParser, PatternReplace (..) )
type Parser = GenParser ParserState
runPrintableWith :: SubParser Text
runPrintableWith state = either (Left . show) Right . runParser (printable >>= \a -> fmap ((,) a) getState) state ""
printable :: Parser Text
printable = fmap (intercalate (pack "")) $ many $ printableElement
printableElement :: Parser Text
printableElement = msum
[ excepts
, lowers
, uppers
, period
, newline
, space
, misc
]
excepts :: Parser Text
excepts = msum =<< fmap (map toParser . exceptions . config) getState
where toParser x = try (string (unpack $ pattern x)) >> pass reset (replacement x)
lowers :: Parser Text
lowers = pass reset . pack =<< many1 lower
uppers :: Parser Text
uppers = do
text <- fmap pack $ many1 upper
state <- getState
if ignore state || not (replaceFilter (config state) text)
then pass reset text
else pass reset $ replace (config state) (stop state) text
period :: Parser Text
period = do
ps <- fmap (periodChars . config) getState
pass set . singleton =<< oneOf ps
space :: Parser Text
space = pass sticky . singleton =<< P.space
newline :: Parser Text
newline = pass inc . singleton =<< P.newline
misc :: Parser Text
misc = pass reset . singleton =<< anyChar
pass :: Parser b -> a -> Parser a
pass m a = m >> return a
reset :: Parser ()
reset = modifyState (\state -> state { stop = None })
set :: Parser ()
set = modifyState (\state -> state { stop = Stop })
inc :: Parser ()
inc = modifyState (\state -> state { stop = inc' (stop state) }) where
inc' None = NewLine
inc' _ = NewSentence
sticky :: Parser ()
sticky = modifyState (\state -> state { stop = inc' (stop state) }) where
inc' None = None
inc' NewLine = NewLine
inc' _ = NewSentence