packages feed

verismith-1.1.0: src/Verismith/Verilog/Distance.hs

-- |
-- Module      : Verismith.Verilog.Distance
-- Description : Definition of the distance function for the abstract syntax tree.
-- Copyright   : (c) 2020, Yann Herklotz
-- License     : GPL-3
-- Maintainer  : yann [at] yannherklotz [dot] com
-- Stability   : experimental
-- Poratbility : POSIX
--
-- Define the distance function for the abstract syntax tree, so that different
-- Verilog files can be compared.  This allows us to define a metric on how
-- different two pieces of Verilog are.  Currently, differences in expressions are
-- ignored, as these are not that interesting.
module Verismith.Verilog.Distance where

import Data.Functor.Foldable (cata)
import Data.Text (Text, unpack)
import Verismith.Verilog.AST
import Verismith.Verilog.Eval

data Pair a b = Pair a b
  deriving (Show)

instance (Eq b) => Eq (Pair a b) where
  Pair _ a == Pair _ b = a == b

instance (Ord b) => Ord (Pair a b) where
  Pair _ a <= Pair _ b = a <= b

eqDistance :: (Eq a) => a -> a -> Int
eqDistance a b = if a == b then 0 else 1
{-# INLINE eqDistance #-}

emptyDistance :: a -> a -> Int
emptyDistance _ _ = 0
{-# INLINE emptyDistance #-}

class Distance a where
  distance :: a -> a -> Int
  udistance :: a -> a -> Int
  udistance = distance
  dempty :: a -> Int
  dempty _ = 1

minimumloc :: (Distance a) => a -> [a] -> Pair Int Int
minimumloc ah [] = Pair 0 $ dempty ah
minimumloc ah b = minimum $ (\(loc, el) -> Pair loc (udistance ah el)) <$> zip [0 ..] b

removeAt :: Int -> [a] -> [a]
removeAt loc lst =
  let (a, b) = splitAt loc lst
   in if null b then a else a ++ tail b

remdist :: (Distance a) => [a] -> [a] -> Int
remdist [] a = distance [] a
remdist a [] = distance [] a
remdist (x : xs) b
  | cost <= dx = udistance xs (removeAt loc b) + cost
  | otherwise = udistance xs b + dx
  where
    Pair loc cost = minimumloc x b
    dx = dempty x

instance (Distance a) => Distance [a] where
  distance [] [] = 0
  distance [] l = sum $ dempty <$> l
  distance l [] = sum $ dempty <$> l
  distance a@(ah : at) b@(bh : bt) =
    let cost = distance ah bh
     in if cost == 0
          then distance at bt
          else
            minimum
              [ distance at b + dempty ah,
                distance bt a + dempty bh,
                distance at bt + cost
              ]

  udistance a b =
    minimum
      [ remdist a b,
        remdist b a
      ]

  dempty [] = 0
  dempty (a : b) = maximum [dempty a, dempty b]

instance (Distance a) => Distance (Maybe a) where
  distance Nothing a = dempty a
  distance a Nothing = dempty a
  distance (Just a) (Just b) = distance a b

  udistance (Just a) (Just b) = udistance a b
  udistance a b = distance a b

  dempty Nothing = 0
  dempty (Just a) = dempty a

instance Distance Char where
  distance = eqDistance

instance Distance Bool where
  distance = eqDistance

instance Distance Integer where
  distance = eqDistance

instance Distance Text where
  distance t1 t2 = distance (unpack t1) (unpack t2)

instance Distance Identifier where
  distance = eqDistance

eval :: ConstExpr -> Integer
eval c = toInteger (cata (evaluateConst []) c)

instance Distance ConstExpr where
  distance c1 c2 = distance (eval c1) $ eval c2
  udistance c1 c2 = udistance (eval c1) $ eval c2

instance Distance Parameter where
  distance _ _ = 0

instance Distance PortType where
  distance = eqDistance

instance Distance PortDir where
  distance = eqDistance

instance Distance (Statement a) where
  distance (TimeCtrl _ s1) (TimeCtrl _ s2) = distance s1 s2
  distance (EventCtrl _ s1) (EventCtrl _ s2) = distance s1 s2
  distance (SeqBlock s1) (SeqBlock s2) = distance s1 s2
  distance (CondStmnt _ st1 sf1) (CondStmnt _ st2 sf2) = distance st1 st2 + distance sf1 sf2
  distance (ForLoop _ _ _ s1) (ForLoop _ _ _ s2) = distance s1 s2
  distance (StmntAnn _ s1) s2 = distance s1 s2
  distance (BlockAssign _) (BlockAssign _) = 0
  distance (NonBlockAssign _) (NonBlockAssign _) = 0
  distance (TaskEnable _) (TaskEnable _) = 0
  distance (SysTaskEnable _) (SysTaskEnable _) = 0
  distance (StmntCase _ _ _ _) (StmntCase _ _ _ _) = 0
  distance _ _ = 1

instance Distance (ModItem a) where
  distance (ModCA _) (ModCA _) = 0
  distance (ModInst _ _ _ _) (ModInst _ _ _ _) = 0
  distance (Initial _) (Initial _) = 0
  distance (Always s1) (Always s2) = distance s1 s2
  distance (Decl _ _ _) (Decl _ _ _) = 0
  distance (ParamDecl _) (ParamDecl _) = 0
  distance (LocalParamDecl _) (LocalParamDecl _) = 0
  distance _ _ = 1

instance Distance Range where
  distance (Range a1 b1) (Range a2 b2) =
    distance a1 a2 + distance b1 b2
  udistance (Range a1 b1) (Range a2 b2) =
    udistance a1 a2 + udistance b1 b2

instance Distance Port where
  distance (Port t1 s1 r1 _) (Port t2 s2 r2 _) =
    distance t1 t2 + distance s1 s2 + distance r1 r2
  udistance (Port t1 s1 r1 _) (Port t2 s2 r2 _) =
    udistance t1 t2 + udistance s1 s2 + udistance r1 r2
  dempty (Port t1 s1 r1 _) = 1 + dempty t1 + dempty s1 + dempty r1

instance Distance (ModDecl a) where
  distance (ModDecl _ min1 mout1 mis1 mp1) (ModDecl _ min2 mout2 mis2 mp2) =
    distance min1 min2 + distance mout1 mout2 + distance mis1 mis2 + distance mp1 mp2
  udistance (ModDecl _ min1 mout1 mis1 mp1) (ModDecl _ min2 mout2 mis2 mp2) =
    udistance min1 min2 + udistance mout1 mout2 + udistance mis1 mis2 + udistance mp1 mp2
  dempty (ModDecl _ min mout mis mp) = 1 + dempty min + dempty mout + dempty mis + dempty mp

instance Distance (Verilog a) where
  distance (Verilog m1) (Verilog m2) = distance m1 m2
  udistance (Verilog m1) (Verilog m2) = udistance m1 m2
  dempty (Verilog m) = 1 + dempty m

instance Distance (SourceInfo a) where
  distance (SourceInfo _ v1) (SourceInfo _ v2) = distance v1 v2
  udistance (SourceInfo _ v1) (SourceInfo _ v2) = udistance v1 v2
  dempty (SourceInfo _ v) = 1 + dempty v