2059 lines
68 KiB
Haskell
2059 lines
68 KiB
Haskell
{-
|
|
Tock: a compiler for parallel languages
|
|
Copyright (C) 2007, 2008 University of Kent
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation, either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-}
|
|
|
|
-- | Parse occam code into an AST.
|
|
module ParseOccam (parseOccamProgram) where
|
|
|
|
import Control.Monad (liftM, when)
|
|
import Control.Monad.State (MonadState, modify, get, put)
|
|
import Data.List
|
|
import qualified Data.Map as Map
|
|
import Data.Maybe
|
|
import Text.ParserCombinators.Parsec
|
|
|
|
import qualified AST as A
|
|
import CompState
|
|
import Errors
|
|
import EvalConstants
|
|
import EvalLiterals
|
|
import Intrinsics
|
|
import LexOccam
|
|
import Metadata
|
|
import ParseUtils
|
|
import Pass
|
|
import ShowCode
|
|
import Types
|
|
import Utils
|
|
|
|
--{{{ the parser monad
|
|
type OccParser = GenParser Token ([WarningReport], CompState)
|
|
|
|
instance CSMR (GenParser tok (a,CompState)) where
|
|
getCompState = getState >>* snd
|
|
|
|
-- We can expose only part of the state to make it look like we are only using
|
|
-- CompState:
|
|
instance MonadState CompState (GenParser tok (a,CompState)) where
|
|
get = getState >>* snd
|
|
put st = do (other, _) <- getState
|
|
setState (other, st)
|
|
|
|
-- The other part of the state is actually the built-up list of warnings:
|
|
instance Warn (GenParser tok ([WarningReport], b)) where
|
|
warnReport w = do (ws, other) <- getState
|
|
setState (ws ++ [w], other)
|
|
|
|
instance Die (GenParser tok st) where
|
|
dieReport (Just m, err) = fail $ packMeta m err
|
|
dieReport (Nothing, err) = fail err
|
|
--}}}
|
|
|
|
--{{{ matching rules for raw tokens
|
|
-- | Extract source position from a `Token`.
|
|
tokenPos :: Token -> SourcePos
|
|
tokenPos (m, _) = metaToSourcePos m
|
|
|
|
genToken :: (Token -> Maybe a) -> OccParser a
|
|
genToken test = token show tokenPos test
|
|
|
|
reserved :: String -> OccParser ()
|
|
reserved name = genToken test
|
|
where
|
|
test (_, TokReserved name')
|
|
= if name' == name then Just () else Nothing
|
|
test _ = Nothing
|
|
|
|
identifier :: OccParser String
|
|
identifier = genToken test
|
|
where
|
|
test (_, TokIdentifier s) = Just s
|
|
test _ = Nothing
|
|
|
|
plainToken :: TokenType -> OccParser ()
|
|
plainToken t = genToken test
|
|
where
|
|
test (_, t') = if t == t' then Just () else Nothing
|
|
--}}}
|
|
--{{{ symbols
|
|
sAmp, sAssign, sBang, sBar, sColon, sColons, sComma, sEq, sLeft, sLeftR,
|
|
sQuest, sRight, sRightR, sSemi
|
|
:: OccParser ()
|
|
|
|
sAmp = reserved "&"
|
|
sAssign = reserved ":="
|
|
sBang = reserved "!"
|
|
sBar = reserved "|"
|
|
sColon = reserved ":"
|
|
sColons = reserved "::"
|
|
sComma = reserved ","
|
|
sEq = reserved "="
|
|
sLeft = reserved "["
|
|
sLeftR = reserved "("
|
|
sQuest = reserved "?"
|
|
sRight = reserved "]"
|
|
sRightR = reserved ")"
|
|
sSemi = reserved ";"
|
|
--}}}
|
|
--{{{ keywords
|
|
sAFTER, sALT, sAND, sANY, sAT, sBITAND, sBITNOT, sBITOR, sBOOL, sBYTE,
|
|
sBYTESIN, sCASE, sCHAN, sDATA, sELSE, sFALSE, sFOR, sFROM, sFUNCTION, sIF,
|
|
sINLINE, sIN, sINITIAL, sINT, sINT16, sINT32, sINT64, sIS, sMINUS, sMOSTNEG,
|
|
sMOSTPOS, sNOT, sOF, sOFFSETOF, sOR, sPACKED, sPAR, sPLACE, sPLACED, sPLUS,
|
|
sPORT, sPRI, sPROC, sPROCESSOR, sPROTOCOL, sREAL32, sREAL64, sRECORD, sREM,
|
|
sRESHAPES, sRESULT, sRETYPES, sROUND, sSEQ, sSIZE, sSKIP, sSTOP, sTIMER,
|
|
sTIMES, sTRUE, sTRUNC, sTYPE, sVAL, sVALOF, sWHILE, sWORKSPACE, sVECSPACE
|
|
:: OccParser ()
|
|
|
|
sAFTER = reserved "AFTER"
|
|
sALT = reserved "ALT"
|
|
sAND = reserved "AND"
|
|
sANY = reserved "ANY"
|
|
sAT = reserved "AT"
|
|
sBITAND = reserved "BITAND"
|
|
sBITNOT = reserved "BITNOT"
|
|
sBITOR = reserved "BITOR"
|
|
sBOOL = reserved "BOOL"
|
|
sBYTE = reserved "BYTE"
|
|
sBYTESIN = reserved "BYTESIN"
|
|
sCASE = reserved "CASE"
|
|
sCHAN = reserved "CHAN"
|
|
sDATA = reserved "DATA"
|
|
sELSE = reserved "ELSE"
|
|
sFALSE = reserved "FALSE"
|
|
sFOR = reserved "FOR"
|
|
sFROM = reserved "FROM"
|
|
sFUNCTION = reserved "FUNCTION"
|
|
sIF = reserved "IF"
|
|
sINLINE = reserved "INLINE"
|
|
sIN = reserved "IN"
|
|
sINITIAL = reserved "INITIAL"
|
|
sINT = reserved "INT"
|
|
sINT16 = reserved "INT16"
|
|
sINT32 = reserved "INT32"
|
|
sINT64 = reserved "INT64"
|
|
sIS = reserved "IS"
|
|
sMINUS = reserved "MINUS"
|
|
sMOSTNEG = reserved "MOSTNEG"
|
|
sMOSTPOS = reserved "MOSTPOS"
|
|
sNOT = reserved "NOT"
|
|
sOF = reserved "OF"
|
|
sOFFSETOF = reserved "OFFSETOF"
|
|
sOR = reserved "OR"
|
|
sPACKED = reserved "PACKED"
|
|
sPAR = reserved "PAR"
|
|
sPLACE = reserved "PLACE"
|
|
sPLACED = reserved "PLACED"
|
|
sPLUS = reserved "PLUS"
|
|
sPORT = reserved "PORT"
|
|
sPRI = reserved "PRI"
|
|
sPROC = reserved "PROC"
|
|
sPROCESSOR = reserved "PROCESSOR"
|
|
sPROTOCOL = reserved "PROTOCOL"
|
|
sREAL32 = reserved "REAL32"
|
|
sREAL64 = reserved "REAL64"
|
|
sRECORD = reserved "RECORD"
|
|
sREM = reserved "REM"
|
|
sRESHAPES = reserved "RESHAPES"
|
|
sRESULT = reserved "RESULT"
|
|
sRETYPES = reserved "RETYPES"
|
|
sROUND = reserved "ROUND"
|
|
sSEQ = reserved "SEQ"
|
|
sSIZE = reserved "SIZE"
|
|
sSKIP = reserved "SKIP"
|
|
sSTOP = reserved "STOP"
|
|
sTIMER = reserved "TIMER"
|
|
sTIMES = reserved "TIMES"
|
|
sTRUE = reserved "TRUE"
|
|
sTRUNC = reserved "TRUNC"
|
|
sTYPE = reserved "TYPE"
|
|
sVAL = reserved "VAL"
|
|
sVALOF = reserved "VALOF"
|
|
sWHILE = reserved "WHILE"
|
|
sWORKSPACE = reserved "WORKSPACE"
|
|
sVECSPACE = reserved "VECSPACE"
|
|
--}}}
|
|
--{{{ markers inserted by the preprocessor
|
|
indent, outdent, eol :: OccParser ()
|
|
|
|
indent = do { plainToken Indent } <?> "indentation increase"
|
|
outdent = do { plainToken Outdent } <?> "indentation decrease"
|
|
eol = do { plainToken EndOfLine } <?> "end of line"
|
|
--}}}
|
|
|
|
--{{{ helper functions
|
|
md :: OccParser Meta
|
|
md
|
|
= do pos <- getPosition
|
|
return $ sourcePosToMeta pos
|
|
|
|
--{{{ try*
|
|
-- These functions let you try a sequence of productions and only retrieve the
|
|
-- results from some of them. In the function name, X represents a value
|
|
-- that'll be thrown away, and V one that'll be kept; you get back a tuple of
|
|
-- the values you wanted.
|
|
--
|
|
-- There isn't anything particularly unusual going on here; it's just a more
|
|
-- succinct way of writing a try (do { ... }) expression.
|
|
|
|
tryXX :: OccParser a -> OccParser b -> OccParser ()
|
|
tryXX a b = try (do { a; b; return () })
|
|
|
|
tryXV :: OccParser a -> OccParser b -> OccParser b
|
|
tryXV a b = try (do { a; b })
|
|
|
|
tryVX :: OccParser a -> OccParser b -> OccParser a
|
|
tryVX a b = try (do { av <- a; b; return av })
|
|
|
|
tryVV :: OccParser a -> OccParser b -> OccParser (a, b)
|
|
tryVV a b = try (do { av <- a; bv <- b; return (av, bv) })
|
|
|
|
tryXXV :: OccParser a -> OccParser b -> OccParser c -> OccParser c
|
|
tryXXV a b c = try (do { a; b; cv <- c; return cv })
|
|
|
|
tryXVX :: OccParser a -> OccParser b -> OccParser c -> OccParser b
|
|
tryXVX a b c = try (do { a; bv <- b; c; return bv })
|
|
|
|
tryXVV :: OccParser a -> OccParser b -> OccParser c -> OccParser (b, c)
|
|
tryXVV a b c = try (do { a; bv <- b; cv <- c; return (bv, cv) })
|
|
|
|
tryVXX :: OccParser a -> OccParser b -> OccParser c -> OccParser a
|
|
tryVXX a b c = try (do { av <- a; b; c; return av })
|
|
|
|
tryVXV :: OccParser a -> OccParser b -> OccParser c -> OccParser (a, c)
|
|
tryVXV a b c = try (do { av <- a; b; cv <- c; return (av, cv) })
|
|
|
|
tryVVX :: OccParser a -> OccParser b -> OccParser c -> OccParser (a, b)
|
|
tryVVX a b c = try (do { av <- a; bv <- b; c; return (av, bv) })
|
|
|
|
tryXVXV :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser (b, d)
|
|
tryXVXV a b c d = try (do { a; bv <- b; c; dv <- d; return (bv, dv) })
|
|
|
|
tryXVVX :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser (b, c)
|
|
tryXVVX a b c d = try (do { a; bv <- b; cv <- c; d; return (bv, cv) })
|
|
|
|
tryVXXV :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser (a, d)
|
|
tryVXXV a b c d = try (do { av <- a; b; c; dv <- d; return (av, dv) })
|
|
|
|
tryVVXV :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser (a, b, d)
|
|
tryVVXV a b c d = try (do { av <- a; bv <- b; c; dv <- d; return (av, bv, dv) })
|
|
|
|
tryVXVXX :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser e -> OccParser (a, c)
|
|
tryVXVXX a b c d e = try (do { av <- a; b; cv <- c; d; e; return (av, cv) })
|
|
--}}}
|
|
|
|
--{{{ subscripts
|
|
maybeSubscripted :: String -> OccParser a -> (Meta -> A.Subscript -> a -> a) -> (a -> OccParser A.Type) -> OccParser a
|
|
maybeSubscripted prodName inner subscripter typer
|
|
= do m <- md
|
|
v <- inner
|
|
t <- typer v
|
|
subs <- postSubscripts t
|
|
return $ foldl (\var sub -> subscripter m sub var) v subs
|
|
<?> prodName
|
|
|
|
postSubscripts :: A.Type -> OccParser [A.Subscript]
|
|
postSubscripts t
|
|
= (do sub <- postSubscript t
|
|
t' <- subscriptType sub t
|
|
rest <- postSubscripts t'
|
|
return $ sub : rest)
|
|
<|> return []
|
|
|
|
postSubscript :: A.Type -> OccParser A.Subscript
|
|
postSubscript t
|
|
= do m <- md
|
|
t' <- resolveUserType m t
|
|
case t' of
|
|
A.Record _ ->
|
|
do f <- tryXV sLeft fieldName
|
|
sRight
|
|
return $ A.SubscriptField m f
|
|
A.Array _ _ ->
|
|
do e <- tryXV sLeft intExpr
|
|
sRight
|
|
return $ A.Subscript m A.CheckBoth e
|
|
_ -> pzero
|
|
|
|
maybeSliced :: OccParser a -> (Meta -> A.Subscript -> a -> a) -> (a -> OccParser A.Type) -> OccParser a
|
|
maybeSliced inner subscripter typer
|
|
= do m <- md
|
|
|
|
(v, ff1) <- tryXVV sLeft inner fromOrFor
|
|
t <- typer v >>= underlyingType m
|
|
case t of
|
|
(A.Array _ _) -> return ()
|
|
_ -> dieP m $ "slice of non-array type " ++ showOccam t
|
|
|
|
e <- intExpr
|
|
sub <- case ff1 of
|
|
"FROM" ->
|
|
(do f <- tryXV sFOR intExpr
|
|
sRight
|
|
return $ A.SubscriptFromFor m e f)
|
|
<|>
|
|
(do sRight
|
|
return $ A.SubscriptFrom m e)
|
|
"FOR" ->
|
|
do sRight
|
|
return $ A.SubscriptFor m e
|
|
|
|
return $ subscripter m sub v
|
|
where
|
|
fromOrFor :: OccParser String
|
|
fromOrFor = (sFROM >> return "FROM") <|> (sFOR >> return "FOR")
|
|
--}}}
|
|
|
|
-- | Parse an optional indented list, where if it's not there we should issue a
|
|
-- warning. (This is for things that are legal in the occam spec, but are
|
|
-- almost certainly programmer errors.)
|
|
maybeIndentedList :: Meta -> String -> OccParser t -> OccParser [t]
|
|
maybeIndentedList m msg inner
|
|
= do try indent
|
|
vs <- many1 inner
|
|
outdent
|
|
return vs
|
|
<|> do addWarning m msg
|
|
return []
|
|
|
|
handleSpecs :: OccParser [A.Specification] -> OccParser a -> (Meta -> A.Specification -> a -> a) -> OccParser a
|
|
handleSpecs specs inner specMarker
|
|
= do m <- md
|
|
ss <- specs
|
|
ss' <- mapM scopeInSpec ss
|
|
v <- inner
|
|
mapM scopeOutSpec ss'
|
|
return $ foldl (\e s -> specMarker m s e) v ss'
|
|
|
|
-- | Run several different parsers with a separator between them.
|
|
-- If you give it [a, b, c] and s, it'll parse [a, s, b, s, c] then
|
|
-- give you back the results from [a, b, c].
|
|
intersperseP :: [OccParser a] -> OccParser b -> OccParser [a]
|
|
intersperseP [] _ = return []
|
|
intersperseP [f] _
|
|
= do a <- f
|
|
return [a]
|
|
intersperseP (f:fs) sep
|
|
= do a <- f
|
|
sep
|
|
as <- intersperseP fs sep
|
|
return $ a : as
|
|
|
|
-- | Find the type of a table literal given the types of its components.
|
|
-- This'll always return an Array; the inner type will either be the type of
|
|
-- the elements if they're all the same (in which case it's either an array
|
|
-- literal, or a record where all the fields are the same type), or Any if
|
|
-- they're not (i.e. if it's a record literal or an empty array).
|
|
tableType :: Meta -> [A.Type] -> OccParser A.Type
|
|
tableType m l = tableType' m (length l) l
|
|
where
|
|
tableType' m len [t] = return $ addDimensions [A.Dimension len] t
|
|
tableType' m len (t1 : rest@(t2 : _))
|
|
= if t1 == t2 then tableType' m len rest
|
|
else return $ addDimensions [A.Dimension len] A.Any
|
|
tableType' m len [] = return $ addDimensions [A.Dimension 0] A.Any
|
|
|
|
-- | Check that the second dimension can be used in a context where the first
|
|
-- is expected.
|
|
isValidDimension :: A.Dimension -> A.Dimension -> Bool
|
|
isValidDimension A.UnknownDimension A.UnknownDimension = True
|
|
isValidDimension A.UnknownDimension (A.Dimension _) = True
|
|
isValidDimension (A.Dimension n1) (A.Dimension n2) = n1 == n2
|
|
isValidDimension _ _ = False
|
|
|
|
-- | Check that the second second of dimensions can be used in a context where
|
|
-- the first is expected.
|
|
areValidDimensions :: [A.Dimension] -> [A.Dimension] -> Bool
|
|
areValidDimensions [] [] = True
|
|
areValidDimensions (d1:ds1) (d2:ds2)
|
|
= if isValidDimension d1 d2
|
|
then areValidDimensions ds1 ds2
|
|
else False
|
|
areValidDimensions _ _ = False
|
|
|
|
-- | Check that a type we've inferred matches the type we expected.
|
|
matchType :: Meta -> A.Type -> A.Type -> OccParser ()
|
|
matchType m et rt
|
|
= case (et, rt) of
|
|
((A.Array ds t), (A.Array ds' t')) ->
|
|
if areValidDimensions ds ds'
|
|
then matchType m t t'
|
|
else bad
|
|
_ -> if rt == et then return () else bad
|
|
where
|
|
bad :: OccParser ()
|
|
bad = dieP m $ "type mismatch (got " ++ showOccam rt ++ "; expected " ++ showOccam et ++ ")"
|
|
|
|
-- | Check that two lists of types match (for example, for parallel assignment).
|
|
matchTypes :: Meta -> [A.Type] -> [A.Type] -> OccParser ()
|
|
matchTypes m ets rts
|
|
= sequence_ [matchType m et rt | (et, rt) <- zip ets rts]
|
|
|
|
-- | Parse a production inside a particular type context.
|
|
inTypeContext :: Maybe A.Type -> OccParser a -> OccParser a
|
|
inTypeContext ctx body
|
|
= do pushTypeContext ctx
|
|
v <- body
|
|
popTypeContext
|
|
return v
|
|
|
|
-- | Parse a production with no particular type context (i.e. where we're
|
|
-- inside some bit of an expression that means we can't tell what the type is).
|
|
noTypeContext :: OccParser a -> OccParser a
|
|
noTypeContext = inTypeContext Nothing
|
|
--}}}
|
|
|
|
--{{{ name scoping
|
|
findName :: A.Name -> OccParser A.Name
|
|
findName thisN
|
|
= do st <- get
|
|
origN <- case lookup (A.nameName thisN) (csLocalNames st) of
|
|
Nothing -> dieP (A.nameMeta thisN) $ "name " ++ A.nameName thisN ++ " not defined"
|
|
Just n -> return n
|
|
if A.nameType thisN /= A.nameType origN
|
|
then dieP (A.nameMeta thisN) $ "expected " ++ show (A.nameType thisN) ++ " (" ++ A.nameName origN ++ " is " ++ show (A.nameType origN) ++ ")"
|
|
else return $ thisN { A.nameName = A.nameName origN }
|
|
|
|
makeUniqueName :: String -> OccParser String
|
|
makeUniqueName s
|
|
= do st <- get
|
|
put $ st { csNameCounter = csNameCounter st + 1 }
|
|
return $ s ++ "_u" ++ show (csNameCounter st)
|
|
|
|
findUnscopedName :: A.Name -> OccParser A.Name
|
|
findUnscopedName n@(A.Name m nt s)
|
|
= do st <- get
|
|
case Map.lookup s (csUnscopedNames st) of
|
|
Just s' -> return $ A.Name m nt s'
|
|
Nothing ->
|
|
do s' <- makeUniqueName s
|
|
modify (\st -> st { csUnscopedNames = Map.insert s s' (csUnscopedNames st) })
|
|
return $ A.Name m nt s'
|
|
|
|
scopeIn :: A.Name -> A.SpecType -> A.AbbrevMode -> OccParser A.Name
|
|
scopeIn n@(A.Name m nt s) t am
|
|
= do st <- getState
|
|
s' <- makeUniqueName s
|
|
let n' = n { A.nameName = s' }
|
|
let nd = A.NameDef {
|
|
A.ndMeta = m,
|
|
A.ndName = s',
|
|
A.ndOrigName = s,
|
|
A.ndNameType = A.nameType n',
|
|
A.ndType = t,
|
|
A.ndAbbrevMode = am,
|
|
A.ndPlacement = A.Unplaced
|
|
}
|
|
defineName n' nd
|
|
modify $ (\st -> st {
|
|
csLocalNames = (s, n') : (csLocalNames st)
|
|
})
|
|
return n'
|
|
|
|
scopeOut :: A.Name -> OccParser ()
|
|
scopeOut n@(A.Name m nt s)
|
|
= do st <- get
|
|
let lns' = case csLocalNames st of
|
|
(s, _):ns -> ns
|
|
otherwise -> dieInternal (Just m, "scopeOut trying to scope out the wrong name")
|
|
put $ st { csLocalNames = lns' }
|
|
|
|
scopeInRep :: A.Replicator -> OccParser A.Replicator
|
|
scopeInRep (A.For m n b c)
|
|
= do n' <- scopeIn n (A.Declaration m A.Int) A.ValAbbrev
|
|
return $ A.For m n' b c
|
|
|
|
scopeOutRep :: A.Replicator -> OccParser ()
|
|
scopeOutRep (A.For m n b c) = scopeOut n
|
|
|
|
scopeInSpec :: A.Specification -> OccParser A.Specification
|
|
scopeInSpec (A.Specification m n st)
|
|
= do n' <- scopeIn n st (abbrevModeOfSpec st)
|
|
return $ A.Specification m n' st
|
|
|
|
scopeOutSpec :: A.Specification -> OccParser ()
|
|
scopeOutSpec (A.Specification _ n _) = scopeOut n
|
|
|
|
scopeInFormal :: A.Formal -> OccParser A.Formal
|
|
scopeInFormal (A.Formal am t n)
|
|
= do n' <- scopeIn n (A.Declaration (A.nameMeta n) t) am
|
|
return (A.Formal am t n')
|
|
|
|
scopeInFormals :: [A.Formal] -> OccParser [A.Formal]
|
|
scopeInFormals fs = mapM scopeInFormal fs
|
|
|
|
scopeOutFormals :: [A.Formal] -> OccParser ()
|
|
scopeOutFormals fs = sequence_ [scopeOut n | (A.Formal am t n) <- fs]
|
|
|
|
--}}}
|
|
|
|
--{{{ grammar productions
|
|
-- These productions are (now rather loosely) based on the ordered syntax in
|
|
-- the occam2.1 manual.
|
|
--
|
|
-- Each production is allowed to consume the thing it's trying to match.
|
|
|
|
--{{{ names
|
|
anyName :: A.NameType -> OccParser A.Name
|
|
anyName nt
|
|
= do m <- md
|
|
s <- identifier
|
|
return $ A.Name m nt s
|
|
<?> show nt
|
|
|
|
name :: A.NameType -> OccParser A.Name
|
|
name nt
|
|
= do n <- anyName nt
|
|
findName n
|
|
|
|
newName :: A.NameType -> OccParser A.Name
|
|
newName nt = anyName nt
|
|
|
|
channelName, dataTypeName, functionName, portName, procName, protocolName,
|
|
recordName, timerName, variableName
|
|
:: OccParser A.Name
|
|
|
|
channelName = name A.ChannelName
|
|
dataTypeName = name A.DataTypeName
|
|
functionName = name A.FunctionName
|
|
portName = name A.PortName
|
|
procName = name A.ProcName
|
|
protocolName = name A.ProtocolName
|
|
recordName = name A.RecordName
|
|
timerName = name A.TimerName
|
|
variableName = name A.VariableName
|
|
|
|
newChannelName, newDataTypeName, newFunctionName, newPortName, newProcName, newProtocolName,
|
|
newRecordName, newTimerName, newVariableName
|
|
:: OccParser A.Name
|
|
|
|
newChannelName = newName A.ChannelName
|
|
newDataTypeName = newName A.DataTypeName
|
|
newFunctionName = newName A.FunctionName
|
|
newPortName = newName A.PortName
|
|
newProcName = newName A.ProcName
|
|
newProtocolName = newName A.ProtocolName
|
|
newRecordName = newName A.RecordName
|
|
newTimerName = newName A.TimerName
|
|
newVariableName = newName A.VariableName
|
|
|
|
-- | A name that isn't scoped.
|
|
-- This is for things like record fields: we don't need to track their scope
|
|
-- because they're only valid with the particular record they're defined in,
|
|
-- but we do need to add a unique suffix so that they don't collide with
|
|
-- keywords in the target language
|
|
unscopedName :: A.NameType -> OccParser A.Name
|
|
unscopedName nt
|
|
= do n <- anyName nt
|
|
findUnscopedName n
|
|
<?> show nt
|
|
|
|
fieldName, tagName, newFieldName, newTagName :: OccParser A.Name
|
|
|
|
fieldName = unscopedName A.FieldName
|
|
tagName = unscopedName A.TagName
|
|
newFieldName = unscopedName A.FieldName
|
|
newTagName = unscopedName A.TagName
|
|
--}}}
|
|
--{{{ types
|
|
-- | A sized array of a production.
|
|
arrayType :: OccParser A.Type -> OccParser A.Type
|
|
arrayType element
|
|
= do (s, t) <- tryXVXV sLeft constIntExpr sRight element
|
|
sVal <- evalIntExpression s
|
|
return $ addDimensions [A.Dimension sVal] t
|
|
|
|
-- | Either a sized or unsized array of a production.
|
|
specArrayType :: OccParser A.Type -> OccParser A.Type
|
|
specArrayType element
|
|
= arrayType element
|
|
<|> do t <- tryXXV sLeft sRight element
|
|
return $ addDimensions [A.UnknownDimension] t
|
|
|
|
dataType :: OccParser A.Type
|
|
dataType
|
|
= do { sBOOL; return A.Bool }
|
|
<|> do { sBYTE; return A.Byte }
|
|
<|> do { sINT; return A.Int }
|
|
<|> do { sINT16; return A.Int16 }
|
|
<|> do { sINT32; return A.Int32 }
|
|
<|> do { sINT64; return A.Int64 }
|
|
<|> do { sREAL32; return A.Real32 }
|
|
<|> do { sREAL64; return A.Real64 }
|
|
<|> arrayType dataType
|
|
<|> do { n <- try dataTypeName; return $ A.UserDataType n }
|
|
<|> do { n <- try recordName; return $ A.Record n }
|
|
<?> "data type"
|
|
|
|
channelType :: OccParser A.Type
|
|
channelType
|
|
= do { sCHAN; optional sOF; p <- protocol; return $ A.Chan A.DirUnknown A.ChanAttributes {A.caWritingShared = False, A.caReadingShared = False} p }
|
|
<|> arrayType channelType
|
|
<?> "channel type"
|
|
|
|
timerType :: OccParser A.Type
|
|
timerType
|
|
= do { sTIMER; return $ A.Timer }
|
|
<|> arrayType timerType
|
|
<?> "timer type"
|
|
|
|
portType :: OccParser A.Type
|
|
portType
|
|
= do { sPORT; sOF; p <- dataType; return $ A.Port p }
|
|
<|> arrayType portType
|
|
<?> "port type"
|
|
--}}}
|
|
--{{{ literals
|
|
--{{{ type utilities for literals
|
|
-- | Can a literal of type rawT be used as a value of type wantT?
|
|
isValidLiteralType :: Meta -> A.Type -> A.Type -> OccParser Bool
|
|
isValidLiteralType m rawT wantT
|
|
= do underT <- resolveUserType m wantT
|
|
case (rawT, underT) of
|
|
-- We don't yet know what type we want -- so assume it's OK for now.
|
|
(_, A.Any) -> return True
|
|
(A.Real32, _) -> return $ isRealType underT
|
|
(A.Int, _) -> return $ isIntegerType underT
|
|
(A.Byte, _) -> return $ isIntegerType underT
|
|
(A.Array (A.Dimension nf:_) _, A.Record _) ->
|
|
-- We can't be sure without looking at the literal itself,
|
|
-- so we need to do that below.
|
|
do fs <- recordFields m wantT
|
|
return $ nf == length fs
|
|
(A.Array (d1:ds1) t1, A.Array (d2:ds2) t2) ->
|
|
-- Check the outermost dimension is OK, then recurse.
|
|
-- We can't just look at all the dimensions because this
|
|
-- might be an array of a record type, or similar.
|
|
if isValidDimension d2 d1
|
|
then do rawT' <- trivialSubscriptType m rawT
|
|
underT' <- trivialSubscriptType m underT
|
|
isValidLiteralType m rawT' underT'
|
|
else return False
|
|
_ -> return $ rawT == wantT
|
|
|
|
-- | Apply dimensions from one type to another as far as possible.
|
|
-- This should only be used when you know the two types are compatible first
|
|
-- (i.e. they've passed isValidLiteralType).
|
|
applyDimensions :: A.Type -> A.Type -> A.Type
|
|
applyDimensions (A.Array ods _) (A.Array tds t) = A.Array (dims ods tds) t
|
|
where
|
|
dims :: [A.Dimension] -> [A.Dimension] -> [A.Dimension]
|
|
dims (d@(A.Dimension _):ods) (A.UnknownDimension:tds)
|
|
= d : dims ods tds
|
|
dims (_:ods) (d:tds)
|
|
= d : dims ods tds
|
|
dims _ ds = ds
|
|
applyDimensions _ t = t
|
|
|
|
-- | Convert a raw array element literal into its real form.
|
|
makeArrayElem :: A.Type -> A.ArrayElem -> OccParser A.ArrayElem
|
|
makeArrayElem t@(A.Array _ _) (A.ArrayElemArray aes)
|
|
= do elemT <- trivialSubscriptType (findMeta aes) t
|
|
liftM A.ArrayElemArray $ mapM (makeArrayElem elemT) aes
|
|
makeArrayElem _ (A.ArrayElemArray es)
|
|
= dieP (findMeta es) $ "unexpected nested array literal"
|
|
-- A nested array literal that's still of array type (i.e. it's not a
|
|
-- record inside the array) -- collapse it.
|
|
makeArrayElem t@(A.Array _ _) (A.ArrayElemExpr (A.Literal _ _ (A.ArrayLiteral m aes)))
|
|
= do elemT <- trivialSubscriptType m t
|
|
liftM A.ArrayElemArray $ mapM (makeArrayElem elemT) aes
|
|
makeArrayElem t (A.ArrayElemExpr e)
|
|
= liftM A.ArrayElemExpr $ makeLiteral e t
|
|
|
|
-- | Given a raw literal and the type that it should be, either produce a
|
|
-- literal of that type, or fail with an appropriate error if it's not a valid
|
|
-- value of that type.
|
|
makeLiteral :: A.Expression -> A.Type -> OccParser A.Expression
|
|
-- A literal.
|
|
makeLiteral x@(A.Literal m t lr) wantT
|
|
= do underT <- resolveUserType m wantT
|
|
|
|
typesOK <- isValidLiteralType m t wantT
|
|
when (not typesOK) $
|
|
dieP m $ "default type of literal (" ++ showOccam t ++ ") cannot be coerced to desired type (" ++ showOccam wantT ++ ")"
|
|
|
|
case (underT, lr) of
|
|
-- An array literal.
|
|
(A.Array _ _, A.ArrayLiteral ml aes) ->
|
|
do elemT <- trivialSubscriptType ml underT
|
|
aes' <- mapM (makeArrayElem elemT) aes
|
|
return $ A.Literal m (applyDimensions t wantT) (A.ArrayLiteral ml aes')
|
|
-- A record literal -- which we need to convert from the raw
|
|
-- representation.
|
|
(A.Record _, A.ArrayLiteral ml aes) ->
|
|
do fs <- recordFields m underT
|
|
es <- sequence [case ae of
|
|
A.ArrayElemExpr e -> makeLiteral e t
|
|
A.ArrayElemArray aes ->
|
|
makeLiteral (A.Literal m t $ A.ArrayLiteral ml aes) t
|
|
| ((_, t), ae) <- zip fs aes]
|
|
return $ A.Literal m wantT (A.RecordLiteral ml es)
|
|
-- Some other kind of literal (one of the trivial types).
|
|
_ -> return $ A.Literal m wantT lr
|
|
-- A subscript; figure out what the type of the thing being subscripted must be
|
|
-- and recurse.
|
|
makeLiteral (A.SubscriptedExpr m sub e) wantT
|
|
= do inWantT <- unsubscriptType sub wantT
|
|
e' <- makeLiteral e inWantT
|
|
return $ A.SubscriptedExpr m sub e'
|
|
-- Something that's not a literal (which we've found inside a table) -- just
|
|
-- check it's the right type.
|
|
makeLiteral e wantT
|
|
= do t <- typeOfExpression e
|
|
matchType (findMeta e) wantT t
|
|
return e
|
|
--}}}
|
|
|
|
typeDecorator :: OccParser (Maybe A.Type)
|
|
typeDecorator
|
|
= do sLeftR
|
|
t <- dataType
|
|
sRightR
|
|
return $ Just t
|
|
<|> return Nothing
|
|
<?> "literal type decorator"
|
|
|
|
literal :: OccParser A.Expression
|
|
literal
|
|
= do m <- md
|
|
(lr, t) <- untypedLiteral
|
|
dec <- typeDecorator
|
|
ctx <- getTypeContext
|
|
let lit = A.Literal m t lr
|
|
case (dec, ctx) of
|
|
(Just wantT, _) -> makeLiteral lit wantT
|
|
(_, Just wantT) -> makeLiteral lit wantT
|
|
_ -> return lit
|
|
<?> "literal"
|
|
|
|
untypedLiteral :: OccParser (A.LiteralRepr, A.Type)
|
|
untypedLiteral
|
|
= do { r <- real; return (r, A.Real32) }
|
|
<|> do { r <- integer; return (r, A.Int) }
|
|
<|> do { r <- byte; return (r, A.Byte) }
|
|
|
|
real :: OccParser A.LiteralRepr
|
|
real
|
|
= do m <- md
|
|
genToken (test m)
|
|
<?> "real literal"
|
|
where
|
|
test m (_, TokRealLiteral s) = Just $ A.RealLiteral m s
|
|
test _ _ = Nothing
|
|
|
|
integer :: OccParser A.LiteralRepr
|
|
integer
|
|
= do m <- md
|
|
genToken (test m)
|
|
<?> "integer literal"
|
|
where
|
|
test m (_, TokIntLiteral s) = Just $ A.IntLiteral m s
|
|
test m (_, TokHexLiteral s) = Just $ A.HexLiteral m (drop 1 s)
|
|
test _ _ = Nothing
|
|
|
|
byte :: OccParser A.LiteralRepr
|
|
byte
|
|
= do m <- md
|
|
genToken (test m)
|
|
<?> "byte literal"
|
|
where
|
|
test m (_, TokCharLiteral s)
|
|
= case splitStringLiteral m (chop 1 1 s) of [lr] -> Just lr
|
|
test _ _ = Nothing
|
|
|
|
-- | Parse a table -- an array literal which might be subscripted or sliced.
|
|
-- (The implication of this is that the type of the expression this parses
|
|
-- isn't necessarily an array type -- it might be something like
|
|
-- @[1, 2, 3][1]@.)
|
|
-- The expression this returns cannot be used directly; it doesn't have array
|
|
-- literals collapsed, and record literals are array literals of type []ANY.
|
|
table :: OccParser A.Expression
|
|
table
|
|
= do e <- maybeSubscripted "table" table' A.SubscriptedExpr typeOfExpression
|
|
rawT <- typeOfExpression e
|
|
ctx <- getTypeContext
|
|
case ctx of
|
|
Just wantT -> makeLiteral e wantT
|
|
_ -> return e
|
|
|
|
table' :: OccParser A.Expression
|
|
table'
|
|
= do m <- md
|
|
(lr, t) <- tableElems
|
|
dec <- typeDecorator
|
|
let lit = A.Literal m t lr
|
|
case dec of
|
|
Just wantT -> makeLiteral lit wantT
|
|
_ -> return lit
|
|
<|> maybeSliced table A.SubscriptedExpr typeOfExpression
|
|
<?> "table'"
|
|
|
|
tableElems :: OccParser (A.LiteralRepr, A.Type)
|
|
tableElems
|
|
= do (lr, dim) <- stringLiteral
|
|
return (lr, A.Array [dim] A.Byte)
|
|
<|> do m <- md
|
|
es <- tryXVX sLeft (noTypeContext $ sepBy1 expression sComma) sRight
|
|
-- Constant fold early, so that tables have a better chance of
|
|
-- becoming constants.
|
|
(es', _, _) <- liftM unzip3 $ sequence [constantFold e | e <- es]
|
|
ets <- mapM typeOfExpression es'
|
|
defT <- tableType m ets
|
|
return (A.ArrayLiteral m (map A.ArrayElemExpr es'), defT)
|
|
<?> "table elements"
|
|
|
|
stringLiteral :: OccParser (A.LiteralRepr, A.Dimension)
|
|
stringLiteral
|
|
= do m <- md
|
|
cs <- stringCont <|> stringLit
|
|
let aes = [A.ArrayElemExpr $ A.Literal m' A.Byte c
|
|
| c@(A.ByteLiteral m' _) <- cs]
|
|
return (A.ArrayLiteral m aes, A.Dimension $ length cs)
|
|
<?> "string literal"
|
|
where
|
|
stringCont :: OccParser [A.LiteralRepr]
|
|
stringCont
|
|
= do m <- md
|
|
s <- genToken test
|
|
rest <- stringCont <|> stringLit
|
|
return $ (splitStringLiteral m s) ++ rest
|
|
where
|
|
test (_, TokStringCont s) = Just (chop 1 2 s)
|
|
test _ = Nothing
|
|
|
|
stringLit :: OccParser [A.LiteralRepr]
|
|
stringLit
|
|
= do m <- md
|
|
s <- genToken test
|
|
return $ splitStringLiteral m s
|
|
where
|
|
test (_, TokStringLiteral s) = Just (chop 1 1 s)
|
|
test _ = Nothing
|
|
|
|
-- | Parse a string literal.
|
|
-- FIXME: This should decode the occam escapes.
|
|
splitStringLiteral :: Meta -> String -> [A.LiteralRepr]
|
|
splitStringLiteral m cs = ssl cs
|
|
where
|
|
ssl [] = []
|
|
ssl ('*':'#':a:b:cs)
|
|
= (A.ByteLiteral m ['*', '#', a, b]) : ssl cs
|
|
ssl ('*':'\n':cs)
|
|
= (A.ByteLiteral m $ tail $ dropWhile (/= '*') cs) : ssl cs
|
|
ssl ('*':c:cs)
|
|
= (A.ByteLiteral m ['*', c]) : ssl cs
|
|
ssl (c:cs)
|
|
= (A.ByteLiteral m [c]) : ssl cs
|
|
--}}}
|
|
--{{{ expressions
|
|
expressionList :: [A.Type] -> OccParser A.ExpressionList
|
|
expressionList types
|
|
= functionMulti types
|
|
<|> do m <- md
|
|
es <- intersperseP (map expressionOfType types) sComma
|
|
return $ A.ExpressionList m es
|
|
-- XXX: Value processes are not supported (because nobody uses them and they're hard to parse)
|
|
<?> "expression list"
|
|
|
|
expression :: OccParser A.Expression
|
|
expression
|
|
= do m <- md
|
|
o <- monadicOperator
|
|
v <- operand
|
|
return $ A.Monadic m o v
|
|
<|> do { m <- md; sMOSTPOS; t <- dataType; return $ A.MostPos m t }
|
|
<|> do { m <- md; sMOSTNEG; t <- dataType; return $ A.MostNeg m t }
|
|
<|> sizeExpr
|
|
<|> do m <- md
|
|
(l, o) <- tryVV operand dyadicOperator
|
|
t <- typeOfExpression l
|
|
r <- operandOfType t
|
|
return $ A.Dyadic m o l r
|
|
<|> do m <- md
|
|
(l, o) <- tryVV operand shiftOperator
|
|
r <- operandOfType A.Int
|
|
return $ A.Dyadic m o l r
|
|
<|> do m <- md
|
|
(l, o) <- tryVV (noTypeContext operand) comparisonOperator
|
|
t <- typeOfExpression l
|
|
r <- operandOfType t
|
|
return $ A.Dyadic m o l r
|
|
<|> do m <- md
|
|
(l, o) <- tryVV operand dyadicOperator
|
|
t <- typeOfExpression l
|
|
r <- operandOfType t
|
|
return $ A.Dyadic m o l r
|
|
<|> associativeOpExpression
|
|
<|> conversion
|
|
<|> operand
|
|
<?> "expression"
|
|
|
|
arrayConstructor :: OccParser A.Expression
|
|
arrayConstructor
|
|
= do m <- md
|
|
sLeft
|
|
r <- replicator
|
|
sBar
|
|
r' <- scopeInRep r
|
|
ctx <- getTypeContext
|
|
subCtx <- case ctx of
|
|
Just t@(A.Array _ _) -> trivialSubscriptType m t >>* Just
|
|
_ -> return Nothing
|
|
e <- inTypeContext subCtx expression
|
|
scopeOutRep r'
|
|
sRight
|
|
return $ A.ExprConstr m $ A.RepConstr m r' e
|
|
<?> "array constructor expression"
|
|
|
|
associativeOpExpression :: OccParser A.Expression
|
|
associativeOpExpression
|
|
= do m <- md
|
|
(l, o) <- tryVV operand associativeOperator
|
|
tl <- typeOfExpression l
|
|
r <- associativeOpExpression <|> operand
|
|
tr <- typeOfExpression r
|
|
matchType m tl tr
|
|
return $ A.Dyadic m o l r
|
|
<?> "associative operator expression"
|
|
|
|
sizeExpr :: OccParser A.Expression
|
|
sizeExpr
|
|
= do m <- md
|
|
sSIZE
|
|
do { t <- dataType; return $ A.SizeType m t }
|
|
<|> do v <- noTypeContext operand
|
|
return $ A.SizeExpr m v
|
|
<|> do v <- noTypeContext (channel <|> timer <|> port)
|
|
return $ A.SizeVariable m v
|
|
<?> "SIZE expression"
|
|
|
|
--{{{ type-constrained expressions
|
|
expressionOfType :: A.Type -> OccParser A.Expression
|
|
expressionOfType wantT
|
|
= do e <- inTypeContext (Just wantT) expression
|
|
t <- typeOfExpression e
|
|
matchType (findMeta e) wantT t
|
|
return e
|
|
|
|
intExpr :: OccParser A.Expression
|
|
intExpr = expressionOfType A.Int <?> "integer expression"
|
|
booleanExpr :: OccParser A.Expression
|
|
booleanExpr = expressionOfType A.Bool <?> "boolean expression"
|
|
|
|
constExprOfType :: A.Type -> OccParser A.Expression
|
|
constExprOfType wantT
|
|
= do e <- expressionOfType wantT
|
|
(e', isConst, (m,msg)) <- constantFold e
|
|
when (not isConst) $
|
|
dieReport (m,"expression is not constant (" ++ msg ++ ")")
|
|
return e'
|
|
|
|
constIntExpr :: OccParser A.Expression
|
|
constIntExpr = constExprOfType A.Int <?> "constant integer expression"
|
|
|
|
operandOfType :: A.Type -> OccParser A.Expression
|
|
operandOfType wantT
|
|
= do o <- inTypeContext (Just wantT) operand
|
|
t <- typeOfExpression o
|
|
matchType (findMeta o) wantT t
|
|
return o
|
|
--}}}
|
|
--{{{ functions
|
|
functionNameValued :: Bool -> OccParser A.Name
|
|
functionNameValued isMulti
|
|
= do n <- functionName
|
|
rts <- returnTypesOfFunction n
|
|
case (rts, isMulti) of
|
|
([_], False) -> return n
|
|
((_:_:_), True) -> return n
|
|
_ -> pzero
|
|
<?> "function name"
|
|
|
|
functionActuals :: [A.Formal] -> OccParser [A.Expression]
|
|
functionActuals fs
|
|
= do let actuals = [expressionOfType t <?> "actual for " ++ show n
|
|
| A.Formal _ t n <- fs]
|
|
es <- intersperseP actuals sComma
|
|
return es
|
|
|
|
functionSingle :: OccParser A.Expression
|
|
functionSingle
|
|
= do m <- md
|
|
n <- tryVX (functionNameValued False) sLeftR
|
|
A.Function _ _ _ fs _ <- specTypeOfName n
|
|
as <- functionActuals fs
|
|
sRightR
|
|
return $ A.FunctionCall m n as
|
|
<?> "single-valued function call"
|
|
|
|
functionMulti :: [A.Type] -> OccParser A.ExpressionList
|
|
functionMulti types
|
|
= do m <- md
|
|
n <- tryVX (functionNameValued True) sLeftR
|
|
A.Function _ _ _ fs _ <- specTypeOfName n
|
|
as <- functionActuals fs
|
|
sRightR
|
|
rts <- returnTypesOfFunction n
|
|
matchTypes m types rts
|
|
return $ A.FunctionCallList m n as
|
|
<?> "multi-valued function call"
|
|
--}}}
|
|
--{{{ intrinsic functions
|
|
intrinsicFunctionName :: Bool -> OccParser (String, [A.Type], [A.Formal])
|
|
intrinsicFunctionName isMulti
|
|
= do n <- anyName A.FunctionName
|
|
let s = A.nameName n
|
|
case (lookup s intrinsicFunctions, isMulti) of
|
|
(Nothing, _) -> pzero
|
|
(Just ([_], _), True) -> pzero
|
|
(Just ((_:_:_), _), False) -> pzero
|
|
(Just (rts, tns), _) ->
|
|
return (s, rts, [A.Formal A.ValAbbrev t (A.Name emptyMeta A.VariableName n)
|
|
| (t, n) <- tns])
|
|
<?> "intrinsic function name"
|
|
|
|
intrinsicFunctionSingle :: OccParser A.Expression
|
|
intrinsicFunctionSingle
|
|
= do m <- md
|
|
(s, _, fs) <- tryVX (intrinsicFunctionName False) sLeftR
|
|
as <- functionActuals fs
|
|
sRightR
|
|
return $ A.IntrinsicFunctionCall m s as
|
|
<?> "single-valued intrinsic function call"
|
|
|
|
-- No support for multi-valued intrinsic functions, because I don't think there
|
|
-- are likely to be any, and supporting them in the C backend is slightly
|
|
-- tricky.
|
|
--}}}
|
|
|
|
monadicOperator :: OccParser A.MonadicOp
|
|
monadicOperator
|
|
= do { reserved "-"; return A.MonadicSubtr }
|
|
<|> do { sMINUS; return A.MonadicMinus }
|
|
<|> do { reserved "~" <|> sBITNOT; return A.MonadicBitNot }
|
|
<|> do { sNOT; return A.MonadicNot }
|
|
<?> "monadic operator"
|
|
|
|
dyadicOperator :: OccParser A.DyadicOp
|
|
dyadicOperator
|
|
= do { reserved "+"; return A.Add }
|
|
<|> do { reserved "-"; return A.Subtr }
|
|
<|> do { reserved "*"; return A.Mul }
|
|
<|> do { reserved "/"; return A.Div }
|
|
<|> do { reserved "\\"; return A.Rem }
|
|
<|> do { sREM; return A.Rem }
|
|
<|> do { sMINUS; return A.Minus }
|
|
<|> do { reserved "/\\" <|> sBITAND; return A.BitAnd }
|
|
<|> do { reserved "\\/" <|> sBITOR; return A.BitOr }
|
|
<|> do { reserved "><"; return A.BitXor }
|
|
<?> "dyadic operator"
|
|
|
|
-- These always need an INT on their right-hand side.
|
|
shiftOperator :: OccParser A.DyadicOp
|
|
shiftOperator
|
|
= do { reserved "<<"; return A.LeftShift }
|
|
<|> do { reserved ">>"; return A.RightShift }
|
|
<?> "shift operator"
|
|
|
|
-- These always return a BOOL, so we have to deal with them specially for type
|
|
-- context.
|
|
comparisonOperator :: OccParser A.DyadicOp
|
|
comparisonOperator
|
|
= do { reserved "="; return A.Eq }
|
|
<|> do { reserved "<>"; return A.NotEq }
|
|
<|> do { reserved "<"; return A.Less }
|
|
<|> do { reserved ">"; return A.More }
|
|
<|> do { reserved "<="; return A.LessEq }
|
|
<|> do { reserved ">="; return A.MoreEq }
|
|
<|> do { sAFTER; return A.After }
|
|
<?> "comparison operator"
|
|
|
|
associativeOperator :: OccParser A.DyadicOp
|
|
associativeOperator
|
|
= do { sAND; return A.And }
|
|
<|> do { sOR; return A.Or }
|
|
<|> do { sPLUS; return A.Plus }
|
|
<|> do { sTIMES; return A.Times }
|
|
<?> "associative operator"
|
|
|
|
conversion :: OccParser A.Expression
|
|
conversion
|
|
= do m <- md
|
|
t <- dataType
|
|
baseT <- underlyingType m t
|
|
(c, o) <- conversionMode
|
|
ot <- typeOfExpression o
|
|
baseOT <- underlyingType m ot
|
|
c <- case (isPreciseConversion baseOT baseT, c) of
|
|
(False, A.DefaultConversion) ->
|
|
dieP m "imprecise conversion must specify ROUND or TRUNC"
|
|
(False, _) -> return c
|
|
(True, A.DefaultConversion) -> return c
|
|
(True, _) ->
|
|
do addWarning m "precise conversion specifies ROUND or TRUNC; ignored"
|
|
return A.DefaultConversion
|
|
return $ A.Conversion m c t o
|
|
<?> "conversion"
|
|
|
|
conversionMode :: OccParser (A.ConversionMode, A.Expression)
|
|
conversionMode
|
|
= do { sROUND; o <- noTypeContext operand; return (A.Round, o) }
|
|
<|> do { sTRUNC; o <- noTypeContext operand; return (A.Trunc, o) }
|
|
<|> do { o <- noTypeContext operand; return (A.DefaultConversion, o) }
|
|
<?> "conversion mode and operand"
|
|
--}}}
|
|
--{{{ operands
|
|
operand :: OccParser A.Expression
|
|
operand
|
|
= maybeSubscripted "operand" operand' A.SubscriptedExpr typeOfExpression
|
|
|
|
operand' :: OccParser A.Expression
|
|
operand'
|
|
= do { m <- md; v <- variable; return $ A.ExprVariable m v }
|
|
<|> literal
|
|
<|> do { sLeftR; e <- expression; sRightR; return e }
|
|
-- XXX value process
|
|
<|> functionSingle
|
|
<|> intrinsicFunctionSingle
|
|
<|> do m <- md
|
|
sBYTESIN
|
|
sLeftR
|
|
(try (do { o <- noTypeContext operand; sRightR; return $ A.BytesInExpr m o }))
|
|
<|> do { t <- dataType; sRightR; return $ A.BytesInType m t }
|
|
<|> do { m <- md; sOFFSETOF; sLeftR; t <- dataType; sComma; f <- fieldName; sRightR; return $ A.OffsetOf m t f }
|
|
<|> do { m <- md; sTRUE; return $ A.True m }
|
|
<|> do { m <- md; sFALSE; return $ A.False m }
|
|
<|> table
|
|
<|> arrayConstructor
|
|
<?> "operand"
|
|
--}}}
|
|
--{{{ variables, channels, timers, ports
|
|
variable :: OccParser A.Variable
|
|
variable
|
|
= maybeSubscripted "variable" variable' A.SubscriptedVariable typeOfVariable
|
|
|
|
variable' :: OccParser A.Variable
|
|
variable'
|
|
= do { m <- md; n <- try variableName; return $ A.Variable m n }
|
|
<|> maybeSliced variable A.SubscriptedVariable typeOfVariable
|
|
<?> "variable'"
|
|
|
|
variableOfType :: A.Type -> OccParser A.Variable
|
|
variableOfType wantT
|
|
= do v <- variable
|
|
t <- typeOfVariable v
|
|
matchType (findMeta v) wantT t
|
|
return v
|
|
|
|
channel :: OccParser A.Variable
|
|
channel
|
|
= maybeSubscripted "channel" channel' A.SubscriptedVariable typeOfVariable
|
|
<?> "channel"
|
|
|
|
channel' :: OccParser A.Variable
|
|
channel'
|
|
= do { m <- md; n <- try channelName; return $ A.Variable m n }
|
|
<|> maybeSliced channel A.SubscriptedVariable typeOfVariable
|
|
<?> "channel'"
|
|
|
|
channelOfType :: A.Type -> OccParser A.Variable
|
|
channelOfType wantT
|
|
= do c <- channel
|
|
t <- typeOfVariable c
|
|
matchType (findMeta c) wantT t
|
|
return c
|
|
|
|
timer :: OccParser A.Variable
|
|
timer
|
|
= maybeSubscripted "timer" timer' A.SubscriptedVariable typeOfVariable
|
|
<?> "timer"
|
|
|
|
timer' :: OccParser A.Variable
|
|
timer'
|
|
= do { m <- md; n <- try timerName; return $ A.Variable m n }
|
|
<|> maybeSliced timer A.SubscriptedVariable typeOfVariable
|
|
<?> "timer'"
|
|
|
|
port :: OccParser A.Variable
|
|
port
|
|
= maybeSubscripted "port" port' A.SubscriptedVariable typeOfVariable
|
|
<?> "port"
|
|
|
|
port' :: OccParser A.Variable
|
|
port'
|
|
= do { m <- md; n <- try portName; return $ A.Variable m n }
|
|
<|> maybeSliced port A.SubscriptedVariable typeOfVariable
|
|
<?> "port'"
|
|
|
|
portOfType :: A.Type -> OccParser A.Variable
|
|
portOfType wantT
|
|
= do p <- port
|
|
t <- typeOfVariable p
|
|
matchType (findMeta p) wantT t
|
|
return p
|
|
--}}}
|
|
--{{{ protocols
|
|
protocol :: OccParser A.Type
|
|
protocol
|
|
= do { n <- try protocolName ; return $ A.UserProtocol n }
|
|
<|> simpleProtocol
|
|
<?> "protocol"
|
|
|
|
simpleProtocol :: OccParser A.Type
|
|
simpleProtocol
|
|
= do { l <- tryVX dataType sColons; sLeft; sRight; r <- dataType; return $ A.Counted l r }
|
|
<|> dataType
|
|
<|> do { sANY; return $ A.Any }
|
|
<?> "simple protocol"
|
|
|
|
sequentialProtocol :: OccParser [A.Type]
|
|
sequentialProtocol
|
|
= do { l <- try $ sepBy1 simpleProtocol sSemi; return l }
|
|
<?> "sequential protocol"
|
|
|
|
taggedProtocol :: OccParser (A.Name, [A.Type])
|
|
taggedProtocol
|
|
= do { t <- tryVX newTagName eol; return (t, []) }
|
|
<|> do { t <- newTagName; sSemi; sp <- sequentialProtocol; eol; return (t, sp) }
|
|
<?> "tagged protocol"
|
|
--}}}
|
|
--{{{ replicators
|
|
replicator :: OccParser A.Replicator
|
|
replicator
|
|
= do m <- md
|
|
n <- tryVX newVariableName sEq
|
|
b <- intExpr
|
|
sFOR
|
|
c <- intExpr
|
|
return $ A.For m n b c
|
|
<?> "replicator"
|
|
--}}}
|
|
--{{{ specifications, declarations, allocations
|
|
allocation :: OccParser [A.Specification]
|
|
allocation
|
|
= do m <- md
|
|
sPLACE
|
|
n <- try variableName <|> try channelName <|> portName
|
|
p <- placement
|
|
sColon
|
|
eol
|
|
nd <- lookupNameOrError n $ dieP m ("Attempted to PLACE unknown variable: " ++ (show $ A.nameName n))
|
|
defineName n $ nd { A.ndPlacement = p }
|
|
return []
|
|
<?> "allocation"
|
|
|
|
placement :: OccParser A.Placement
|
|
placement
|
|
= do e <- tryXV (optional sAT) intExpr
|
|
return $ A.PlaceAt e
|
|
<|> do tryXX sIN sWORKSPACE
|
|
return $ A.PlaceInWorkspace
|
|
<|> do tryXX sIN sVECSPACE
|
|
return $ A.PlaceInVecspace
|
|
<?> "placement"
|
|
|
|
specification :: OccParser [A.Specification]
|
|
specification
|
|
= do { m <- md; (ns, d) <- declaration; return [A.Specification m n d | n <- ns] }
|
|
<|> do { a <- abbreviation; return [a] }
|
|
<|> do { d <- definition; return [d] }
|
|
<?> "specification"
|
|
|
|
declaration :: OccParser ([A.Name], A.SpecType)
|
|
declaration
|
|
= declOf dataType newVariableName
|
|
<|> declOf channelType newChannelName
|
|
<|> declOf timerType newTimerName
|
|
<|> declOf portType newPortName
|
|
<?> "declaration"
|
|
|
|
declOf :: OccParser A.Type -> OccParser A.Name -> OccParser ([A.Name], A.SpecType)
|
|
declOf spec newName
|
|
= do m <- md
|
|
(d, ns) <- tryVVX spec (sepBy1 newName sComma) sColon
|
|
eol
|
|
return (ns, A.Declaration m d)
|
|
|
|
abbreviation :: OccParser A.Specification
|
|
abbreviation
|
|
= valIsAbbrev
|
|
<|> initialIsAbbrev
|
|
<|> isAbbrev newVariableName variable
|
|
<|> isAbbrev newChannelName channel
|
|
<|> chanArrayAbbrev
|
|
<|> isAbbrev newTimerName timer
|
|
<|> isAbbrev newPortName port
|
|
<?> "abbreviation"
|
|
|
|
valIsAbbrev :: OccParser A.Specification
|
|
valIsAbbrev
|
|
= do m <- md
|
|
(n, t, e) <- do { n <- tryXVX sVAL newVariableName sIS; e <- expression; sColon; eol; t <- typeOfExpression e; return (n, t, e) }
|
|
<|> do { (s, n) <- tryXVVX sVAL dataSpecifier newVariableName sIS; e <- expressionOfType s; sColon; eol; return (n, s, e) }
|
|
-- Do constant folding early, so that we can use names defined this
|
|
-- way as constants elsewhere.
|
|
(e', _, _) <- constantFold e
|
|
return $ A.Specification m n $ A.IsExpr m A.ValAbbrev t e'
|
|
<?> "VAL IS abbreviation"
|
|
|
|
initialIsAbbrev :: OccParser A.Specification
|
|
initialIsAbbrev
|
|
= do m <- md
|
|
(t, n) <- tryXVVX sINITIAL dataSpecifier newVariableName sIS
|
|
e <- expressionOfType t
|
|
sColon
|
|
eol
|
|
return $ A.Specification m n $ A.IsExpr m A.Original t e
|
|
<?> "INITIAL IS abbreviation"
|
|
|
|
isAbbrev :: OccParser A.Name -> OccParser A.Variable -> OccParser A.Specification
|
|
isAbbrev newName oldVar
|
|
= do m <- md
|
|
(n, v) <- tryVXV newName sIS oldVar
|
|
sColon
|
|
eol
|
|
t <- typeOfVariable v
|
|
return $ A.Specification m n $ A.Is m A.Abbrev t v
|
|
<|> do m <- md
|
|
(s, n, v) <- tryVVXV specifier newName sIS oldVar
|
|
sColon
|
|
eol
|
|
t <- typeOfVariable v
|
|
matchType m s t
|
|
return $ A.Specification m n $ A.Is m A.Abbrev s v
|
|
<?> "IS abbreviation"
|
|
|
|
chanArrayAbbrev :: OccParser A.Specification
|
|
chanArrayAbbrev
|
|
= do m <- md
|
|
(n, cs) <- tryVXXV newChannelName sIS sLeft (sepBy1 channel sComma)
|
|
sRight
|
|
sColon
|
|
eol
|
|
ts <- mapM typeOfVariable cs
|
|
t <- tableType m ts
|
|
case t of
|
|
(A.Array _ (A.Chan {})) -> return ()
|
|
_ -> dieP m $ "types do not match in channel array abbreviation"
|
|
return $ A.Specification m n $ A.IsChannelArray m t cs
|
|
<|> do m <- md
|
|
(ct, s, n) <- try (do s <- channelSpecifier
|
|
n <- newChannelName
|
|
sIS
|
|
sLeft
|
|
ct <- trivialSubscriptType m s
|
|
case ct of
|
|
A.Chan {} -> return (ct, s, n)
|
|
_ -> pzero)
|
|
cs <- sepBy1 (channelOfType ct) sComma
|
|
sRight
|
|
sColon
|
|
eol
|
|
return $ A.Specification m n $ A.IsChannelArray m s cs
|
|
<?> "channel array abbreviation"
|
|
|
|
specMode :: OccParser () -> OccParser A.SpecMode
|
|
specMode keyword
|
|
= do tryXX sINLINE keyword
|
|
return A.InlineSpec
|
|
<|> do keyword
|
|
return A.PlainSpec
|
|
<?> "specification mode"
|
|
|
|
definition :: OccParser A.Specification
|
|
definition
|
|
= do m <- md
|
|
sDATA
|
|
sTYPE
|
|
do { n <- tryVX newDataTypeName sIS; t <- dataType; sColon; eol; return $ A.Specification m n (A.DataType m t) }
|
|
<|> do { n <- newRecordName; eol; indent; rec <- structuredType; outdent; sColon; eol; return $ A.Specification m n rec }
|
|
<|> do m <- md
|
|
sPROTOCOL
|
|
n <- newProtocolName
|
|
do { sIS; p <- sequentialProtocol; sColon; eol; return $ A.Specification m n $ A.Protocol m p }
|
|
<|> do { eol; indent; sCASE; eol; ps <- maybeIndentedList m "empty CASE protocol" taggedProtocol; outdent; sColon; eol; return $ A.Specification m n $ A.ProtocolCase m ps }
|
|
<|> do m <- md
|
|
sm <- specMode sPROC
|
|
n <- newProcName
|
|
fs <- formalList
|
|
eol
|
|
indent
|
|
fs' <- scopeInFormals fs
|
|
p <- process
|
|
scopeOutFormals fs'
|
|
outdent
|
|
sColon
|
|
eol
|
|
return $ A.Specification m n $ A.Proc m sm fs' p
|
|
<|> do m <- md
|
|
(rs, sm) <- tryVV (sepBy1 dataType sComma) (specMode sFUNCTION)
|
|
n <- newFunctionName
|
|
fs <- formalList
|
|
do { sIS; fs' <- scopeInFormals fs; el <- expressionList rs; scopeOutFormals fs'; sColon; eol; return $ A.Specification m n $ A.Function m sm rs fs' (Left $ A.Only m el) }
|
|
<|> do { eol; indent; fs' <- scopeInFormals fs; vp <- valueProcess rs; scopeOutFormals fs'; outdent; sColon; eol; return $ A.Specification m n $ A.Function m sm rs fs' (Left vp) }
|
|
<|> retypesAbbrev
|
|
<?> "definition"
|
|
|
|
retypesReshapes :: OccParser ()
|
|
retypesReshapes
|
|
= sRETYPES <|> sRESHAPES
|
|
|
|
retypesAbbrev :: OccParser A.Specification
|
|
retypesAbbrev
|
|
= do m <- md
|
|
(s, n) <- tryVVX dataSpecifier newVariableName retypesReshapes
|
|
v <- variable
|
|
sColon
|
|
eol
|
|
origT <- typeOfVariable v
|
|
checkRetypes m origT s
|
|
return $ A.Specification m n $ A.Retypes m A.Abbrev s v
|
|
<|> do m <- md
|
|
(s, n) <- tryVVX channelSpecifier newChannelName retypesReshapes
|
|
c <- channel
|
|
sColon
|
|
eol
|
|
origT <- typeOfVariable c
|
|
checkRetypes m origT s
|
|
return $ A.Specification m n $ A.Retypes m A.Abbrev s c
|
|
<|> do m <- md
|
|
(s, n) <- tryXVVX sVAL dataSpecifier newVariableName retypesReshapes
|
|
e <- expression
|
|
sColon
|
|
eol
|
|
origT <- typeOfExpression e
|
|
checkRetypes m origT s
|
|
return $ A.Specification m n $ A.RetypesExpr m A.ValAbbrev s e
|
|
<?> "RETYPES/RESHAPES abbreviation"
|
|
|
|
-- | Check that a RETYPES\/RESHAPES is safe.
|
|
checkRetypes :: Meta -> A.Type -> A.Type -> OccParser ()
|
|
-- Retyping channels is always "safe".
|
|
checkRetypes _ (A.Chan {}) (A.Chan {}) = return ()
|
|
checkRetypes m fromT toT
|
|
= do bf <- bytesInType fromT
|
|
bt <- bytesInType toT
|
|
case (bf, bt) of
|
|
(BIJust a, BIJust b) ->
|
|
when (a /= b) $ dieP m "size mismatch in RETYPES"
|
|
(BIJust a, BIOneFree b _) ->
|
|
when (not ((b <= a) && (a `mod` b == 0))) $ dieP m "size mismatch in RETYPES"
|
|
(_, BIManyFree) ->
|
|
dieP m "multiple free dimensions in RETYPES/RESHAPES type"
|
|
-- Otherwise we have to do a runtime check.
|
|
_ -> return ()
|
|
|
|
dataSpecifier :: OccParser A.Type
|
|
dataSpecifier
|
|
= dataType
|
|
<|> specArrayType dataSpecifier
|
|
<?> "data specifier"
|
|
|
|
channelSpecifier :: OccParser A.Type
|
|
channelSpecifier
|
|
= channelType
|
|
<|> specArrayType channelSpecifier
|
|
<?> "channel specifier"
|
|
|
|
timerSpecifier :: OccParser A.Type
|
|
timerSpecifier
|
|
= timerType
|
|
<|> specArrayType timerSpecifier
|
|
<?> "timer specifier"
|
|
|
|
portSpecifier :: OccParser A.Type
|
|
portSpecifier
|
|
= portType
|
|
<|> specArrayType portSpecifier
|
|
<?> "port specifier"
|
|
|
|
specifier :: OccParser A.Type
|
|
specifier
|
|
= dataType
|
|
<|> channelType
|
|
<|> timerType
|
|
<|> portType
|
|
<|> specArrayType specifier
|
|
<?> "specifier"
|
|
|
|
--{{{ PROCs and FUNCTIONs
|
|
formalList :: OccParser [A.Formal]
|
|
formalList
|
|
= do m <- md
|
|
sLeftR
|
|
fs <- option [] formalArgSet
|
|
sRightR
|
|
return fs
|
|
<?> "formal list"
|
|
|
|
formalItem :: OccParser (A.AbbrevMode, A.Type) -> OccParser A.Name -> OccParser [A.Formal]
|
|
formalItem spec name
|
|
= do (am, t) <- spec
|
|
names am t
|
|
where
|
|
names :: A.AbbrevMode -> A.Type -> OccParser [A.Formal]
|
|
names am t
|
|
= do n <- name
|
|
fs <- tail am t
|
|
return $ (A.Formal am t n) : fs
|
|
|
|
tail :: A.AbbrevMode -> A.Type -> OccParser [A.Formal]
|
|
tail am t
|
|
= do sComma
|
|
-- We must try formalArgSet first here, so that we don't
|
|
-- accidentally parse a DATA TYPE name thinking it's a formal
|
|
-- name.
|
|
formalArgSet <|> names am t
|
|
<|> return []
|
|
|
|
-- | Parse a set of formal arguments.
|
|
formalArgSet :: OccParser [A.Formal]
|
|
formalArgSet
|
|
= formalItem formalVariableType newVariableName
|
|
<|> formalItem (aa channelSpecifier) newChannelName
|
|
<|> formalItem (aa timerSpecifier) newTimerName
|
|
<|> formalItem (aa portSpecifier) newPortName
|
|
where
|
|
aa :: OccParser A.Type -> OccParser (A.AbbrevMode, A.Type)
|
|
aa = liftM (\t -> (A.Abbrev, t))
|
|
|
|
formalVariableType :: OccParser (A.AbbrevMode, A.Type)
|
|
formalVariableType
|
|
= do sVAL
|
|
s <- dataSpecifier
|
|
return (A.ValAbbrev, s)
|
|
<|> do s <- dataSpecifier
|
|
return (A.Abbrev, s)
|
|
<?> "formal variable type"
|
|
|
|
valueProcess :: [A.Type] -> OccParser (A.Structured A.ExpressionList)
|
|
valueProcess rs
|
|
= do m <- md
|
|
sVALOF
|
|
eol
|
|
indent
|
|
p <- process
|
|
sRESULT
|
|
el <- expressionList rs
|
|
eol
|
|
outdent
|
|
return $ A.ProcThen m p (A.Only m el)
|
|
<|> handleSpecs specification (valueProcess rs) A.Spec
|
|
<?> "value process"
|
|
--}}}
|
|
--{{{ RECORDs
|
|
structuredType :: OccParser A.SpecType
|
|
structuredType
|
|
= do m <- md
|
|
isPacked <- recordKeyword
|
|
eol
|
|
indent
|
|
fs <- many1 structuredTypeField
|
|
outdent
|
|
return $ A.RecordType m isPacked (concat fs)
|
|
<?> "structured type"
|
|
|
|
recordKeyword :: OccParser Bool
|
|
recordKeyword
|
|
= do { sPACKED; sRECORD; return True }
|
|
<|> do { sRECORD; return False }
|
|
|
|
structuredTypeField :: OccParser [(A.Name, A.Type)]
|
|
structuredTypeField
|
|
= do t <- dataType
|
|
fs <- sepBy1 newFieldName sComma
|
|
sColon
|
|
eol
|
|
return [(f, t) | f <- fs]
|
|
<?> "structured type field"
|
|
--}}}
|
|
--}}}
|
|
--{{{ processes
|
|
process :: OccParser A.Process
|
|
process
|
|
= assignment
|
|
<|> caseInput
|
|
<|> inputProcess
|
|
<|> output
|
|
<|> do { m <- md; sSKIP; eol; return $ A.Skip m }
|
|
<|> do { m <- md; sSTOP; eol; return $ A.Stop m }
|
|
<|> seqProcess
|
|
<|> ifProcess
|
|
<|> caseProcess
|
|
<|> whileProcess
|
|
<|> parallel
|
|
<|> altProcess
|
|
<|> procInstance
|
|
<|> intrinsicProc
|
|
<|> handleSpecs (allocation <|> specification) process
|
|
(\m s p -> A.Seq m (A.Spec m s (A.Only m p)))
|
|
<?> "process"
|
|
|
|
--{{{ assignment (:=)
|
|
assignment :: OccParser A.Process
|
|
assignment
|
|
= do m <- md
|
|
vs <- tryVX (sepBy1 variable sComma) sAssign
|
|
-- We ignore dimensions here because we do the check at runtime.
|
|
ts <- sequence [liftM removeFixedDimensions $ typeOfVariable v | v <- vs]
|
|
es <- expressionList ts
|
|
eol
|
|
return $ A.Assign m vs es
|
|
<?> "assignment"
|
|
--}}}
|
|
--{{{ input (?)
|
|
inputProcess :: OccParser A.Process
|
|
inputProcess
|
|
= do m <- md
|
|
(c, i) <- input
|
|
return $ A.Input m c i
|
|
<?> "input process"
|
|
|
|
input :: OccParser (A.Variable, A.InputMode)
|
|
input
|
|
= channelInput
|
|
<|> timerInput
|
|
<|> do m <- md
|
|
p <- tryVX port sQuest
|
|
A.Port t <- typeOfVariable p
|
|
v <- variableOfType t
|
|
eol
|
|
return (p, A.InputSimple m [A.InVariable m v])
|
|
<?> "input"
|
|
|
|
channelInput :: OccParser (A.Variable, A.InputMode)
|
|
channelInput
|
|
= do m <- md
|
|
c <- tryVX channel sQuest
|
|
pis <- protocolItems c
|
|
case pis of
|
|
Left ts ->
|
|
do is <- intersperseP (map inputItem ts) sSemi
|
|
eol
|
|
return (c, A.InputSimple m is)
|
|
Right nts ->
|
|
do sCASE
|
|
tl <- taggedList nts
|
|
eol
|
|
return (c, A.InputCase m (A.Only m (tl (A.Skip m))))
|
|
<?> "channel input"
|
|
|
|
timerInput :: OccParser (A.Variable, A.InputMode)
|
|
timerInput
|
|
= do m <- md
|
|
c <- tryVX timer sQuest
|
|
do { v <- variableOfType A.Int; eol; return (c, A.InputTimerRead m (A.InVariable m v)) }
|
|
<|> do { sAFTER; e <- intExpr; eol; return (c, A.InputTimerAfter m e) }
|
|
<?> "timer input"
|
|
|
|
taggedList :: [(A.Name, [A.Type])] -> OccParser (A.Process -> A.Variant)
|
|
taggedList nts
|
|
= do m <- md
|
|
tag <- tagName
|
|
ts <- checkJust (Just m, "unknown tag in protocol") $ lookup tag nts
|
|
is <- sequence [sSemi >> inputItem t | t <- ts]
|
|
return $ A.Variant m tag is
|
|
<?> "tagged list"
|
|
|
|
inputItem :: A.Type -> OccParser A.InputItem
|
|
inputItem t
|
|
= case t of
|
|
(A.Counted ct it) ->
|
|
do m <- md
|
|
v <- variableOfType ct
|
|
sColons
|
|
w <- variableOfType (addDimensions [A.UnknownDimension] it)
|
|
return $ A.InCounted m v w
|
|
A.Any ->
|
|
do m <- md
|
|
v <- variable
|
|
return $ A.InVariable m v
|
|
_ ->
|
|
do m <- md
|
|
v <- variableOfType t
|
|
return $ A.InVariable m v
|
|
<?> "input item"
|
|
--}}}
|
|
--{{{ variant input (? CASE)
|
|
caseInputItems :: A.Variable -> OccParser [(A.Name, [A.Type])]
|
|
caseInputItems c
|
|
= do pis <- protocolItems c
|
|
case pis of
|
|
Left _ -> dieP (findMeta c) "CASE input on channel of non-variant protocol"
|
|
Right nts -> return nts
|
|
|
|
caseInput :: OccParser A.Process
|
|
caseInput
|
|
= do m <- md
|
|
c <- tryVX channel (do {sQuest; sCASE; eol})
|
|
nts <- caseInputItems c
|
|
vs <- maybeIndentedList m "empty ? CASE" (variant nts)
|
|
return $ A.Input m c (A.InputCase m (A.Several m vs))
|
|
<?> "case input"
|
|
|
|
variant :: [(A.Name, [A.Type])] -> OccParser (A.Structured A.Variant)
|
|
variant nts
|
|
= do m <- md
|
|
tl <- taggedList nts
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.Only m (tl p)
|
|
<|> handleSpecs specification (variant nts) A.Spec
|
|
<?> "variant"
|
|
--}}}
|
|
--{{{ output (!)
|
|
output :: OccParser A.Process
|
|
output
|
|
= channelOutput
|
|
<|> do m <- md
|
|
p <- tryVX port sBang
|
|
A.Port t <- typeOfVariable p
|
|
e <- expressionOfType t
|
|
eol
|
|
return $ A.Output m p [A.OutExpression m e]
|
|
<?> "output"
|
|
|
|
channelOutput :: OccParser A.Process
|
|
channelOutput
|
|
= do m <- md
|
|
c <- tryVX channel sBang
|
|
-- This is an ambiguity in the occam grammar; you can't tell in "a ! b"
|
|
-- whether b is a variable or a tag, without knowing the type of a.
|
|
pis <- protocolItems c
|
|
case pis of
|
|
Left ts ->
|
|
do os <- intersperseP (map outputItem ts) sSemi
|
|
eol
|
|
return $ A.Output m c os
|
|
Right nts ->
|
|
do tag <- tagName
|
|
ts <- checkJust (Just m, "unknown tag in protocol") $ lookup tag nts
|
|
os <- sequence [sSemi >> outputItem t | t <- ts]
|
|
eol
|
|
return $ A.OutputCase m c tag os
|
|
<?> "channel output"
|
|
|
|
outputItem :: A.Type -> OccParser A.OutputItem
|
|
outputItem t
|
|
= case t of
|
|
(A.Counted ct it) ->
|
|
do m <- md
|
|
a <- expressionOfType ct
|
|
sColons
|
|
b <- expressionOfType (addDimensions [A.UnknownDimension] it)
|
|
return $ A.OutCounted m a b
|
|
A.Any ->
|
|
do m <- md
|
|
e <- expression
|
|
t <- typeOfExpression e
|
|
return $ A.OutExpression m e
|
|
_ ->
|
|
do m <- md
|
|
e <- expressionOfType t
|
|
return $ A.OutExpression m e
|
|
<?> "output item"
|
|
--}}}
|
|
--{{{ SEQ
|
|
seqProcess :: OccParser A.Process
|
|
seqProcess
|
|
= do m <- md
|
|
sSEQ
|
|
do { eol; ps <- maybeIndentedList m "empty SEQ" process; return $ A.Seq m (A.Several m (map (A.Only m) ps)) }
|
|
<|> do { r <- replicator; eol; indent; r' <- scopeInRep r; p <- process; scopeOutRep r'; outdent; return $ A.Seq m (A.Rep m r' (A.Only m p)) }
|
|
<?> "SEQ process"
|
|
--}}}
|
|
--{{{ IF
|
|
ifProcess :: OccParser A.Process
|
|
ifProcess
|
|
= do m <- md
|
|
c <- conditional
|
|
return $ A.If m c
|
|
<?> "IF process"
|
|
|
|
conditional :: OccParser (A.Structured A.Choice)
|
|
conditional
|
|
= do m <- md
|
|
sIF
|
|
do { eol; cs <- maybeIndentedList m "empty IF" ifChoice; return $ A.Several m cs }
|
|
<|> do { r <- replicator; eol; indent; r' <- scopeInRep r; c <- ifChoice; scopeOutRep r'; outdent; return $ A.Rep m r' c }
|
|
<?> "conditional"
|
|
|
|
ifChoice :: OccParser (A.Structured A.Choice)
|
|
ifChoice
|
|
= guardedChoice
|
|
<|> conditional
|
|
<|> handleSpecs specification ifChoice A.Spec
|
|
<?> "choice"
|
|
|
|
guardedChoice :: OccParser (A.Structured A.Choice)
|
|
guardedChoice
|
|
= do m <- md
|
|
b <- booleanExpr
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.Only m (A.Choice m b p)
|
|
<?> "guarded choice"
|
|
--}}}
|
|
--{{{ CASE
|
|
caseProcess :: OccParser A.Process
|
|
caseProcess
|
|
= do m <- md
|
|
sCASE
|
|
sel <- expression
|
|
t <- typeOfExpression sel
|
|
t' <- underlyingType m t
|
|
when (not $ isCaseableType t') $ dieP m "case selector has non-CASEable type"
|
|
eol
|
|
os <- maybeIndentedList m "empty CASE" (caseOption t)
|
|
return $ A.Case m sel (A.Several m os)
|
|
<?> "CASE process"
|
|
|
|
caseOption :: A.Type -> OccParser (A.Structured A.Option)
|
|
caseOption t
|
|
= do m <- md
|
|
ces <- tryVX (sepBy (constExprOfType t) sComma) eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.Only m (A.Option m ces p)
|
|
<|> do m <- md
|
|
sELSE
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.Only m (A.Else m p)
|
|
<|> handleSpecs specification (caseOption t) A.Spec
|
|
<?> "option"
|
|
--}}}
|
|
--{{{ WHILE
|
|
whileProcess :: OccParser A.Process
|
|
whileProcess
|
|
= do m <- md
|
|
sWHILE
|
|
b <- booleanExpr
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.While m b p
|
|
<?> "WHILE process"
|
|
--}}}
|
|
--{{{ PAR
|
|
parallel :: OccParser A.Process
|
|
parallel
|
|
= do m <- md
|
|
isPri <- parKeyword
|
|
do { eol; ps <- maybeIndentedList m "empty PAR" process; return $ A.Par m isPri (A.Several m (map (A.Only m) ps)) }
|
|
<|> do { r <- replicator; eol; indent; r' <- scopeInRep r; p <- process; scopeOutRep r'; outdent; return $ A.Par m isPri (A.Rep m r' (A.Only m p)) }
|
|
<|> processor
|
|
<?> "PAR process"
|
|
|
|
parKeyword :: OccParser A.ParMode
|
|
parKeyword
|
|
= do { sPAR; return A.PlainPar }
|
|
<|> do { tryXX sPRI sPAR; return A.PriPar }
|
|
<|> do { tryXX sPLACED sPAR; return A.PlacedPar }
|
|
|
|
-- XXX PROCESSOR as a process isn't really legal, surely?
|
|
processor :: OccParser A.Process
|
|
processor
|
|
= do m <- md
|
|
sPROCESSOR
|
|
e <- intExpr
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.Processor m e p
|
|
<?> "PLACED PAR process"
|
|
--}}}
|
|
--{{{ ALT
|
|
altProcess :: OccParser A.Process
|
|
altProcess
|
|
= do m <- md
|
|
(isPri, a) <- alternation
|
|
return $ A.Alt m isPri a
|
|
<?> "ALT process"
|
|
|
|
alternation :: OccParser (Bool, A.Structured A.Alternative)
|
|
alternation
|
|
= do m <- md
|
|
isPri <- altKeyword
|
|
do { eol; as <- maybeIndentedList m "empty ALT" alternative; return (isPri, A.Several m as) }
|
|
<|> do { r <- replicator; eol; indent; r' <- scopeInRep r; a <- alternative; scopeOutRep r'; outdent; return (isPri, A.Rep m r' a) }
|
|
<?> "alternation"
|
|
|
|
altKeyword :: OccParser Bool
|
|
altKeyword
|
|
= do { sALT; return False }
|
|
<|> do { tryXX sPRI sALT; return True }
|
|
|
|
-- The reason the CASE guards end up here is because they have to be handled
|
|
-- specially: you can't tell until parsing the guts of the CASE what the processes
|
|
-- are.
|
|
alternative :: OccParser (A.Structured A.Alternative)
|
|
alternative
|
|
-- FIXME: Check we don't have PRI ALT inside ALT.
|
|
= do (isPri, a) <- alternation
|
|
return a
|
|
-- These are special cases to deal with c ? CASE inside ALTs -- the normal
|
|
-- guards are below.
|
|
<|> do m <- md
|
|
(b, c) <- tryVXVXX booleanExpr sAmp channel sQuest (sCASE >> eol)
|
|
nts <- caseInputItems c
|
|
vs <- maybeIndentedList m "empty ? CASE" (variant nts)
|
|
return $ A.Only m (A.AlternativeCond m b c (A.InputCase m $ A.Several m vs) (A.Skip m))
|
|
<|> do m <- md
|
|
c <- tryVXX channel sQuest (sCASE >> eol)
|
|
nts <- caseInputItems c
|
|
vs <- maybeIndentedList m "empty ? CASE" (variant nts)
|
|
return $ A.Only m (A.Alternative m c (A.InputCase m $ A.Several m vs) (A.Skip m))
|
|
<|> guardedAlternative
|
|
<|> handleSpecs specification alternative A.Spec
|
|
<?> "alternative"
|
|
|
|
guardedAlternative :: OccParser (A.Structured A.Alternative)
|
|
guardedAlternative
|
|
= do m <- md
|
|
makeAlt <- guard
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.Only m (makeAlt p)
|
|
<?> "guarded alternative"
|
|
|
|
guard :: OccParser (A.Process -> A.Alternative)
|
|
guard
|
|
= do m <- md
|
|
(c, im) <- input
|
|
return $ A.Alternative m c im
|
|
<|> do m <- md
|
|
b <- tryVX booleanExpr sAmp
|
|
do { (c, im) <- input; return $ A.AlternativeCond m b c im }
|
|
<|> do { sSKIP; eol; return $ A.AlternativeSkip m b }
|
|
<?> "guard"
|
|
--}}}
|
|
--{{{ PROC calls
|
|
procInstance :: OccParser A.Process
|
|
procInstance
|
|
= do m <- md
|
|
n <- tryVX procName sLeftR
|
|
st <- specTypeOfName n
|
|
let fs = case st of A.Proc _ _ fs _ -> fs
|
|
as <- actuals fs
|
|
sRightR
|
|
eol
|
|
return $ A.ProcCall m n as
|
|
<?> "PROC instance"
|
|
|
|
actuals :: [A.Formal] -> OccParser [A.Actual]
|
|
actuals fs = intersperseP (map actual fs) sComma
|
|
|
|
actual :: A.Formal -> OccParser A.Actual
|
|
actual (A.Formal am t n)
|
|
= do case am of
|
|
A.ValAbbrev ->
|
|
do e <- expressionOfType t
|
|
return $ A.ActualExpression t e
|
|
_ ->
|
|
case stripArrayType t of
|
|
A.Chan {} -> var (channelOfType t)
|
|
A.Timer -> var timer
|
|
A.Port _ -> var (portOfType t)
|
|
_ -> var (variableOfType t)
|
|
<?> "actual of type " ++ showOccam t ++ " for " ++ show n
|
|
where
|
|
var inner = liftM (A.ActualVariable am t) inner
|
|
--}}}
|
|
--{{{ intrinsic PROC call
|
|
intrinsicProcName :: OccParser (String, [A.Formal])
|
|
intrinsicProcName
|
|
= do n <- anyName A.ProcName
|
|
let s = A.nameName n
|
|
case lookup s intrinsicProcs of
|
|
Just atns -> return (s, [A.Formal am t (A.Name emptyMeta A.VariableName n)
|
|
| (am, t, n) <- atns])
|
|
Nothing -> pzero
|
|
|
|
intrinsicProc :: OccParser A.Process
|
|
intrinsicProc
|
|
= do m <- md
|
|
(s, fs) <- tryVX intrinsicProcName sLeftR
|
|
as <- actuals fs
|
|
sRightR
|
|
eol
|
|
return $ A.IntrinsicProcCall m s as
|
|
<?> "intrinsic PROC instance"
|
|
--}}}
|
|
--}}}
|
|
--{{{ top-level forms
|
|
|
|
-- | An item at the top level is either a specification, or the end of the
|
|
-- file.
|
|
topLevelItem :: OccParser A.AST
|
|
topLevelItem
|
|
= handleSpecs (allocation <|> specification) topLevelItem
|
|
(\m s inner -> A.Spec m s inner)
|
|
<|> do m <- md
|
|
eof
|
|
-- Stash the current locals so that we can either restore them
|
|
-- when we get back to the file we included this one from, or
|
|
-- pull the TLP name from them at the end.
|
|
modify $ (\ps -> ps { csMainLocals = csLocalNames ps })
|
|
return $ A.Several m []
|
|
|
|
-- | A source file is a series of nested specifications.
|
|
-- The later specifications must be in scope for the earlier ones.
|
|
-- We represent this as an 'AST' -- a @Structured ()@.
|
|
sourceFile :: OccParser (A.AST, [WarningReport], CompState)
|
|
sourceFile
|
|
= do p <- topLevelItem
|
|
(w, s) <- getState
|
|
return (p, w, s)
|
|
--}}}
|
|
--}}}
|
|
|
|
--{{{ entry points for the parser itself
|
|
-- | Parse a token stream with the given production.
|
|
runTockParser :: [Token] -> OccParser t -> CompState -> PassM t
|
|
runTockParser toks prod cs
|
|
= do case runParser prod ([], cs) "" toks of
|
|
Left err ->
|
|
-- If a position was encoded into the message, use that;
|
|
-- else use the parser position.
|
|
let errMeta = sourcePosToMeta $ errorPos err
|
|
(msgMeta, msg) = unpackMeta $ show err
|
|
m = Just errMeta >> msgMeta
|
|
in dieReport (m, "Parse error: " ++ msg)
|
|
Right r -> return r
|
|
|
|
-- | Parse an occam program.
|
|
parseOccamProgram :: [Token] -> PassM A.AST
|
|
parseOccamProgram toks
|
|
= do cs <- get
|
|
(p, ws, cs') <- runTockParser toks sourceFile cs
|
|
put cs'
|
|
mapM_ warnReport ws
|
|
return p
|
|
--}}}
|
|
|