{-| Module : Similarity License : GPL Maintainer : helium@cs.uu.nl Stability : experimental Portability : portable Check whether identifiers look alike. Identifiers are alike if: - they are the same when compared insensitive w.r.t. case - they differ in one character (filter and fi1ter) - one has a character more than the other (concatMap and concattMap) - two characters are swapped (filtre and filter) -} module Helium.Utils.Similarity(similar) where import Data.Char import Helium.Utils.Utils(internalError) similar :: String -> String -> Bool -- ignore primitives similar ('$':c:_) _ | isAlpha c = False similar _ ('$':c:_) | isAlpha c = False -- normal test similar name1' name2' = name1 == name2 || oneDiff name1 name2 || oneMore name1 name2 || oneMore name2 name1 || name1 `elem` swap name2 where name1 = map toUpper name1' name2 = map toUpper name2' oneMore :: String -> String -> Bool oneMore xs ys = length xs - length ys == 1 && ys `elem` dropOne xs dropOne :: String -> [String] dropOne [] = [] dropOne (x:xs) = xs : map (x:) (dropOne xs) oneDiff :: String -> String -> Bool oneDiff xs ys = length xs == length ys && length (filter (== True) (zipWith (==) xs ys)) == length xs - 1 swap :: [a] -> [[a]] swap [_] = [] swap (x:y:xs) = (y:x:xs) : map (x:) (swap (y:xs)) swap [] = internalError "Similarity" "swap" "empty string"