Commit 5fd21ebf by Zachary Snow

improved parsing in declaration contexts

- support for additional assignment statements in loop initializations
- greatly improved error messaging in these contexts
- decl parser takes in the ending token; significant related refactoring
- pass through elaboration system tasks
- removed non-blocking assignment operator precedence hack
- preliminary nosim test suite for features unsupported by iverilog
parent 6ee558b6
......@@ -625,6 +625,11 @@ traverseNodesM exprMapper declMapper typeMapper lhsMapper stmtMapper =
a' <- traverseAssertionStmtsM stmtMapper a
a'' <- traverseAssertionExprsM exprMapper a'
return $ AssertionItem (mx, a'')
moduleItemMapper (ElabTask severity (Args pnArgs kwArgs)) = do
pnArgs' <- mapM exprMapper pnArgs
kwArgs' <- fmap (zip kwNames) $ mapM exprMapper kwExprs
return $ ElabTask severity $ Args pnArgs' kwArgs'
where (kwNames, kwExprs) = unzip kwArgs
genItemMapper = traverseGenItemExprsM exprMapper
......
......@@ -14,6 +14,7 @@ module Language.SystemVerilog.AST.ModuleItem
, NInputGateKW (..)
, NOutputGateKW (..)
, AssignOption (..)
, Severity (..)
) where
import Data.List (intercalate)
......@@ -24,7 +25,7 @@ import Language.SystemVerilog.AST.ShowHelp
import Language.SystemVerilog.AST.Attr (Attr)
import Language.SystemVerilog.AST.Decl (Direction)
import Language.SystemVerilog.AST.Description (PackageItem)
import Language.SystemVerilog.AST.Expr (Expr(Nil), pattern Ident, Range, showRanges, ParamBinding, showParams)
import Language.SystemVerilog.AST.Expr (Expr(Nil), pattern Ident, Range, showRanges, ParamBinding, showParams, Args)
import Language.SystemVerilog.AST.GenItem (GenItem)
import Language.SystemVerilog.AST.LHS (LHS)
import Language.SystemVerilog.AST.Stmt (Stmt, AssertionItem, Timing(Delay))
......@@ -41,6 +42,7 @@ data ModuleItem
| Modport Identifier [ModportDecl]
| Initial Stmt
| Final Stmt
| ElabTask Severity Args
| MIPackageItem PackageItem
| NInputGate NInputGateKW Expr Identifier LHS [Expr]
| NOutputGate NOutputGateKW Expr Identifier [LHS] Expr
......@@ -58,6 +60,7 @@ instance Show ModuleItem where
show (Modport x l) = printf "modport %s(\n%s\n);" x (indent $ intercalate ",\n" $ map showModportDecl l)
show (Initial s ) = printf "initial %s" (show s)
show (Final s ) = printf "final %s" (show s)
show (ElabTask s a) = printf "%s%s;" (show s) (show a)
show (NInputGate kw d x lhs exprs) =
showGate kw d x $ show lhs : map show exprs
show (NOutputGate kw d x lhss expr) =
......@@ -148,3 +151,16 @@ instance Show AssignOption where
show AssignOptionNone = ""
show (AssignOptionDelay de) = printf "#(%s)" (show de)
show (AssignOptionDrive s0 s1) = printf "(%s, %s)" (show s0) (show s1)
data Severity
= SeverityInfo
| SeverityWarning
| SeverityError
| SeverityFatal
deriving Eq
instance Show Severity where
show SeverityInfo = "$info"
show SeverityWarning = "$warning"
show SeverityError = "$error"
show SeverityFatal = "$fatal"
......@@ -401,7 +401,6 @@ time { Token Lit_time _ _ }
-- operator precedences, from *lowest* to *highest*
%nonassoc DefaultStrength
%nonassoc DriveStrength ChargeStrength
%nonassoc Asgn
%nonassoc NoElse
%nonassoc "else"
%right "|->" "|=>" "#-#" "#=#"
......@@ -630,17 +629,17 @@ Strength :: { Strength }
DeclTokens(delim) :: { [DeclToken] }
: DeclTokensBase(DeclTokens(delim), delim) { $1 }
DeclTokensBase(repeat, delim) :: { [DeclToken] }
: DeclToken delim { [$1] }
: DeclToken DTDelim(delim) { [$1, $2] }
| DeclToken repeat { [$1] ++ $2 }
| IdentifierP ParamBindings repeat { [uncurry DTIdent $1, DTParams (fst $1) $2] ++ $3 }
| DeclTokenAsgn "," repeat { [$1, DTComma (tokenPosition $2)] ++ $3 }
| DeclTokenAsgn delim { [$1] }
| DeclTokenAsgn DTDelim(delim) { [$1, $2] }
DeclToken :: { DeclToken }
: "," { DTComma $ tokenPosition $1 }
| "[" "]" { DTAutoDim $ tokenPosition $1 }
| "const" { DTConst $ tokenPosition $1 }
| "var" { DTVar $ tokenPosition $1 }
| PartSelectP { uncurry DTRange $1 }
| PartSelectP { uncurry (DTRange $ fst $1) (snd $1) }
| IdentifierP { uncurry DTIdent $1 }
| DirectionP { uncurry DTDir $1 }
| LHSConcatP { uncurry DTConcat $1 }
......@@ -655,21 +654,23 @@ DeclToken :: { DeclToken }
| "{" StreamOp Concat "}" { DTStream (tokenPosition $1) $2 (RawNum 1) (map toLHS $3) }
| "type" "(" Expr ")" { uncurry DTType $ makeTypeOf $1 $3 }
| IncOrDecOperatorP { DTAsgn (fst $1) (AsgnOp $ snd $1) Nothing (RawNum 1) }
| "<=" opt(DelayOrEvent) Expr %prec Asgn { DTAsgn (tokenPosition $1) AsgnOpNonBlocking $2 $3 }
| IdentifierP "::" Identifier { uncurry DTPSIdent $1 $3 }
| IdentifierP ParamBindings "::" Identifier { uncurry DTCSIdent $1 $2 $4 }
DTDelim(delim) :: { DeclToken }
: delim { DTEnd (tokenPosition $1) (head $ tokenString $1) }
DeclTokenAsgn :: { DeclToken }
: "=" opt(DelayOrEvent) Expr { DTAsgn (tokenPosition $1) AsgnOpEq $2 $3 }
| AsgnBinOpP Expr { uncurry DTAsgn $1 Nothing $2 }
| "<=" opt(DelayOrEvent) Expr { DTAsgn (tokenPosition $1) AsgnOpNonBlocking $2 $3 }
PortDeclTokens(delim) :: { [DeclToken] }
: DeclTokensBase(PortDeclTokens(delim), delim) { $1 }
| GenericInterfaceDecl PortDeclTokens(delim) { $1 ++ $2}
| GenericInterfaceDecl delim { $1 }
| GenericInterfaceDecl DTDelim(delim) { $1 ++ [$2] }
| AttributeInstanceP PortDeclTokens(delim) { uncurry DTAttr $1 : $2 }
ModuleDeclTokens(delim) :: { [DeclToken] }
: DeclTokensBase(ModuleDeclTokens(delim), delim) { $1 }
| GenericInterfaceDecl ModuleDeclTokens(delim) { $1 ++ $2}
| GenericInterfaceDecl delim { $1 }
| GenericInterfaceDecl DTDelim(delim) { $1 ++ [$2] }
GenericInterfaceDecl :: { [DeclToken] }
: "interface" IdentifierP { [DTType (tokenPosition $1) (\Unspecified -> InterfaceT "" ""), uncurry DTIdent $2] }
......
......@@ -45,7 +45,7 @@ module Language.SystemVerilog.Parser.ParseDecl
, parseDTsAsDeclsOrAsgns
) where
import Data.List (findIndex, findIndices, partition, uncons)
import Data.List (findIndex, partition, uncons)
import Language.SystemVerilog.AST
import Language.SystemVerilog.Parser.Tokens (Position(..))
......@@ -57,7 +57,7 @@ data DeclToken
| DTConst Position
| DTVar Position
| DTAsgn Position AsgnOp (Maybe Timing) Expr
| DTRange Position (PartSelectMode, Range)
| DTRange Position PartSelectMode Range
| DTIdent Position Identifier
| DTPSIdent Position Identifier Identifier
| DTCSIdent Position Identifier [ParamBinding] Identifier
......@@ -73,59 +73,34 @@ data DeclToken
| DTSigning Position Signing
| DTLifetime Position Lifetime
| DTAttr Position Attr
deriving (Show, Eq)
-- entrypoints besides `parseDTsAsDeclOrStmt` use this to disallow `DTAsgn` with
-- a non-blocking operator, binary assignment operator, or a timing control
-- because we don't expect to see those assignment operators in declarations
forbidNonEqAsgn :: [DeclToken] -> a -> a
forbidNonEqAsgn [] = id
forbidNonEqAsgn (tok @ (DTAsgn _ op mt _) : toks) =
if op /= AsgnOpEq then
parseError tok $ "unexpected " ++ opKind
++ " assignment operator in declaration"
else if mt /= Nothing then
parseError tok "unexpected timing modifier in declaration"
else
forbidNonEqAsgn toks
where opKind = if op == AsgnOpNonBlocking then "non-blocking" else "binary"
forbidNonEqAsgn (_ : toks) = forbidNonEqAsgn toks
| DTEnd Position Char
deriving Eq
-- [PUBLIC]: parser for module port declarations, including interface ports
-- Example: `input foo, bar, One inst`
parseDTsAsPortDecls :: [DeclToken] -> ([Identifier], [ModuleItem])
parseDTsAsPortDecls pieces =
parseDTsAsPortDecls' $
case last pieces of
DTComma{} -> init pieces
_ -> pieces
parseDTsAsPortDecls = parseDTsAsPortDecls' . dropTrailingComma
where
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 =
forbidNonEqAsgn pieces `seq`
if isSimpleList
then (simpleIdents, [])
else (portNames declarations, applyAttrs [] pieces declarations)
where
commaIdxs = findIndices isComma pieces
identIdxs = findIndices isIdent pieces
isSimpleList =
all even identIdxs &&
all odd commaIdxs &&
odd (length pieces) &&
length pieces == length commaIdxs + length identIdxs
simpleIdents = map extractIdent $ filter isIdent pieces
declarations = propagateDirections Input $ parseDTsAsDecls pieces'
maybeSimpleIdents = parseDTsAsIdents pieces
Just simpleIdents = maybeSimpleIdents
isSimpleList = maybeSimpleIdents /= Nothing
extractIdent = \(DTIdent _ x) -> x
declarations = propagateDirections Input $ parseDTsAsDecls pieces'
pieces' = filter (not . isDTAttr) pieces
isDTAttr :: DeclToken -> Bool
isDTAttr DTAttr{} = True
isDTAttr _ = False
pieces' = filter (not . isAttr) pieces
propagateDirections :: Direction -> [Decl] -> [Decl]
propagateDirections dir (decl @ (Variable _ InterfaceT{} _ _ _) : decls) =
......@@ -149,12 +124,9 @@ parseDTsAsPortDecls' pieces =
portName :: Decl -> Identifier
portName (Variable _ _ ident _ _) = ident
portName (Net _ _ _ _ ident _ _) = ident
portName CommentDecl{} = ""
portName decl =
error $ "unexpected non-variable port declaration: " ++ (show decl)
portName _ = ""
applyAttrs :: [Attr] -> [DeclToken] -> [Decl] -> [ModuleItem]
applyAttrs _ [] [] = []
applyAttrs _ tokens (CommentDecl c : decls) =
MIPackageItem (Decl $ CommentDecl c) : applyAttrs [] tokens decls
applyAttrs attrs (DTAttr _ attr : tokens) decls =
......@@ -165,41 +137,53 @@ parseDTsAsPortDecls' pieces =
wrapDecl attrs decl : applyAttrs attrs tokens decls
applyAttrs attrs (_ : tokens) decls =
applyAttrs attrs tokens decls
applyAttrs _ [] _ = error "applyAttrs internal invariant failed"
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 =
forbidNonEqAsgn tokens `seq`
if isElabTask $ head tokens then
asElabTask tokens
if maybeElabTask /= Nothing then
[elabTask]
else if any isPorts tokens then
parseDTsAsIntantiations tokens
else
map (MIPackageItem . Decl) $ parseDTsAsDecl tokens
where
isElabTask :: DeclToken -> Bool
isElabTask (DTIdent _ x) = elem x elabTasks
where elabTasks = ["$fatal", "$error", "$warning", "$info"]
isElabTask _ = False
-- internal; approximates the behavior of the elaboration system tasks
asElabTask :: [DeclToken] -> [ModuleItem]
asElabTask [DTIdent _ name, DTPorts _ args] =
if name == "$info"
then [] -- just drop them for simplicity
else [Instance "ThisModuleDoesNotExist" [] name' [] args]
where name' = "__sv2v_elab_" ++ tail name
asElabTask [DTIdent pos name] =
asElabTask [DTIdent pos name, DTPorts pos []]
asElabTask tokens =
error $ "could not parse elaboration system task: " ++ show tokens
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]
......@@ -207,18 +191,12 @@ parseDTsAsIntantiations (DTIdent _ name : DTParams _ params : tokens) =
step tokens
where
step :: [DeclToken] -> [ModuleItem]
step [] = parseError endTok "unexpected end of instantiation list"
step toks = inst : rest
step [] = []
step toks = inst : step restToks
where
(delimTok, rest) =
if null restToks
then (endTok, [])
else (head restToks, step $ tail restToks)
inst = Instance name params x rs p
(x, rs, p) = parseDTsAsIntantiation instToks delimTok
(instToks, restToks) = break isComma toks
-- TODO: all public interfaces should take the ending token
endTok = last tokens
(instToks, delimTok : restToks) = break isCommaOrEnd toks
parseDTsAsIntantiations (DTIdent pos name : tokens) =
parseDTsAsIntantiations $ DTIdent pos name : DTParams pos [] : tokens
parseDTsAsIntantiations tokens =
......@@ -230,16 +208,20 @@ parseDTsAsIntantiation :: [DeclToken] -> DeclToken
-> (Identifier, [Range], [PortBinding])
parseDTsAsIntantiation l0 delimTok =
if null l0 then
parseError delimTok "expected instantiation before delimiter"
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 delimiter"
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
......@@ -247,7 +229,7 @@ parseDTsAsIntantiation l0 delimTok =
DTPorts _ ports = portsTok
ranges = map asRange rangeToks
asRange :: DeclToken -> Range
asRange (DTRange _ (NonIndexed, s)) = s
asRange (DTRange _ NonIndexed s) = s
asRange (DTBit _ s) = (RawNum 0, BinOp Sub s (RawNum 1))
asRange tok = parseError tok "expected instantiation dimensions"
......@@ -255,61 +237,61 @@ parseDTsAsIntantiation l0 delimTok =
-- [PUBLIC]: parser for generic, comma-separated declarations
parseDTsAsDecls :: [DeclToken] -> [Decl]
parseDTsAsDecls tokens =
forbidNonEqAsgn tokens `seq`
concat $ map finalize $ parseDTsAsComponents tokens
concatMap finalize $ parseDTsAsComponents tokens
-- internal; used for "single" declarations, i.e., declarations appearing
-- outside of a port list
parseDTsAsDecl :: [DeclToken] -> [Decl]
parseDTsAsDecl tokens =
if length components /= 1
then parseError tok $ "unexpected comma-separated declarations"
else finalize $ head components
where
components = parseDTsAsComponents tokens
_ : (pos, _, _) : _ = components
tok = DTComma pos
if null rest
then finalize component
else parseError (head rest) "unexpected token in declaration"
where (component, rest) = parseDTsAsComponent tokens
-- [PUBLIC]: parser for single block item declarations or assign or arg-less
-- subroutine call statements
parseDTsAsDeclOrStmt :: [DeclToken] -> ([Decl], [Stmt])
parseDTsAsDeclOrStmt (DTAsgn pos (AsgnOp op) mt e : tok : toks) =
parseDTsAsDeclOrStmt $ (tok : toks) ++ [DTAsgn pos (AsgnOp op) mt e]
parseDTsAsDeclOrStmt tokens =
if not hasLeadingDecl
then ([], [traceStmt pos, stmt])
else (parseDTsAsDecl 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 =
l0 /= l5 && tripLookahead l5
where
pos = tokPos $ last tokens
stmt = case last tokens of
DTAsgn _ op mt e -> Asgn op mt lhs e
DTPorts _ ports -> asSubroutine lhsToks (portsToArgs ports)
_ -> asSubroutine tokens (Args [] [])
lhsToks = init tokens
lhs = case takeLHS lhsToks of
Nothing -> error $ "could not parse as LHS: " ++ show lhsToks
Just l -> l
hasLeadingDecl = tokens /= l5 && tripLookahead l5
(_, l1) = takeDir tokens
(_, l1) = takeDir l0
(_, l2) = takeLifetime l1
(_, l3) = takeVarOrNet l2
(_, l4) = takeType l3
(_, l5) = takeRanges l4
traceStmt :: Position -> Stmt
traceStmt pos = CommentStmt $ "Trace: " ++ show pos
-- 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
asSubroutine :: [DeclToken] -> Args -> Stmt
asSubroutine [DTIdent _ x] = Subroutine $ Ident x
asSubroutine [DTPSIdent _ p x] = Subroutine $ PSIdent p x
asSubroutine [DTCSIdent _ c p x] = Subroutine $ CSIdent c p x
asSubroutine tokens =
case takeLHS tokens of
Just lhs -> Subroutine $ lhsToExpr lhs
Nothing -> error $ "invalid block item decl or stmt: " ++ show tokens
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
......@@ -324,66 +306,68 @@ portsToArgs bindings =
-- is only used for `for` loop initialization lists
parseDTsAsDeclsOrAsgns :: [DeclToken] -> Either [Decl] [(LHS, Expr)]
parseDTsAsDeclsOrAsgns tokens =
forbidNonEqAsgn tokens `seq`
if hasLeadingAsgn || tripLookahead tokens
then Right $ parseDTsAsAsgns tokens
else Left $ map checkDecl $ parseDTsAsDecls tokens
if declLookahead tokens
then Left decls
else Right asgns
where
hasLeadingAsgn =
-- if there is an asgn token before the next comma
case (findIndex isComma tokens, findIndex isAsgnToken tokens) of
(Just a, Just b) -> a > b
(Nothing, Just _) -> True
_ -> False
checkDecl :: Decl -> Decl
checkDecl (decl @ (Variable _ _ _ _ Nil)) =
error $ "for loop declaration missing initialization: "
++ init (show decl)
checkDecl decl = decl
decls = concatMap finalize components
components = map checkComponent $ parseDTsAsComponents tokens
asgns = parseDTsAsAsgns $ shiftIncOrDec tokens
checkComponent :: Component -> Component
checkComponent (pos, base, trips) =
(pos, base, map (checkTriplet pos) trips)
checkTriplet :: Position -> Triplet -> Triplet
checkTriplet pos (x, _, Nil) =
parseError pos $ "for loop declaration of " ++ show x
++ " is missing initialization"
checkTriplet _ trip = trip
-- internal parser for basic assignment lists
parseDTsAsAsgns :: [DeclToken] -> [(LHS, Expr)]
parseDTsAsAsgns tokens =
case l1 of
[] -> [asgn]
DTComma{} : remaining -> asgn : parseDTsAsAsgns remaining
_ -> error $ "bad assignment tokens: " ++ show 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
(lhsToks, l0) = break isDTAsgn tokens
lhs = case takeLHS lhsToks of
Nothing -> error $ "could not parse as LHS: " ++ show lhsToks
Just l -> l
DTAsgn _ AsgnOpEq Nothing expr : l1 = l0
asgn = (lhs, expr)
isDTAsgn :: DeclToken -> Bool
isDTAsgn (DTAsgn _ _ Nothing _) = True
isDTAsgn _ = False
isAsgnToken :: DeclToken -> Bool
isAsgnToken (DTBit{} ) = True
isAsgnToken (DTConcat{}) = True
isAsgnToken (DTStream{}) = True
isAsgnToken (DTDot{} ) = True
isAsgnToken (DTAsgn _ op _ _) = op /= AsgnOpEq
isAsgnToken _ = False
takeLHS :: [DeclToken] -> Maybe LHS
takeLHS [] = Nothing
takeLHS (t : ts) =
foldl takeLHSStep (takeLHSStart t) ts
takeLHSStart :: DeclToken -> Maybe LHS
takeLHSStart (DTConcat _ lhss) = Just $ LHSConcat lhss
takeLHSStart (DTStream _ o e lhss) = Just $ LHSStream o e lhss
takeLHSStart (DTIdent _ x ) = Just $ LHSIdent x
takeLHSStart _ = Nothing
takeLHSStep :: Maybe LHS -> DeclToken -> Maybe LHS
takeLHSStep (Just curr) (DTBit _ e ) = Just $ LHSBit curr e
takeLHSStep (Just curr) (DTRange _ (m,r)) = Just $ LHSRange curr m r
takeLHSStep (Just curr) (DTDot _ x ) = Just $ LHSDot curr x
takeLHSStep _ _ = Nothing
(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 (DTConcat _ lhss) = LHSConcat lhss
takeLHSStart (DTStream _ o e lhss) = LHSStream o e lhss
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)
-- batches together separate declaration lists
......@@ -404,13 +388,11 @@ parseDTsAsComponents tokens =
where (component, tokens') = parseDTsAsComponent tokens
parseDTsAsComponent :: [DeclToken] -> (Component, [DeclToken])
parseDTsAsComponent [] = error "parseDTsAsComponent unexpected end of tokens"
parseDTsAsComponent l0 =
if l /= Nothing && l /= Just Automatic then
error $ "unexpected non-automatic lifetime: " ++ show l0
parseError (head l1) "unexpected non-automatic lifetime"
else if dir == Local && length l2 == length l5 then
error $ "declaration(s) missing type information: "
++ show (position, tps)
parseError (head l0) "declaration missing type information"
else
(component, l6)
where
......@@ -419,50 +401,37 @@ parseDTsAsComponent l0 =
(von, l3) = takeVarOrNet l2
(tf , l4) = takeType l3
(rs , l5) = takeRanges l4
(tps, l6) = takeTrips l5 True
(tps, l6) = takeTrips l5
position = tokPos $ head l0
base = von dir $ tf rs
component = (position, base, tps)
takeTrips :: [DeclToken] -> Bool -> ([Triplet], [DeclToken])
takeTrips [] True = error "incomplete declaration"
takeTrips [] False = ([], [])
takeTrips l0 force =
if not force && not (tripLookahead l0)
then ([], l0)
else (trip : trips, l5)
takeTrips :: [DeclToken] -> ([Triplet], [DeclToken])
takeTrips l0 =
(trip : trips, l5)
where
(x, l1) = takeIdent l0
(a, l2) = takeRanges l1
(e, l3) = takeAsgn l2
(_, l4) = takeComma l3
l4 = takeCommaOrEnd l3
trip = (x, a, e)
(trips, l5) = takeTrips l4 False
(trips, l5) =
if tripLookahead l4
then takeTrips l4
else ([], l4)
tripLookahead :: [DeclToken] -> Bool
tripLookahead [] = False
tripLookahead l0 =
not (null l0) &&
-- every triplet *must* begin with an identifier
if not (isIdent $ head l0) then
False
-- if the identifier is the last token, or if it assigned a value, then we
-- know we must have a valid triplet ahead
else if null l1 || asgn /= Nil then
True
-- if there is an ident followed by some number of ranges, and that's it,
-- then there is a trailing declaration of an array ahead
else if (not $ null l1) && (null l2) then
True
-- if there is a comma after the identifier (and optional ranges and
-- assignment) that we're looking at, then we know this identifier is not a
-- type name, as type names must be followed by a first identifier before a
-- comma or the end of the list
else
(not $ null l3) && (isComma $ head l3)
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
(asgn, l3) = takeAsgn l2
(_, l1) = takeIdent l0
(_, l2) = takeRanges l1
(_, l3) = takeAsgn l2
takeDir :: [DeclToken] -> (Direction, [DeclToken])
takeDir (DTDir _ dir : rest) = (dir , rest)
......@@ -473,8 +442,8 @@ takeLifetime (DTLifetime _ l : rest) = (Just l, rest)
takeLifetime rest = (Nothing, rest)
takeVarOrNet :: [DeclToken] -> (Direction -> Type -> DeclBase, [DeclToken])
takeVarOrNet (DTConst{} : tok @ DTConst{} : _) =
parseError tok "duplicate const modifier"
takeVarOrNet (DTConst{} : DTConst pos : _) =
parseError pos "duplicate const modifier"
takeVarOrNet (DTConst _ : tokens) = takeVarOrNet tokens
takeVarOrNet (DTNet _ n s : tokens) = (\d -> Net d n s, tokens)
takeVarOrNet tokens = (Variable, tokens)
......@@ -499,8 +468,8 @@ takeType (DTIdent pos tn : rest) =
(_, Nothing) -> True
-- if comma is first, then this ident is a declaration
(Just a, Just b) -> a < b
takeType (DTVar{} : tok @ DTVar{} : _) =
parseError tok "duplicate var modifier"
takeType (DTVar{} : DTVar pos : _) =
parseError pos "duplicate var modifier"
takeType (DTVar _ : rest) =
case tf [] of
Implicit sg [] -> (IntegerVector TLogic sg, rest')
......@@ -509,19 +478,18 @@ takeType (DTVar _ : rest) =
takeType rest = (Implicit Unspecified, rest)
takeRanges :: [DeclToken] -> ([Range], [DeclToken])
takeRanges [] = ([], [])
takeRanges (token : tokens) =
case token of
DTRange _ (NonIndexed, r) -> (r : rs, rest )
DTBit _ s -> (asRange s : rs, rest )
takeRanges tokens =
case head tokens of
DTRange _ NonIndexed r -> (r : rs, rest)
DTBit _ s -> (asRange s : rs, rest)
DTAutoDim _ ->
case rest of
(DTAsgn _ AsgnOpEq Nothing (Pattern l) : _) -> autoDim l
(DTAsgn _ AsgnOpEq Nothing (Concat l) : _) -> autoDim l
_ -> ([] , token : tokens)
_ -> ([] , token : tokens)
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 tokens
(rs, rest) = takeRanges $ tail tokens
asRange s = (RawNum 0, BinOp Sub s (RawNum 1))
autoDim :: [a] -> ([Range], [DeclToken])
autoDim l =
......@@ -531,35 +499,53 @@ takeRanges (token : tokens) =
lo = RawNum 0
hi = RawNum $ fromIntegral $ n - 1
-- Matching `AsgnOpEq` and `AsgnOpNonBlocking` here allows tripLookahead to work
-- both for standard declarations and in `parseDTsAsDeclOrStmt`, where we're
-- checking for an assignment statement. The other entry points disallow
-- `AsgnOpNonBlocking`, so this doesn't liberalize the parser.
takeAsgn :: [DeclToken] -> (Expr, [DeclToken])
takeAsgn (DTAsgn _ op Nothing e : rest) =
if op == AsgnOpEq || op == AsgnOpNonBlocking
then (e , rest)
else (Nil, rest)
takeAsgn (tok @ (DTAsgn _ 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 tok $ "unexpected " ++ surprise ++ " in declaration"
takeAsgn rest = (Nil, rest)
takeComma :: [DeclToken] -> (Bool, [DeclToken])
takeComma [] = (False, [])
takeComma (DTComma{} : rest) = (True, rest)
takeComma toks = error $ "expected comma or end of decl, got: " ++ show toks
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 = error $ "takeIdent didn't find identifier: " ++ show tokens
takeIdent tokens = parseError (head tokens) "expected identifier"
isAttr :: DeclToken -> Bool
isAttr DTAttr{} = True
isAttr _ = False
isAsgn :: DeclToken -> Bool
isAsgn DTAsgn{} = True
isAsgn _ = False
isIdent :: DeclToken -> Bool
isIdent (DTIdent{}) = True
isIdent DTIdent{} = True
isIdent _ = False
isComma :: DeclToken -> Bool
isComma (DTComma{}) = True
isComma DTComma{} = True
isComma _ = False
isCommaOrEnd :: DeclToken -> Bool
isCommaOrEnd DTEnd{} = True
isCommaOrEnd tok = isComma tok
isPorts :: DeclToken -> Bool
isPorts DTPorts{} = True
isPorts _ = False
......@@ -570,7 +556,7 @@ tokPos (DTAutoDim p) = p
tokPos (DTConst p) = p
tokPos (DTVar p) = p
tokPos (DTAsgn p _ _ _) = p
tokPos (DTRange p _) = p
tokPos (DTRange p _ _) = p
tokPos (DTIdent p _) = p
tokPos (DTPSIdent p _ _) = p
tokPos (DTCSIdent p _ _ _) = p
......@@ -586,7 +572,13 @@ tokPos (DTDot p _) = p
tokPos (DTSigning p _) = p
tokPos (DTLifetime p _) = p
tokPos (DTAttr p _) = p
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
parseError :: DeclToken -> String -> a
parseError tok msg = error $ show pos ++ ": Parse error: " ++ msg
where pos = tokPos tok
instance Loc DeclToken where
parseError = parseError . tokPos
module top;
if (1 == 0)
wire foo;
else
$info("foo");
endmodule
module top;
initial begin
integer x, y;
x = 0;
y = 3;
for (x += 1; x < y; x++)
$display("A x = %0d", x);
end
initial begin
integer x, y;
x = 0;
for (x += 1, y = 3; x < y; x++)
$display("B x = %0d", x);
end
initial begin
integer x, y;
x = 0;
for (y = 3, x += 1; x < y; x++)
$display("C x = %0d", x);
end
initial
for (integer x = 0, y = 3; x < y; x++)
$display("D x = %0d", x);
initial
for (integer x = 0, byte y = 3; x < y; x++)
$display("E x = %0d", x);
initial begin
integer x, y;
x = 0;
for (x++, y = 3; x < y; x++)
$display("F x = %0d", x);
end
initial begin
integer x, y;
x = 0;
for (y = 3, x++; x < y; x++)
$display("G x = %0d", x);
end
initial begin
integer x, y;
x = 0;
for (--x, y = 3; x < y; x++)
$display("H x = %0d", x);
end
initial begin
integer x, y;
x = 0;
for (y = 3, --x; x < y; x++)
$display("I x = %0d", x);
end
initial begin
integer x, y;
x = 0;
y = 2;
for (--y, ++y, y++, ++x, --x, --x; x < y; x++)
$display("J x = %0d", x);
end
endmodule
module top;
`define LOOP(ID, START) \
x = 0; \
for (x = START; x < 3; x = x + 1) \
$display(`"ID x = %0d`", x);
initial begin : blk
integer x;
`LOOP(A, 1)
`LOOP(B, 1)
`LOOP(C, 1)
`LOOP(D, 0)
`LOOP(E, 0)
`LOOP(F, 1)
`LOOP(G, 1)
`LOOP(H, -1)
`LOOP(I, -1)
`LOOP(J, -1)
end
endmodule
// pattern: auto_dim_int\.sv:3:15: Parse error: expected comma or end of declarations
module top;
integer x [] = 1;
endmodule
// pattern: block_start_1\.sv:4:9: Parse error: expected primary token or type
module top;
initial begin
,;
end
endmodule
// pattern: block_start_2\.sv:4:9: Parse error: expected primary token or type
module top;
initial begin
= 1;
end
endmodule
// pattern: block_start_3\.sv:4:9: Parse error: expected primary token or type
module top;
initial begin
P::Q = 1;
end
endmodule
// pattern: block_start_4\.sv:4:11: Parse error: unexpected statement token
module top;
initial begin
a , ;
end
endmodule
// pattern: decl_bare\.sv:3:5: Parse error: declaration missing type information
module top;
a;
endmodule
// pattern: decl_missing_comma\.sv:3:15: Parse error: expected comma or end of declarations
module top;
integer a b;
endmodule
// pattern: decl_trailing_comma\.sv:3:16: Parse error: unexpected token in declaration
module top;
integer a, ;
endmodule
// pattern: elab_task_stray_after_args\.sv:3:12: Parse error: unexpected token after elaboration system task
module top;
$info(), ;
endmodule
// pattern: elab_task_stray_before_args\.sv:3:11: Parse error: unexpected token after elaboration system task
module top;
$info , ();
endmodule
// pattern: elab_task_stray_no_args\.sv:3:11: Parse error: unexpected token after elaboration system task
module top;
$info , ;
endmodule
// pattern: for loop declaration missing initialization
// pattern: for_loop_decl_no_init\.sv:4:14: Parse error: for loop declaration of "x" is missing initialization
module top;
initial
for (integer x; x < 3; x = x + 1)
......
// pattern: for_loop_init_bare\.sv:3:19: Parse error: expected assignment operator
module top;
initial for (a,; 1; a++) ;
endmodule
// pattern: for_loop_init_delay\.sv:4:20: Parse error: unexpected timing modifier in for loop initialization
module top;
integer x;
initial for (x = #1 1; 1; x++) ;
endmodule
// pattern: for_loop_init_nblk\.sv:4:20: Parse error: unexpected non-blocking assignment in for loop initialization
module top;
integer x;
initial for (x <= 1; 1; x++) ;
endmodule
// pattern: for_loop_init_stray\.sv:3:22: Parse error: expected ',' or ';'
module top;
initial for (a++ b++; 1; a++) ;
endmodule
// pattern: instantiation_extra_comma\.sv:3:18: Parse error: expected instantiation before delimiter
// pattern: instantiation_extra_comma\.sv:3:18: Parse error: expected instantiation before ','
module top;
example a(), , b();
endmodule
// pattern: instantiation_missing_ports\.sv:3:14: Parse error: expected port connections before delimiter
// pattern: instantiation_missing_ports\.sv:3:14: Parse error: expected port connections before ','
module top;
example a, c();
endmodule
// pattern: instantiation_trailing_comma\.sv:3:16: Parse error: unexpected end of instantiation list
// pattern: instantiation_trailing_comma\.sv:3:18: Parse error: expected instantiation before ';'
module top;
example a(), ;
endmodule
// pattern: port_list_incomplete\.sv:2:17: Parse error: expected identifier
module top(input);
endmodule
// pattern: run_on_decl_item.sv:3:16: Parse error: unexpected comma-separated declarations
// pattern: run_on_decl_item\.sv:3:16: Parse error: unexpected token in declaration
module top;
integer x, byte y;
endmodule
// pattern: run_on_decl_stmt.sv:4:20: Parse error: unexpected comma-separated declarations
// pattern: run_on_decl_stmt\.sv:4:20: Parse error: unexpected token in declaration
module top;
initial begin
integer x, byte y;
......
module top;
$info;
$info("%b", 1);
$warning;
$warning("%b", 2);
$error;
$error("%b", 3);
$fatal;
$fatal("%b", 4);
endmodule
#!/bin/bash
addTest() {
test=$1
eval "test_$test() { assertConverts $test.sv; }"
suite_addTest test_$test
}
source ../lib/functions.sh
source ../lib/discover.sh
. shunit2
module top;
trireg (small) x;
trireg (medium) y;
trireg (large) z;
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment