packages feed

Wordlint 0.2.0.3 → 0.2.0.4

raw patch · 7 files changed

+665/−3 lines, 7 files

Files

Wordlint.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                Wordlint-version:             0.2.0.3+version:             0.2.0.4 synopsis:            Plaintext prose redundancy linter.  description:         Wordlint locates matching pairs of words repeated within a user-defined@@ -41,10 +41,16 @@ extra-source-files: changelog extra-doc-files:    doc/Wordlint.haddock                     man/man1/wordlint.1+library+  hs-source-dirs:      src+  exposed-modules:       Wordlint.Args, Wordlint.Linters, Wordlint.Output, Wordlint.Wordpairs, Wordlint.Words+  build-depends:       base >=4.7 && <4.8, cmdargs >=0.10.0 && <0.11.0, boxes >=0.1 && <0.2+  default-language:    Haskell2010  executable wordlint-  hs-source-dirs:      src, src/Wordlint+  hs-source-dirs:      src   main-is:             Wordlint.hs+  other-modules:       Wordlint.Args, Wordlint.Linters, Wordlint.Output, Wordlint.Wordpairs, Wordlint.Words   other-extensions:    DeriveDataTypeable   build-depends:       base >=4.7 && <4.8, cmdargs >=0.10.0 && <0.11.0, boxes >=0.1 && <0.2   default-language:    Haskell2010
man/man1/wordlint.1 view
@@ -1,4 +1,4 @@-.TH "WORDLINT" "1" "2015\-02\-24" "0.2.0.3+.TH "WORDLINT" "1" "2015\-02\-24" "0.2.0.4 .SH Name .PP wordlint \- plaintext redundancy linter
+ src/Wordlint/Args.hs view
@@ -0,0 +1,167 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Wordlint.Args where++import System.Console.CmdArgs +import Control.Monad+import Wordlint.Words+import Wordlint.Wordpairs++-- CLI Arguments+-- Data structure and init function for CmdArgs+data Arguments  = Arguments +        {file :: String+        -- linting options+        ,matchlength :: Int+        ,words_ :: Int+        ,lines_ :: Int+        ,percent_ :: Double+        -- filters+        ,nocaps :: Bool+        ,nopunct :: Bool -- ,ignore-punctuation :: Bool goddammit+        ,blacklist :: String+        -- output options+        ,all_ :: Bool+        ,human :: Bool+        ,sort_ :: String+        }+        deriving (Data, Typeable, Show, Read)++cliargs :: Arguments+cliargs = Arguments+        {file = ""          &= help "If not present, read from stdin" &= typFile+        -- linting options+        ,matchlength = 5      &= help   "Minimum length of matched words" &= typ "Int"+        ,words_   = 0    &= help   "Maximum distance between matches - number of words. Default 250." &= typ "Int"+        ,lines_   = 0    &= help   "Maximum distance between matches - number of lines" &= typ "Int"+        ,percent_ = 0    &= help   "Maximum distance between matches - percentage of words." &= typ "Double"++        -- filters+        ,nocaps    = False  &= help "Ignore capitalization when finding matches."+        ,nopunct   = False  &=  help "Ignore punctuation when finding matches."+        ,blacklist = ""     &=  help "File with newline-separated list of words to filter from output." +                            &= typFile++        -- output options+        ,all_   = False      &= help "Show all matched results regardless of intervening distance"+        ,human  = False      &= help "Print resutlts in human-readable form."+        ,sort_  = "position" &= help "Sort alphabetically, by position, or by intervening distance"+                             &= typ "word|position|distance|error"+        } +        &= help "wordlint [OPTION]...[-f FILE]..."+        &= summary "Wordlint v0.2.0.4 Gardner 2014 WTFPL"+        &= details ["Wordlint finds pairs of repeated words within a given"+                   ,"numerical range of words, lines or percentage of the input."+                   ,"This should be useful to curb redundancy in prose."]+--------------------------------------------------------------------------------+--+-- Input file/stdin functions+--+--------------------------------------------------------------------------------+checkFileStdin :: String -> Maybe String+checkFileStdin s | null s = Nothing +                 | otherwise = Just s+++accessInputFileData :: Maybe String -> IO String+accessInputFileData f =+  case f of+   Nothing ->  getContents+   Just fp ->  readFile fp ++accessBlacklistFileData :: Maybe String -> IO String+accessBlacklistFileData f =+    case f of+        Nothing -> return ""+        Just fp -> readFile fp ++setBlacklistData :: String -> Maybe [String]+setBlacklistData a | null a = Nothing+                   | otherwise = Just $ lines a+                                       ++--------------------------------------------------------------------------------+--+-- Filter functions+--+--------------------------------------------------------------------------------++runFilterFlags :: (NumOps a) => Words a -> Arguments -> Maybe [String] -> Words a+runFilterFlags w arg blist = runCapsFilter arg $ runBlacklistFilter blist $+                             runPunctFilter arg $ runBlacklistFilter blist w++runPunctFilter :: (NumOps a) => Arguments -> Words a -> Words a+runPunctFilter arg wordlist = if nopunct arg+                           then filterWordPunctuation wordlist+                           else wordlist++runCapsFilter :: (NumOps a) => Arguments -> Words a -> Words a+runCapsFilter arg wordlist = if nocaps arg+                           then filterWordCapitalization wordlist+                           else wordlist++runBlacklistFilter :: (NumOps a) => Maybe [String] -> Words a -> Words a+runBlacklistFilter blist wordlist = case blist of+                                        Nothing -> wordlist+                                        Just x -> filterWordBlacklist wordlist x++--------------------------------------------------------------------------------+--+-- Output flag functions+--+--------------------------------------------------------------------------------++-- Handle --sort flag+checkSortFlag :: (Num a, Ord a, NumOps a)  => String -> Wordpairs a -> Wordpairs a+checkSortFlag  x y | x == "position" = sortWordPairsByPosition y +                   | x == "distance" = sortWordPairsByDistance y+                   | x == "word" = y+                   | x == "error" = sortWordPairsByPosition y+                   | otherwise = y+                  ++-- Header to print if --human flag is present+checkIfHumanHeader :: Arguments -> IO ()+checkIfHumanHeader cargs = when (human cargs)+                     $ putStrLn ("Running "+                                 ++ getTypeOfCheck cargs+                                 ++ " check[s] on "+                                 ++ file cargs+                                 ++ "\nWith minimum distance[s] of "+                                 ++ getTypeOfCheck cargs+                                 ++ " between words of length "+                                 ++ show (matchlength cargs) ++ "\n")++getTypeOfCheck :: Arguments -> String+getTypeOfCheck c  = isWordsFlag c ++ " " ++ isLinesFlag c ++ " " ++ isPercentFlag c+  +  +getTypeNumbers :: Arguments -> String+getTypeNumbers c  = convertWordsFlag c ++ " " +++                    convertLinesFlag c ++ " " ++ convertPercentFlag c+++isWordsFlag :: Arguments -> String+isWordsFlag c | words_ c /= 0 ||+                words_ c == 0 && lines_ c == 0 && percent_ c == 0 = "word"+              | otherwise = ""+  +isLinesFlag :: Arguments -> String+isLinesFlag c | lines_ c /= 0 = "line"+              | otherwise = ""+  +isPercentFlag :: Arguments -> String+isPercentFlag c | percent_ c /= 0 = "percentage"+                | otherwise = ""++convertWordsFlag :: Arguments -> String+convertWordsFlag c | words_ c /= 0 = show $ words_ c+                   | otherwise = ""+       +convertLinesFlag :: Arguments -> String+convertLinesFlag c | lines_ c /= 0 = show $ lines_ c+                   | otherwise = ""+       +convertPercentFlag :: Arguments -> String+convertPercentFlag c | percent_ c /= 0 = show $ percent_ c+                     | otherwise = ""
+ src/Wordlint/Linters.hs view
@@ -0,0 +1,94 @@+module Wordlint.Linters where++import Control.Monad+import Data.List+import Data.Function +import Wordlint.Args+import Wordlint.Words+import Wordlint.Wordpairs++-- Linter data type which holds external input and parameters+-- necessary to easily +data Linter = Linter {+  inputdata        :: String+  ,wordlength      :: Int+  ,allornot        :: Bool+  ,maybeblacklist  :: Maybe [String]+  ,word'           :: Int+  ,line'           :: Int+  ,percent'        :: Double+  ,args            :: Arguments+  ,result          :: Wordpairs Double+}++-- Get and run linters+getLinter :: Arguments -> IO Linter+getLinter cargs = do+  -- get filename+    dat <- accessInputFileData . checkFileStdin $ file cargs+    blist <- liftM setBlacklistData $ accessBlacklistFileData. checkFileStdin $ blacklist cargs+    let mlen = matchlength cargs+    let isall = all_ cargs +    let w' = words_ cargs+    let l = lines_ cargs+    let p = percent_ cargs+    let w = if l == 0 && p == 0.0 &&  w' == 0+               then 250+               else w' +    return $ Linter dat mlen isall blist w l p cargs []++runAllLinters :: Linter -> Wordpairs Double+runAllLinters linter = intersectWordpairs y+  where words'' = distorall linter (getWordpairListWords linter) (word' linter)+        cwords = commensurateWordpairs words''+        lines'' = distorall linter (getWordpairListLines linter) (line' linter)+        clines = commensurateWordpairs lines''+        perc   = distorall linter (getWordpairListPercent linter) (percent' linter)+        y      = sortBy (flip compare `on` length) [cwords,clines,perc]++intersectWordpairs :: [Wordpairs Double] -> Wordpairs Double+intersectWordpairs [] = []+intersectWordpairs [a,[],[]] = a+intersectWordpairs [a,b,[]] = a `intersect` b+intersectWordpairs [a,b,c] = a `intersect` intersect b c+intersectWordpairs _ = []+++getWordpairList :: (Num a, NumOps a) => Linter -> String -> Wordpairs a+getWordpairList linter type' = instring+  where instring = sortWordsByString . filterMatchingWords $ dwords+        dwords   = checkWordList fwords wordlen+        fwords   = runFilterFlags cwords arg blist+        cwords   = zipWords in' type'+        wordlen  = wordlength linter+        in' = inputdata linter+        arg     = args linter+        blist    = maybeblacklist linter++distorall :: (Eq a, Ord a, Num a, NumOps a) => Linter -> Wordpairs a -> a -> Wordpairs a+distorall linter wordpairs num = +  if allornot linter+  then wordpairs+  else filterWordpairsByDistance wordpairs num++getWordpairListWords :: Linter -> Wordpairs Int+getWordpairListWords l = if word' l /= 0 then getWordpairList l "word" else []++getWordpairListLines :: Linter -> Wordpairs Int+getWordpairListLines l = if line' l /= 0 then getWordpairList l "line" else []++getWordpairListPercent :: Linter -> Wordpairs Double+getWordpairListPercent l = if percent' l /= 0 then getWordpairList l "percent" else []++commensurateWordpairs :: Wordpairs Int -> Wordpairs Double+commensurateWordpairs = map commensurateWordpair++commensurateWordpair :: Wordpair Int -> Wordpair Double+commensurateWordpair x = Wordpair a b c+                         where a = commensurateWords $ wone x+                               b = commensurateWords $ wtwo x+                               c = fromIntegral $ pdiff x++commensurateWords :: Word Int -> Word Double+commensurateWords x = Word (lemma x) pos (line x) (column x)+  where pos = fromIntegral $ position x
+ src/Wordlint/Output.hs view
@@ -0,0 +1,80 @@+module Wordlint.Output where++import Data.List+import Text.PrettyPrint.Boxes +import Wordlint.Args+import Wordlint.Linters+import Wordlint.Words+import Wordlint.Wordpairs++produceOutput :: (Show a, Eq a, Ord a, Num a, NumOps a) => Linter -> Wordpairs a -> IO ()+produceOutput linter wordpairs+    | ishuman =+      do mapM_ putStrLn (processHumanData wordpairs')+         putStrLn ""+         summary+    | iserrorfmt = putStrLn $ processDataError wordpairs' filename+    | otherwise = processMachineData wordpairs'+    where cargs = args linter+          filename = if file cargs /= "" then file cargs else "stdin"+          ishuman = human cargs+          sortflag = sort_ cargs+          iserrorfmt = sortflag == "error"+          wordpairs' = checkSortFlag sortflag wordpairs+          summary+            = summaryData (length wordpairs) (matchlength cargs)+                (length $ inputdata linter)++processHumanData :: (Show a) => Wordpairs a -> [String]+processHumanData [] = ["No (more) matches found"]+processHumanData (x:xs) = ("\'" ++ word ++ "\'"+                         ++ " at coordinates "+                         ++ coordinates+                         ++ " with an intervening distance of "+                         ++ distance') : processHumanData xs+                         where word = getWordPairString x+                               coordinates = show (getWordpairCoords x)+                               distance' = take 7 (show (pdiff x))++processMachineData :: (Show a) => Wordpairs a -> IO ()+processMachineData x = printBox $ hsep 2 left (map (vcat left . map text) +                                    (transpose $ ["Coord1", "Coord2", "Word", "Distance"]:processMachineData' x))++processMachineData' :: (Show a) => Wordpairs a -> [[String]]+processMachineData' [] = []+processMachineData' (x:xs) = words (coordinates1 coordinates'+                                    ++ " "+                                    ++ coordinates2 coordinates'+                                    ++ " "+                                    ++ word+                                    ++ " "+                                    ++ distance') : processMachineData' xs+                                      where word = getWordPairString x+                                            coordinates' = getWordpairCoords x+                                            coordinates1 ((r1,s1),(_,_)) = show r1 ++ "," ++ show s1+                                            coordinates2 ((_,_),(r2,s2)) = show r2 ++ "," ++ show s2+                                            distance' = take 7 $ show (pdiff x)+-- Function if "error" is passed as --sort flag argument+processDataError :: (NumOps a) => Wordpairs a -> String -> String+processDataError [] _ = ""+processDataError (x:xs) fname = (fname ++ ":" ++ linum1 ++ ":"  ++  colnum1 ++ ":" ++ word ++ "\n") +++                              (fname ++ ":" ++ linum2 ++ ":"  ++  colnum2 ++ ":" ++ word ++ "\n") +++                        processDataError xs fname+                         where word = getWordPairString x+                               coordinates' = getWordpairCoords x+                               coordinates1 ((r1,_),(_,_)) = show r1+                               coordinates1' ((_,_),(r1,_)) = show r1+                               linum1 = coordinates1 coordinates'+                               linum2 = coordinates1' coordinates'+                               coordinates2 ((_,s1),(_,_)) = show s1 +                               coordinates2' ((_,_),(_,s1)) = show s1 +                               colnum1 = coordinates2 coordinates'+                               colnum2 = coordinates2' coordinates'+                               +-- Function that provides summary totals when -h flag is passed+summaryData :: Int -> Int -> Int -> IO()+summaryData x y z = do+            putStrLn ""+            putStrLn $ "Found " ++ show x  ++ " pairs of words "+              ++ show y ++ " or more characters in length out of "+              ++ show z ++ " words total."
+ src/Wordlint/Wordpairs.hs view
@@ -0,0 +1,114 @@+module Wordlint.Wordpairs where+import  Data.List +import Wordlint.Words++--+-- This module contains types and functions for working with pairs of matching+-- words and the distances between them. The key type is a Wordpair, which+-- contains two Word elements and the difference between their Positions.+--++data Wordpair a = Wordpair +                {wone  :: Word a+                ,wtwo  :: Word a+                ,pdiff :: a +                } ++type Wordpairs a = [Wordpair a]++-- Wordpair equality based on coordinates so that+-- union of multiple checks can be found regardless+--+instance Eq (Wordpair a) where+         x == y = getWordpairCoords x == getWordpairCoords y+-----------------------------------------------------------------+--+-- Creating and filtering Wordpairs+--+-----------------------------------------------------------------+makeWordpairs :: (Num a, NumOps a) => Word a -> Word a -> Wordpair a+makeWordpairs wx@(Word _ x _ _) wy@(Word _ y _ _) = Wordpair wx wy (y-x)++-- Convert a list of matching (but separately-located) Words into a list of+-- Wordpair elements. This counts by two+sortWordsByString :: (Num a, NumOps a) =>  Words a -> Wordpairs a+sortWordsByString [] = []+sortWordsByString [_] = []+sortWordsByString [x,xs] =  [makeWordpairs x xs | x `checkWordEquality` xs] +sortWordsByString ( x:y:xs ) = if x `checkWordEquality` y+                               then makeWordpairs x y : sortWordsByString (y:xs)+                               else sortWordsByString (y:xs)++filterWordpairsByDistance :: (Num a, Eq a, Ord a, NumOps a) => Wordpairs a -> a -> Wordpairs a+filterWordpairsByDistance [] _ = []++filterWordpairsByDistance (x:xs) i = if pdiff x <= i+                               then x : filterWordpairsByDistance xs i+                               else filterWordpairsByDistance xs i++-----------------------------------------------------------------+--+-- Sorting wordpairs+--+-----------------------------------------------------------------++sortWordPairsByPosition :: (Num a, Ord a, NumOps a) => Wordpairs a -> Wordpairs a+sortWordPairsByPosition [] = []+sortWordPairsByPosition [_] = []+sortWordPairsByPosition xs = sortBy positionsort xs+  where positionsort (Wordpair (Word _ x _ _) _ _ ) (Wordpair (Word _ y _ _) _ _) +                | x < y = LT+                | x > y = GT+                | x == y = EQ+        positionsort Wordpair{} Wordpair{} = EQ++sortWordPairsByDistance :: (Num a, Ord a, NumOps a) => Wordpairs a -> Wordpairs a+sortWordPairsByDistance [] = []+sortWordPairsByDistance [_] = []+sortWordPairsByDistance xs = sortBy positionsort xs+  where positionsort (Wordpair _ _ x ) (Wordpair _ _ y ) +                | x < y = LT+                | x > y = GT+                | x == y = EQ+        positionsort Wordpair{} Wordpair{} = EQ++-----------------------------------------------------------------+--+-- Functions to extract data from Wordpairs for easier printing+--+-----------------------------------------------------------------++getWordPairString :: Wordpair a -> String+getWordPairString wp = if wordone == wordtwo+                        then wordone+                        else+                        "Error with word pair"+  where +    wordone = lemma $ wone wp+    wordtwo = lemma $ wtwo wp++getWordpairPositions :: (NumOps a) => Wordpair a -> (a,a)+getWordpairPositions wp = (position $ wone wp,position $ wtwo wp)++getWordpairLines :: (NumOps a) => Wordpair a -> (Int,Int)+getWordpairLines wp = (line $ wone wp,line $ wtwo wp)+++-- return ((Line,Col)(Line,Col))+getWordpairCoords :: Wordpair a -> ((Int,Int),(Int,Int))+getWordpairCoords wp = ((line firstword,column firstword),(line secondword,column secondword))+    where+      firstword = wone wp+      secondword = wtwo wp++showFirstWordpairCoords :: (Show a) => Wordpair a -> String+showFirstWordpairCoords x = lin ++ "," ++ col+  where lin = show $ line wor+        col = show $ column wor+        wor = wone x++showSecondWordpairCoords :: (Show a) => Wordpair a -> String+showSecondWordpairCoords x = lin ++ "," ++ col+  where lin = show $ line wor+        col = show $ column wor+        wor = wtwo x
+ src/Wordlint/Words.hs view
@@ -0,0 +1,201 @@+module Wordlint.Words where+import Data.Char (isPunctuation, toLower)+import Data.List + +--+-- This module contains types and functions for working with words and their+-- positions used in processing of file. A "Word" is a data structure+-- containing a word string; its position by word count, line, or percentage of+-- file used to calculate intervening distances; and its line and and column+-- positions used when returning data.+--+-- To create each word, four sets of functions each provide one element and+-- these are zipped together for each word in the file. A Word's "lemma" is+-- first returned by the basic prelude function `words` and therefore includes+-- punctuation marks. While this is required in order to identify the correct+-- coordinates for each Word, this default is also helpful to catch, for+-- example, the repetition of transition words such as "Furthermore,".+--+-- Punctuation and capitalization filters, as well as a user-defined word+-- blacklist, are available to filter the list of Words prior to matching+-- duplicates. +++data Word a = Word +    { lemma :: String+    ,position :: a+    ,line :: Int+    ,column :: Int } ++type Words a = [Word a] ++--Words are tested for equality and ordered by their lemma +instance Eq (Word a)  where+    x == y = lemma x == lemma y++instance Ord (Word a) where+    compare x y = compare (lemma x) (lemma y)++-----------------------------------------------------------------+--+--Word creation+--+-----------------------------------------------------------------++--+-- Position+-- Create list of tuples containing lemma and word position+-- "Position" depends on type-of-check+-- For word and line checks, Position is an Int representing+-- the word count and line positions, respectively.+-- A percentage check returns a Position of type Double,+-- representing the Word's position as a percentage of+-- total words in the file.+-- ++class NumOps a where+    createPos :: String -> String -> [(String, a)]++instance NumOps Double where+    createPos text _ = createPercentPos text++instance NumOps Int where+    createPos = wordOrLine ++wordOrLine :: String -> String -> [(String, Int)]+wordOrLine s t = case t of+    "word" -> createWordPos s+    "line" -> createLinePos s+    _    -> createWordPos s++createWordPos :: String -> [(String, Int)]+createWordPos s  = zip (words s) [1..]++createLinePos :: String -> [(String, Int)]+createLinePos = createWordLinePos++createPercentPos :: String  -> [(String, Double)]+createPercentPos s =  getWordPercentPos wrdlst wrdln+  where wrdln = length $ words s+        wrdlst = createWordPos s++getWordPercentPos :: [(String, Int)] -> Int ->  [(String, Double)]+getWordPercentPos []  _ = []+getWordPercentPos (x:xs) y = divWordPercentPos x y : getWordPercentPos xs y++divWordPercentPos :: (String, Int) -> Int -> (String, Double)+divWordPercentPos (s,x) y = (s,p) +  where xi = fromIntegral x+        yi = fromIntegral y+        p = (xi/yi)*100+--+-- Line coordinate+-- Create list of tuples containing lemma and line position++createWordLinePos :: String -> [(String, Int)]+createWordLinePos xs = setWordLines $ zip (getWordLines xs) [1..]++-- Makes lists of words by line, fed to the functions below+getWordLines :: String -> [[String]]+getWordLines xs = fmap words (lines xs)++setWordLine :: ([String], Int) -> [(String, Int)]+setWordLine (([],_)) = []+setWordLine ((x:xs,i)) = (x,i) : setWordLine (xs,i)++setWordLines :: [([String], Int)] -> [(String, Int)]+setWordLines [] = []+setWordLines (x:xs) = setWordLine x ++ setWordLines xs++-- Column coordinate+-- 1) lines on file and zip infinite lists for each char. [[(Char,Int)]]+-- 2) compare first char of words from each inner [[String]] +--    and add Int from [[(Char,Int)]] when Char == first Char of string++createWordColPos :: String -> [(String, Int)]+createWordColPos xs = setWordCols lin $ numWordCols lin+  where lin = lines xs++-- number columns in each line of file+numWordCols :: [String] -> [[(Char,Int)]]+numWordCols [[]] = [[]]+numWordCols [] = [[]]+numWordCols (x:xs) = zip x [1..] : numWordCols xs++-- call filter on lines+setWordCols :: [String] -> [[(Char,Int)]] -> [(String,Int)]+setWordCols [] [] = []+setWordCols (_:_) [] = []+setWordCols [] (_:_) = []+setWordCols (x:xs) (y:ys) = filtWordCols (words x) y ++ setWordCols xs ys++filtWordCols :: [String] -> [(Char,Int)] -> [(String,Int)]+filtWordCols [] _ = []+filtWordCols _ [] = []+filtWordCols w@(x:xs) c@(y:ys) = if (fst y == ' ') || (fst y /= head x)+                         then filtWordCols w ys+                         else (x,snd y) : filtWordCols xs (drop (length x) c )++--+-- Master functoion to create a list of words from a file.    +zipWords :: (NumOps a) => String -> String -> Words a+zipWords s t = zipWith4 Word (words s) (wordpos s t) linepos colpos+  where +    linepos = snd . unzip $ createWordLinePos s+    colpos = snd . unzip $ createWordColPos s+    wordpos u v = snd . unzip $ createPos u v++-----------------------------------------------------------------+--+-- Functions that operate on Word(s) type+--+-----------------------------------------------------------------++-- Check a word against the minimum word length (matchlength Arguments in Wordlint.Args)+isCheckWordLong :: (NumOps a) => Word a -> Int -> Bool+isCheckWordLong (Word w _ _ _) x = length w > x++-- Filter Words based on minimum word length+checkWordList :: (NumOps a) => Words a -> Int -> Words a+checkWordList [] _ = []+checkWordList (x:xs) i = if isCheckWordLong x i+                     then x : checkWordList xs i+                     else checkWordList xs i++-- Equality function checking for string match in different coordinate positions+-- Converts coordinates to tuples to ensure proper equality checking+checkWordEquality :: (NumOps a) => Word a -> Word a -> Bool+checkWordEquality (Word a _ b c) (Word x _ y z) = coord1 /= coord2 && a==x+  where coord1 = (b,c) +        coord2 = (y,z)++-- Function to determine distance between two words. Uses Position of Word+-- rather than coordinates so that it is type-of-search independent+--+checkWordDistance :: (Num a, NumOps a) => Word a -> Word a -> a+checkWordDistance (Word _ x _ _) (Word _ y _ _) = x - y++-- Filter all but matching pairs of words +filterMatchingWords :: (NumOps a) => Words a -> Words a+filterMatchingWords [] = []+filterMatchingWords xs = sort $ intersectBy checkWordEquality xs xs++-- The following filter functions are denoted by the --nopunct, --nocaps and+-- --blacklist flags. These functions are used prior to creating Wordpair+-- types from Words and should not affect the above checkWordEquality function+-- used in building the former.++-- Filter punctuation with --nopunct flag+filterWordPunctuation :: (NumOps a) => Words a -> Words a+filterWordPunctuation xs = [Word [a | a <- lemma x, not $ isPunctuation a]+                           (position x) (line x) (column x) | x <- xs]++-- Filter capitals with --nocaps flag+filterWordCapitalization :: (NumOps a) => Words a -> Words a+filterWordCapitalization xs = [Word [toLower a | a <- lemma x]+                           (position x) (line x) (column x) | x <- xs]+++--Filter blacklist given in file denoted by -b flag+filterWordBlacklist :: (NumOps a) => Words a -> [String] -> Words a+filterWordBlacklist xs blacklist = [x | x <- xs, let w = lemma x, w `notElem` blacklist]