packages feed

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

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

-- |
-- Module      : Verismith.Verilog.AST
-- Description : Definition of the Verilog AST types.
-- Copyright   : (c) 2018-2019, Yann Herklotz
-- License     : GPL-3
-- Maintainer  : yann [at] yannherklotz [dot] com
-- Stability   : experimental
-- Poratbility : POSIX
--
-- Defines the types to build a Verilog AST.
module Verismith.Verilog.AST
  ( -- * Top level types
    SourceInfo (..),
    infoTop,
    infoSrc,
    Verilog (..),

    -- * Primitives

    -- ** Identifier
    Identifier (..),

    -- ** Control
    Delay (..),
    Event (..),

    -- ** Operators
    BinaryOperator (..),
    UnaryOperator (..),

    -- ** Task
    Task (..),
    taskName,
    taskExpr,

    -- ** Left hand side value
    LVal (..),
    regId,
    regExprId,
    regExpr,
    regSizeId,
    regSizeRange,
    regConc,

    -- ** Ports
    PortDir (..),
    PortType (..),
    Port (..),
    portType,
    portSigned,
    portSize,
    portName,

    -- * Expression
    Expr (..),
    _Id,
    ConstExpr (..),
    ConstExprF (..),
    constToExpr,
    exprToConst,
    Range (..),
    constNum,
    constParamId,
    constConcat,
    constUnOp,
    constPrim,
    constLhs,
    constBinOp,
    constRhs,
    constCond,
    constTrue,
    constFalse,
    constStr,

    -- * Assignment
    Assign (..),
    assignReg,
    assignDelay,
    assignExpr,
    ContAssign (..),
    contAssignNetLVal,
    contAssignExpr,

    -- ** Parameters
    Parameter (..),
    paramIdent,
    paramValue,
    LocalParam (..),
    localParamIdent,
    localParamValue,

    -- * Statment
    CaseType (..),
    CasePair (..),
    Statement (..),
    statDelay,
    statDStat,
    statEvent,
    statEStat,
    statements,
    stmntBA,
    stmntNBA,
    stmntTask,
    stmntSysTask,
    stmntCondExpr,
    stmntCondTrue,
    stmntCondFalse,
    stmntCaseType,
    stmntCaseExpr,
    stmntCasePair,
    stmntCaseDefault,
    forAssign,
    forExpr,
    forIncr,
    forStmnt,

    -- * Module
    ModDecl (..),
    modId,
    modOutPorts,
    modInPorts,
    modItems,
    modParams,
    _ModDeclAnn,
    _ModDecl,
    ModItem (..),
    modContAssign,
    modInstId,
    modInstName,
    modInstConns,
    _Initial,
    _Always,
    paramDecl,
    localParamDecl,
    traverseModItem,
    declDir,
    declPort,
    declVal,
    ModConn (..),
    modConnName,
    modExpr,

    -- * Useful Lenses and Traversals
    aModule,
    getModule,
    getSourceId,
    mainModule,
    Annotations (..),
  )
where

import Control.DeepSeq (NFData)
import Control.Lens hiding ((<|))
import Data.Data
import Data.Data.Lens
import Data.Functor.Foldable.TH (makeBaseFunctor)
import Data.List.NonEmpty (NonEmpty (..), (<|))
import Data.String (IsString, fromString)
import Data.Text (Text, pack)
import GHC.Generics (Generic)
import Verismith.Verilog.BitVec

class (Functor m) => Annotations m where
  removeAnn :: m a -> m a
  clearAnn :: m a -> m ()
  clearAnn = fmap (\_ -> ()) . removeAnn
  collectAnn :: m a -> [a]

-- | Identifier in Verilog. This is just a string of characters that can either
-- be lowercase and uppercase for now. This might change in the future though,
-- as Verilog supports many more characters in Identifiers.
newtype Identifier = Identifier {getIdentifier :: Text}
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeWrapped ''Identifier)

instance IsString Identifier where
  fromString = Identifier . pack

instance Semigroup Identifier where
  Identifier a <> Identifier b = Identifier $ a <> b

instance Monoid Identifier where
  mempty = Identifier mempty

-- | Verilog syntax for adding a delay, which is represented as @#num@.
newtype Delay = Delay {_getDelay :: Int}
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeWrapped ''Delay)

