mirror of https://github.com/zachjs/sv2v.git
huge pass at revamping AST to be more general, easy to work with
This commit is contained in:
parent
e795109f5f
commit
50b7bf2839
|
|
@ -34,8 +34,8 @@ getStmtLHSs :: Stmt -> [LHS]
|
|||
getStmtLHSs (Block _ stmts) = concat $ map getStmtLHSs stmts
|
||||
getStmtLHSs (Case kw e cases (Just stmt)) = (getStmtLHSs stmt) ++ (getStmtLHSs $ Case kw e cases Nothing)
|
||||
getStmtLHSs (Case _ _ cases Nothing) = concat $ map getStmtLHSs $ map snd cases
|
||||
getStmtLHSs (BlockingAssignment lhs _) = [lhs]
|
||||
getStmtLHSs (NonBlockingAssignment lhs _) = [lhs]
|
||||
getStmtLHSs (AsgnBlk lhs _) = [lhs]
|
||||
getStmtLHSs (Asgn lhs _) = [lhs]
|
||||
getStmtLHSs (For _ _ _ stmt) = getStmtLHSs stmt
|
||||
getStmtLHSs (If _ s1 s2) = (getStmtLHSs s1) ++ (getStmtLHSs s2)
|
||||
getStmtLHSs (Timing _ s) = getStmtLHSs s
|
||||
|
|
@ -56,8 +56,8 @@ getRegIdents (AlwaysC _ stmt) =
|
|||
getRegIdents _ = Set.empty
|
||||
|
||||
convertModuleItem :: RegIdents -> ModuleItem -> ModuleItem
|
||||
convertModuleItem idents (LocalNet (Logic mr) ident val) =
|
||||
LocalNet (t mr) ident val
|
||||
convertModuleItem idents (MIDecl (Variable dir (Logic mr) ident a me)) =
|
||||
MIDecl $ Variable dir (t mr) ident a me
|
||||
where
|
||||
t = if Set.member ident idents then Reg else Wire
|
||||
convertModuleItem idents (Generate items) = Generate $ map (convertGenItem $ convertModuleItem idents) items
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ convert = map convertDescription
|
|||
|
||||
convertDescription :: Description -> Description
|
||||
convertDescription (Module name ports items) =
|
||||
-- Insert the new items right after the LocalNet for the item to preserve
|
||||
-- Insert the new items right after the Variable for the item to preserve
|
||||
-- declaration order, which some toolchains care about.
|
||||
Module name ports $ concat $ map addUnflattener items'
|
||||
where
|
||||
|
|
@ -49,18 +49,18 @@ convertDescription (Module name ports items) =
|
|||
items' = map (convertModuleItem dimMap) items
|
||||
outputs = Set.fromList $ mapMaybe getOutput items
|
||||
getOutput :: ModuleItem -> Maybe Identifier
|
||||
getOutput (PortDecl Output _ ident) = Just ident
|
||||
getOutput (MIDecl (Variable Output _ ident _ _)) = Just ident
|
||||
getOutput _ = Nothing
|
||||
getExtraDims :: ModuleItem -> Maybe (Identifier, (Type, Range))
|
||||
getExtraDims (LocalNet t ident _) =
|
||||
getExtraDims (MIDecl (Variable _ t ident _ _)) =
|
||||
if length rs > 1
|
||||
then Just (ident, (tf $ tail rs, head rs))
|
||||
else Nothing
|
||||
where (tf, rs) = typeDims t
|
||||
getExtraDims _ = Nothing
|
||||
addUnflattener :: ModuleItem -> [ModuleItem]
|
||||
addUnflattener (LocalNet t ident val) =
|
||||
LocalNet t ident val :
|
||||
addUnflattener (orig @ (MIDecl (Variable _ _ ident _ _))) =
|
||||
orig :
|
||||
case Map.lookup ident dimMap of
|
||||
Nothing -> []
|
||||
Just desc -> unflattener outputs (ident, desc)
|
||||
|
|
@ -84,10 +84,10 @@ simplify other = other
|
|||
unflattener :: Set.Set Identifier -> (Identifier, (Type, Range)) -> [ModuleItem]
|
||||
unflattener outputs (arr, (t, (majorHi, majorLo))) =
|
||||
[ Comment $ "sv2v packed-array-flatten unflattener for " ++ arr
|
||||
, LocalNet t arrUnflat (Left [(majorHi, majorLo)])
|
||||
, MIDecl $ Variable Local t arrUnflat [(majorHi, majorLo)] Nothing
|
||||
, Generate
|
||||
[ GenModuleItem $ Genvar index
|
||||
, GenModuleItem $ MIIntegerV $ IntegerV (arrUnflat ++ "_repeater_index") (Right Nothing)
|
||||
, GenModuleItem $ MIDecl $ Variable Local IntegerT (arrUnflat ++ "_repeater_index") [] Nothing
|
||||
, GenFor
|
||||
(index, majorLo)
|
||||
(BinOp Le (Ident index) majorHi)
|
||||
|
|
@ -110,7 +110,7 @@ unflattener outputs (arr, (t, (majorHi, majorLo))) =
|
|||
(minorHi, minorLo) = head $ snd $ typeDims t
|
||||
size = simplify $ BinOp Add (BinOp Sub minorHi minorLo) (Number "1")
|
||||
localparam :: Identifier -> Expr -> GenItem
|
||||
localparam x v = GenModuleItem $ MILocalparam $ Localparam Nothing x v
|
||||
localparam x v = GenModuleItem $ MIDecl $ Localparam (Implicit []) x v
|
||||
origRange = ( (BinOp Add (Ident startBit)
|
||||
(BinOp Sub size (Number "1")))
|
||||
, Ident startBit )
|
||||
|
|
@ -120,16 +120,13 @@ typeDims (Reg r) = (Reg , r)
|
|||
typeDims (Wire r) = (Wire , r)
|
||||
typeDims (Logic r) = (Logic , r)
|
||||
typeDims (Alias t r) = (Alias t, r)
|
||||
typeDims (Implicit r) = (Implicit, r)
|
||||
typeDims (IntegerT ) = (error "ranges cannot be applied to IntegerT", [])
|
||||
typeDims (Enum t v r) = (Enum t v, r)
|
||||
|
||||
prefix :: Identifier -> Identifier
|
||||
prefix ident = "_sv2v_" ++ ident
|
||||
|
||||
rewriteRangesOrAssignment :: DimMap -> RangesOrAssignment -> RangesOrAssignment
|
||||
rewriteRangesOrAssignment dimMap (Right (Just e)) =
|
||||
Right $ Just $ rewriteExpr dimMap e
|
||||
rewriteRangesOrAssignment _ other = other
|
||||
|
||||
rewriteRange :: DimMap -> Range -> Range
|
||||
rewriteRange dimMap (a, b) = (r a, r b)
|
||||
where r = rewriteExpr dimMap
|
||||
|
|
@ -208,8 +205,8 @@ rewriteStmt dimMap orig = rs orig
|
|||
case def of
|
||||
Nothing -> Nothing
|
||||
Just stmt -> Just $ rs stmt
|
||||
rs (BlockingAssignment lhs expr) = convertAssignment BlockingAssignment lhs expr
|
||||
rs (NonBlockingAssignment lhs expr) = convertAssignment NonBlockingAssignment lhs expr
|
||||
rs (AsgnBlk lhs expr) = convertAssignment AsgnBlk lhs expr
|
||||
rs (Asgn lhs expr) = convertAssignment Asgn lhs expr
|
||||
rs (For (x1, e1) cc (x2, e2) stmt) = For (x1, e1') cc' (x2, e2') (rs stmt)
|
||||
where
|
||||
e1' = rewriteExpr dimMap e1
|
||||
|
|
@ -236,18 +233,15 @@ rewriteStmt dimMap orig = rs orig
|
|||
constructor (rewriteLHS dimMap lhs) (rewriteExpr dimMap expr)
|
||||
|
||||
convertModuleItem :: DimMap -> ModuleItem -> ModuleItem
|
||||
convertModuleItem dimMap (LocalNet t x val) =
|
||||
convertModuleItem dimMap (MIDecl (Variable d t x a me)) =
|
||||
if Map.member x dimMap
|
||||
then LocalNet t' x val'
|
||||
else LocalNet t x val'
|
||||
then MIDecl $ Variable d t' x a' me'
|
||||
else MIDecl $ Variable d t x a' me'
|
||||
where
|
||||
(tf, rs) = typeDims t
|
||||
t' = tf $ flattenRanges rs
|
||||
val' = rewriteRangesOrAssignment dimMap val
|
||||
convertModuleItem dimMap (PortDecl dir rs x) =
|
||||
if Map.member x dimMap
|
||||
then PortDecl dir (flattenRanges rs) x
|
||||
else PortDecl dir rs x
|
||||
a' = map (rewriteRange dimMap) a
|
||||
me' = maybe Nothing (Just . rewriteExpr dimMap) me
|
||||
convertModuleItem dimMap (Generate items) =
|
||||
Generate $ map (convertGenItem dimMap) items
|
||||
convertModuleItem dimMap (Assign lhs expr) =
|
||||
|
|
@ -264,11 +258,9 @@ convertModuleItem dimMap (Instance m params x (Just l)) =
|
|||
convertPortBinding :: PortBinding -> PortBinding
|
||||
convertPortBinding (p, Nothing) = (p, Nothing)
|
||||
convertPortBinding (p, Just e) = (p, Just $ rewriteExpr dimMap e)
|
||||
convertModuleItem _ (Comment x) = Comment x
|
||||
convertModuleItem _ (Genvar x) = Genvar x
|
||||
convertModuleItem _ (MIParameter x) = MIParameter x
|
||||
convertModuleItem _ (MILocalparam x) = MILocalparam x
|
||||
convertModuleItem _ (MIIntegerV x) = MIIntegerV x
|
||||
convertModuleItem _ (Comment x) = Comment x
|
||||
convertModuleItem _ (Genvar x) = Genvar x
|
||||
convertModuleItem _ (MIDecl x) = MIDecl x
|
||||
|
||||
convertGenItem :: DimMap -> GenItem -> GenItem
|
||||
convertGenItem dimMap item = convertGenItem' item
|
||||
|
|
|
|||
|
|
@ -32,11 +32,9 @@ convertStmt f = f . convertStmt'
|
|||
Case kw expr cases' def'
|
||||
where
|
||||
cases' = map (\(exprs, stmt) -> (exprs, cs stmt)) cases
|
||||
def' = case def of
|
||||
Nothing -> Nothing
|
||||
Just stmt -> Just (cs stmt)
|
||||
convertStmt' (BlockingAssignment lhs expr) = BlockingAssignment lhs expr
|
||||
convertStmt' (NonBlockingAssignment lhs expr) = NonBlockingAssignment lhs expr
|
||||
def' = maybe Nothing (Just . cs) def
|
||||
convertStmt' (AsgnBlk lhs expr) = AsgnBlk lhs expr
|
||||
convertStmt' (Asgn lhs expr) = Asgn lhs expr
|
||||
convertStmt' (For a b c stmt) = For a b c (cs stmt)
|
||||
convertStmt' (If e s1 s2) = If e (cs s1) (cs s2)
|
||||
convertStmt' (Timing sense stmt) = Timing sense (cs stmt)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@
|
|||
- Author: Zachary Snow <zach@zachjs.com>
|
||||
-
|
||||
- Conversion for `typedef`
|
||||
-
|
||||
- Aliased types can (probably) appear in all item declarations, including
|
||||
- modules, blocks, and function parameters.
|
||||
-}
|
||||
|
||||
-- TODO: Right now we only support typedefs for module data items. Function
|
||||
-- parameters, block items, etc., probably support typedefs, too.
|
||||
|
||||
-- TODO FIXME XXX: `Cast` contains a type, which we'll need to resolve/convert?
|
||||
|
||||
module Convert.Typedef (convert) where
|
||||
|
||||
import Data.Maybe
|
||||
|
|
@ -37,9 +35,11 @@ convertDescription types (Module name ports items) =
|
|||
convertDescription _ other = other
|
||||
|
||||
resolveType :: Types -> Type -> Type
|
||||
resolveType _ (Reg rs) = Reg rs
|
||||
resolveType _ (Wire rs) = Wire rs
|
||||
resolveType _ (Logic rs) = Logic rs
|
||||
resolveType _ (Reg rs) = Reg rs
|
||||
resolveType _ (Wire rs) = Wire rs
|
||||
resolveType _ (Logic rs) = Logic rs
|
||||
resolveType _ (Implicit rs) = Implicit rs
|
||||
resolveType _ (IntegerT ) = IntegerT
|
||||
resolveType _ (Enum Nothing vals rs) = Enum Nothing vals rs
|
||||
resolveType types (Enum (Just t) vals rs) = Enum (Just $ resolveType types t) vals rs
|
||||
resolveType types (Alias st rs1) =
|
||||
|
|
@ -48,9 +48,72 @@ resolveType types (Alias st rs1) =
|
|||
(Wire rs2) -> Wire $ rs2 ++ rs1
|
||||
(Logic rs2) -> Logic $ rs2 ++ rs1
|
||||
(Enum t v rs2) -> Enum t v $ rs2 ++ rs1
|
||||
(Implicit rs2) -> Implicit $ rs2 ++ rs1
|
||||
(IntegerT ) -> error $ "resolveType encountered packed `integer` on " ++ st
|
||||
(Alias _ _) -> error $ "resolveType invariant failed on " ++ st
|
||||
|
||||
convertDecl :: Types -> Decl -> Decl
|
||||
convertDecl types decl =
|
||||
case decl of
|
||||
Parameter t x e -> Parameter (rt t) x (re e)
|
||||
Localparam t x e -> Localparam (rt t) x (re e)
|
||||
Variable d t x a me -> Variable d (rt t) x a me'
|
||||
where me' = if isJust me then Just (re $ fromJust me) else me
|
||||
where
|
||||
rt = resolveType types
|
||||
re = convertExpr types
|
||||
|
||||
convertModuleItem :: Types -> ModuleItem -> ModuleItem
|
||||
convertModuleItem types (LocalNet t ident val) =
|
||||
LocalNet (resolveType types t) ident val
|
||||
convertModuleItem types (MIDecl decl) =
|
||||
MIDecl $ convertDecl types decl
|
||||
convertModuleItem types (Function t x decls stmt) =
|
||||
Function (resolveType types t) x
|
||||
(map (convertDecl types) decls)
|
||||
(convertStmt types stmt)
|
||||
convertModuleItem types (Assign lhs expr) =
|
||||
Assign lhs (convertExpr types expr)
|
||||
convertModuleItem types (AlwaysC kw stmt) =
|
||||
AlwaysC kw (convertStmt types stmt)
|
||||
convertModuleItem _ other = other
|
||||
|
||||
convertStmt :: Types -> Stmt -> Stmt
|
||||
convertStmt types = rs
|
||||
where
|
||||
rd = convertDecl types
|
||||
re = convertExpr types
|
||||
rs :: Stmt -> Stmt
|
||||
rs (Block header stmts) =
|
||||
Block header' (map rs stmts)
|
||||
where header' = maybe Nothing (\(x, decls) -> Just (x, map rd decls)) header
|
||||
rs (Case kw e cases def) = Case kw (re e)
|
||||
(map convertCase cases) def'
|
||||
where
|
||||
convertCase (exprs, stmt) = (map re exprs, rs stmt)
|
||||
def' = maybe Nothing (Just . rs) def
|
||||
rs (AsgnBlk lhs expr) = AsgnBlk lhs (re expr)
|
||||
rs (Asgn lhs expr) = Asgn lhs (re expr)
|
||||
rs (For (x1, e1) e (x2, e2) stmt) =
|
||||
For (x1, re e1) (re e) (x2, re e2) (rs stmt)
|
||||
rs (If e s1 s2) = If (re e) (rs s1) (rs s2)
|
||||
rs (Timing sense stmt) = Timing sense (rs stmt)
|
||||
rs (Null) = Null
|
||||
|
||||
convertExpr :: Types -> Expr -> Expr
|
||||
convertExpr types = re
|
||||
where
|
||||
re :: Expr -> Expr
|
||||
re (String s) = String s
|
||||
re (Number s) = Number s
|
||||
re (ConstBool b) = ConstBool b
|
||||
re (Ident i ) = Ident i
|
||||
re (IdentRange i r) = IdentRange i r
|
||||
re (IdentBit i e) = IdentBit i (re e)
|
||||
re (Repeat e l) = Repeat (re e) (map re l)
|
||||
re (Concat l ) = Concat (map re l)
|
||||
re (Call f l) = Call f (map re l)
|
||||
re (UniOp o e) = UniOp o (re e)
|
||||
re (BinOp o e1 e2) = BinOp o (re e1) (re e2)
|
||||
re (Mux e1 e2 e3) = Mux (re e1) (re e2) (re e3)
|
||||
re (Bit e n) = Bit (re e) n
|
||||
-- This is the reason we have to convert expressions in this module.
|
||||
re (Cast t e) = Cast (resolveType types t) (re e)
|
||||
|
|
|
|||
|
|
@ -10,19 +10,15 @@ module Language.SystemVerilog.AST
|
|||
, UniOp (..)
|
||||
, BinOp (..)
|
||||
, Sense (..)
|
||||
, BlockItemDeclaration (..)
|
||||
, Parameter (..)
|
||||
, Localparam (..)
|
||||
, IntegerV (..)
|
||||
, GenItem (..)
|
||||
, AlwaysKW (..)
|
||||
, CaseKW (..)
|
||||
, Decl (..)
|
||||
, AST
|
||||
, PortBinding
|
||||
, Case
|
||||
, Range
|
||||
, GenCase
|
||||
, RangesOrAssignment
|
||||
) where
|
||||
|
||||
import Data.List
|
||||
|
|
@ -65,49 +61,64 @@ data Direction
|
|||
= Input
|
||||
| Output
|
||||
| Inout
|
||||
| Local
|
||||
deriving Eq
|
||||
|
||||
instance Show Direction where
|
||||
show Input = "input"
|
||||
show Output = "output"
|
||||
show Inout = "inout"
|
||||
show Local = ""
|
||||
|
||||
data Type
|
||||
= Reg [Range]
|
||||
| Wire [Range]
|
||||
| Logic [Range]
|
||||
| Alias String [Range]
|
||||
= Reg [Range]
|
||||
| Wire [Range]
|
||||
| Logic [Range]
|
||||
| Alias Identifier [Range]
|
||||
| Implicit [Range]
|
||||
| IntegerT
|
||||
| Enum (Maybe Type) [(Identifier, Maybe Expr)] [Range]
|
||||
deriving Eq
|
||||
|
||||
instance Show Type where
|
||||
show (Reg r) = "reg" ++ (showRanges r)
|
||||
show (Wire r) = "wire" ++ (showRanges r)
|
||||
show (Logic r) = "logic" ++ (showRanges r)
|
||||
show (Alias t r) = t ++ (showRanges r)
|
||||
show (Reg r) = "reg" ++ (showRanges r)
|
||||
show (Wire r) = "wire" ++ (showRanges r)
|
||||
show (Logic r) = "logic" ++ (showRanges r)
|
||||
show (Alias t r) = t ++ (showRanges r)
|
||||
show (Implicit r) = (showRanges r)
|
||||
show (IntegerT ) = "integer"
|
||||
show (Enum mt vals r) = printf "enum %s{%s}%s" tStr (commas $ map showVal vals) (showRanges r)
|
||||
where
|
||||
tStr = case mt of
|
||||
Nothing -> ""
|
||||
Just t -> (show t) ++ " "
|
||||
tStr = maybe "" showPad mt
|
||||
showVal :: (Identifier, Maybe Expr) -> String
|
||||
showVal (x, e) = x ++ (showAssignment e)
|
||||
|
||||
data Decl
|
||||
= Parameter Type Identifier Expr
|
||||
| Localparam Type Identifier Expr
|
||||
| Variable Direction Type Identifier [Range] (Maybe Expr)
|
||||
deriving Eq
|
||||
|
||||
instance Show Decl where
|
||||
showList l _ = unlines' $ map show l
|
||||
show (Parameter t x e) = printf "parameter %s%s = %s;" (showPad t) x (show e)
|
||||
show (Localparam t x e) = printf "localparam %s%s = %s;" (showPad t) x (show e)
|
||||
show (Variable d t x a me) = printf "%s%s %s%s%s;" (showPad d) (show t) x (showRanges a) (showAssignment me)
|
||||
|
||||
data ModuleItem
|
||||
= Comment String
|
||||
| MIParameter Parameter
|
||||
| MILocalparam Localparam
|
||||
| MIIntegerV IntegerV
|
||||
| PortDecl Direction [Range] Identifier
|
||||
| LocalNet Type Identifier RangesOrAssignment
|
||||
| MIDecl Decl
|
||||
| AlwaysC AlwaysKW Stmt
|
||||
| Assign LHS Expr
|
||||
| Instance Identifier [PortBinding] Identifier (Maybe [PortBinding]) -- `Nothing` represents `.*`
|
||||
| Function (Maybe FuncRet) Identifier [(Bool, BlockItemDeclaration)] Stmt
|
||||
| Function Type Identifier [Decl] Stmt
|
||||
| Genvar Identifier
|
||||
| Generate [GenItem]
|
||||
deriving Eq
|
||||
|
||||
-- "function inputs and outputs are inferred to be of type reg if no internal
|
||||
-- data types for the ports are declared"
|
||||
|
||||
data AlwaysKW
|
||||
= Always
|
||||
| AlwaysComb
|
||||
|
|
@ -121,61 +132,29 @@ instance Show AlwaysKW where
|
|||
show AlwaysFF = "always_ff"
|
||||
show AlwaysLatch = "always_latch"
|
||||
|
||||
-- "function inputs and outputs are inferred to be of type reg if no internal
|
||||
-- data types for the ports are declared"
|
||||
|
||||
type PortBinding = (Identifier, Maybe Expr)
|
||||
|
||||
data Parameter = Parameter (Maybe Range) Identifier Expr deriving Eq
|
||||
instance Show Parameter where
|
||||
show (Parameter r n e) = printf "parameter %s%s = %s;" (showRange r) n (show e)
|
||||
|
||||
data Localparam = Localparam (Maybe Range) Identifier Expr deriving Eq
|
||||
instance Show Localparam where
|
||||
show (Localparam r n e) = printf "localparam %s%s = %s;" (showRange r) n (show e)
|
||||
|
||||
data IntegerV = IntegerV Identifier RangesOrAssignment deriving Eq
|
||||
instance Show IntegerV where
|
||||
show (IntegerV x v ) = printf "integer %s%s;" x (showRangesOrAssignment v)
|
||||
|
||||
instance Show ModuleItem where
|
||||
show thing = case thing of
|
||||
Comment c -> "// " ++ c
|
||||
MIParameter nest -> show nest
|
||||
MILocalparam nest -> show nest
|
||||
MIIntegerV nest -> show nest
|
||||
PortDecl d r x -> printf "%s%s %s;" (show d) (showRanges r) x
|
||||
LocalNet t x v -> printf "%s %s%s;" (show t) x (showRangesOrAssignment v)
|
||||
MIDecl nest -> show nest
|
||||
AlwaysC k b -> printf "%s %s" (show k) (show b)
|
||||
Assign a b -> printf "assign %s = %s;" (show a) (show b)
|
||||
Instance m params i ports
|
||||
| null params -> printf "%s %s%s;" m i (showMaybePorts ports)
|
||||
| otherwise -> printf "%s #%s %s%s;" m (showPorts params) i (showMaybePorts ports)
|
||||
Function t x i b -> printf "function %s%s;\n%s\n%s\nendfunction" (showFuncRet t) x (indent $ unlines' $ map showFunctionItem i) (indent $ show b)
|
||||
Function t x i b -> printf "function %s%s;\n%s\n%s\nendfunction" (showPad t) x (indent $ show i) (indent $ show b)
|
||||
Genvar x -> printf "genvar %s;" x
|
||||
Generate b -> printf "generate\n%s\nendgenerate" (indent $ unlines' $ map show b)
|
||||
where
|
||||
showMaybePorts :: Maybe [(Identifier, Maybe Expr)] -> String
|
||||
showMaybePorts Nothing = "(.*)"
|
||||
showMaybePorts (Just ports) = showPorts ports
|
||||
showPorts :: [(Identifier, Maybe Expr)] -> String
|
||||
showPorts ports = indentedParenList [ if i == "" then show (fromJust arg) else printf ".%s(%s)" i (if isJust arg then show $ fromJust arg else "") | (i, arg) <- ports ]
|
||||
showFunctionItem :: (Bool, BlockItemDeclaration) -> String
|
||||
showFunctionItem (b, item) = prefix ++ (show item)
|
||||
where prefix = if b then "input " else ""
|
||||
|
||||
type FuncRet = Either Range ()
|
||||
|
||||
showFuncRet :: Maybe FuncRet -> String
|
||||
showFuncRet Nothing = ""
|
||||
showFuncRet (Just (Left r)) = showRange $ Just r
|
||||
showFuncRet (Just (Right ())) = "integer "
|
||||
|
||||
type RangesOrAssignment = Either [Range] (Maybe Expr)
|
||||
|
||||
showRangesOrAssignment :: Either [Range] (Maybe Expr) -> String
|
||||
showRangesOrAssignment (Left ranges) = showRanges ranges
|
||||
showRangesOrAssignment (Right val) = showAssignment val
|
||||
showMaybePorts = maybe "(.*)" showPorts
|
||||
showPorts :: [PortBinding] -> String
|
||||
showPorts ports = indentedParenList $ map showPort ports
|
||||
showPort :: PortBinding -> String
|
||||
showPort (i, arg) =
|
||||
if i == ""
|
||||
then show (fromJust arg)
|
||||
else printf ".%s(%s)" i (if isJust arg then show $ fromJust arg else "")
|
||||
|
||||
showAssignment :: Maybe Expr -> String
|
||||
showAssignment Nothing = ""
|
||||
|
|
@ -190,6 +169,13 @@ showRange :: Maybe Range -> String
|
|||
showRange Nothing = ""
|
||||
showRange (Just (h, l)) = printf "[%s:%s] " (show h) (show l)
|
||||
|
||||
showPad :: Show t => t -> String
|
||||
showPad x =
|
||||
if str == ""
|
||||
then ""
|
||||
else str ++ " "
|
||||
where str = show x
|
||||
|
||||
indent :: String -> String
|
||||
indent a = '\t' : f a
|
||||
where
|
||||
|
|
@ -331,13 +317,13 @@ instance Show CaseKW where
|
|||
show CaseX = "casex"
|
||||
|
||||
data Stmt
|
||||
= Block (Maybe (Identifier, [BlockItemDeclaration])) [Stmt]
|
||||
| Case CaseKW Expr [Case] (Maybe Stmt)
|
||||
| BlockingAssignment LHS Expr
|
||||
| NonBlockingAssignment LHS Expr
|
||||
| For (Identifier, Expr) Expr (Identifier, Expr) Stmt
|
||||
| If Expr Stmt Stmt
|
||||
| Timing Sense Stmt
|
||||
= Block (Maybe (Identifier, [Decl])) [Stmt]
|
||||
| Case CaseKW Expr [Case] (Maybe Stmt)
|
||||
| For (Identifier, Expr) Expr (Identifier, Expr) Stmt
|
||||
| AsgnBlk LHS Expr
|
||||
| Asgn LHS Expr
|
||||
| If Expr Stmt Stmt
|
||||
| Timing Sense Stmt
|
||||
| Null
|
||||
deriving Eq
|
||||
|
||||
|
|
@ -345,32 +331,27 @@ commas :: [String] -> String
|
|||
commas = intercalate ", "
|
||||
|
||||
instance Show Stmt where
|
||||
show (Block Nothing b ) = printf "begin\n%s\nend" $ indent $ unlines' $ map show b
|
||||
show (Block (Just (a, i)) b ) = printf "begin : %s\n%s\nend" a $ indent $ unlines' $ (map show i ++ map show b)
|
||||
show (Case kw a b Nothing ) = printf "%s (%s)\n%s\nendcase" (show kw) (show a) (indent $ unlines' $ map showCase b)
|
||||
show (Case kw a b (Just c) ) = printf "%s (%s)\n%s\n\tdefault:\n%s\nendcase" (show kw) (show a) (indent $ unlines' $ map showCase b) (indent $ indent $ show c)
|
||||
show (BlockingAssignment a b ) = printf "%s = %s;" (show a) (show b)
|
||||
show (NonBlockingAssignment a b ) = printf "%s <= %s;" (show a) (show b)
|
||||
show (For (a, b) c (d, e) f) = printf "for (%s = %s; %s; %s = %s)\n%s" a (show b) (show c) d (show e) $ indent $ show f
|
||||
show (If a b Null ) = printf "if (%s)\n%s" (show a) (indent $ show b)
|
||||
show (If a b c ) = printf "if (%s)\n%s\nelse\n%s" (show a) (indent $ show b) (indent $ show c)
|
||||
show (Timing t s ) = printf "@(%s) %s" (show t) (show s)
|
||||
show (Null ) = ";"
|
||||
|
||||
data BlockItemDeclaration
|
||||
-- TODO: Maybe BIDReg should use [Range] for the first arg as well, but it's
|
||||
-- really not clear to me what *useful* purpose this would have.
|
||||
= BIDReg (Maybe Range) Identifier [Range]
|
||||
| BIDParameter Parameter
|
||||
| BIDLocalparam Localparam
|
||||
| BIDIntegerV IntegerV
|
||||
deriving Eq
|
||||
|
||||
instance Show BlockItemDeclaration where
|
||||
show (BIDReg mr x rs) = printf "reg %s%s%s;" (showRange mr) x (showRanges rs)
|
||||
show (BIDParameter nest) = show nest
|
||||
show (BIDLocalparam nest) = show nest
|
||||
show (BIDIntegerV nest) = show nest
|
||||
show (Block header stmts) =
|
||||
printf "begin%s\n%s\nend" extra (block stmts)
|
||||
where
|
||||
block :: Show t => [t] -> String
|
||||
block = indent . unlines' . map show
|
||||
extra = case header of
|
||||
Nothing -> ""
|
||||
Just (x, i) -> printf " : %s\n%s" x (block i)
|
||||
show (Case kw e cs def) =
|
||||
printf "%s (%s)\n%s%s\nendcase" (show kw) (show e) (indent $ unlines' $ map showCase cs) defStr
|
||||
where
|
||||
defStr = case def of
|
||||
Nothing -> ""
|
||||
Just c -> printf "\n\tdefault:\n%s" (indent $ indent $ show c)
|
||||
show (For (a,b) c (d,e) f) = printf "for (%s = %s; %s; %s = %s)\n%s" a (show b) (show c) d (show e) $ indent $ show f
|
||||
show (AsgnBlk v e) = printf "%s = %s;" (show v) (show e)
|
||||
show (Asgn v e) = printf "%s <= %s;" (show v) (show e)
|
||||
show (If a b Null) = printf "if (%s)\n%s" (show a) (indent $ show b)
|
||||
show (If a b c ) = printf "if (%s)\n%s\nelse\n%s" (show a) (indent $ show b) (indent $ show c)
|
||||
show (Timing t s ) = printf "@(%s) %s" (show t) (show s)
|
||||
show (Null ) = ";"
|
||||
|
||||
type Case = ([Expr], Stmt)
|
||||
|
||||
|
|
@ -413,7 +394,7 @@ data GenItem
|
|||
|
||||
instance Show GenItem where
|
||||
showList i _ = unlines' $ map show i
|
||||
show (GenBlock Nothing i) = printf "begin\n%s\nend" (indent $ unlines' $ map show i)
|
||||
show (GenBlock Nothing i) = printf "begin\n%s\nend" (indent $ unlines' $ map show i)
|
||||
show (GenBlock (Just x) i) = printf "begin : %s\n%s\nend" x (indent $ unlines' $ map show i)
|
||||
show (GenCase e c Nothing ) = printf "case (%s)\n%s\nendcase" (show e) (indent $ unlines' $ map showCase c)
|
||||
show (GenCase e c (Just d)) = printf "case (%s)\n%s\n\tdefault:\n%s\nendcase" (show e) (indent $ unlines' $ map showCase c) (indent $ indent $ show d)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
module Language.SystemVerilog.Parser.Parse (descriptions) where
|
||||
|
||||
import Data.Bits
|
||||
import Data.List
|
||||
|
||||
import Data.Maybe
|
||||
|
||||
import Language.SystemVerilog.AST
|
||||
import Language.SystemVerilog.Parser.Tokens
|
||||
}
|
||||
|
|
@ -200,7 +199,7 @@ ParamDecls :: { [ModuleItem] }
|
|||
: ParamDecl(")") { $1 }
|
||||
| ParamDecl(",") ParamDecls { $1 ++ $2 }
|
||||
ParamDecl(delim) :: { [ModuleItem] }
|
||||
: "parameter" opt(Range) DeclAsgns delim { map (MIParameter . (uncurry $ Parameter $2)) $3 }
|
||||
: "parameter" ParamType DeclAsgns delim { map (MIDecl . (uncurry $ Parameter $2)) $3 }
|
||||
|
||||
Identifier :: { Identifier }
|
||||
: simpleIdentifier { tokenString $1 }
|
||||
|
|
@ -223,15 +222,16 @@ PortDeclsFollow :: { [ModuleItem] }
|
|||
| PortDecl(",") PortDeclsFollow { $1 ++ $2 }
|
||||
|
||||
PortDecl(delim) :: { [ModuleItem] }
|
||||
: "inout" opt(NetType) Dimensions Identifiers delim { portDeclToModuleItems Inout $2 $3 (zip $4 (repeat Nothing)) }
|
||||
| "input" opt(NetType) Dimensions Identifiers delim { portDeclToModuleItems Input $2 $3 (zip $4 (repeat Nothing)) }
|
||||
| "output" Dimensions Identifiers delim { portDeclToModuleItems Output Nothing $2 (zip $3 (repeat Nothing)) }
|
||||
| "output" "wire" Dimensions Identifiers delim { portDeclToModuleItems Output (Just Wire ) $3 (zip $4 (repeat Nothing)) }
|
||||
| "output" "reg" Dimensions VariablePortIdentifiers delim { portDeclToModuleItems Output (Just Reg ) $3 $4 }
|
||||
| "output" "logic" Dimensions VariablePortIdentifiers delim { portDeclToModuleItems Output (Just Logic) $3 $4 }
|
||||
: "inout" NetType Dimensions Identifiers delim { portDeclToModuleItems Inout ($2 $3) (zip $4 (repeat Nothing)) }
|
||||
| "input" NetType Dimensions Identifiers delim { portDeclToModuleItems Input ($2 $3) (zip $4 (repeat Nothing)) }
|
||||
| "output" Dimensions Identifiers delim { portDeclToModuleItems Output (Implicit $2) (zip $3 (repeat Nothing)) }
|
||||
| "output" "wire" Dimensions Identifiers delim { portDeclToModuleItems Output (Wire $3) (zip $4 (repeat Nothing)) }
|
||||
| "output" "reg" Dimensions VariablePortIdentifiers delim { portDeclToModuleItems Output (Reg $3) $4 }
|
||||
| "output" "logic" Dimensions VariablePortIdentifiers delim { portDeclToModuleItems Output (Logic $3) $4 }
|
||||
NetType :: { [Range] -> Type }
|
||||
: "wire" { Wire }
|
||||
| "logic" { Logic }
|
||||
: "wire" { Wire }
|
||||
| "logic" { Logic }
|
||||
| {- empty -} { Implicit }
|
||||
VariablePortIdentifiers :: { [(Identifier, Maybe Expr)] }
|
||||
: VariablePortIdentifier { [$1] }
|
||||
| VariablePortIdentifiers "," VariablePortIdentifier { $1 ++ [$3] }
|
||||
|
|
@ -245,18 +245,15 @@ ModuleItems :: { [ModuleItem] }
|
|||
|
||||
ModuleItem :: { [ModuleItem] }
|
||||
: PortDecl(";") { $1 }
|
||||
-- TODO: Allowing Ranges on aliases creates conflicts
|
||||
| Identifier VariableIdentifiers ";" { map (uncurry $ LocalNet (Alias $1 [])) $2 }
|
||||
| Identifier DimensionsNonEmpty VariableIdentifiers ";" { map (uncurry $ LocalNet (Alias $1 $2)) $3 }
|
||||
| TypeNonAlias VariableIdentifiers ";" { map (uncurry $ LocalNet $1) $2 }
|
||||
| ParameterDeclaration { map MIParameter $1 }
|
||||
| LocalparamDeclaration { map MILocalparam $1 }
|
||||
| IntegerDeclaration { map MIIntegerV $1 }
|
||||
| Identifier VariableIdentifiers ";" { map (\(x, a, e) -> MIDecl $ Variable Local (Alias $1 []) x a e) $2 }
|
||||
| Identifier DimensionsNonEmpty VariableIdentifiers ";" { map (\(x, a, e) -> MIDecl $ Variable Local (Alias $1 $2) x a e) $3 }
|
||||
| TypeNonAlias VariableIdentifiers ";" { map (\(x, a, e) -> MIDecl $ Variable Local $1 x a e) $2 }
|
||||
| Declaration { map MIDecl $1 }
|
||||
| "assign" LHS "=" Expr ";" { [Assign $2 $4] }
|
||||
| AlwaysKW Stmt { [AlwaysC $1 $2] }
|
||||
| Identifier ModuleInstantiations ";" { map (uncurry $ Instance $1 []) $2 }
|
||||
| Identifier ParameterBindings ModuleInstantiations ";" { map (uncurry $ Instance $1 $2) $3 }
|
||||
| "function" opt(RangeOrType) Identifier FunctionItems Stmt "endfunction" { [Function $2 $3 $4 $5] }
|
||||
| "function" ParamType Identifier FunctionItems Stmt "endfunction" { [Function $2 $3 $4 $5] }
|
||||
| "genvar" Identifiers ";" { map Genvar $2 }
|
||||
| "generate" GenItems "endgenerate" { [Generate $2] }
|
||||
|
||||
|
|
@ -273,35 +270,32 @@ ModuleInstantiation :: { (Identifier, Maybe [PortBinding]) }
|
|||
: Identifier "(" Bindings ")" { ($1, Just $3) }
|
||||
| Identifier "(" ".*" ")" { ($1, Nothing) }
|
||||
|
||||
FunctionItems :: { [(Bool, BlockItemDeclaration)] }
|
||||
: "(" FunctionPortList ";" BlockItemDeclarations { (map ((,) True) $2) ++ (map ((,) False) $4) }
|
||||
| "(" FunctionPortList ";" { (map ((,) True) $2) }
|
||||
FunctionItems :: { [Decl] }
|
||||
: "(" FunctionPortList ";" BlockItemDeclarations { $2 ++ $4 }
|
||||
| "(" FunctionPortList ";" { $2 }
|
||||
| ";" FunctionItemDeclarations { $2 }
|
||||
FunctionPortList :: { [BlockItemDeclaration] }
|
||||
FunctionPortList :: { [Decl] }
|
||||
: FunctionInputDeclaration(")") { $1 }
|
||||
| FunctionInputDeclaration(",") FunctionPortList { $1 ++ $2 }
|
||||
FunctionItemDeclarations :: { [(Bool, BlockItemDeclaration)] }
|
||||
FunctionItemDeclarations :: { [Decl] }
|
||||
: FunctionItemDeclaration { $1 }
|
||||
| FunctionItemDeclarations FunctionItemDeclaration { $1 ++ $2 }
|
||||
FunctionItemDeclaration :: { [(Bool, BlockItemDeclaration)] }
|
||||
: BlockItemDeclaration { map ((,) False) $1 }
|
||||
| FunctionInputDeclaration(";") { map ((,) True ) $1 }
|
||||
FunctionInputDeclaration(delim) :: { [BlockItemDeclaration] }
|
||||
: "input" opt("reg") opt(Range) Identifiers delim { map (\x -> BIDReg $3 x []) $4 }
|
||||
| "input" "integer" Identifiers delim { map (\x -> BIDIntegerV $ IntegerV x $ Left []) $3 }
|
||||
FunctionItemDeclaration :: { [Decl] }
|
||||
: BlockItemDeclaration { $1 }
|
||||
| FunctionInputDeclaration(";") { $1 }
|
||||
FunctionInputDeclaration(delim) :: { [Decl] }
|
||||
: "input" Dimensions Identifiers delim { map (\x -> Variable Input (Implicit $2) x [] Nothing) $3 }
|
||||
| "input" "reg" Dimensions Identifiers delim { map (\x -> Variable Input (Reg $3) x [] Nothing) $4 }
|
||||
| "input" "integer" Identifiers delim { map (\x -> Variable Input IntegerT x [] Nothing) $3 }
|
||||
|
||||
ParameterDeclaration :: { [Parameter] }
|
||||
: "parameter" opt(Range) DeclAsgns ";" { map (uncurry $ Parameter $2) $3 }
|
||||
Declaration :: { [Decl] }
|
||||
: "parameter" ParamType DeclAsgns ";" { map (uncurry $ Parameter $2) $3 }
|
||||
| "localparam" ParamType DeclAsgns ";" { map (uncurry $ Localparam $2) $3 }
|
||||
| "integer" VariableIdentifiers ";" { map (\(x, a, e) -> Variable Local IntegerT x a e) $2 }
|
||||
|
||||
LocalparamDeclaration :: { [Localparam] }
|
||||
: "localparam" opt(Range) DeclAsgns ";" { map (uncurry $ Localparam $2) $3 }
|
||||
|
||||
IntegerDeclaration :: { [IntegerV] }
|
||||
: "integer" VariableIdentifiers ";" { map (uncurry IntegerV) $2 }
|
||||
|
||||
RangeOrType :: { Either Range () }
|
||||
: Range { Left $1 }
|
||||
| "integer" { Right () }
|
||||
ParamType :: { Type }
|
||||
: Dimensions { Implicit $1 }
|
||||
| "integer" { IntegerT }
|
||||
|
||||
EventControl :: { Sense }
|
||||
: "@" "(" Sense ")" { $3 }
|
||||
|
|
@ -310,13 +304,12 @@ EventControl :: { Sense }
|
|||
| "@" "*" { SenseStar }
|
||||
| "@*" { SenseStar }
|
||||
|
||||
VariableIdentifiers :: { [(Identifier, Either [Range] (Maybe Expr))] }
|
||||
VariableIdentifiers :: { [(Identifier, [Range], Maybe Expr)] }
|
||||
: VariableType { [$1] }
|
||||
| VariableIdentifiers "," VariableType { $1 ++ [$3] }
|
||||
VariableType :: { (Identifier, Either [Range] (Maybe Expr)) }
|
||||
: Identifier { ($1, Right $ Nothing) }
|
||||
| Identifier "=" Expr { ($1, Right $ Just $3) }
|
||||
| Identifier DimensionsNonEmpty { ($1, Left $2) }
|
||||
VariableType :: { (Identifier, [Range], Maybe Expr) }
|
||||
: Identifier Dimensions { ($1, $2, Nothing) }
|
||||
| Identifier Dimensions "=" Expr { ($1, $2, Just $4) }
|
||||
|
||||
Dimensions :: { [Range] }
|
||||
: {- empty -} { [] }
|
||||
|
|
@ -360,7 +353,7 @@ BindingsNonEmpty :: { [(Identifier, Maybe Expr)] }
|
|||
: Binding { [$1] }
|
||||
| Binding "," BindingsNonEmpty { $1 : $3}
|
||||
Binding :: { (Identifier, Maybe Expr) }
|
||||
: "." Identifier "(" MaybeExpr ")" { ($2, $4) }
|
||||
: "." Identifier "(" opt(Expr) ")" { ($2, $4) }
|
||||
| "." Identifier { ($2, Just $ Ident $2) }
|
||||
| Expr { ("", Just $1) }
|
||||
|
||||
|
|
@ -379,20 +372,18 @@ Stmt :: { Stmt }
|
|||
| "if" "(" Expr ")" Stmt "else" Stmt { If $3 $5 $7 }
|
||||
| "if" "(" Expr ")" Stmt %prec NoElse { If $3 $5 Null }
|
||||
| "for" "(" Identifier "=" Expr ";" Expr ";" Identifier "=" Expr ")" Stmt { For ($3, $5) $7 ($9, $11) $13 }
|
||||
| LHS "=" Expr ";" { BlockingAssignment $1 $3 }
|
||||
| LHS "<=" Expr ";" { NonBlockingAssignment $1 $3 }
|
||||
| LHS "=" Expr ";" { AsgnBlk $1 $3 }
|
||||
| LHS "<=" Expr ";" { Asgn $1 $3 }
|
||||
| CaseKW "(" Expr ")" Cases opt(CaseDefault) "endcase" { Case $1 $3 $5 $6 }
|
||||
| EventControl Stmt { Timing $1 $2 }
|
||||
|
||||
BlockItemDeclarations :: { [BlockItemDeclaration] }
|
||||
BlockItemDeclarations :: { [Decl] }
|
||||
: BlockItemDeclaration { $1 }
|
||||
| BlockItemDeclarations BlockItemDeclaration { $1 ++ $2 }
|
||||
|
||||
BlockItemDeclaration :: { [BlockItemDeclaration] }
|
||||
: "reg" opt(Range) BlockVariableIdentifiers ";" { map (uncurry $ BIDReg $2) $3 }
|
||||
| ParameterDeclaration { map BIDParameter $1 }
|
||||
| LocalparamDeclaration { map BIDLocalparam $1 }
|
||||
| IntegerDeclaration { map BIDIntegerV $1 }
|
||||
BlockItemDeclaration :: { [Decl] }
|
||||
: "reg" Dimensions BlockVariableIdentifiers ";" { map (\(x, rs) -> Variable Local (Reg $2) x rs Nothing) $3 }
|
||||
| Declaration { $1 }
|
||||
BlockVariableIdentifiers :: { [(Identifier, [Range])] }
|
||||
: BlockVariableType { [$1] }
|
||||
| BlockVariableIdentifiers "," BlockVariableType { $1 ++ [$3] }
|
||||
|
|
@ -420,16 +411,12 @@ Number :: { String }
|
|||
: number { tokenString $1 }
|
||||
|
||||
String :: { String }
|
||||
: string { toString $1 }
|
||||
: string { tail $ init $ tokenString $1 }
|
||||
|
||||
CallArgs :: { [Expr] }
|
||||
: Expr { [$1] }
|
||||
| CallArgs "," Expr { $1 ++ [$3] }
|
||||
|
||||
MaybeExpr :: { Maybe Expr }
|
||||
: { Nothing }
|
||||
| Expr { Just $1 }
|
||||
|
||||
Exprs :: { [Expr] }
|
||||
: Expr { [$1] }
|
||||
| Exprs "," Expr { $1 ++ [$3] }
|
||||
|
|
@ -513,44 +500,19 @@ parseError a = case a of
|
|||
[] -> error "Parse error: no tokens left to parse."
|
||||
Token t s p : _ -> error $ "Parse error: unexpected token '" ++ s ++ "' (" ++ show t ++ ") at " ++ show p ++ "."
|
||||
|
||||
toString :: Token -> String
|
||||
toString = tail . init . tokenString
|
||||
|
||||
portDeclToModuleItems
|
||||
:: Direction
|
||||
-> (Maybe ([Range] -> Type))
|
||||
-> [Range]
|
||||
-> [(Identifier, Maybe Expr)]
|
||||
-> [ModuleItem]
|
||||
portDeclToModuleItems dir Nothing rs l =
|
||||
map (PortDecl dir rs) $ map toIdentifier $ l
|
||||
where
|
||||
toIdentifier (x, Just _) = error "ParseError: Incomplete port decl cannot have initialization"
|
||||
toIdentifier (x, Nothing) = x
|
||||
portDeclToModuleItems dir (Just tf) rs l =
|
||||
concat $ map toItems l
|
||||
where
|
||||
toItems (x, e) =
|
||||
[ PortDecl dir rs x
|
||||
, LocalNet (tf rs) x (Right e) ]
|
||||
portDeclToModuleItems :: Direction -> Type -> [PortBinding] -> [ModuleItem]
|
||||
portDeclToModuleItems dir t l =
|
||||
map (\(x, me) -> MIDecl $ Variable dir t x [] me) l
|
||||
|
||||
getPortNames :: [ModuleItem] -> [Identifier]
|
||||
getPortNames items =
|
||||
mapMaybe getPortName items
|
||||
where
|
||||
getPortName :: ModuleItem -> Maybe Identifier
|
||||
getPortName (PortDecl _ _ ident) = Just ident
|
||||
getPortName (MIDecl (Variable Local _ _ _ _)) = Nothing
|
||||
getPortName (MIDecl (Variable _ _ ident _ _)) = Just ident
|
||||
getPortName _ = Nothing
|
||||
|
||||
stmtsToStmt :: [Stmt] -> Stmt
|
||||
stmtsToStmt [] = error "stmtsToStmt given empty list!"
|
||||
stmtsToStmt [s] = s
|
||||
stmtsToStmt ss = Block Nothing ss
|
||||
|
||||
moduleItemsToSingleGenItem :: [ModuleItem] -> GenItem
|
||||
moduleItemsToSingleGenItem [x] = GenModuleItem x
|
||||
moduleItemsToSingleGenItem other = error $ "multiple module items in a generate block where only one was allowed" ++ show other
|
||||
|
||||
genItemsToGenItem :: [GenItem] -> GenItem
|
||||
genItemsToGenItem [] = error "genItemsToGenItem given empty list!"
|
||||
genItemsToGenItem [x] = x
|
||||
|
|
|
|||
Loading…
Reference in New Issue