Skip to content

Instantly share code, notes, and snippets.

@frasertweedale
Last active July 19, 2019 06:51
Show Gist options
  • Save frasertweedale/8d775c959e8c6ff54156a28c170a9662 to your computer and use it in GitHub Desktop.
Save frasertweedale/8d775c959e8c6ff54156a28c170a9662 to your computer and use it in GitHub Desktop.
Haskell script to remove bindings from Haskell source files
-- Copyright (C) 2019 Fraser Tweedale
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero 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 Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-- Remove declarations (top-level or otherwise) from a Haskell source file.
--
-- Targets for removal are read, line-by-line, from the file given as program argument.
--
-- Does type signatures and bindings ("foo"), or other constructions ("data Foo",
-- "class Foo", etc.)
--
-- Transforms stdin and writes stdout. To modify a file, write it to a temporary file
-- then copy the temporary file to the input file:
--
-- runhaskell rmdecl targets.txt < Foo.hs > tmp && mv tmp Foo.hs
module Main where
import Data.Monoid (First(..))
import Data.List (isPrefixOf)
import System.Environment (getArgs)
main = do
targets <- getArgs
interact (unlines . goTargets targets . lines)
data Mode
= Keep [String] -- accumulated comment lines
| Drop Int -- drop, declaration indent recorded
goTargets :: [String] -> [String] -> [String]
goTargets targets = go (Keep []) where
-- Nothing left
go (Keep com) [] = com
go (Drop _) [] = []
-- Keep mode; look for target symbol
go (Keep com) (line:rest) =
case getFirst (foldMap (First . hit line) targets) of
-- Hit. Drop this line /and precent comment lines/; enter drop mode.
Just n -> go (Drop n) rest
-- No hit; comment line. Add to comment accumulator
_ | "--" `isPrefixOf` line -> go (Keep (com <> [line])) rest
-- No hit; non-comment line. Write out comments and line.
| otherwise -> com <> (line : go (Keep []) rest)
-- Drop mode; drop empty lines
go mode@(Drop _) ("":rest) = go mode rest
-- Drop mode; check if indent reduced to level at which target found
go (Drop n) (line:rest)
-- we are still in the declaration
| n < length (takeWhile (== ' ') line) = go (Drop n) rest
-- declaration has ended. Don't drop the current line but
-- DO reassess it, e.g. type decl may have finished, but the
-- current line may be the start of the definition.
| otherwise = go (Keep []) (line:rest)
hit :: String {- line -} -> String {- target -} -> Maybe Int
hit line target
| content == target || (target <> " ") `isPrefixOf` content
= Just indent
| otherwise = Nothing
where
indent = length (takeWhile (== ' ') line)
content = drop indent line
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment