fourmolu-0.4.0.0: src/Ormolu/Processing/Cpp.hs
{-# LANGUAGE OverloadedStrings #-}
-- | Support for CPP.
module Ormolu.Processing.Cpp
( cppLines,
)
where
import Data.Char (isSpace)
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
import qualified Data.List as L
import Data.Maybe (isJust)
-- | State of the CPP processor.
data State
= -- | Outside of CPP directives
Outside
| -- | In a conditional expression, with a positive nesting count
InConditional Int
| -- | In a continuation (after @\\@), but not in a conditional expression
InContinuation
deriving (Eq, Show)
-- | Return an 'IntSet' containing all lines which are affected by CPP.
cppLines :: String -> IntSet
cppLines input = IntSet.fromAscList $ go Outside (lines input `zip` [1 ..])
where
go _ [] = []
go state ((line, i) : ls)
| any for ["define ", "include ", "undef "] =
i : go contState ls
| any for ["ifdef ", "ifndef ", "if "] =
let state' = case state of
InConditional nc -> InConditional (nc + 1)
_ -> InConditional 1
in i : go state' ls
| for "endif" =
let state' = case state of
InConditional nc | nc > 1 -> InConditional (nc - 1)
_ -> Outside
in i : go state' ls
| otherwise =
let is = case state of
Outside -> []
_ -> [i]
state' = case state of
InContinuation -> contState
_ -> state
in is <> go state' ls
where
for directive = isJust $ do
s <- dropWhile isSpace <$> L.stripPrefix "#" line
L.stripPrefix directive s
contState =
if "\\" `L.isSuffixOf` line && not inConditional
then InContinuation
else Outside
where
inConditional = case state of
InConditional {} -> True
_ -> False