{-# LANGUAGE CPP #-}
module Data.GraphViz.PreProcessing(preProcess) where
import Data.GraphViz.Exception (GraphvizException (NotDotCode), throw)
import Data.GraphViz.Parsing
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as T
import Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as B
#if !(MIN_VERSION_base(4,8,0))
import Data.Monoid (Monoid (..), mconcat)
#endif
preProcess :: Text -> Text
preProcess t = case fst $ runParser parseOutUnwanted t of
(Right r) -> B.toLazyText r
(Left l) -> throw (NotDotCode l)
parseOutUnwanted :: Parse Builder
parseOutUnwanted = mconcat <$> many getNext
where
getNext = parseOK
`onFail`
parseConcatStrings
`onFail`
parseHTML
`onFail`
parseUnwanted
`onFail`
fmap B.singleton next
parseOK = B.fromLazyText
<$> many1Satisfy (`notElem` ['\n', '\r', '\\', '/', '"', '<'])
parseUnwanted :: (Monoid m) => Parse m
parseUnwanted = oneOf [ parseLineComment
, parseMultiLineComment
, parsePreProcessor
, parseSplitLine
]
parsePreProcessor :: (Monoid m) => Parse m
parsePreProcessor = newline *> character '#' *> consumeLine *> pure mempty
parseLineComment :: (Monoid m) => Parse m
parseLineComment = string "//"
*> consumeLine
*> pure mempty
parseMultiLineComment :: (Monoid m) => Parse m
parseMultiLineComment = bracket start end (many inner) *> pure mempty
where
start = string "/*"
end = string "*/"
inner = (many1Satisfy ('*' /=) *> pure ())
`onFail`
(character '*' *> satisfy ('/' /=) *> inner)
parseConcatStrings :: Parse Builder
parseConcatStrings = wrapQuotes . mconcat <$> sepBy1 parseString parseConcat
where
qParse = bracket (character '"') (commit $ character '"')
parseString = qParse (mconcat <$> many parseInner)
parseInner = (string "\\\"" *> pure (B.fromLazyText $ T.pack "\\\""))
`onFail`
(string "\\\\" *> pure (B.fromLazyText $ T.pack "\\\\"))
`onFail`
parseSplitLine
`onFail`
fmap B.singleton (satisfy (quoteChar /=))
parseConcat = parseSep *> character '+' *> parseSep
parseSep = many $ whitespace1 `onFail` parseUnwanted
wrapQuotes str = qc `mappend` str `mappend` qc
qc = B.singleton '"'
parseSplitLine :: (Monoid m) => Parse m
parseSplitLine = character '\\' *> newline *> pure mempty
parseHTML :: Parse Builder
parseHTML = fmap (addAngled . mconcat)
. parseAngled $ many inner
where
inner = parseHTML
`onFail`
(B.fromLazyText <$> many1Satisfy (\c -> c /= open && c /= close))
addAngled str = B.singleton open `mappend` str `mappend` B.singleton close
open = '<'
close = '>'