packages feed

mcm-0.6.4.10: VarsParser.hs

-- MCM - Machine Configuration Manager; manages the contents of files and directories
-- Copyright (c) 2013-2016 Anthony Doggett <mcm@interfaces.org.uk>
--
-- Licence:
--     This program is free software: you can redistribute it and/or modify
--     it under the terms of the GNU General Public License as published by
--     the Free Software Foundation, either version 3 of the License, or
--     (at your option) any later version.
--
--     This program is distributed in the hope that it will be useful,
--     but WITHOUT ANY WARRANTY; without even the implied warranty of
--     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--     GNU General Public License for more details.
--
--     You should have received a copy of the GNU General Public License
--     along with this program.  If not, see <http://www.gnu.org/licenses/>.

module VarsParser (expandString)
where

import Control.Monad(unless)
import Data.Char (isAlphaNum)
import qualified Data.Text.Lazy as T
import Text.ParserCombinators.Poly.Text

import ParserTypes (Ident(..), VarsExpand(..))

-- Perform substitution on strings like "Fifty @(pound)s of @rice"
expandString :: VarsExpand a -> T.Text -> Either String a
expandString ve t = case runParser (pSplitter ve) t of
    (Left s, _) -> Left s
    (Right e, v) -> if T.null v then Right e else Left $ "Failed to parse end: " ++ T.unpack v

isVar :: Char -> Bool
isVar c = isAlphaNum c || c `elem` "_"

doReplace :: VarsExpand a -> T.Text -> T.Text -> Parser a
doReplace ve t r = case vexpand ve (Ident t) r of
    Just s -> return s
    Nothing -> failBad $ "Failed to lookup @" ++ T.unpack t

char :: Char -> Parser ()
char c = do
    n <- next
    unless (c == n) $ fail "Failed to match char"

pSplitter :: VarsExpand a -> Parser a
pSplitter ve = vcollapse ve <$> (many . oneOf)
    [vnoexpand ve <$> many1Satisfy (/= '@')
    ,do char '@'
        oneOf
            [do v <- many1Satisfy isVar
                commit $ doReplace ve v $ T.cons '@' v
            ,do char '('
                v <- many1Satisfy isVar
                commit $ char ')' `adjustErr ` ("expected closing bracket\n"++)
                doReplace ve v $ T.concat [T.pack "@(", v, T.pack ")"]
            ,do char '@'
                commit $ return $ vescapeexpand ve (T.pack "@") (T.pack "@@")
            ]
    ]