ParseDecl.hs 22 KB
Newer Older
1 2 3
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
4
 - Advanced parser for declarations, module instantiations, and some statements.
5
 -
6 7 8 9
 - 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
10 11 12 13 14 15 16 17 18
 - 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
19
 - token.
20
 -
21
 - While I previously had some success dealing with these conflicts with
22 23 24
 - increasingly convoluted grammars, this became more and more untenable as I
 - added support for more SystemVerilog constructs.
 -
25 26 27 28 29 30 31
 - 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.
 -
32 33 34 35
 - 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.
36 37 38 39 40 41
 -}

module Language.SystemVerilog.Parser.ParseDecl
( DeclToken (..)
, parseDTsAsPortDecls
, parseDTsAsModuleItems
42 43
, parseDTsAsTFDecls
, parseDTsAsDecl
44
, parseDTsAsDeclOrStmt
45
, parseDTsAsDeclsOrAsgns
46 47
) where

48
import Data.List (findIndex, partition, uncons)
49 50

import Language.SystemVerilog.AST
51
import Language.SystemVerilog.Parser.Tokens (Position(..))
52 53 54

-- [PUBLIC]: combined (irregular) tokens for declarations
data DeclToken
55 56
    = DTComma    Position
    | DTAutoDim  Position
57 58
    | DTConst    Position
    | DTVar      Position
59
    | DTAsgn     Position AsgnOp (Maybe Timing) Expr
60
    | DTRange    Position PartSelectMode Range
61 62
    | DTIdent    Position Identifier
    | DTPSIdent  Position Identifier Identifier
63
    | DTCSIdent  Position Identifier [ParamBinding] Identifier
64 65
    | DTDir      Position Direction
    | DTType     Position (Signing -> [Range] -> Type)
66
    | DTNet      Position NetType Strength
67
    | DTParams   Position [ParamBinding]
68
    | DTPorts    Position [PortBinding]
69
    | DTBit      Position Expr
70
    | DTLHSBase  Position LHS
71 72 73
    | DTDot      Position Identifier
    | DTSigning  Position Signing
    | DTLifetime Position Lifetime
74
    | DTAttr     Position Attr
75
    | DTEnd      Position Char
76 77


78 79 80
-- [PUBLIC]: parser for module port declarations, including interface ports
-- Example: `input foo, bar, One inst`
parseDTsAsPortDecls :: [DeclToken] -> ([Identifier], [ModuleItem])
81 82 83 84
parseDTsAsPortDecls = parseDTsAsPortDecls' . dropTrailingComma
    where
        dropTrailingComma :: [DeclToken] -> [DeclToken]
        dropTrailingComma [] = []
85
        dropTrailingComma [DTComma{}, end@DTEnd{}] = [end]
86
        dropTrailingComma (tok : toks) = tok : dropTrailingComma toks
87 88 89 90

-- internal parseDTsAsPortDecls after the removal of an optional trailing comma
parseDTsAsPortDecls' :: [DeclToken] -> ([Identifier], [ModuleItem])
parseDTsAsPortDecls' pieces =
91 92
    if isSimpleList
        then (simpleIdents, [])
93
        else (portNames declarations, applyAttrs [] pieces declarations)
94
    where
95 96 97
        maybeSimpleIdents = parseDTsAsIdents pieces
        Just simpleIdents = maybeSimpleIdents
        isSimpleList = maybeSimpleIdents /= Nothing
98

99 100
        declarations = propagateDirections Input $
            parseDTsAsDecls ModeDefault pieces'
101

102
        pieces' = filter (not . isAttr) pieces
103

104
        portNames :: [Decl] -> [Identifier]
Zachary Snow committed
105 106 107
        portNames = filter (not . null) . map portName
        portName :: Decl -> Identifier
        portName (Variable _ _ ident _ _) = ident
108
        portName (Net  _ _ _ _ ident _ _) = ident
109
        portName _ = ""
110

111 112 113 114 115 116 117 118 119 120 121
        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
122
        applyAttrs _ [] _ = undefined