instance Num Delay where
  Delay a + Delay b = Delay $ a + b
  Delay a - Delay b = Delay $ a - b
  Delay a * Delay b = Delay $ a * b
  negate (Delay a) = Delay $ negate a
  abs (Delay a) = Delay $ abs a
  signum (Delay a) = Delay $ signum a
  fromInteger = Delay . fromInteger

-- | Binary operators that are currently supported in the verilog generation.
data BinaryOperator
  = BinPlus
  | BinMinus
  | BinTimes
  | BinDiv
  | BinMod
  | BinEq
  | BinNEq
  | BinCEq
  | BinCNEq
  | BinLAnd
  | BinLOr
  | BinLT
  | BinLEq
  | BinGT
  | BinGEq
  | BinAnd
  | BinOr
  | BinXor
  | BinXNor
  | BinXNorInv
  | BinPower
  | BinLSL
  | BinLSR
  | BinASL
  | BinASR
  deriving (Eq, Show, Ord, Data, Generic, NFData)

-- | Unary operators that are currently supported by the generator.
data UnaryOperator
  = UnPlus
  | UnMinus
  | UnLNot
  | UnNot
  | UnAnd
  | UnNand
  | UnOr
  | UnNor
  | UnXor
  | UnNxor
  | UnNxorInv
  deriving (Eq, Show, Ord, Data, Generic, NFData)

