packages feed

verismith-1.1.0: src/Verismith/Verilog2005/Utils.hs

-- Module      : Verismith.Verilog2005.Utils
-- Description : AST utilitary functions.
-- Copyright   : (c) 2023 Quentin Corradi
-- License     : GPL-3
-- Maintainer  : q [dot] corradi22 [at] imperial [dot] ac [dot] uk
-- Stability   : experimental
-- Portability : POSIX

{-# LANGUAGE OverloadedLists #-}

module Verismith.Verilog2005.Utils
  ( makeIdent,
    regroup,
    addAttributed,
    genexprnumber,
    constifyIdent,
    constifyMaybeRange,
    trConstifyGenExpr,
    constifyExpr,
    constifyLV,
    expr2netlv,
    netlv2expr,
    toStatement,
    fromStatement,
    fromMybStmt,
    toMGIBlockDecl,
    fromMGIBlockDecl1,
    fromMGIBlockDecl_add,
    toStdBlockDecl,
    toSpecBlockedItem,
    fromSpecBlockedItem,
    toMGBlockedItem,
    fromMGBlockedItem1,
    fromMGBlockedItem_add,
    fromMGBlockedItem,
  )
where

import Numeric.Natural
import Text.Printf (printf)
import Data.Functor.Compose
import Data.Functor.Identity
import qualified Data.ByteString as BS
import Data.ByteString.Internal (c2w, packChars)
import qualified Data.HashSet as HS
import Data.List.NonEmpty (NonEmpty (..), (<|), toList)
import qualified Data.List.NonEmpty as NE
import Verismith.Verilog2005.Lexer (VerilogVersion (..), isIdentSimple)
import Verismith.Verilog2005.AST
import Verismith.Utils (nonEmpty, foldrMap1)

-- AST utils

makeIdent :: BS.ByteString -> Identifier
makeIdent =
  Identifier . BS.concatMap
    (\w -> if 33 <= w && w <= 126 then BS.pack [w] else packChars $ printf "\\%02x" w)

-- | Groups `x`s into `y`s by converting a single `x` and merging previous `x`s to the result
regroup :: (x -> y) -> (x -> y -> Maybe y) -> NonEmpty x -> NonEmpty y
regroup mk add = foldrMap1 ((:|[]) . mk) (\e (h :| t) -> maybe (mk e :| h : t) (:| t) $ add e h)

-- | Merges `Attributed x`s if we can merge `x`s
addAttributed :: (x -> y -> Maybe y) -> Attributed x -> Attributed y -> Maybe (Attributed y)
addAttributed f (Attributed na x) (Attributed a y) =
  if a /= na then Nothing else Attributed a <$> f x y

-- | Makes a Verilog2005 expression out of a number
genexprnumber :: Natural -> GenExpr i r a
genexprnumber = ExprPrim . PrimNumber Nothing False . NDecimal

-- | converts HierIdent into Identifier
constifyIdent :: HierIdent -> Maybe Identifier
constifyIdent (HierIdent p i) = case p of [] -> Just i; _ -> Nothing

-- | the other way
unconstIdent :: Identifier -> HierIdent
unconstIdent = HierIdent []

-- | converts Prim into GenPrim i r
constifyGenPrim ::
  (si -> Maybe di) ->
  (Maybe DimRange -> Maybe r) ->
  GenPrim si (Maybe DimRange) a ->
  Maybe (GenPrim di r a)
constifyGenPrim fi fr x = case x of
  PrimNumber s b n -> Just $ PrimNumber s b n
  PrimReal s -> Just $ PrimReal s
  PrimIdent s rng -> PrimIdent <$> fi s <*> fr rng
  PrimConcat e -> PrimConcat <$> mapM ce e
  PrimMultConcat m e -> PrimMultConcat m <$> mapM ce e
  PrimFun s a e -> PrimFun <$> fi s <*> pure a <*> mapM ce e
  PrimSysFun s e -> PrimSysFun s <$> mapM ce e
  PrimMinTypMax (MTMSingle e) -> PrimMinTypMax . MTMSingle <$> ce e
  PrimMinTypMax (MTMFull l t h) -> PrimMinTypMax <$> (MTMFull <$> ce l <*> ce t <*> ce h)
  PrimString s -> Just $ PrimString s
  where
    ce = trConstifyGenExpr fi fr

-- | the other way
unconstPrim :: GenPrim Identifier (Maybe CRangeExpr) a -> GenPrim HierIdent (Maybe DimRange) a
unconstPrim x = case x of
  PrimNumber s b n -> PrimNumber s b n
  PrimReal s -> PrimReal s
  PrimIdent s rng -> PrimIdent (unconstIdent s) (GenDimRange [] . unconstRange <$> rng)
  PrimConcat e -> PrimConcat (NE.map trUnconstExpr e)
  PrimMultConcat m e -> PrimMultConcat m (NE.map trUnconstExpr e)
  PrimFun s a e -> PrimFun (unconstIdent s) a (map trUnconstExpr e)
  PrimSysFun s e -> PrimSysFun s (map trUnconstExpr e)
  PrimMinTypMax (MTMSingle e) -> PrimMinTypMax (MTMSingle (trUnconstExpr e))
  PrimMinTypMax (MTMFull l t h) ->
    PrimMinTypMax $ MTMFull (trUnconstExpr l) (trUnconstExpr t) (trUnconstExpr h)
  PrimString s -> PrimString s

-- | converts `GenExpr si (MaybeDimRange) a` into `GenExpr i r a`
trConstifyGenExpr ::
  (si -> Maybe di) ->
  (Maybe DimRange -> Maybe r) ->
  GenExpr si (Maybe DimRange) a ->
  Maybe (GenExpr di r a)
trConstifyGenExpr fi fr x = case x of
  ExprPrim p -> ExprPrim <$> constifyGenPrim fi fr p
  ExprUnOp op a p -> ExprUnOp op a <$> constifyGenPrim fi fr p
  ExprBinOp lhs op a rhs -> ExprBinOp <$> ce lhs <*> pure op <*> pure a <*> ce rhs
  ExprCond c a t f -> ExprCond <$> ce c <*> pure a <*> ce t <*> ce f
  where
    ce = trConstifyGenExpr fi fr

-- | converts Expr's `DimRange` into CExpr's `CRangeExpr`
constifyMaybeRange :: Maybe DimRange -> Maybe (Maybe CRangeExpr)
constifyMaybeRange =
  maybe (Just Nothing) $ \(GenDimRange l r) -> if null l then Just <$> constifyRange r else Nothing

-- | converts Expr's `GenExpr` into CExpr `GenExpr`
trConstifyExpr ::
  GenExpr HierIdent (Maybe DimRange) a -> Maybe (GenExpr Identifier (Maybe CRangeExpr) a)
trConstifyExpr = trConstifyGenExpr constifyIdent constifyMaybeRange

-- | the other way
trUnconstExpr :: GenExpr Identifier (Maybe CRangeExpr) a -> GenExpr HierIdent (Maybe DimRange) a
trUnconstExpr x = case x of
  ExprPrim p -> ExprPrim (unconstPrim p)
  ExprUnOp op a p -> ExprUnOp op a (unconstPrim p)
  ExprBinOp lhs op a rhs -> ExprBinOp (trUnconstExpr lhs) op a (trUnconstExpr rhs)
  ExprCond c a t f -> ExprCond (trUnconstExpr c) a (trUnconstExpr t) (trUnconstExpr f)

-- | converts Expr to CExpr
constifyExpr :: Expr -> Maybe CExpr
constifyExpr (Expr e) = CExpr <$> trConstifyExpr e

-- | the other way
unconstExpr :: CExpr -> Expr
unconstExpr (CExpr e) = Expr (trUnconstExpr e)

-- | converts RangeExpr into CRangeExpr
constifyRange :: RangeExpr -> Maybe CRangeExpr
constifyRange x = case x of
  GRESingle e -> GRESingle <$> constifyExpr e
  GREPair r2 -> Just $ GREPair r2
  GREBaseOff b mp o -> (\cb -> GREBaseOff cb mp o) <$> constifyExpr b

-- | the other way
unconstRange :: CRangeExpr -> RangeExpr
unconstRange x = case x of
  GRESingle e -> GRESingle (unconstExpr e)
  GREPair r2 -> GREPair r2
  GREBaseOff b mp o -> GREBaseOff (unconstExpr b) mp o

-- | converts DimRange into CDimRange
constifyDR :: GenDimRange Expr -> Maybe (GenDimRange CExpr)
constifyDR (GenDimRange dim rng) = GenDimRange <$> mapM constifyExpr dim <*> constifyRange rng

-- | the other way
unconstDR :: GenDimRange CExpr -> GenDimRange Expr
unconstDR (GenDimRange dim rng) = GenDimRange (map unconstExpr dim) (unconstRange rng)

-- | converts variable lvalue into net lvalue
constifyLV :: VarLValue -> Maybe NetLValue
constifyLV v = case v of
  LVSingle hi mdr -> LVSingle hi <$> maybe (Just Nothing) (fmap Just . constifyDR) mdr
  LVConcat l -> LVConcat <$> mapM constifyLV l

-- | the other way
unconstLV :: NetLValue -> VarLValue
unconstLV n = case n of
  LVSingle hi dr -> LVSingle hi (unconstDR <$> dr)
  LVConcat l -> LVConcat (NE.map unconstLV l)

-- | converts expression into net lvalue
expr2netlv :: Expr -> Maybe NetLValue
expr2netlv (Expr x) = aux x
  where
    aux x = case x of
      ExprPrim (PrimConcat c) -> LVConcat <$> mapM aux c
      ExprPrim (PrimIdent s sub) -> LVSingle s <$> maybe (Just Nothing) (Just . constifyDR) sub
      _ -> Nothing

-- | the other way
netlv2expr :: NetLValue -> Expr
netlv2expr = Expr . aux
  where
    aux x = case x of
      LVConcat e -> ExprPrim $ PrimConcat $ NE.map aux e
      LVSingle s dr -> ExprPrim $ PrimIdent s $ unconstDR <$> dr

-- | Converts Function statements to statements
toStatement :: FunctionStatement -> Statement
toStatement x = case x of
  FSBlockAssign va -> SBlockAssign True va Nothing
  FSCase zox e l d -> SCase zox e (map (\(FCaseItem p v) -> CaseItem p $ mybf v) l) (mybf d)
  FSIf e t f -> SIf e (mybf t) (mybf f)
  FSDisable hi -> SDisable hi
  FSLoop ls b -> SLoop ls $ toStatement <$> b
  FSBlock h ps b -> SBlock h ps $ fmap toStatement <$> b
  where mybf = fmap $ fmap toStatement

-- | the other way
fromStatement :: Statement -> Maybe FunctionStatement
fromStatement x = case x of
  SBlockAssign True va Nothing -> Just $ FSBlockAssign va
  SCase zox e l d -> FSCase zox e <$> traverse (\(CaseItem p v) -> FCaseItem p <$> mybf v) l <*> mybf d
  SIf e t f -> FSIf e <$> mybf t <*> mybf f
  SDisable hi -> Just $ FSDisable hi
  SLoop ls b -> FSLoop ls <$> traverse fromStatement b
  SBlock h ps b -> FSBlock h ps <$> traverse (traverse fromStatement) b
  _ -> Nothing
  where mybf s = traverse (traverse fromStatement) s

-- | Converts MybStmt to AttrStmt
fromMybStmt :: MybStmt -> AttrStmt
fromMybStmt x = case x of
  Attributed _ Nothing -> Attributed [] $ SIf (Expr $ genexprnumber 1) x $ Attributed [] Nothing
  Attributed a (Just s) -> Attributed a s

type BD f t = BlockDecl (Compose f Identified) t

-- | Converts ModGenSingleItem's `BlockDecl` into ModGenBlockedItem's `BlockDecl`
toMGIBlockDecl :: BD NonEmpty t -> NonEmpty (BD Identity t)
toMGIBlockDecl x = case x of
  BDReg sr d -> conv (BDReg sr) d
  BDInt d -> conv BDInt d
  BDReal d -> conv BDReal d
  BDTime d -> conv BDTime d
  BDRealTime d -> conv BDRealTime d
  BDEvent d -> conv BDEvent d
  BDLocalParam t d -> conv (BDLocalParam t) d
  where
    conv f = fmap (f . Compose . Identity) . getCompose

-- | Converts one ModGenBlockedItem's `BlockDecl` into ModGenSingleItem's `BlockDecl`
fromMGIBlockDecl1 :: BD Identity t -> BD NonEmpty t
fromMGIBlockDecl1 x = case x of
  BDReg sr d -> conv (BDReg sr) d
  BDInt d -> conv BDInt d
  BDReal d -> conv BDReal d
  BDTime d -> conv BDTime d
  BDRealTime d -> conv BDRealTime d
  BDEvent d -> conv BDEvent d
  BDLocalParam t d -> conv (BDLocalParam t) d
  where
    conv f = f . Compose . (:|[]) . runIdentity . getCompose

-- | Merges one ModGenBlockedItem's `BlockDecl` with one ModGenSingleItem's `BlockDecl`
fromMGIBlockDecl_add :: BD Identity t -> BD NonEmpty t -> Maybe (BD NonEmpty t)
fromMGIBlockDecl_add x y = case (x, y) of
  (BDReg nsr d, BDReg sr l) | nsr == sr -> add (BDReg sr) d l
  (BDInt d, BDInt l) -> add BDInt d l
  (BDReal d, BDReal l) -> add BDReal d l
  (BDTime d, BDTime l) -> add BDTime d l
  (BDRealTime d, BDRealTime l) -> add BDRealTime d l
  (BDEvent d, BDEvent l) -> add BDEvent d l
  (BDLocalParam nt d, BDLocalParam t l) | nt == t -> add (BDLocalParam t) d l
  _ -> Nothing
  where
    add f x l = Just $ f $ Compose $ runIdentity (getCompose x) <| getCompose l

-- | Converts ModGenSingleItem like `BlockDecl` into StdBlockDecl `BlockDecl`
toStdBlockDecl :: BD NonEmpty t -> NonEmpty (Identified (BlockDecl Identity t))
toStdBlockDecl x = case x of
  BDReg sr d -> conv (BDReg sr) d
  BDInt d -> conv BDInt d
  BDReal d -> conv BDReal d
  BDTime d -> conv BDTime d
  BDRealTime d -> conv BDRealTime d
  BDEvent d -> conv BDEvent d
  BDLocalParam t d -> conv (BDLocalParam t) d
  where
    conv f = fmap (fmap $ f . Identity) . getCompose

-- | Converts `SpecifySingleItem` into `SpecifyBlockedItem`s
toSpecBlockedItem :: SpecifySingleItem -> NonEmpty SpecifyBlockedItem
toSpecBlockedItem x = case x of
  SISpecParam rng d -> conv (SISpecParam rng) d
  SIPulsestyleOnevent st -> conv SIPulsestyleOnevent st
  SIPulsestyleOndetect st -> conv SIPulsestyleOndetect st
  SIShowcancelled st -> conv SIShowcancelled st
  SINoshowcancelled st -> conv SINoshowcancelled st
  SIPathDeclaration mpc con pol eds v -> [SIPathDeclaration mpc con pol eds v]
  SISetup a -> [SISetup a]
  SIHold a -> [SIHold a]
  SISetupHold a aa -> [SISetupHold a aa]
  SIRecovery a -> [SIRecovery a]
  SIRemoval a -> [SIRemoval a]
  SIRecrem a aa -> [SIRecrem a aa]
  SISkew a -> [SISkew a]
  SITimeSkew a ev rem -> [SITimeSkew a ev rem]
  SIFullSkew a tcl ev rem -> [SIFullSkew a tcl ev rem]
  SIPeriod ref tcl s -> [SIPeriod ref tcl s]
  SIWidth ref tcl t s -> [SIWidth ref tcl t s]
  SINoChange ref dat st en s -> [SINoChange ref dat st en s]
  where
    conv f = fmap (f . Identity)

fromSpecBlockedItem1 :: SpecifyBlockedItem -> SpecifySingleItem
fromSpecBlockedItem1 x = case x of
  SISpecParam rng d -> conv (SISpecParam rng) d
  SIPulsestyleOnevent st -> conv SIPulsestyleOnevent st
  SIPulsestyleOndetect st -> conv SIPulsestyleOndetect st
  SIShowcancelled st -> conv SIShowcancelled st
  SINoshowcancelled st -> conv SINoshowcancelled st
  SIPathDeclaration mpc con pol eds v -> SIPathDeclaration mpc con pol eds v
  SISetup a -> SISetup a
  SIHold a -> SIHold a
  SISetupHold a aa -> SISetupHold a aa
  SIRecovery a -> SIRecovery a
  SIRemoval a -> SIRemoval a
  SIRecrem a aa -> SIRecrem a aa
  SISkew a -> SISkew a
  SITimeSkew a ev rem -> SITimeSkew a ev rem
  SIFullSkew a tcl ev rem -> SIFullSkew a tcl ev rem
  SIPeriod ref tcl s -> SIPeriod ref tcl s
  SIWidth ref tcl t s -> SIWidth ref tcl t s
  SINoChange ref dat st en s -> SINoChange ref dat st en s
  where
    conv f = f . (:|[]) . runIdentity

fromSpecBlockedItem_add :: SpecifyBlockedItem -> SpecifySingleItem -> Maybe SpecifySingleItem
fromSpecBlockedItem_add x y = case (x, y) of
  (SISpecParam nrng d, SISpecParam rng l) | nrng == rng-> add (SISpecParam rng) d l
  (SIPulsestyleOnevent st, SIPulsestyleOnevent l) -> add SIPulsestyleOnevent st l
  (SIPulsestyleOndetect st, SIPulsestyleOndetect l) -> add SIPulsestyleOndetect st l
  (SIShowcancelled st, SIShowcancelled l) -> add SIShowcancelled st l
  (SINoshowcancelled st, SINoshowcancelled l) -> add SINoshowcancelled st l
  _ -> Nothing
  where
    add f x y = Just $ f $ runIdentity x <| y

-- | Converts `SpecifyBlockedItem`s into `SpecifySingleItem`s
fromSpecBlockedItem :: [SpecifyBlockedItem] -> [SpecifySingleItem]
fromSpecBlockedItem = nonEmpty [] $ toList . regroup fromSpecBlockedItem1 fromSpecBlockedItem_add

-- | Converts `ModGenSingleItem` into `ModGenBlockedItem`s
toMGBlockedItem :: ModGenSingleItem -> NonEmpty ModGenBlockedItem
toMGBlockedItem x = case x of
  MGINetInit nt ds np ni -> conv (MGINetInit nt ds np) ni
  MGINetDecl nt np nd -> conv (MGINetDecl nt np) nd
  MGITriD ds np ni -> conv (MGITriD ds np) ni
  MGITriC cs np nd -> conv (MGITriC cs np) nd
  MGIBlockDecl d -> fmap MGIBlockDecl $ toMGIBlockDecl d
  MGIGenVar i -> conv MGIGenVar i
  MGITask b i d s -> [MGITask b i d s]
  MGIFunc b t i d s -> [MGIFunc b t i d s]
  MGIDefParam po -> conv MGIDefParam po
  MGIContAss ds d3 na -> conv (MGIContAss ds d3) na
  MGICMos r d3 l -> conv (MGICMos r d3) l
  MGIEnable r b ds d3 l -> conv (MGIEnable r b ds d3) l
  MGIMos r np d3 l -> conv (MGIMos r np d3) l
  MGINIn nt n ds d2 l -> conv (MGINIn nt n ds d2) l
  MGINOut r ds d2 l -> conv (MGINOut r ds d2) l
  MGIPassEn r b d2 l -> conv (MGIPassEn r b d2) l
  MGIPass r l -> conv (MGIPass r) l
  MGIPull b ds l -> conv (MGIPull b ds) l
  MGIUDPInst udp ds d2 i -> conv (MGIUDPInst udp ds d2) i
  MGIModInst mod pa i -> conv (MGIModInst mod pa) i
  MGIUnknownInst t p i -> conv (MGIUnknownInst t p) i
  MGIInitial s -> [MGIInitial s]
  MGIAlways s -> [MGIAlways s]
  MGILoopGen ii iv c ui uv b -> [MGILoopGen ii iv c ui uv b]
  MGICondItem ci -> [MGICondItem ci]
  where
    conv f = fmap (f . Identity)

fromMGBlockedItem1 :: ModGenBlockedItem -> ModGenSingleItem
fromMGBlockedItem1 x = case x of
  MGINetInit nt ds np ni -> conv (MGINetInit nt ds np) ni
  MGINetDecl nt np nd -> conv (MGINetDecl nt np) nd
  MGITriD ds np ni -> conv (MGITriD ds np) ni
  MGITriC cs np nd -> conv (MGITriC cs np) nd
  MGIBlockDecl d -> MGIBlockDecl $ fromMGIBlockDecl1 d
  MGIGenVar i -> conv MGIGenVar i
  MGITask b i d s -> MGITask b i d s
  MGIFunc b t i d s -> MGIFunc b t i d s
  MGIDefParam po -> conv MGIDefParam po
  MGIContAss ds d3 na -> conv (MGIContAss ds d3) na
  MGICMos r d3 i -> conv (MGICMos r d3) i
  MGIEnable r b ds d3 i -> conv (MGIEnable r b ds d3) i
  MGIMos r np d3 i -> conv (MGIMos r np d3) i
  MGINIn nt n ds d2 i -> conv (MGINIn nt n ds d2) i
  MGINOut r ds d2 i -> conv (MGINOut r ds d2) i
  MGIPassEn r b d2 i -> conv (MGIPassEn r b d2) i
  MGIPass r i -> conv (MGIPass r) i
  MGIPull b ds i -> conv (MGIPull b ds) i
  MGIUDPInst udp ds d2 i -> conv (MGIUDPInst udp ds d2) i
  MGIModInst mod pa i -> conv (MGIModInst mod pa) i
  MGIUnknownInst t p i -> conv (MGIUnknownInst t p) i
  MGIInitial s -> MGIInitial s
  MGIAlways s -> MGIAlways s
  MGILoopGen ii iv c ui uv b -> MGILoopGen ii iv c ui uv b
  MGICondItem ci -> MGICondItem ci
  where
    conv f = f . (:|[]) . runIdentity

fromMGBlockedItem_add :: ModGenBlockedItem -> ModGenSingleItem -> Maybe ModGenSingleItem
fromMGBlockedItem_add x y = case (x, y) of
  (MGINetInit nnt nds nnp ni, MGINetInit nt ds np l) | nnt == nt && nds == ds && nnp == np ->
    add (MGINetInit nt ds np) ni l
  (MGINetDecl nnt nnp nd, MGINetDecl nt np l) | nnt == nt && nnp == np ->
    add (MGINetDecl nt np) nd l
  (MGITriD nds nnp ni, MGITriD ds np l) | nds == ds && nnp == np -> add (MGITriD ds np) ni l
  (MGITriC ncs nnp nd, MGITriC cs np l) | ncs == cs && nnp == np -> add (MGITriC cs np) nd l
  (MGIBlockDecl d, MGIBlockDecl l) -> MGIBlockDecl <$> fromMGIBlockDecl_add d l
  (MGIGenVar i, MGIGenVar l) -> add MGIGenVar i l
  (MGIDefParam po, MGIDefParam l) -> add MGIDefParam po l
  (MGIContAss nds nd3 na, MGIContAss ds d3 l) | nds == ds && nd3 == d3 ->
    add (MGIContAss ds d3) na l
  (MGICMos nr nd3 i, MGICMos r d3 l) | nr == r && nd3 == d3 -> add (MGICMos r d3) i l
  (MGIEnable nr nb nds nd3 i, MGIEnable r b ds d3 l)
   | nr == r && nb == b && nds == ds && nd3 == d3 -> add (MGIEnable r b ds d3) i l
  (MGIMos nr nnp nd3 i, MGIMos r np d3 l) | nr == r && nnp == np && nd3 == d3 ->
    add (MGIMos r np d3) i l
  (MGINIn nnt nn nds nd2 i, MGINIn nt n ds d2 l)
    | nnt == nt && nn == n && nds == ds && nd2 == d2 -> add (MGINIn nt n ds d2) i l
  (MGINOut nr nds nd2 i, MGINOut r ds d2 l) | nr == r && nds == ds && nd2 == d2 ->
    add (MGINOut r ds d2) i l
  (MGIPassEn nr nb nd2 i, MGIPassEn r b d2 l) | nr == r && nb == b && nd2 == d2 ->
    add (MGIPassEn r b d2) i l
  (MGIPass nr i, MGIPass r l) | nr == r -> add (MGIPass r) i l
  (MGIPull nb nds i, MGIPull b ds l) | nb == b && nds == ds -> add (MGIPull b ds) i l
  (MGIUDPInst nudp nds nd2 i, MGIUDPInst udp ds d2 l)
    | nudp == udp && nds == ds && nd2 == d2 -> add (MGIUDPInst udp ds d2) i l
  (MGIModInst nmod npa i, MGIModInst mod pa l) | nmod == mod && npa == pa ->
    add (MGIModInst mod pa) i l
  (MGIUnknownInst nt np i, MGIUnknownInst t p l) | nt == t && np == p ->
    add (MGIUnknownInst t p) i l
  _ -> Nothing
  where
    add f x y = Just $ f $ runIdentity x <| y

-- | Converts `ModGenBlockedItem`s into `ModGenSingleItem`s
fromMGBlockedItem :: [Attributed ModGenBlockedItem] -> [Attributed ModGenSingleItem]
fromMGBlockedItem =
  nonEmpty [] $ toList . regroup (fmap fromMGBlockedItem1) (addAttributed fromMGBlockedItem_add)