123 124 125 126

        wrapDecl :: [Attr] -> Decl -> ModuleItem
        wrapDecl attrs decl = foldr MIAttr (MIPackageItem $ Decl decl) attrs

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
-- internal utility for carying forward port directions in a port list
propagateDirections :: Direction -> [Decl] -> [Decl]
propagateDirections dir (decl@(Variable _ InterfaceT{} _ _ _) : decls) =
    decl : propagateDirections dir decls
propagateDirections lastDir (Variable currDir t x a e : decls) =
    decl : propagateDirections dir decls
    where
        decl = Variable dir t x a e
        dir = if currDir == Local then lastDir else currDir
propagateDirections lastDir (Net currDir n s t x a e : decls) =
    decl : propagateDirections dir decls
    where
        decl = Net dir n s t x a e
        dir = if currDir == Local then lastDir else currDir
propagateDirections dir (decl : decls) =
    decl : propagateDirections dir decls
propagateDirections _ [] = []

145 146 147 148 149 150 151 152
-- 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

153 154 155 156 157

-- [PUBLIC]: parser for single (semicolon-terminated) declarations (including
-- parameters) and module instantiations
parseDTsAsModuleItems :: [DeclToken] -> [ModuleItem]
parseDTsAsModuleItems tokens =
158 159
    if maybeElabTask /= Nothing then
        [elabTask]
160
    else if any isPorts tokens then
161 162 163
        parseDTsAsIntantiations tokens
    else
        map (MIPackageItem . Decl) $ parseDTsAsDecl tokens
164
    where
165 166 167 168 169 170
        Just elabTask = maybeElabTask
        maybeElabTask = asElabTask tokens

-- internal; attempt to parse an elaboration system task
asElabTask :: [DeclToken] -> Maybe ModuleItem
asElabTask tokens = do
171
    DTIdent _ x@('$' : _) <- return $ head tokens
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    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]
187 188 189

-- internal; parser for module instantiations
parseDTsAsIntantiations :: [DeclToken] -> [ModuleItem]
190 191
parseDTsAsIntantiations (DTIdent _ name : DTParams _ params : tokens) =
    step tokens
192
    where
193
        step :: [DeclToken] -> [ModuleItem]
194 195
        step [] = []
        step toks = inst : step restToks
196
            where
197 198
                inst = Instance name params x rs p
                (x, rs, p) = parseDTsAsIntantiation instToks delimTok
199
                (instToks, delimTok : restToks) = break isCommaOrEnd toks
200 201
parseDTsAsIntantiations (DTIdent pos name : tokens) =
    parseDTsAsIntantiations $ DTIdent pos name : DTParams pos [] : tokens
202
parseDTsAsIntantiations tokens =
203 204 205 206 207 208 209 210
    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
211
        parseError delimTok $ "expected instantiation before " ++ delimStr
212 213 214
    else if not (isIdent nameTok) then
        parseError nameTok "expected instantiation name"
    else if null l1 then
215
        parseError delimTok $ "expected port connections before " ++ delimStr
216 217 218 219 220
    else if seq ranges not (isPorts portsTok) then
        parseError portsTok "expected port connections"
    else
        (name, ranges, ports)
    where
221 222 223 224
        delimChar = case delimTok of
                        DTEnd _ char -> char
                        _ -> ','
        delimStr = ['\'', delimChar, '\'']
225 226 227 228 229 230 231
        Just (nameTok, l1) = uncons l0
        rangeToks = init l1
        portsTok = last l1
        DTIdent _ name = nameTok
        DTPorts _ ports = portsTok
        ranges = map asRange rangeToks
        asRange :: DeclToken -> Range
232
        asRange (DTRange _ NonIndexed s) = s
233 234
        asRange (DTBit _ s) = (RawNum 0, BinOp Sub s (RawNum 1))
        asRange tok = parseError tok "expected instantiation dimensions"
235 236