-- | Constant expression, which are known before simulation at compile time.
data ConstExpr
  = ConstNum
      { _constNum :: {-# UNPACK #-} !BitVec
      }
  | ParamId
      { _constParamId :: {-# UNPACK #-} !Identifier
      }
  | ConstConcat
      { _constConcat :: !(NonEmpty ConstExpr)
      }
  | ConstUnOp
      { _constUnOp :: !UnaryOperator,
        _constPrim :: !ConstExpr
      }
  | ConstBinOp
      { _constLhs :: !ConstExpr,
        _constBinOp :: !BinaryOperator,
        _constRhs :: !ConstExpr
      }
  | ConstCond
      { _constCond :: !ConstExpr,
        _constTrue :: !ConstExpr,
        _constFalse :: !ConstExpr
      }
  | ConstStr
      { _constStr :: {-# UNPACK #-} !Text
      }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''ConstExpr)

$(makeBaseFunctor ''ConstExpr)

constToExpr :: ConstExpr -> Expr
constToExpr (ConstNum a) = Number a
constToExpr (ParamId a) = Id a
constToExpr (ConstConcat a) = Concat $ fmap constToExpr a
constToExpr (ConstUnOp a b) = UnOp a $ constToExpr b
constToExpr (ConstBinOp a b c) = BinOp (constToExpr a) b $ constToExpr c
constToExpr (ConstCond a b c) =
  Cond (constToExpr a) (constToExpr b) $ constToExpr c
constToExpr (ConstStr a) = Str a

exprToConst :: Expr -> ConstExpr
exprToConst (Number a) = ConstNum a
exprToConst (Id a) = ParamId a
exprToConst (Concat a) = ConstConcat $ fmap exprToConst a
exprToConst (UnOp a b) = ConstUnOp a $ exprToConst b
exprToConst (BinOp a b c) = ConstBinOp (exprToConst a) b $ exprToConst c
exprToConst (Cond a b c) =
  ConstCond (exprToConst a) (exprToConst b) $ exprToConst c
exprToConst (Str a) = ConstStr a
exprToConst _ = error "Not a constant expression"

instance Num ConstExpr where
  a + b = ConstBinOp a BinPlus b
  a - b = ConstBinOp a BinMinus b
  a * b = ConstBinOp a BinTimes b
  negate = ConstUnOp UnMinus
  abs = undefined
  signum = undefined
  fromInteger = ConstNum . fromInteger

instance Semigroup ConstExpr where
  (ConstConcat a) <> (ConstConcat b) = ConstConcat $ a <> b
  (ConstConcat a) <> b = ConstConcat $ a <> (b :| [])
  a <> (ConstConcat b) = ConstConcat $ a <| b
  a <> b = ConstConcat $ a <| b :| []

instance Monoid ConstExpr where
  mempty = ConstNum 0

instance IsString ConstExpr where
  fromString = ConstStr . fromString

instance Plated ConstExpr where
  plate = uniplate

-- | Range that can be associated with any port or left hand side. Contains the
-- msb and lsb bits as 'ConstExpr'. This means that they can be generated using
-- parameters, which can in turn be changed at synthesis time.
data Range = Range
  { rangeMSB :: !ConstExpr,
    rangeLSB :: !ConstExpr
  }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

instance Num Range where
  (Range s1 a) + (Range s2 b) = Range (s1 + s2) $ a + b
  (Range s1 a) - (Range s2 b) = Range (s1 - s2) . max 0 $ a - b
  (Range s1 a) * (Range s2 b) = Range (s1 * s2) $ a * b
  negate = undefined
  abs = id
  signum _ = 1
  fromInteger = flip Range 0 . fromInteger . (-) 1

-- | Verilog expression, which can either be a primary expression, unary
-- expression, binary operator expression or a conditional expression.
data Expr
  = Number {-# UNPACK #-} !BitVec
  | Id {-# UNPACK #-} !Identifier
  | VecSelect {-# UNPACK #-} !Identifier !Expr
  | RangeSelect {-# UNPACK #-} !Identifier !Range
  | Concat !(NonEmpty Expr)
  | UnOp !UnaryOperator !Expr
  | BinOp !Expr !BinaryOperator !Expr
  | Cond !Expr !Expr !Expr
  | Appl !Identifier !Expr
  | Str {-# UNPACK #-} !Text
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''Expr)
$(makePrisms ''Expr)

$(makeBaseFunctor ''Expr)

instance Num Expr where
  a + b = BinOp a BinPlus b
  a - b = BinOp a BinMinus b
  a * b = BinOp a BinTimes b
  negate = UnOp UnMinus
  abs = undefined
  signum = undefined
  fromInteger = Number . fromInteger

instance Semigroup Expr where
  (Concat a) <> (Concat b) = Concat $ a <> b
  (Concat a) <> b = Concat $ a <> (b :| [])
  a <> (Concat b) = Concat $ a <| b
  a <> b = Concat $ a <| b :| []

instance Monoid Expr where
  mempty = Number 0

instance IsString Expr where
  fromString = Str . fromString

instance Plated Expr where
  plate = uniplate

-- | Verilog syntax for an event, such as @\@x@, which is used for always blocks
data Event
  = EId {-# UNPACK #-} !Identifier
  | EExpr !Expr
  | EAll
  | EPosEdge {-# UNPACK #-} !Identifier
  | ENegEdge {-# UNPACK #-} !Identifier
  | EOr !Event !Event
  | EComb !Event !Event
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeBaseFunctor ''Event)

instance Plated Event where
  plate = uniplate

-- | Task call, which is similar to function calls.
data Task = Task
  { _taskName :: {-# UNPACK #-} !Identifier,
    _taskExpr :: [Expr]
  }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''Task)

-- | Type that represents the left hand side of an assignment, which can be a
-- concatenation such as in:
--
-- @
-- {a, b, c} = 32'h94238;
-- @
data LVal
  = RegId
      { _regId :: {-# UNPACK #-} !Identifier
      }
  | RegExpr
      { _regExprId :: {-# UNPACK #-} !Identifier,
        _regExpr :: !Expr
      }
  | RegSize
      { _regSizeId :: {-# UNPACK #-} !Identifier,
        _regSizeRange :: {-# UNPACK #-} !Range
      }
  | RegConcat
      { _regConc :: [Expr]
      }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''LVal)

instance IsString LVal where
  fromString = RegId . fromString

-- | Different port direction that are supported in Verilog.
data PortDir
  = PortIn
  | PortOut
  | PortInOut
  deriving (Eq, Show, Ord, Data, Generic, NFData)

-- | Currently, only @wire@ and @reg@ are supported, as the other net types are
-- not that common and not a priority.
data PortType
  = Wire
  | Reg
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''PortType)

-- | Port declaration. It contains information about the type of the port, the
-- size, and the port name. It used to also contain information about if it was
-- an input or output port. However, this is not always necessary and was more
-- cumbersome than useful, as a lot of ports can be declared without input and
-- output port.
--
-- This is now implemented inside '(ModDecl ann)' itself, which uses a list of output
-- and input ports.
data Port = Port
  { _portType :: !PortType,
    _portSigned :: !Bool,
    _portSize :: {-# UNPACK #-} !Range,
    _portName :: {-# UNPACK #-} !Identifier
  }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''Port)

-- | This is currently a type because direct module declaration should also be
-- added:
--
-- @
-- mod a(.y(y1), .x1(x11), .x2(x22));
-- @
data ModConn
  = ModConn
      { _modExpr :: !Expr
      }
  | ModConnNamed
      { _modConnName :: {-# UNPACK #-} !Identifier,
        _modExpr :: !Expr
      }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''ModConn)

data Assign = Assign
  { _assignReg :: !LVal,
    _assignDelay :: !(Maybe Delay),
    _assignExpr :: !Expr
  }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''Assign)

-- | Type for continuous assignment.
--
-- @
-- assign x = 2'b1;
-- @
data ContAssign = ContAssign
  { _contAssignNetLVal :: {-# UNPACK #-} !Identifier,
    _contAssignExpr :: !Expr
  }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''ContAssign)

-- | Case pair which contains an expression followed by a statement which will
-- get executed if the expression matches the expression in the case statement.
data CasePair a = CasePair
  { _casePairExpr :: !Expr,
    _casePairStmnt :: !(Statement a)
  }
  deriving (Eq, Show, Ord, Functor, Data, Generic, NFData)

traverseStmntCasePair ::
  (Functor f) =>
  (Statement a1 -> f (Statement a2)) ->
  CasePair a1 ->
  f (CasePair a2)
traverseStmntCasePair f (CasePair a s) = CasePair a <$> f s

-- | Type of case statement, which determines how it is interpreted.
data CaseType
  = CaseStandard
  | CaseX
  | CaseZ
  deriving (Eq, Show, Ord, Data, Generic, NFData)

-- | Statements in Verilog.
data Statement a
  = -- | Time control (@#NUM@)
    TimeCtrl
      { _statDelay :: {-# UNPACK #-} !Delay,
        _statDStat :: Maybe (Statement a)
      }
  | EventCtrl
      { _statEvent :: !Event,
        _statEStat :: Maybe (Statement a)
      }
  | -- | Sequential block (@begin ... end@)
    SeqBlock {_statements :: [Statement a]}
  | -- | blocking assignment (@=@)
    BlockAssign {_stmntBA :: !Assign}
  | -- | Non blocking assignment (@<=@)
    NonBlockAssign {_stmntNBA :: !Assign}
  | TaskEnable {_stmntTask :: !Task}
  | SysTaskEnable {_stmntSysTask :: !Task}
  | CondStmnt
      { _stmntCondExpr :: Expr,
        _stmntCondTrue :: Maybe (Statement a),
        _stmntCondFalse :: Maybe (Statement a)
      }
  | StmntCase
      { _stmntCaseType :: !CaseType,
        _stmntCaseExpr :: !Expr,
        _stmntCasePair :: ![CasePair a],
        _stmntCaseDefault :: !(Maybe (Statement a))
      }
  | -- | Loop bounds shall be statically computable for a for loop.
    ForLoop
      { _forAssign :: !Assign,
        _forExpr :: Expr,
        _forIncr :: !Assign,
        _forStmnt :: Statement a
      }
  | StmntAnn a (Statement a)
  deriving (Eq, Show, Ord, Data, Functor, Generic, NFData)

$(makeLenses ''Statement)

instance Plated (Statement a) where
  plate f (TimeCtrl d s) = TimeCtrl d <$> traverse f s
  plate f (EventCtrl d s) = EventCtrl d <$> traverse f s
  plate f (SeqBlock s) = SeqBlock <$> traverse f s
  plate f (CondStmnt e s1 s2) = CondStmnt e <$> traverse f s1 <*> traverse f s2
  plate f (StmntCase a b c d) =
    StmntCase a b
      <$> traverse (traverseStmntCasePair f) c
      <*> traverse f d
  plate f (ForLoop a b c d) = ForLoop a b c <$> f d
  plate _ a = pure a

instance Semigroup (Statement a) where
  (SeqBlock a) <> (SeqBlock b) = SeqBlock $ a <> b
  (SeqBlock a) <> b = SeqBlock $ a <> [b]
  a <> (SeqBlock b) = SeqBlock $ a : b
  a <> b = SeqBlock [a, b]

instance Monoid (Statement a) where
  mempty = SeqBlock []

instance Annotations Statement where
  removeAnn (StmntAnn _ s) = removeAnn s
  removeAnn (TimeCtrl e s) = TimeCtrl e $ fmap removeAnn s
  removeAnn (EventCtrl e s) = EventCtrl e $ fmap removeAnn s
  removeAnn (SeqBlock s) = SeqBlock $ fmap removeAnn s
  removeAnn (CondStmnt c ms1 ms2) = CondStmnt c (fmap removeAnn ms1) $ fmap removeAnn ms2
  removeAnn (StmntCase ct ce cp cdef) = StmntCase ct ce (fmap removeAnn cp) $ fmap removeAnn cdef
  removeAnn (ForLoop a b c s) = ForLoop a b c $ removeAnn s
  removeAnn s = s
  collectAnn (StmntAnn _ s) = collectAnn s
  collectAnn (TimeCtrl _ s) = concatMap collectAnn s
  collectAnn (EventCtrl _ s) = concatMap collectAnn s
  collectAnn (SeqBlock s) = concatMap collectAnn s
  collectAnn (CondStmnt _ ms1 ms2) = concatMap collectAnn ms1 <> concatMap collectAnn ms2
  collectAnn (StmntCase _ _ cp cdef) = concatMap collectAnn cp <> concatMap collectAnn cdef
  collectAnn (ForLoop _ _ _ s) = collectAnn s
  collectAnn _ = []

instance Annotations CasePair where
  removeAnn (CasePair e s) = CasePair e $ removeAnn s
  collectAnn (CasePair _ s) = collectAnn s

-- | Parameter that can be assigned in blocks or modules using @parameter@.
data Parameter = Parameter
  { _paramIdent :: {-# UNPACK #-} !Identifier,
    _paramValue :: ConstExpr
  }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''Parameter)

-- | Local parameter that can be assigned anywhere using @localparam@. It cannot
-- be changed by initialising the module.
data LocalParam = LocalParam
  { _localParamIdent :: {-# UNPACK #-} !Identifier,
    _localParamValue :: ConstExpr
  }
  deriving (Eq, Show, Ord, Data, Generic, NFData)

$(makeLenses ''LocalParam)

-- | Module item which is the body of the module expression.
data ModItem a
  = ModCA {_modContAssign :: !ContAssign}
  | ModInst
      { _modInstId :: {-# UNPACK #-} !Identifier,
        _modInstDecl :: [ModConn],
        _modInstName :: {-# UNPACK #-} !Identifier,
        _modInstConns :: [ModConn]
      }
  | Initial !(Statement a)
  | Always !(Statement a)
  | Property
      { _moditemPropLabel :: {-# UNPACK #-} !Identifier,
        _moditemPropEvent :: !Event,
        _moditemPropBodyL :: Maybe Expr,
        _moditemPropBodyR :: Expr
      }
  | Decl
      { _declDir :: !(Maybe PortDir),
        _declPort :: !Port,
        _declVal :: Maybe ConstExpr
      }
  | ParamDecl {_paramDecl :: NonEmpty Parameter}
  | LocalParamDecl {_localParamDecl :: NonEmpty LocalParam}
  | ModItemAnn a (ModItem a)
  deriving (Eq, Show, Ord, Functor, Data, Generic, NFData)

$(makePrisms ''ModItem)

$(makeLenses ''ModItem)

instance Annotations ModItem where
  removeAnn (ModItemAnn _ mi) = removeAnn mi
  removeAnn (Initial s) = Initial $ removeAnn s
  removeAnn (Always s) = Always $ removeAnn s
  removeAnn mi = mi
  collectAnn (ModItemAnn _ mi) = collectAnn mi
  collectAnn (Initial s) = collectAnn s
  collectAnn (Always s) = collectAnn s
  collectAnn mi = []

-- | 'module' module_identifier [list_of_ports] ';' { module_item } 'end_module'
data ModDecl a
  = ModDecl
      { _modId :: {-# UNPACK #-} !Identifier,
        _modOutPorts :: ![Port],
        _modInPorts :: ![Port],
        _modItems :: ![ModItem a],
        _modParams :: ![Parameter]
      }
  | ModDeclAnn a (ModDecl a)
  deriving (Eq, Show, Ord, Functor, Data, Generic, NFData)

instance Plated (ModDecl a) where
  plate f (ModDeclAnn b m) = ModDeclAnn b <$> plate f m
  plate _ m = pure m

$(makeLenses ''ModDecl)
$(makePrisms ''ModDecl)

instance Annotations ModDecl where
  removeAnn (ModDecl i out inp mis params) = ModDecl i out inp (fmap removeAnn mis) params
  removeAnn (ModDeclAnn _ mi) = mi
  collectAnn (ModDecl _ _ _ mis _) = concatMap collectAnn mis
  collectAnn (ModDeclAnn a mi) = a : collectAnn mi

traverseModConn :: (Applicative f) => (Expr -> f Expr) -> ModConn -> f ModConn
traverseModConn f (ModConn e) = ModConn <$> f e
traverseModConn f (ModConnNamed a e) = ModConnNamed a <$> f e

traverseModItem :: (Applicative f) => (Expr -> f Expr) -> (ModItem ann) -> f (ModItem ann)
traverseModItem f (ModCA (ContAssign a e)) = ModCA . ContAssign a <$> f e
traverseModItem f (ModInst a b c e) =
  ModInst a b c <$> sequenceA (traverseModConn f <$> e)
traverseModItem _ e = pure e

-- | The complete sourcetext for the Verilog module.
newtype Verilog a = Verilog {getVerilog :: [ModDecl a]}
  deriving (Eq, Show, Ord, Functor, Data, Generic, NFData)

$(makeWrapped ''Verilog)

instance Semigroup (Verilog a) where
  Verilog a <> Verilog b = Verilog $ a <> b

instance Monoid (Verilog a) where
  mempty = Verilog mempty

instance Annotations Verilog where
  removeAnn (Verilog v) = Verilog $ fmap removeAnn v
  collectAnn (Verilog v) = concatMap collectAnn v

-- | Top level type which contains all the source code and associated
-- information.
data SourceInfo a = SourceInfo
  { _infoTop :: {-# UNPACK #-} !Text,
    _infoSrc :: !(Verilog a)
  }
  deriving (Eq, Show, Ord, Functor, Data, Generic, NFData)

$(makeLenses ''SourceInfo)

instance Semigroup (SourceInfo a) where
  (SourceInfo t v) <> (SourceInfo _ v2) = SourceInfo t $ v <> v2

instance Monoid (SourceInfo a) where
  mempty = SourceInfo mempty mempty

instance Annotations SourceInfo where
  removeAnn (SourceInfo t v) = SourceInfo t $ removeAnn v
  collectAnn (SourceInfo t v) = collectAnn v

-- | Attributes which can be set to various nodes in the AST.
--
-- @
-- (* synthesis *)
-- @
data Attribute
  = AttrAssign Identifier ConstExpr
  | AttrName Identifier
  deriving (Eq, Show, Ord, Data, Generic, NFData)

-- | Annotations which can be added to the AST. These are supported in all the
-- nodes of the AST and a custom type can be declared for them.
data Annotation a
  = Ann a
  | AnnAttrs [Attribute]
  deriving (Eq, Show, Ord, Data, Generic, NFData)

getModule :: Traversal' (Verilog a) (ModDecl a)
getModule = _Wrapped . traverse
{-# INLINE getModule #-}

getSourceId :: Traversal' (Verilog a) Text
getSourceId = getModule . modId . _Wrapped
{-# INLINE getSourceId #-}

-- | May need to change this to Traversal to be safe. For now it will fail when
-- the main has not been properly set with.
aModule :: Identifier -> Lens' (SourceInfo a) (ModDecl a)
aModule t = lens get_ set_
  where
    set_ (SourceInfo top main) v =
      SourceInfo top (main & getModule %~ update (getIdentifier t) v)
    update top v m@(ModDecl (Identifier i) _ _ _ _)
      | i == top = v
      | otherwise = m
    update top v (ModDeclAnn _ m) = update top v m
    get_ (SourceInfo _ main) =
      head . filter (f $ getIdentifier t) $ main ^.. getModule
    f top (ModDecl (Identifier i) _ _ _ _) = i == top
    f top (ModDeclAnn _ m) = f top m

-- | May need to change this to Traversal to be safe. For now it will fail when
-- the main has not been properly set with.
mainModule :: Lens' (SourceInfo a) (ModDecl a)
mainModule = lens get_ set_
  where
    set_ (SourceInfo top main) v =
      SourceInfo top (main & getModule %~ update top v)
    update top v m@(ModDecl (Identifier i) _ _ _ _)
      | i == top = v
      | otherwise = m
    update top v (ModDeclAnn _ m) = update top v m
    get_ (SourceInfo top main) = head . filter (f top) $ main ^.. getModule
    f top (ModDecl (Identifier i) _ _ _ _) = i == top
    f top (ModDeclAnn _ m) = f top m