{-# OPTIONS_GHC -Wall -Werror #-} {-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards #-} #undef MEGAPARSEC_7_OR_LATER #ifdef MIN_VERSION_GLASGOW_HASKELL -- GHC >= 7.10.1.0 #if MIN_VERSION_GLASGOW_HASKELL(8,0,0,0) -- GHC >= 8.0.0.0 #if MIN_VERSION_megaparsec(7,0,0) #define MEGAPARSEC_7_OR_LATER #endif #endif #endif module Text.SExpression.Default ( LiteralParsers(..) , LiteralParsersM , mkLiteralParsers , overrideStringP , overrideNumberP , overrideBoolP , parseStringDef , parseNumberDef , parseBoolDef ) where import Data.Semigroup (Last(..)) import Data.Default import Text.SExpression.Types (SExpr(..), Parser) import Control.Monad (void) import Text.Megaparsec ( (<|>) , many , notFollowedBy #ifdef MEGAPARSEC_7_OR_LATER , noneOf #endif , some ) import Text.Megaparsec.Char ( char , digitChar , string , alphaNumChar #ifndef MEGAPARSEC_7_OR_LATER , noneOf #endif ) -- | Partial parser configuration data LiteralParsersM = LiteralParsersM { parseStringM :: Maybe (Last (Parser SExpr)) , parseNumberM :: Maybe (Last (Parser SExpr)) , parseBoolM :: Maybe (Last (Parser SExpr)) } -- | Fully defined parser configuration data LiteralParsers = LiteralParsers { parseString :: Parser SExpr , parseNumber :: Parser SExpr , parseBool :: Parser SExpr } instance Semigroup LiteralParsersM where (<>) (LiteralParsersM ps pn pb) (LiteralParsersM ps' pn' pb') = LiteralParsersM (ps <> ps') (pn <> pn') (pb <> pb') instance Default LiteralParsersM where def = LiteralParsersM { parseStringM = Just $ Last parseStringDef , parseNumberM = Just $ Last parseNumberDef , parseBoolM = Just $ Last parseBoolDef } instance Default LiteralParsers where def = mkLiteralParsers def -- | Smart constructor for parser configuration -- that allows overriding the default literal parsers mkLiteralParsers :: (LiteralParsersM -> LiteralParsersM) -- ^ Cumulative override function -> LiteralParsers mkLiteralParsers f = case f def of LiteralParsersM{..} -> let Just (Last parseString) = parseStringM Just (Last parseNumber) = parseNumberM Just (Last parseBool) = parseBoolM in LiteralParsers parseString parseNumber parseBool -- | String parser override function overrideStringP :: Parser SExpr -- ^ String parser -> (LiteralParsersM -> LiteralParsersM) overrideStringP sp lp = lp <> LiteralParsersM { parseStringM = Just $ Last sp , parseNumberM = Nothing , parseBoolM = Nothing } -- | Number parser override function overrideNumberP :: Parser SExpr -- ^ Number parser -> (LiteralParsersM -> LiteralParsersM) overrideNumberP np lp = lp <> LiteralParsersM { parseStringM = Nothing , parseNumberM = Just $ Last np , parseBoolM = Nothing } -- | Boolean parser override function overrideBoolP :: Parser SExpr -- ^ Bool parser -> (LiteralParsersM -> LiteralParsersM) overrideBoolP bp lp = lp <> LiteralParsersM { parseStringM = Nothing , parseNumberM = Nothing , parseBoolM = Just $ Last bp } -- | Default parser for s-expression boolean literals parseBoolDef :: Parser SExpr parseBoolDef = do b <- string "#t" <* notFollowedBy alphaNumChar <|> string "#f" <* notFollowedBy alphaNumChar case b of "#t" -> return $ Bool True "#f" -> return $ Bool False _ -> fail "Not a boolean" -- | Default parser for s-expression numeric literals parseNumberDef :: Parser SExpr -- ^ parser parseNumberDef = (Number . read) <$> some digitChar -- | Default parser for s-expression string literals parseStringDef :: Parser SExpr -- ^ parser parseStringDef = do void $ char '"' s <- many (noneOf "\"") void $ char '"' pure $ String s