237 238
-- [PUBLIC]: parser for comma-separated task/function port declarations
parseDTsAsTFDecls :: [DeclToken] -> [Decl]
239
parseDTsAsTFDecls = propagateDirections Input . parseDTsAsDecls ModeDefault
240 241


242
-- [PUBLIC]; used for "single" declarations, i.e., declarations appearing
243 244
-- outside of a port list
parseDTsAsDecl :: [DeclToken] -> [Decl]
245
parseDTsAsDecl = parseDTsAsDecls ModeSingle
246 247


248
-- [PUBLIC]: parser for single block item declarations or assign or arg-less
249 250 251
-- subroutine call statements
parseDTsAsDeclOrStmt :: [DeclToken] -> ([Decl], [Stmt])
parseDTsAsDeclOrStmt tokens =
252 253 254 255 256 257 258 259
    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 =
260
    length l0 /= length l6 && tripLookahead l6
261
    where
262
        (_, l1) = takeDir      l0
263
        (_, l2) = takeLifetime l1
264 265 266 267
        (_, l3) = takeConst    l2
        (_, l4) = takeVarOrNet l3
        (_, l5) = takeType     l4
        (_, l6) = takeRanges   l5
268

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
-- 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)
284

285
-- read the given tokens as the root of a subroutine invocation
286 287 288 289 290
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
291

292
-- converts port bindings to call args
293 294
portsToArgs :: [PortBinding] -> Args
portsToArgs bindings =
295 296 297 298 299
    Args pnArgs kwArgs
    where
        (pnBindings, kwBindings) = partition (null . fst) bindings
        pnArgs = map snd pnBindings
        kwArgs = kwBindings
300

301 302 303 304
-- [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 =
305
    if declLookahead tokens
306 307
        then Left $ parseDTsAsDecls ModeForLoop tokens
        else Right $ parseDTsAsAsgns $ shiftIncOrDec tokens
308 309 310 311

-- internal parser for basic assignment lists
parseDTsAsAsgns :: [DeclToken] -> [(LHS, Expr)]
parseDTsAsAsgns tokens =
312 313 314 315 316 317 318 319
    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 ';'"
320
    where
321 322 323 324 325 326 327 328 329 330 331
        (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]
332
shiftIncOrDec (tok@(DTAsgn _ AsgnOp{} _ _) : toks) =
333 334 335 336 337 338 339 340 341 342 343 344
    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
345 346
takeLHSStart (DTLHSBase _ lhs) = lhs
takeLHSStart (DTIdent _ x) = LHSIdent x
347 348 349 350 351 352 353
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)
354 355


356
type DeclBase = Identifier -> [Range] -> Expr -> Decl
Zachary Snow committed
357
type Triplet = (Identifier, [Range], Expr)
358

359 360 361 362 363
data Mode
    = ModeForLoop -- initialization always required
    | ModeSingle -- single declaration (not port list)
    | ModeDefault -- comma separated, multiple declarations
    deriving Eq
364 365

-- internal; entrypoint of the critical portion of our parser
366 367
parseDTsAsDecls :: Mode -> [DeclToken] -> [Decl]
parseDTsAsDecls mode l0 =
368
    if l /= Nothing && l /= Just Automatic then
369
        parseError (head l1) "unexpected non-automatic lifetime"
370
    else if dir == Local && isImplicit t && not (isNet $ head l3) then
371
        parseError (head l0) "declaration missing type information"
372 373 374 375
    else if null l7 then
        decls
    else if mode == ModeSingle then
        parseError (head l7) "unexpected token in declaration"
376
    else
377
        decls ++ parseDTsAsDecls mode l7
378
    where
379 380 381 382 383
        initReason
            | hasDriveStrength (head l3) = "net with drive strength"
            | mode == ModeForLoop = "for loop"
            | con = "const"
            | otherwise = ""
384 385
        (dir, l1) = takeDir      l0
        (l  , l2) = takeLifetime l1
386
        (con, l3) = takeConst    l2
387 388 389
        (von, l4) = takeVarOrNet l3
        (tf , l5) = takeType     l4
        (rs , l6) = takeRanges   l5
390 391
        (tps, l7) = takeTrips    l6 initReason
        pos = tokPos $ head l0
392
        base = von dir t
393 394 395
        t = case (dir, tf rs) of
                (Output, Implicit sg _) -> IntegerVector TLogic sg rs
                (_, typ) -> typ
396 397 398 399 400 401 402
        decls =
            CommentDecl ("Trace: " ++ show pos) :
            map (\(x, a, e) -> base x a e) tps

hasDriveStrength :: DeclToken -> Bool
hasDriveStrength (DTNet _ _ DriveStrength{}) = True
hasDriveStrength _ = False
403

404 405 406 407
isImplicit :: Type -> Bool
isImplicit Implicit{} = True
isImplicit _ = False

408 409
takeTrips :: [DeclToken] -> String -> ([Triplet], [DeclToken])
takeTrips l0 initReason =
410
    (trip : trips, l5)
411
    where
Zachary Snow committed
412 413
        (x, l1) = takeIdent  l0
        (a, l2) = takeRanges l1
414
        (e, l3) = takeAsgn   l2 initReason
415
        l4 = takeCommaOrEnd  l3
Zachary Snow committed
416
        trip = (x, a, e)
417 418
        (trips, l5) =
            if tripLookahead l4
419
                then takeTrips l4 initReason
420
                else ([], l4)
421 422 423

tripLookahead :: [DeclToken] -> Bool
tripLookahead l0 =
424
    not (null l0) &&
425
    -- every triplet *must* begin with an identifier
426 427 428 429
    isIdent (head l0) &&
    -- expecting to see a comma or the ending token after the identifier and
    -- optional ranges and/or assignment
    isCommaOrEnd (head l3)
430
    where
431 432
        (_, l1) = takeIdent  l0
        (_, l2) = takeRanges l1
433
        (_, l3) = takeAsgn   l2 ""
434 435

takeDir :: [DeclToken] -> (Direction, [DeclToken])
436 437
takeDir (DTDir _ dir : rest) = (dir  , rest)
takeDir                rest  = (Local, rest)
438

439
takeLifetime :: [DeclToken] -> (Maybe Lifetime, [DeclToken])
440 441
takeLifetime (DTLifetime _ l : rest) = (Just  l, rest)
takeLifetime                   rest  = (Nothing, rest)
442

443 444
takeConst :: [DeclToken] -> (Bool, [DeclToken])
takeConst (DTConst{} : DTConst pos : _) =
445
    parseError pos "duplicate const modifier"
446 447
takeConst (DTConst pos : DTNet _ typ _ : _) =
    parseError pos $ show typ ++ " cannot be const"
448 449 450 451 452 453
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"
454 455 456 457 458 459 460 461
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
462
takeVarOrNet tokens = (Variable, tokens)
463

464
takeType :: [DeclToken] -> ([Range] -> Type, [DeclToken])
465
takeType (DTIdent _ a  : DTDot _ b      : rest) = (InterfaceT a  b      , rest)
466 467 468
takeType (DTType  _ tf : DTSigning _ sg : rest) = (tf       sg          , rest)
takeType (DTType  _ tf                  : rest) = (tf       Unspecified , rest)
takeType (DTSigning _ sg                : rest) = (Implicit sg          , rest)
469 470
takeType (DTPSIdent _ ps    tn          : rest) = (PSAlias ps    tn     , rest)
takeType (DTCSIdent _ ps pm tn          : rest) = (CSAlias ps pm tn     , rest)
471
takeType (DTIdent pos tn                : rest) =
472
    if couldBeTypename
473
        then (Alias tn            ,                  rest)
474
        else (Implicit Unspecified, DTIdent pos tn : rest)
475 476
    where
        couldBeTypename =
477
            case (findIndex isIdent rest, findIndex isComma rest) of
478 479 480 481 482 483
                -- 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
484 485
takeType (DTVar{} : DTVar pos : _) =
    parseError pos "duplicate var modifier"
