2019-03-06 06:51:09 +01:00
|
|
|
{- sv2v
|
|
|
|
|
- Author: Zachary Snow <zach@zachjs.com>
|
|
|
|
|
-
|
|
|
|
|
- Conversion for `packed struct`
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
module Convert.Struct (convert) where
|
|
|
|
|
|
2019-04-10 19:16:23 +02:00
|
|
|
import Data.Maybe (fromJust, isJust)
|
|
|
|
|
import Data.List (elemIndex, sortOn)
|
2019-03-06 06:51:09 +01:00
|
|
|
import Data.Tuple (swap)
|
|
|
|
|
import Control.Monad.Writer
|
2019-04-10 19:42:56 +02:00
|
|
|
import Text.Read (readMaybe)
|
2019-03-06 06:51:09 +01:00
|
|
|
import qualified Data.Map.Strict as Map
|
|
|
|
|
|
|
|
|
|
import Convert.Traverse
|
|
|
|
|
import Language.SystemVerilog.AST
|
|
|
|
|
|
|
|
|
|
type TypeFunc = [Range] -> Type
|
|
|
|
|
type StructInfo = (Type, Map.Map Identifier (Range, Expr))
|
|
|
|
|
type Structs = Map.Map TypeFunc StructInfo
|
|
|
|
|
type Types = Map.Map Identifier Type
|
|
|
|
|
|
|
|
|
|
convert :: AST -> AST
|
|
|
|
|
convert = traverseDescriptions convertDescription
|
|
|
|
|
|
|
|
|
|
convertDescription :: Description -> Description
|
|
|
|
|
convertDescription description =
|
2019-03-31 23:00:55 +02:00
|
|
|
traverseModuleItems (traverseExprs $ traverseNestedExprs $ convertOnlyExpr structs types) $
|
2019-03-06 06:51:09 +01:00
|
|
|
traverseModuleItems (traverseTypes $ convertType structs) $
|
|
|
|
|
traverseModuleItems (traverseAsgns $ convertAsgn structs types) $
|
|
|
|
|
description
|
|
|
|
|
where
|
|
|
|
|
structs = execWriter $ collectModuleItemsM
|
|
|
|
|
(collectTypesM collectType) description
|
|
|
|
|
typesA = execWriter $ collectModuleItemsM
|
|
|
|
|
(collectDeclsM collectDecl) description
|
|
|
|
|
typesB = execWriter $ collectModuleItemsM
|
|
|
|
|
collectFunction description
|
|
|
|
|
types = Map.union typesA typesB
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- write down unstructured versions of a packed struct type
|
|
|
|
|
collectType :: Type -> Writer Structs ()
|
2019-03-22 21:57:13 +01:00
|
|
|
collectType (Struct (Packed sg) fields _) = do
|
|
|
|
|
-- TODO: How should we combine the structs Signing with that of the types it
|
|
|
|
|
-- contains?
|
2019-03-06 06:51:09 +01:00
|
|
|
if canUnstructure
|
|
|
|
|
then tell $ Map.singleton
|
2019-03-22 21:57:13 +01:00
|
|
|
(Struct (Packed sg) fields)
|
2019-03-06 06:51:09 +01:00
|
|
|
(unstructType, unstructFields)
|
|
|
|
|
else return ()
|
|
|
|
|
where
|
|
|
|
|
zero = Number "0"
|
|
|
|
|
typeRange :: Type -> Range
|
|
|
|
|
typeRange t =
|
|
|
|
|
if null ranges then (zero, zero) else head ranges
|
|
|
|
|
where ranges = snd $ typeRanges t
|
|
|
|
|
|
|
|
|
|
-- extract info about the fields
|
|
|
|
|
fieldTypes = map fst fields
|
|
|
|
|
fieldRanges = map typeRange fieldTypes
|
|
|
|
|
fieldSizes = map rangeSize fieldRanges
|
|
|
|
|
|
|
|
|
|
-- layout the fields into the unstructured type; note that `scanr` is
|
|
|
|
|
-- used here because SystemVerilog structs are laid out backwards
|
|
|
|
|
fieldLos = map simplify $ tail $ scanr (BinOp Add) (Number "0") fieldSizes
|
|
|
|
|
fieldHis = map simplify $ init $ scanr (BinOp Add) (Number "-1") fieldSizes
|
|
|
|
|
|
|
|
|
|
-- create the mapping structure for the unstructured fields
|
|
|
|
|
unstructOffsets = map simplify $ map snd fieldRanges
|
|
|
|
|
unstructRanges = zip fieldHis fieldLos
|
|
|
|
|
keys = map snd fields
|
|
|
|
|
vals = zip unstructRanges unstructOffsets
|
|
|
|
|
unstructFields = Map.fromList $ zip keys vals
|
|
|
|
|
|
|
|
|
|
-- create the unstructured type
|
|
|
|
|
tf = fst $ typeRanges $ head fieldTypes
|
|
|
|
|
structSize = foldl1 (BinOp Add) fieldSizes
|
|
|
|
|
packedRange = (simplify $ BinOp Sub structSize (Number "1"), zero)
|
|
|
|
|
unstructType = tf [packedRange]
|
|
|
|
|
|
|
|
|
|
-- TODO: For now, we only convert packed structs which contain fields
|
|
|
|
|
-- with all the same base type. We might be able to get away with
|
|
|
|
|
-- converting everything to a Logic type. This should work in cases of
|
|
|
|
|
-- mixed `wire`/`logic` or `reg`/`logic`.
|
|
|
|
|
fieldClasses = map (show . fst . typeRanges) fieldTypes
|
2019-03-30 07:33:31 +01:00
|
|
|
isComplex :: Type -> Bool
|
|
|
|
|
isComplex (Struct _ _ _ ) = True
|
|
|
|
|
isComplex (Enum _ _ _ ) = True
|
|
|
|
|
isComplex (Alias _ _) = True
|
|
|
|
|
isComplex _ = False
|
|
|
|
|
canUnstructure =
|
2019-03-30 08:32:08 +01:00
|
|
|
all (head fieldClasses ==) fieldClasses &&
|
2019-03-30 07:33:31 +01:00
|
|
|
not (any isComplex fieldTypes)
|
2019-03-06 06:51:09 +01:00
|
|
|
|
|
|
|
|
collectType _ = return ()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- convert a struct type to its unstructured equivalent
|
|
|
|
|
convertType :: Structs -> Type -> Type
|
|
|
|
|
convertType structs t1 =
|
|
|
|
|
case Map.lookup tf1 structs of
|
|
|
|
|
Nothing -> t1
|
2019-04-09 03:28:33 +02:00
|
|
|
Just (t2, _) -> tf2 (rs1 ++ rs2)
|
2019-03-06 06:51:09 +01:00
|
|
|
where (tf2, rs2) = typeRanges t2
|
|
|
|
|
where (tf1, rs1) = typeRanges t1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- write down the type a declarations
|
|
|
|
|
collectDecl :: Decl -> Writer Types ()
|
2019-03-22 21:57:13 +01:00
|
|
|
collectDecl (Variable _ (Implicit _ []) _ _ _) = return ()
|
2019-03-06 06:51:09 +01:00
|
|
|
collectDecl (Variable _ t x a _) =
|
|
|
|
|
-- We add the unpacked dimensions to the type so that our type traversal can
|
|
|
|
|
-- correctly match-off the dimensions whenever we see a `Bit` or `Range`
|
|
|
|
|
-- expression.
|
|
|
|
|
tell $ Map.singleton x (tf $ rs ++ a)
|
|
|
|
|
where (tf, rs) = typeRanges t
|
|
|
|
|
collectDecl (Parameter t x _) = tell $ Map.singleton x t
|
|
|
|
|
collectDecl (Localparam t x _) = tell $ Map.singleton x t
|
|
|
|
|
|
|
|
|
|
-- write down the return type of a function
|
|
|
|
|
collectFunction :: ModuleItem -> Writer Types ()
|
2019-03-07 19:19:31 +01:00
|
|
|
collectFunction (MIPackageItem (Function _ t f _ _)) = tell $ Map.singleton f t
|
2019-03-06 06:51:09 +01:00
|
|
|
collectFunction _ = return ()
|
|
|
|
|
|
2019-03-31 23:00:55 +02:00
|
|
|
convertOnlyExpr :: Structs -> Types -> Expr -> Expr
|
|
|
|
|
convertOnlyExpr structs types expr =
|
|
|
|
|
snd $ convertAsgn structs types (LHSIdent "", expr)
|
2019-03-06 06:51:09 +01:00
|
|
|
|
|
|
|
|
convertAsgn :: Structs -> Types -> (LHS, Expr) -> (LHS, Expr)
|
|
|
|
|
convertAsgn structs types (lhs, expr) =
|
|
|
|
|
(lhs', expr')
|
|
|
|
|
where
|
|
|
|
|
(typ, lhs') = convertLHS lhs
|
|
|
|
|
expr' = snd $ convertSubExpr $ convertExpr typ expr
|
|
|
|
|
|
|
|
|
|
-- converting LHSs by looking at the innermost types first
|
|
|
|
|
convertLHS :: LHS -> (Type, LHS)
|
|
|
|
|
convertLHS (LHSIdent x) =
|
|
|
|
|
case Map.lookup x types of
|
2019-03-22 21:57:13 +01:00
|
|
|
Nothing -> (Implicit Unspecified [], LHSIdent x)
|
2019-03-06 06:51:09 +01:00
|
|
|
Just t -> (t, LHSIdent x)
|
|
|
|
|
convertLHS (LHSBit l e) =
|
2019-04-09 03:28:33 +02:00
|
|
|
case l' of
|
|
|
|
|
LHSRange lInner NonIndexed (_, loI) ->
|
|
|
|
|
(t', LHSBit lInner (simplify $ BinOp Add loI e'))
|
|
|
|
|
LHSRange lInner IndexedPlus (baseI, _) ->
|
|
|
|
|
(t', LHSBit lInner (simplify $ BinOp Add baseI e'))
|
|
|
|
|
_ -> (t', LHSBit l' e')
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(t, l') = convertLHS l
|
2019-04-09 03:28:33 +02:00
|
|
|
t' = case typeRanges t of
|
|
|
|
|
(_, []) -> Implicit Unspecified []
|
|
|
|
|
(tf, rs) -> tf $ tail rs
|
2019-04-02 07:00:02 +02:00
|
|
|
e' = snd $ convertSubExpr e
|
2019-04-09 03:28:33 +02:00
|
|
|
convertLHS (LHSRange lOuter NonIndexed rOuterOrig) =
|
|
|
|
|
case lOuter' of
|
2019-04-05 19:53:52 +02:00
|
|
|
LHSRange lInner NonIndexed (_, loI) ->
|
2019-04-09 03:28:33 +02:00
|
|
|
(t, LHSRange lInner NonIndexed (simplify hi, simplify lo))
|
2019-03-31 23:35:00 +02:00
|
|
|
where
|
|
|
|
|
lo = BinOp Add loI loO
|
2019-04-01 08:26:40 +02:00
|
|
|
hi = BinOp Add loI hiO
|
2019-04-09 03:28:33 +02:00
|
|
|
LHSRange lInner IndexedPlus (baseI, _) ->
|
|
|
|
|
(t, LHSRange lInner IndexedPlus (simplify base, simplify len))
|
|
|
|
|
where
|
|
|
|
|
base = BinOp Add baseI loO
|
|
|
|
|
len = rangeSize rOuter
|
|
|
|
|
_ -> (t, LHSRange lOuter' NonIndexed rOuter)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
2019-04-02 07:00:02 +02:00
|
|
|
hiO = snd $ convertSubExpr $ fst rOuterOrig
|
|
|
|
|
loO = snd $ convertSubExpr $ snd rOuterOrig
|
|
|
|
|
rOuter = (hiO, loO)
|
2019-04-09 03:28:33 +02:00
|
|
|
(t, lOuter') = convertLHS lOuter
|
|
|
|
|
convertLHS (LHSRange l m r) =
|
|
|
|
|
(t', LHSRange l' m r')
|
|
|
|
|
where
|
|
|
|
|
hi = snd $ convertSubExpr $ fst r
|
|
|
|
|
lo = snd $ convertSubExpr $ snd r
|
|
|
|
|
r' = (hi, lo)
|
|
|
|
|
(t, l') = convertLHS l
|
|
|
|
|
t' = case typeRanges t of
|
|
|
|
|
(_, []) -> Implicit Unspecified []
|
|
|
|
|
(tf, rs) -> tf $ tail rs
|
2019-03-06 06:51:09 +01:00
|
|
|
convertLHS (LHSDot l x ) =
|
|
|
|
|
case t of
|
2019-03-22 21:57:13 +01:00
|
|
|
InterfaceT _ _ _ -> (Implicit Unspecified [], LHSDot l' x)
|
2019-03-06 06:51:09 +01:00
|
|
|
Struct _ _ _ -> case Map.lookup structTf structs of
|
|
|
|
|
Nothing -> (fieldType, LHSDot l' x)
|
2019-04-05 19:53:52 +02:00
|
|
|
Just (structT, m) -> (tf [tr], LHSRange l' NonIndexed r)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(tf, _) = typeRanges structT
|
|
|
|
|
(r @ (hi, lo), base) = m Map.! x
|
|
|
|
|
hi' = BinOp Add base $ BinOp Sub hi lo
|
|
|
|
|
lo' = base
|
|
|
|
|
tr = (simplify hi', simplify lo')
|
2019-03-22 21:57:13 +01:00
|
|
|
Implicit sg _ -> (Implicit sg [], LHSDot l' x)
|
2019-03-07 03:55:27 +01:00
|
|
|
_ -> error $ "convertLHS encountered dot for bad type: " ++ show (t, l, x)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(t, l') = convertLHS l
|
|
|
|
|
Struct p fields [] = t
|
|
|
|
|
structTf = Struct p fields
|
|
|
|
|
fieldType = lookupFieldType fields x
|
|
|
|
|
convertLHS (LHSConcat lhss) =
|
2019-03-22 21:57:13 +01:00
|
|
|
(Implicit Unspecified [], LHSConcat $ map (snd . convertLHS) lhss)
|
2019-03-06 06:51:09 +01:00
|
|
|
|
|
|
|
|
-- try expression conversion by looking at the *outermost* type first
|
|
|
|
|
convertExpr :: Type -> Expr -> Expr
|
2019-03-31 20:58:47 +02:00
|
|
|
-- TODO: This is really a conversion for using default patterns to
|
|
|
|
|
-- populate arrays. Maybe this should be somewhere else?
|
|
|
|
|
convertExpr (IntegerVector t sg (r:rs)) (Pattern [(Just "default", e)]) =
|
|
|
|
|
Repeat (rangeSize r) [e']
|
|
|
|
|
where e' = convertExpr (IntegerVector t sg rs) e
|
|
|
|
|
convertExpr (Struct (Packed sg) fields (_:rs)) (Bit e _) =
|
|
|
|
|
convertExpr (Struct (Packed sg) fields rs) e
|
|
|
|
|
convertExpr (Struct (Packed _) fields _) (Pattern [(Just "default", e)]) =
|
2019-03-30 07:33:31 +01:00
|
|
|
Concat $ take (length fields) (repeat e)
|
2019-04-11 00:33:33 +02:00
|
|
|
convertExpr (Struct (Packed sg) fields []) (Pattern itemsOrig) =
|
|
|
|
|
if length items /= length fields then
|
|
|
|
|
error $ "struct pattern " ++ show items ++
|
|
|
|
|
" doesn't have the same # of items as " ++ show structTf
|
|
|
|
|
else if itemsFieldNames /= fieldNames then
|
|
|
|
|
error $ "struct pattern " ++ show items ++ " has fields " ++
|
|
|
|
|
show itemsFieldNames ++ ", but struct type has fields " ++
|
|
|
|
|
show fieldNames
|
|
|
|
|
else if Map.notMember structTf structs then
|
|
|
|
|
Pattern items
|
|
|
|
|
else
|
|
|
|
|
Concat $ map packItem items
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
subMap = \(Just ident, subExpr) ->
|
|
|
|
|
(Just ident, convertExpr (lookupFieldType fields ident) subExpr)
|
2019-03-22 21:57:13 +01:00
|
|
|
structTf = Struct (Packed sg) fields
|
2019-04-11 00:33:33 +02:00
|
|
|
itemsNamed =
|
2019-03-06 06:51:09 +01:00
|
|
|
-- if the pattern does not use identifiers, use the
|
|
|
|
|
-- identifiers from the struct type definition in order
|
2019-04-11 00:33:33 +02:00
|
|
|
if not (all (isJust . fst) itemsOrig)
|
|
|
|
|
then zip (map (Just. snd) fields) (map snd itemsOrig)
|
|
|
|
|
else itemsOrig
|
|
|
|
|
items = sortOn itemPosition $ map subMap itemsNamed
|
2019-04-10 19:16:23 +02:00
|
|
|
fieldNames = map snd fields
|
2019-04-11 00:33:33 +02:00
|
|
|
itemsFieldNames = map (fromJust . fst) items
|
2019-04-10 19:16:23 +02:00
|
|
|
itemPosition = \(Just x, _) -> fromJust $ elemIndex x fieldNames
|
2019-04-10 19:42:56 +02:00
|
|
|
packItem (Just x, Number n) =
|
2019-04-11 00:33:33 +02:00
|
|
|
if size /= show resSize
|
|
|
|
|
then error $ "literal " ++ show n ++ " for " ++ show x
|
|
|
|
|
++ " doesn't have struct field size " ++ show size
|
|
|
|
|
else Number res
|
2019-04-10 19:42:56 +02:00
|
|
|
where
|
|
|
|
|
Number size = rangeSize $ lookupUnstructRange structTf x
|
|
|
|
|
unticked = case n of
|
|
|
|
|
'\'' : rest -> rest
|
|
|
|
|
rest -> rest
|
2019-04-11 00:33:33 +02:00
|
|
|
resSize = (read $ takeWhile (/= '\'') res) :: Int
|
|
|
|
|
res = case readMaybe unticked :: Maybe Int of
|
|
|
|
|
Nothing ->
|
|
|
|
|
if unticked == n
|
|
|
|
|
then n
|
|
|
|
|
else size ++ n
|
|
|
|
|
Just num -> size ++ "'d" ++ show num
|
2019-04-10 19:42:56 +02:00
|
|
|
packItem (_, itemExpr) = itemExpr
|
2019-03-06 06:51:09 +01:00
|
|
|
convertExpr _ other = other
|
|
|
|
|
|
|
|
|
|
-- try expression conversion by looking at the *innermost* type first
|
|
|
|
|
convertSubExpr :: Expr -> (Type, Expr)
|
|
|
|
|
convertSubExpr (Ident x) =
|
|
|
|
|
case Map.lookup x types of
|
2019-03-22 21:57:13 +01:00
|
|
|
Nothing -> (Implicit Unspecified [], Ident x)
|
2019-03-06 06:51:09 +01:00
|
|
|
Just t -> (t, Ident x)
|
2019-03-23 00:24:45 +01:00
|
|
|
convertSubExpr (Dot e x) =
|
2019-03-08 02:03:35 +01:00
|
|
|
case subExprType of
|
|
|
|
|
Struct _ _ _ ->
|
|
|
|
|
if Map.notMember structTf structs
|
2019-03-23 00:24:45 +01:00
|
|
|
then (fieldType, Dot e' x)
|
2019-04-05 19:53:52 +02:00
|
|
|
else (fieldType, Range e' NonIndexed r)
|
2019-03-23 00:24:45 +01:00
|
|
|
_ -> (Implicit Unspecified [], Dot e' x)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(subExprType, e') = convertSubExpr e
|
|
|
|
|
Struct p fields [] = subExprType
|
|
|
|
|
structTf = Struct p fields
|
|
|
|
|
fieldType = lookupFieldType fields x
|
|
|
|
|
r = lookupUnstructRange structTf x
|
2019-04-09 03:28:33 +02:00
|
|
|
convertSubExpr (Range eOuter NonIndexed (rOuter @ (hiO, loO))) =
|
2019-03-06 06:51:09 +01:00
|
|
|
-- VCS doesn't allow ranges to be cascaded, so we need to combine
|
|
|
|
|
-- nested Ranges into a single range. My understanding of the
|
2019-03-31 23:35:00 +02:00
|
|
|
-- semantics are that a range returns a new, zero-indexed sub-range.
|
2019-03-06 06:51:09 +01:00
|
|
|
case eOuter' of
|
2019-04-05 19:53:52 +02:00
|
|
|
Range eInner NonIndexed (_, loI) ->
|
2019-04-09 03:28:33 +02:00
|
|
|
(t, Range eInner NonIndexed (simplify hi, simplify lo))
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
lo = BinOp Add loI loO
|
2019-04-01 08:26:40 +02:00
|
|
|
hi = BinOp Add loI hiO
|
2019-04-09 03:28:33 +02:00
|
|
|
Range eInner IndexedPlus (baseI, _) ->
|
|
|
|
|
(t, Range eInner IndexedPlus (simplify base, simplify len))
|
|
|
|
|
where
|
|
|
|
|
base = BinOp Add baseI loO
|
|
|
|
|
len = rangeSize rOuter
|
|
|
|
|
_ -> (t, Range eOuter' NonIndexed rOuter)
|
2019-03-06 06:51:09 +01:00
|
|
|
where (t, eOuter') = convertSubExpr eOuter
|
2019-04-09 03:28:33 +02:00
|
|
|
convertSubExpr (Range e m r) =
|
|
|
|
|
(t', Range e' m r)
|
|
|
|
|
where
|
|
|
|
|
(t, e') = convertSubExpr e
|
|
|
|
|
t' = case typeRanges t of
|
|
|
|
|
(_, []) -> Implicit Unspecified []
|
|
|
|
|
(tf, rs) -> tf $ tail rs
|
2019-03-06 06:51:09 +01:00
|
|
|
convertSubExpr (Concat exprs) =
|
2019-03-22 21:57:13 +01:00
|
|
|
(Implicit Unspecified [], Concat $ map (snd . convertSubExpr) exprs)
|
2019-03-06 06:51:09 +01:00
|
|
|
convertSubExpr (BinOp op e1 e2) =
|
2019-03-22 21:57:13 +01:00
|
|
|
(Implicit Unspecified [], BinOp op e1' e2')
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(_, e1') = convertSubExpr e1
|
|
|
|
|
(_, e2') = convertSubExpr e2
|
2019-03-31 23:00:55 +02:00
|
|
|
convertSubExpr (Bit e i) =
|
2019-03-31 23:35:00 +02:00
|
|
|
case e' of
|
2019-04-05 19:53:52 +02:00
|
|
|
Range eInner NonIndexed (_, loI) ->
|
2019-03-31 23:35:00 +02:00
|
|
|
(t', Bit eInner (simplify $ BinOp Add loI i'))
|
2019-04-09 03:28:33 +02:00
|
|
|
Range eInner IndexedPlus (baseI, _) ->
|
|
|
|
|
(t', Bit eInner (simplify $ BinOp Add baseI i'))
|
2019-03-31 23:35:00 +02:00
|
|
|
_ -> (t', Bit e' i')
|
2019-03-31 23:00:55 +02:00
|
|
|
where
|
|
|
|
|
(t, e') = convertSubExpr e
|
|
|
|
|
t' = case typeRanges t of
|
|
|
|
|
(_, []) -> Implicit Unspecified []
|
|
|
|
|
(tf, rs) -> tf $ tail rs
|
|
|
|
|
(_, i') = convertSubExpr i
|
2019-03-06 06:51:09 +01:00
|
|
|
-- TODO: There are other expression cases that we probably need to
|
|
|
|
|
-- recurse into. That said, it's not clear to me how much we really
|
|
|
|
|
-- expect to see things like concatenated packed structs, for example.
|
2019-03-22 21:57:13 +01:00
|
|
|
convertSubExpr other = (Implicit Unspecified [], other)
|
2019-03-06 06:51:09 +01:00
|
|
|
|
|
|
|
|
-- lookup the range of a field in its unstructured type
|
|
|
|
|
lookupUnstructRange :: TypeFunc -> Identifier -> Range
|
|
|
|
|
lookupUnstructRange structTf fieldName =
|
|
|
|
|
fieldRangeMap Map.! fieldName
|
|
|
|
|
where fieldRangeMap = Map.map fst $ snd $ structs Map.! structTf
|
|
|
|
|
|
|
|
|
|
-- lookup the type of a field in the given field list
|
|
|
|
|
lookupFieldType :: [(Type, Identifier)] -> Identifier -> Type
|
|
|
|
|
lookupFieldType fields fieldName = fieldMap Map.! fieldName
|
|
|
|
|
where fieldMap = Map.fromList $ map swap fields
|