{- sv2v - Author: Zachary Snow - - Advanced parser for declarations, module instantiations, and some statements. - - This module exists because the SystemVerilog grammar is not LALR(1), and - Happy can only produce LALR(1) parsers. This module provides an interface for - parsing a list of "DeclTokens" into `Decl`s, `ModuleItem`s, or `Stmt`s. This - works through a series of functions which have use a greater lookahead for - resolving the conflicts. - - Consider the following two module declarations: - module Test(one two, three [1:0], four); - module Test(one two, three [1:0] four); - - When `{one} two ,` is on the stack, it is impossible to know whether to A) - shift `three` to add to the current declaration list; or B) to reduce the - stack and begin a new port declaration; without looking ahead more than 1 - token. - - While I previously had some success dealing with these conflicts with - increasingly convoluted grammars, this became more and more untenable as I - added support for more SystemVerilog constructs. - - Because declarations and statements are subject to the same kind of - conflicts, this module additionally provides an interface for parsing - DeclTokens as either declarations or the basic statements (either assignments - or task/function calls) with which they can conflict. The initialization - portion of a for loop also allows for declarations and assignments, and so a - similar interface is provided for this case. - - Parameter port lists allow the omission of the `parameter` and `localparam` - keywords, and so require special handling in the same vein as other - declarations. However, this custom parsing is not necessary for parameter - declarations outside of parameter port lists because the leading keyword is - otherwise required. - - This parser is very liberal, and so accepts some syntactically invalid files. - In the future, we may add some basic type-checking to complain about - malformed input files. However, we generally assume that users have tested - their code with a commercial simulator before running it through our tool. -} module Language.SystemVerilog.Parser.ParseDecl ( DeclToken (..) , parseDTsAsPortDecls , parseDTsAsModuleItems , parseDTsAsTFDecls , parseDTsAsDecl , parseDTsAsDeclOrStmt , parseDTsAsDeclsOrAsgns , parseDTsAsParams ) where import Data.List (findIndex, partition, uncons) import Language.SystemVerilog.AST import Language.SystemVerilog.Parser.Tokens (Position(..)) -- [PUBLIC]: combined (irregular) tokens for declarations data DeclToken = DTComma Position | DTAutoDim Position | DTConst Position | DTVar Position | DTAsgn Position AsgnOp (Maybe Timing) Expr | DTRange Position PartSelectMode Range | DTIdent Position Identifier | DTPSIdent Position Identifier Identifier | DTCSIdent Position Identifier [ParamBinding] Identifier | DTDir Position Direction | DTType Position (Signing -> [Range] -> Type) | DTNet Position NetType Strength | DTParams Position [ParamBinding] | DTPorts Position [PortBinding] | DTBit Position Expr | DTLHSBase Position LHS | DTDot Position Identifier | DTSigning Position Signing | DTLifetime Position Lifetime | DTAttr Position Attr | DTEnd Position Char | DTParamKW Position ParamScope | DTTypeDecl Position | DTTypeAsgn Position -- [PUBLIC]: parser for module port declarations, including interface ports -- Example: `input foo, bar, One inst` parseDTsAsPortDecls :: [DeclToken] -> ([Identifier], [ModuleItem]) parseDTsAsPortDecls = parseDTsAsPortDecls' . dropTrailingComma -- internal utility to drop a single trailing comma in port or parameter lists, -- which we allow for compatibility with Yosys dropTrailingComma :: [DeclToken] -> [DeclToken] dropTrailingComma [] = [] dropTrailingComma [DTComma{}, end@DTEnd{}] = [end] dropTrailingComma (tok : toks) = tok : dropTrailingComma toks -- internal parseDTsAsPortDecls after the removal of an optional trailing comma parseDTsAsPortDecls' :: [DeclToken] -> ([Identifier], [ModuleItem]) parseDTsAsPortDecls' pieces = if isSimpleList then (simpleIdents, []) else (portNames declarations, applyAttrs [] pieces declarations) where maybeSimpleIdents = parseDTsAsIdents pieces Just simpleIdents = maybeSimpleIdents isSimpleList = maybeSimpleIdents /= Nothing declarations = parseDTsAsDecls Input ModeDefault pieces' pieces' = filter (not . isAttr) pieces portNames :: [Decl] -> [Identifier] portNames = filter (not . null) . map portName portName :: Decl -> Identifier portName (Variable _ _ ident _ _) = ident portName (Net _ _ _ _ ident _ _) = ident portName _ = "" applyAttrs :: [Attr] -> [DeclToken] -> [Decl] -> [ModuleItem] applyAttrs _ tokens (CommentDecl c : decls) = MIPackageItem (Decl $ CommentDecl c) : applyAttrs [] tokens decls applyAttrs attrs (DTAttr _ attr : tokens) decls = applyAttrs (attr : attrs) tokens decls applyAttrs attrs [] [decl] = [wrapDecl attrs decl] applyAttrs attrs (DTComma{} : tokens) (decl : decls) = wrapDecl attrs decl : applyAttrs attrs tokens decls applyAttrs attrs (_ : tokens) decls = applyAttrs attrs tokens decls applyAttrs _ [] _ = undefined wrapDecl :: [Attr] -> Decl -> ModuleItem wrapDecl attrs decl = foldr MIAttr (MIPackageItem $ Decl decl) attrs -- internal utility for a simple list of port identifiers parseDTsAsIdents :: [DeclToken] -> Maybe [Identifier] parseDTsAsIdents [DTIdent _ x, DTEnd _ _] = Just [x] parseDTsAsIdents [_, _] = Nothing parseDTsAsIdents (DTIdent _ x : DTComma _ : rest) = fmap (x :) (parseDTsAsIdents rest) parseDTsAsIdents _ = Nothing -- [PUBLIC]: parser for single (semicolon-terminated) declarations (including -- parameters) and module instantiations parseDTsAsModuleItems :: [DeclToken] -> [ModuleItem] parseDTsAsModuleItems tokens = if maybeElabTask /= Nothing then [elabTask] else if any isPorts tokens then parseDTsAsIntantiations tokens else map (MIPackageItem . Decl) $ parseDTsAsDecl tokens where Just elabTask = maybeElabTask maybeElabTask = asElabTask tokens -- internal; attempt to parse an elaboration system task asElabTask :: [DeclToken] -> Maybe ModuleItem asElabTask tokens = do DTIdent _ x@('$' : _) <- return $ head tokens severity <- lookup x elabTasks Just $ ElabTask severity args where args = case tail tokens of [DTEnd{}] -> Args [] [] [DTPorts _ ports, DTEnd{}] -> portsToArgs ports DTPorts{} : tok : _ -> parseError tok msg toks -> parseError (head toks) msg msg = "unexpected token after elaboration system task" -- lookup table for elaboration system task severities elabTasks :: [(String, Severity)] elabTasks = map (\x -> (show x, x)) [SeverityInfo, SeverityWarning, SeverityError, SeverityFatal] -- internal; parser for module instantiations parseDTsAsIntantiations :: [DeclToken] -> [ModuleItem] parseDTsAsIntantiations (DTIdent _ name : DTParams _ params : tokens) = step tokens where step :: [DeclToken] -> [ModuleItem] step [] = [] step toks = inst : step restToks where inst = Instance name params x rs p (x, rs, p) = parseDTsAsIntantiation instToks delimTok (instToks, delimTok : restToks) = break isCommaOrEnd toks parseDTsAsIntantiations (DTIdent pos name : tokens) = parseDTsAsIntantiations $ DTIdent pos name : DTParams pos [] : tokens parseDTsAsIntantiations tokens = parseError (head tokens) "expected module or interface name at beginning of instantiation list" -- internal; parser for an individual instantiations parseDTsAsIntantiation :: [DeclToken] -> DeclToken -> (Identifier, [Range], [PortBinding]) parseDTsAsIntantiation l0 delimTok = if null l0 then parseError delimTok $ "expected instantiation before " ++ delimStr else if not (isIdent nameTok) then parseError nameTok "expected instantiation name" else if null l1 then parseError delimTok $ "expected port connections before " ++ delimStr else if seq ranges not (isPorts portsTok) then parseError portsTok "expected port connections" else (name, ranges, ports) where delimChar = case delimTok of DTEnd _ char -> char _ -> ',' delimStr = ['\'', delimChar, '\''] Just (nameTok, l1) = uncons l0 rangeToks = init l1 portsTok = last l1 DTIdent _ name = nameTok DTPorts _ ports = portsTok ranges = map asRange rangeToks asRange :: DeclToken -> Range asRange (DTRange _ NonIndexed s) = s asRange (DTBit _ s) = (RawNum 0, BinOp Sub s (RawNum 1)) asRange tok = parseError tok "expected instantiation dimensions" -- [PUBLIC]: parser for comma-separated task/function port declarations parseDTsAsTFDecls :: [DeclToken] -> [Decl] parseDTsAsTFDecls = parseDTsAsDecls Input ModeDefault -- [PUBLIC]; used for "single" declarations, i.e., declarations appearing -- outside of a port list parseDTsAsDecl :: [DeclToken] -> [Decl] parseDTsAsDecl = parseDTsAsDecls Local ModeSingle -- [PUBLIC]: parser for single block item declarations or assign or arg-less -- subroutine call statements parseDTsAsDeclOrStmt :: [DeclToken] -> ([Decl], [Stmt]) parseDTsAsDeclOrStmt tokens = if declLookahead tokens then (parseDTsAsDecl tokens, []) else ([], parseDTsAsStmt $ shiftIncOrDec tokens) -- check if the necessary tokens for a complete declaration exist at the -- beginning of the given token list declLookahead :: [DeclToken] -> Bool declLookahead l0 = length l0 /= length l6 && tripLookahead l6 where (_, l1) = takeDir l0 Local (_, l2) = takeLifetime l1 (_, l3) = takeConst l2 (_, l4) = takeVarOrNet l3 (_, l5) = takeType l4 (_, l6) = takeRanges l5 -- internal; parser for leading statements in a procedural block parseDTsAsStmt :: [DeclToken] -> [Stmt] parseDTsAsStmt l0 = [traceStmt $ head l0, stmt] where (lhs, _) = takeLHS l0 (expr, l1) = takeExpr l0 stmt = case init l1 of [DTAsgn _ op mt e] -> Asgn op mt lhs e [DTPorts _ ports] -> Subroutine expr (portsToArgs ports) [] -> Subroutine expr (Args [] []) tok : _ -> parseError tok "unexpected statement token" traceStmt :: DeclToken -> Stmt traceStmt tok = CommentStmt $ "Trace: " ++ show (tokPos tok) -- read the given tokens as the root of a subroutine invocation takeExpr :: [DeclToken] -> (Expr, [DeclToken]) takeExpr (DTPSIdent _ p x : toks) = (PSIdent p x, toks) takeExpr (DTCSIdent _ c p x : toks) = (CSIdent c p x, toks) takeExpr toks = (lhsToExpr lhs, rest) where (lhs, rest) = takeLHS toks -- converts port bindings to call args portsToArgs :: [PortBinding] -> Args portsToArgs bindings = Args pnArgs kwArgs where (pnBindings, kwBindings) = partition (null . fst) bindings pnArgs = map snd pnBindings kwArgs = kwBindings -- [PUBLIC]: parser for comma-separated declarations or assignment lists; this -- is only used for `for` loop initialization lists parseDTsAsDeclsOrAsgns :: [DeclToken] -> Either [Decl] [(LHS, Expr)] parseDTsAsDeclsOrAsgns tokens = if declLookahead tokens then Left $ parseDTsAsDecls Local ModeForLoop tokens else Right $ parseDTsAsAsgns $ shiftIncOrDec tokens -- internal parser for basic assignment lists parseDTsAsAsgns :: [DeclToken] -> [(LHS, Expr)] parseDTsAsAsgns tokens = if not (isAsgn asgnTok) then parseError asgnTok "expected assignment operator" else if mt /= Nothing then unexpected "timing modifier" else (lhs, expr) : case head remaining of DTEnd{} -> [] DTComma{} -> parseDTsAsAsgns $ tail remaining tok -> parseError tok "expected ',' or ';'" where (lhs, asgnTok : remaining) = takeLHS tokens DTAsgn _ op mt rhs = asgnTok expr = case op of AsgnOpEq -> rhs AsgnOpNonBlocking -> unexpected "non-blocking assignment" AsgnOp binop -> BinOp binop (lhsToExpr lhs) rhs unexpected surprise = parseError asgnTok $ "unexpected " ++ surprise ++ " in for loop initialization" shiftIncOrDec :: [DeclToken] -> [DeclToken] shiftIncOrDec (tok@(DTAsgn _ AsgnOp{} _ _) : toks) = before ++ tok : delim : shiftIncOrDec after where (before, delim : after) = break isCommaOrEnd toks shiftIncOrDec [] = [] shiftIncOrDec toks = before ++ delim : shiftIncOrDec after where (before, delim : after) = break isCommaOrEnd toks takeLHS :: [DeclToken] -> (LHS, [DeclToken]) takeLHS tokens = takeLHSStep (takeLHSStart tok) toks where tok : toks = tokens takeLHSStart :: DeclToken -> LHS takeLHSStart (DTLHSBase _ lhs) = lhs takeLHSStart (DTIdent _ x) = LHSIdent x takeLHSStart tok = parseError tok "expected primary token or type" takeLHSStep :: LHS -> [DeclToken] -> (LHS, [DeclToken]) takeLHSStep curr (DTBit _ e : toks) = takeLHSStep (LHSBit curr e ) toks takeLHSStep curr (DTRange _ m r : toks) = takeLHSStep (LHSRange curr m r) toks takeLHSStep curr (DTDot _ x : toks) = takeLHSStep (LHSDot curr x ) toks takeLHSStep lhs toks = (lhs, toks) type DeclBase = Identifier -> [Range] -> Expr -> Decl type Triplet = (Identifier, [Range], Expr) data Mode = ModeForLoop -- initialization always required | ModeSingle -- single declaration (not port list) | ModeDefault -- comma separated, multiple declarations deriving Eq -- internal; entrypoint of the critical portion of our parser parseDTsAsDecls :: Direction -> Mode -> [DeclToken] -> [Decl] parseDTsAsDecls backupDir mode l0 = if l /= Nothing && l /= Just Automatic then parseError (head l1) "unexpected non-automatic lifetime" else if dir == Local && isImplicit t && not (isNet $ head l3) then parseError (head l0) "declaration missing type information" else if null l7 then decls else if mode == ModeSingle then parseError (head l7) "unexpected token in declaration" else decls ++ parseDTsAsDecls dir mode l7 where initReason | hasDriveStrength (head l3) = "net with drive strength" | mode == ModeForLoop = "for loop" | con = "const" | otherwise = "" (dir, l1) = takeDir l0 backupDir (l , l2) = takeLifetime l1 (con, l3) = takeConst l2 (von, l4) = takeVarOrNet l3 (tf , l5) = takeType l4 (rs , l6) = takeRanges l5 (tps, l7) = takeTrips l6 initReason base = von dir t t = tf rs decls = traceComment l0 : map (\(x, a, e) -> base x a e) tps traceComment :: [DeclToken] -> Decl traceComment = CommentDecl . ("Trace: " ++) . show . tokPos . head hasDriveStrength :: DeclToken -> Bool hasDriveStrength (DTNet _ _ DriveStrength{}) = True hasDriveStrength _ = False isImplicit :: Type -> Bool isImplicit Implicit{} = True isImplicit _ = False takeTrips :: [DeclToken] -> String -> ([Triplet], [DeclToken]) takeTrips l0 initReason = (trip : trips, l5) where (x, l1) = takeIdent l0 (a, l2) = takeRanges l1 (e, l3) = takeAsgn l2 initReason l4 = takeCommaOrEnd l3 trip = (x, a, e) (trips, l5) = if tripLookahead l4 then takeTrips l4 initReason else ([], l4) tripLookahead :: [DeclToken] -> Bool tripLookahead l0 = not (null l0) && -- every triplet *must* begin with an identifier isIdent (head l0) && -- expecting to see a comma or the ending token after the identifier and -- optional ranges and/or assignment isCommaOrEnd (head l3) where (_, l1) = takeIdent l0 (_, l2) = takeRanges l1 (_, l3) = takeAsgn l2 "" -- [PUBLIC]: parser for parameter lists in headers of modules, interfaces, etc. parseDTsAsParams :: [DeclToken] -> [Decl] parseDTsAsParams = parseDTsAsParams' Parameter . dropTrailingComma -- internal variant of the above, used recursively after the optional trailing -- comma has been removed parseDTsAsParams' :: ParamScope -> [DeclToken] -> [Decl] parseDTsAsParams' _ [] = [] parseDTsAsParams' prevParamScope l0 = nextStep paramScope l2 where nextStep = if isParamType then parseDTsAsParamType else parseDTsAsParam (paramScope , l1) = takeParamScope l0 prevParamScope (isParamType, l2) = takeParamType l1 -- parse one or more regular parameter declarations at the beginning of the decl -- token list before continuing on to subsequent declarations parseDTsAsParam :: ParamScope -> [DeclToken] -> [Decl] parseDTsAsParam paramScope l0 = traceComment l0 : map base tps ++ parseDTsAsParams' paramScope l3 where (tfO, l1) = takeType l0 (rsO, l2) = takeRanges l1 (tps, l3) = takeTrips l2 "" base :: Triplet -> Decl (tf, rs) = typeRanges $ tfO rsO -- for packing tolerance base (x, a, e) = Param paramScope (tf $ a ++ rs) x e -- parse a single type parameter declaration at the beginning of the decl token -- list before continuing on to subsequent declarations parseDTsAsParamType :: ParamScope -> [DeclToken] -> [Decl] parseDTsAsParamType paramScope l0 = traceComment l0 : ParamType paramScope x t : nextStep paramScope l3 where nextStep = if typeAsgnLookahead l3 then parseDTsAsParamType else parseDTsAsParams' (x, l1) = takeIdent l0 (t, l2) = takeTypeAsgn l1 l3 = takeCommaOrEnd l2 -- take the optional default value assignment for a type parameter declaration; -- note that aliases are parsed as regular assignments to avoid conflicts, and -- so are converted to types when encountered takeTypeAsgn :: [DeclToken] -> (Type, [DeclToken]) takeTypeAsgn (DTTypeAsgn _ : l0) = (tf rs, l2) where (tf, l1) = takeType l0 (rs, l2) = takeRanges l1 takeTypeAsgn (DTAsgn pos AsgnOpEq Nothing expr : toks) = case exprToType expr of Nothing -> parseError pos "unexpected non-type parameter assignment" Just t -> (t, toks) takeTypeAsgn toks = (UnknownType, toks) -- check whether the front of the tokens could plausibly form a type parameter -- declaration and optional assignment; type parameter declaration assignments -- can only be "escaped" using an explicit non-type parameter/localparam keyword typeAsgnLookahead :: [DeclToken] -> Bool typeAsgnLookahead l0 = not (null l0) && -- every type assignment *must* begin with an identifier isIdent (head l0) && -- expecting to see a comma or the ending token after the identifier and -- optional assignment isCommaOrEnd (head l2) where (_, l1) = takeIdent l0 (_, l2) = takeTypeAsgn l1 takeDir :: [DeclToken] -> Direction -> (Direction, [DeclToken]) takeDir (DTDir _ dir : rest) _ = (dir, rest) takeDir rest dir = (dir, rest) takeLifetime :: [DeclToken] -> (Maybe Lifetime, [DeclToken]) takeLifetime (DTLifetime _ l : rest) = (Just l, rest) takeLifetime rest = (Nothing, rest) takeConst :: [DeclToken] -> (Bool, [DeclToken]) takeConst (DTConst{} : DTConst pos : _) = parseError pos "duplicate const modifier" takeConst (DTConst pos : DTNet _ typ _ : _) = parseError pos $ show typ ++ " cannot be const" takeConst (DTConst{} : tokens) = (True, tokens) takeConst tokens = (False, tokens) takeVarOrNet :: [DeclToken] -> (Direction -> Type -> DeclBase, [DeclToken]) takeVarOrNet (DTNet{} : DTVar pos : _) = parseError pos "unexpected var after net type" takeVarOrNet (DTNet pos n s : tokens) = if n /= TTrireg && isChargeStrength s then parseError pos "only trireg can have a charge strength" else (\d -> Net d n s, tokens) where isChargeStrength :: Strength -> Bool isChargeStrength ChargeStrength{} = True isChargeStrength _ = False takeVarOrNet tokens = (Variable, tokens) takeType :: [DeclToken] -> ([Range] -> Type, [DeclToken]) takeType (DTIdent _ a : DTDot _ b : rest) = (InterfaceT a b , rest) takeType (DTType _ tf : DTSigning _ sg : rest) = (tf sg , rest) takeType (DTType _ tf : rest) = (tf Unspecified , rest) takeType (DTSigning _ sg : rest) = (Implicit sg , rest) takeType (DTPSIdent _ ps tn : rest) = (PSAlias ps tn , rest) takeType (DTCSIdent _ ps pm tn : rest) = (CSAlias ps pm tn , rest) takeType (DTIdent pos tn : rest) = if couldBeTypename then (Alias tn , rest) else (Implicit Unspecified, DTIdent pos tn : rest) where couldBeTypename = case (findIndex isIdent rest, findIndex isComma rest) of -- no identifiers left => no decl asgns (Nothing, _) -> False -- an identifier is left, and no more commas (_, Nothing) -> True -- if comma is first, then this ident is a declaration (Just a, Just b) -> a < b takeType (DTVar{} : DTVar pos : _) = parseError pos "duplicate var modifier" takeType (DTVar _ : rest) = case tf [] of Implicit sg [] -> (IntegerVector TLogic sg, rest') _ -> (tf, rest') where (tf, rest') = takeType rest takeType rest = (Implicit Unspecified, rest) takeRanges :: [DeclToken] -> ([Range], [DeclToken]) takeRanges tokens = case head tokens of DTRange _ NonIndexed r -> (r : rs, rest) DTBit _ s -> (asRange s : rs, rest) DTAutoDim _ -> case head $ tail tokens of DTAsgn _ AsgnOpEq Nothing (Pattern l) -> autoDim l DTAsgn _ AsgnOpEq Nothing (Concat l) -> autoDim l _ -> ([], tokens) _ -> ([], tokens) where (rs, rest) = takeRanges $ tail tokens asRange s = (RawNum 0, BinOp Sub s (RawNum 1)) autoDim :: [a] -> ([Range], [DeclToken]) autoDim l = ((lo, hi) : rs, rest) where n = length l lo = RawNum 0 hi = RawNum $ fromIntegral $ n - 1 takeAsgn :: [DeclToken] -> String -> (Expr, [DeclToken]) takeAsgn (DTAsgn pos op mt e : rest) _ = if op == AsgnOpNonBlocking then unexpected "non-blocking assignment operator" else if op /= AsgnOpEq then unexpected "binary assignment operator" else if mt /= Nothing then unexpected "timing modifier" else (e, rest) where unexpected surprise = parseError pos $ "unexpected " ++ surprise ++ " in declaration" takeAsgn rest "" = (Nil, rest) takeAsgn toks initReason = parseError (head toks) $ initReason ++ " declaration is missing initialization" takeCommaOrEnd :: [DeclToken] -> [DeclToken] takeCommaOrEnd tokens = if isCommaOrEnd tok then toks else parseError tok "expected comma or end of declarations" where tok : toks = tokens takeIdent :: [DeclToken] -> (Identifier, [DeclToken]) takeIdent (DTIdent _ x : rest) = (x, rest) takeIdent tokens = parseError (head tokens) "expected identifier" takeParamScope :: [DeclToken] -> ParamScope -> (ParamScope, [DeclToken]) takeParamScope (DTParamKW _ psc : toks) _ = (psc, toks) takeParamScope toks psc = (psc, toks) takeParamType :: [DeclToken] -> (Bool, [DeclToken]) takeParamType (DTTypeDecl _ : toks) = (True , toks) takeParamType toks = (False, toks) isAttr :: DeclToken -> Bool isAttr DTAttr{} = True isAttr _ = False isAsgn :: DeclToken -> Bool isAsgn DTAsgn{} = True isAsgn _ = False isIdent :: DeclToken -> Bool isIdent DTIdent{} = True isIdent _ = False isComma :: DeclToken -> Bool isComma DTComma{} = True isComma _ = False isCommaOrEnd :: DeclToken -> Bool isCommaOrEnd DTEnd{} = True isCommaOrEnd tok = isComma tok isPorts :: DeclToken -> Bool isPorts DTPorts{} = True isPorts _ = False isNet :: DeclToken -> Bool isNet DTNet{} = True isNet _ = False tokPos :: DeclToken -> Position tokPos (DTComma p) = p tokPos (DTAutoDim p) = p tokPos (DTConst p) = p tokPos (DTVar p) = p tokPos (DTAsgn p _ _ _) = p tokPos (DTRange p _ _) = p tokPos (DTIdent p _) = p tokPos (DTPSIdent p _ _) = p tokPos (DTCSIdent p _ _ _) = p tokPos (DTDir p _) = p tokPos (DTType p _) = p tokPos (DTNet p _ _) = p tokPos (DTParams p _) = p tokPos (DTPorts p _) = p tokPos (DTBit p _) = p tokPos (DTLHSBase p _) = p tokPos (DTDot p _) = p tokPos (DTSigning p _) = p tokPos (DTLifetime p _) = p tokPos (DTAttr p _) = p tokPos (DTEnd p _) = p tokPos (DTParamKW p _) = p tokPos (DTTypeDecl p) = p tokPos (DTTypeAsgn p) = p class Loc t where parseError :: t -> String -> a instance Loc Position where parseError pos msg = error $ show pos ++ ": Parse error: " ++ msg instance Loc DeclToken where parseError = parseError . tokPos