486 487 488 489 490
takeType (DTVar _ : rest) =
    case tf [] of
        Implicit sg [] -> (IntegerVector TLogic sg, rest')
        _ -> (tf, rest')
    where (tf, rest') = takeType rest
491
takeType rest = (Implicit Unspecified, rest)
492 493

takeRanges :: [DeclToken] -> ([Range], [DeclToken])
494 495 496 497 498 499 500 501 502 503
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)
504
    where
505
        (rs, rest) = takeRanges $ tail tokens
506
        asRange s = (RawNum 0, BinOp Sub s (RawNum 1))
507 508 509 510 511
        autoDim :: [a] -> ([Range], [DeclToken])
        autoDim l =
            ((lo, hi) : rs, rest)
            where
                n = length l
512 513
                lo = RawNum 0
                hi = RawNum $ fromIntegral $ n - 1
514

515 516
takeAsgn :: [DeclToken] -> String -> (Expr, [DeclToken])
takeAsgn (DTAsgn pos op mt e : rest) _ =
517 518 519 520 521 522 523 524 525 526
    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 =
527 528 529 530 531
            parseError pos $ "unexpected " ++ surprise ++ " in declaration"
takeAsgn rest "" = (Nil, rest)
takeAsgn toks initReason =
    parseError (head toks) $
        initReason ++ " declaration is missing initialization"
532

533 534 535 536 537 538
takeCommaOrEnd :: [DeclToken] -> [DeclToken]
takeCommaOrEnd tokens =
    if isCommaOrEnd tok
        then toks
        else parseError tok "expected comma or end of declarations"
    where tok : toks = tokens
539 540

takeIdent :: [DeclToken] -> (Identifier, [DeclToken])
541
takeIdent (DTIdent _ x : rest) = (x, rest)
542
takeIdent tokens = parseError (head tokens) "expected identifier"
543 544


545 546 547 548 549 550 551 552
isAttr :: DeclToken -> Bool
isAttr DTAttr{} = True
isAttr _ = False

isAsgn :: DeclToken -> Bool
isAsgn DTAsgn{} = True
isAsgn _ = False

553
isIdent :: DeclToken -> Bool
554
isIdent DTIdent{} = True
555
isIdent _ = False
556 557

isComma :: DeclToken -> Bool
558
isComma DTComma{} = True
559 560
isComma _ = False

561 562 563 564
isCommaOrEnd :: DeclToken -> Bool
isCommaOrEnd DTEnd{} = True
isCommaOrEnd tok = isComma tok

565 566 567 568
isPorts :: DeclToken -> Bool
isPorts DTPorts{} = True
isPorts _ = False

569 570 571 572
isNet :: DeclToken -> Bool
isNet DTNet{} = True
isNet _ = False

573 574 575
tokPos :: DeclToken -> Position
tokPos (DTComma    p) = p
tokPos (DTAutoDim  p) = p
576 577
tokPos (DTConst    p) = p
tokPos (DTVar      p) = p
578
tokPos (DTAsgn     p _ _ _) = p
579
tokPos (DTRange    p _ _) = p
580 581
tokPos (DTIdent    p _) = p
tokPos (DTPSIdent  p _ _) = p
582
tokPos (DTCSIdent  p _ _ _) = p
583 584
tokPos (DTDir      p _) = p
tokPos (DTType     p _) = p
585
tokPos (DTNet      p _ _) = p
586
tokPos (DTParams   p _) = p
587
tokPos (DTPorts    p _) = p
588
tokPos (DTBit      p _) = p
589
tokPos (DTLHSBase  p _) = p
590 591 592
tokPos (DTDot      p _) = p
tokPos (DTSigning  p _) = p
tokPos (DTLifetime p _) = p
593
tokPos (DTAttr     p _) = p
594 595 596 597 598 599 600
tokPos (DTEnd      p _) = p

class Loc t where
    parseError :: t -> String -> a

instance Loc Position where
    parseError pos msg = error $ show pos ++ ": Parse error: " ++ msg
601

602 603
instance Loc DeclToken where
    parseError = parseError . tokPos