{-# LANGUAGE OverloadedStrings #-} -- | Support for CPP. module Ormolu.Processing.Cpp ( State (..), processLine, unmaskLine, ) where import Control.Monad import Data.Char (isSpace) import qualified Data.List as L import Data.Maybe (isJust) import Data.String import Data.Text (Text) import qualified Data.Text as T -- | State of the CPP processor. data State = -- | Outside of CPP directives Outside | -- | In a conditional expression InConditional | -- | In a continuation (after @\\@) InContinuation deriving (Eq, Show) -- | Automatically mask the line when needed and update the 'State'. processLine :: String -> State -> (String, State) processLine line state | for "define " = (masked, state') | for "include " = (masked, state') | for "undef " = (masked, state') | for "ifdef " = (masked, InConditional) | for "ifndef " = (masked, InConditional) | for "if " = (masked, InConditional) | for "else" = (masked, InConditional) | for "elif" = (masked, InConditional) | for "endif" = (masked, state') | otherwise = case state of Outside -> (line, Outside) InConditional -> (masked, InConditional) InContinuation -> (masked, state') where for directive = isJust $ do s <- dropWhile isSpace <$> L.stripPrefix "#" line void (L.stripPrefix directive s) masked = maskLine line state' = if "\\" `L.isSuffixOf` line then InContinuation else Outside -- | Mask the given line. maskLine :: String -> String maskLine x = maskPrefix ++ x -- | If the given line is masked, unmask it. Otherwise return the line -- unchanged. unmaskLine :: Text -> Text unmaskLine x = case T.stripPrefix maskPrefix (T.stripStart x) of Nothing -> x Just x' -> x' -- | Mask prefix for CPP. maskPrefix :: IsString s => s maskPrefix = "-- ORMOLU_CPP_MASK"