
I've checked these all against the Darcs history using a script (check-copyright, in my misccode collection). Anything Neil or I did as part of our PhDs is copyright University of Kent; more recent work belongs to us, as appropriate.
2153 lines
73 KiB
Haskell
2153 lines
73 KiB
Haskell
{-
|
|
Tock: a compiler for parallel languages
|
|
Copyright (C) 2007, 2008, 2009, 2010 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 (join, liftM, when)
|
|
import Control.Monad.State (MonadState, get, put)
|
|
import Data.Char
|
|
import Data.List
|
|
import qualified Data.Map as Map
|
|
import Data.Maybe
|
|
import qualified Data.Set as Set
|
|
import Text.ParserCombinators.Parsec
|
|
import Text.ParserCombinators.Parsec.Error
|
|
import Text.Regex
|
|
|
|
import qualified AST as A
|
|
import CompState
|
|
import Errors
|
|
import Intrinsics
|
|
import LexOccam
|
|
import Metadata
|
|
import ParseUtils
|
|
import Pass
|
|
import ShowCode
|
|
import Types
|
|
import Utils
|
|
|
|
data OccParserState = OccParserState
|
|
{ csLocalNames :: [(String, (A.Name, NameType, Bool))]
|
|
, compState :: CompState
|
|
}
|
|
|
|
--{{{ the parser monad
|
|
type OccParser = GenParser Token OccParserState
|
|
|
|
instance CSMR (GenParser tok OccParserState) where
|
|
getCompState = getState >>* compState
|
|
|
|
instance CSM (GenParser tok OccParserState) where
|
|
putCompState cs = do st <- getState
|
|
setState $ st { compState = cs }
|
|
|
|
addLocalName :: (String, (A.Name, NameType)) -> OccParser ()
|
|
addLocalName (s, (n, nt))
|
|
= do st <- getState
|
|
setState $ st { csLocalNames = (s, (n, nt, True)) : csLocalNames st }
|
|
|
|
-- The other part of the state is actually the built-up list of warnings:
|
|
instance Warn (GenParser tok OccParserState) where
|
|
warnReport w@(_,t,_) = modifyCompState $
|
|
\cs -> cs { csWarnings =
|
|
if t `Set.member` csEnabledWarnings (csOpts cs)
|
|
then csWarnings cs ++ [w]
|
|
else csWarnings cs }
|
|
|
|
|
|
instance Die (GenParser tok OccParserState) where
|
|
dieReport (Just m, err) = do st <- getCompState
|
|
fail $ packWarnings (csWarnings st) $ packMeta m $ err
|
|
dieReport (Nothing, err) = do st <- getCompState
|
|
fail $ packWarnings (csWarnings st) err
|
|
|
|
packWarnings :: [WarningReport] -> String -> String
|
|
packWarnings ws = (("\0\1\2\3" ++ show ws ++ "\0") ++)
|
|
|
|
unpackWarnings :: String -> ([WarningReport], String)
|
|
unpackWarnings ws = if "\0\1\2\3" `isInfixOf` ws then (nub w, s) else ([], ws)
|
|
where
|
|
(w, s) = findAllWarnings ws
|
|
findAllWarnings :: String -> ([WarningReport], String)
|
|
findAllWarnings s
|
|
= case b of
|
|
[] -> ([], s)
|
|
'\0':'\1':'\2':'\3':rest ->
|
|
let (warningText, _:otherText) = span (/='\0') rest
|
|
(furtherWarnings, remainingText) = findAllWarnings otherText
|
|
in (read warningText ++ furtherWarnings, a ++ remainingText)
|
|
(_:bs) -> let (furtherWarnings, remainingText) = findAllWarnings bs
|
|
in (furtherWarnings, a ++ "\0" ++ remainingText)
|
|
where
|
|
(a, b) = span (/= '\0') s
|
|
--}}}
|
|
|
|
--{{{ matching rules for raw tokens
|
|
-- | Extract source position from a `Token`.
|
|
tokenPos :: Token -> SourcePos
|
|
tokenPos (Token m _) = metaToSourcePos m
|
|
|
|
genToken :: (Token -> Maybe a) -> OccParser a
|
|
genToken test = token show tokenPos test
|
|
|
|
reserved :: String -> OccParser ()
|
|
reserved name = genToken test
|
|
where
|
|
test (Token _ (TokReserved name'))
|
|
= if name' == name then Just () else Nothing
|
|
test _ = Nothing
|
|
|
|
identifier :: OccParser String
|
|
identifier = genToken test
|
|
where
|
|
test (Token _ (TokIdentifier s)) = Just s
|
|
test _ = Nothing
|
|
|
|
plainToken :: TokenType -> OccParser ()
|
|
plainToken t = genToken test
|
|
where
|
|
test (Token _ t') = if t == t' then Just () else Nothing
|
|
--}}}
|
|
--{{{ symbols
|
|
sAmp, sAssign, sBang, sBar, sColon, sColons, sComma, sDoubleQuest, sEq, sLeft, sLeftR,
|
|
sQuest, sRight, sRightR, sSemi
|
|
:: OccParser ()
|
|
|
|
sAmp = reserved "&"
|
|
sAssign = reserved ":="
|
|
sBang = reserved "!"
|
|
sBar = reserved "|"
|
|
sColon = reserved ":"
|
|
sColons = reserved "::"
|
|
sComma = reserved ","
|
|
sDoubleQuest = reserved "??"
|
|
sEq = reserved "="
|
|
sLeft = reserved "["
|
|
sLeftR = reserved "("
|
|
sQuest = reserved "?"
|
|
sRight = reserved "]"
|
|
sRightR = reserved ")"
|
|
sSemi = reserved ";"
|
|
--}}}
|
|
--{{{ keywords
|
|
|
|
sAFTER, sALT, sANY, sAT, sBOOL, sBYTE, sBYTESIN, sCASE, sCHAN, sCLAIM, sCLONE,
|
|
sDATA, sDEFINED, sELSE, sFALSE, sFOR, sFORK, sFORKING, sFROM, sFUNCTION,
|
|
sIF, sINLINE, sIN, sINITIAL, sINT, sINT16, sINT32, sINT64, sIS, sMOBILE,
|
|
sMOSTNEG, sMOSTPOS, sOF, sOFFSETOF, sPACKED, sPAR, sPLACE, sPLACED, sPORT,
|
|
sPRI, sPROC, sPROCESSOR, sPROTOCOL, sREAL32, sREAL64, sRECORD,
|
|
sREC_RECURSIVE, sRESHAPES, sRESULT, sRETYPES, sROUND, sSEQ, sSHARED, sSIZE,
|
|
sSKIP, sSTEP, sSTOP, sTIMER, sTRUE, sTRUNC, sTYPE, sVAL, sVALOF, sWHILE,
|
|
sWORKSPACE, sVECSPACE :: OccParser ()
|
|
|
|
sAFTER = reserved "AFTER"
|
|
sALT = reserved "ALT"
|
|
sANY = reserved "ANY"
|
|
sAT = reserved "AT"
|
|
sBOOL = reserved "BOOL"
|
|
sBYTE = reserved "BYTE"
|
|
sBYTESIN = reserved "BYTESIN"
|
|
sCASE = reserved "CASE"
|
|
sCHAN = reserved "CHAN"
|
|
sCLAIM = reserved "CLAIM"
|
|
sCLONE = reserved "CLONE"
|
|
sDATA = reserved "DATA"
|
|
sDEFINED = reserved "DEFINED"
|
|
sELSE = reserved "ELSE"
|
|
sFALSE = reserved "FALSE"
|
|
sFOR = reserved "FOR"
|
|
sFORK = reserved "FORK"
|
|
sFORKING = reserved "FORKING"
|
|
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"
|
|
sMOBILE = reserved "MOBILE"
|
|
sMOSTNEG = reserved "MOSTNEG"
|
|
sMOSTPOS = reserved "MOSTPOS"
|
|
sOF = reserved "OF"
|
|
sOFFSETOF = reserved "OFFSETOF"
|
|
sPACKED = reserved "PACKED"
|
|
sPAR = reserved "PAR"
|
|
sPLACE = reserved "PLACE"
|
|
sPLACED = reserved "PLACED"
|
|
sPORT = reserved "PORT"
|
|
sPRI = reserved "PRI"
|
|
sPROC = reserved "PROC"
|
|
sPROCESSOR = reserved "PROCESSOR"
|
|
sPROTOCOL = reserved "PROTOCOL"
|
|
sREAL32 = reserved "REAL32"
|
|
sREAL64 = reserved "REAL64"
|
|
sREC_RECURSIVE = reserved "REC" <|> reserved "RECURSIVE"
|
|
sRECORD = reserved "RECORD"
|
|
sRESHAPES = reserved "RESHAPES"
|
|
sRESULT = reserved "RESULT"
|
|
sRETYPES = reserved "RETYPES"
|
|
sROUND = reserved "ROUND"
|
|
sSEQ = reserved "SEQ"
|
|
sSHARED = reserved "SHARED"
|
|
sSIZE = reserved "SIZE"
|
|
sSKIP = reserved "SKIP"
|
|
sSTEP = reserved "STEP"
|
|
sSTOP = reserved "STOP"
|
|
sTIMER = reserved "TIMER"
|
|
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) })
|
|
|
|
tryXXX :: OccParser a -> OccParser b -> OccParser c -> OccParser ()
|
|
tryXXX a b c = try (do { a; b; c; return () })
|
|
|
|
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) })
|
|
|
|
tryVXVX :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser (a, c)
|
|
tryVXVX a b c d = try (do { av <- a; b; cv <- c; d; return (av, cv) })
|
|
|
|
_tryVVXX :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser (a, b)
|
|
_tryVVXX a b c d = try (do { av <- a; bv <- b; c; d; return (av, bv) })
|
|
|
|
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) })
|
|
|
|
tryVVVX :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser (a, b, c)
|
|
tryVVVX a b c d = try (do { av <- a; bv <- b; cv <- c; d; return (av, bv, cv) })
|
|
|
|
tryVVVXV :: OccParser a -> OccParser b -> OccParser c -> OccParser d -> OccParser e -> OccParser (a, b, c, e)
|
|
tryVVVXV a b c d e = try (do { av <- a; bv <- b; cv <- c; d; ev <- e; return (av, bv, cv, ev) })
|
|
--}}}
|
|
|
|
--{{{ subscripts
|
|
maybeSubscripted :: String -> OccParser a -> (Meta -> A.Subscript -> a -> a) -> OccParser a
|
|
maybeSubscripted prodName inner subscripter
|
|
= do m <- md
|
|
v <- inner
|
|
subs <- many postSubscript
|
|
return $ foldl (\var sub -> subscripter m sub var) v subs
|
|
<?> prodName
|
|
|
|
postSubscript :: OccParser A.Subscript
|
|
postSubscript
|
|
-- AMBIGUITY: in [x], x may be a variable or a field name.
|
|
= do m <- md
|
|
e <- tryXV sLeft expression
|
|
sRight
|
|
return $ A.Subscript m A.CheckBoth e
|
|
<|> do m <- md
|
|
f <- tryXV sLeft fieldName
|
|
sRight
|
|
return $ A.SubscriptField m f
|
|
<?> "subscript"
|
|
|
|
maybeSliced :: OccParser a -> (Meta -> A.Subscript -> a -> a) -> OccParser a
|
|
maybeSliced inner subscripter
|
|
= do m <- md
|
|
|
|
(v, ff1) <- tryXVV sLeft inner fromOrFor
|
|
|
|
e <- expression
|
|
sub <- case ff1 of
|
|
"FROM" ->
|
|
(do f <- tryXV sFOR expression
|
|
sRight
|
|
return $ A.SubscriptFromFor m A.CheckBoth e f)
|
|
<|>
|
|
(do sRight
|
|
return $ A.SubscriptFrom m A.CheckBoth e)
|
|
"FOR" ->
|
|
do sRight
|
|
return $ A.SubscriptFor m A.CheckBoth 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 warnP m WarnParserOddity msg
|
|
return []
|
|
|
|
handleSpecs :: OccParser ([NameSpec], OccParser ()) -> OccParser a -> (Meta -> A.Specification -> a -> a) -> OccParser a
|
|
handleSpecs specs inner specMarker
|
|
= do m <- md
|
|
(ss, after) <- specs
|
|
ss' <- mapM scopeInSpec ss
|
|
v <- inner
|
|
mapM scopeOutSpec (reverse ss')
|
|
after
|
|
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
|
|
|
|
--}}}
|
|
|
|
--{{{ name scoping
|
|
findName :: A.Name -> NameType -> OccParser A.Name
|
|
findName thisN thisNT
|
|
= do st <- getState
|
|
(origN, origNT) <-
|
|
case lookup (A.nameName thisN) [(s, (n, nt)) | (s, (n, nt, True)) <- csLocalNames st] of
|
|
Just def -> return def
|
|
_ -> dieP (A.nameMeta thisN) $ "name " ++ A.nameName thisN ++ " not defined"
|
|
++ "; possibilities were: " ++ show (map fst (csLocalNames st))
|
|
if thisNT /= origNT
|
|
then dieP (A.nameMeta thisN) $ "expected " ++ show thisNT ++ " (" ++ A.nameName origN ++ " is " ++ show origNT ++ ")"
|
|
else return $ thisN { A.nameName = A.nameName origN }
|
|
|
|
scopeIn :: A.Name -> NameType -> A.SpecType -> A.AbbrevMode -> (Maybe A.Name, A.NameSource) -> OccParser A.Name
|
|
scopeIn n@(A.Name m s) nt specType am (munged, ns)
|
|
= do let s' = if isOperator s
|
|
then occamOperatorTranslateDefault s
|
|
else s
|
|
n' <- maybe (makeUniqueName m s' >>* A.Name m) return munged
|
|
let nd = A.NameDef {
|
|
A.ndMeta = m,
|
|
A.ndName = A.nameName n',
|
|
A.ndOrigName = s,
|
|
A.ndSpecType = specType,
|
|
A.ndAbbrevMode = am,
|
|
A.ndNameSource = ns,
|
|
A.ndPlacement = A.Unplaced
|
|
}
|
|
defineName n' nd
|
|
addLocalName (s, (n', nt))
|
|
return n'
|
|
|
|
scopeOut :: A.Name -> OccParser ()
|
|
scopeOut n@(A.Name m _)
|
|
= do st <- getState
|
|
case csLocalNames st of
|
|
((_, (old, _, _)):rest)
|
|
| old == n -> setState $ st { csLocalNames = rest }
|
|
| otherwise -> dieInternal (Just m, "scoping out not in order; "
|
|
++ " tried to scope out: " ++ A.nameName n ++ " but found: " ++ A.nameName old)
|
|
_ -> dieInternal (Just m, "scoping out name when stack is empty")
|
|
|
|
scopeInRep :: A.Name -> OccParser A.Name
|
|
scopeInRep n
|
|
= scopeIn n VariableName (A.Declaration (A.nameMeta n) A.Int) A.ValAbbrev normalName
|
|
|
|
scopeOutRep :: A.Name -> OccParser ()
|
|
scopeOutRep n = scopeOut n
|
|
|
|
-- | A specification, along with the 'NameType' of the name it defines.
|
|
type NameSpec = (A.Specification, NameType, (Maybe A.Name, A.NameSource))
|
|
|
|
normalName :: (Maybe A.Name, A.NameSource)
|
|
normalName = (Nothing, A.NameUser)
|
|
|
|
scopeInSpec :: NameSpec -> OccParser A.Specification
|
|
scopeInSpec (spec@(A.Specification m n st), nt, ns)
|
|
-- If it's recursive, the spec has already been defined:
|
|
| isRecursive st
|
|
= do modifyName n $ \nd -> nd {A.ndSpecType = st}
|
|
return spec
|
|
| otherwise
|
|
= do n' <- scopeIn n nt st (abbrevModeOfSpec st) ns
|
|
return $ A.Specification m n' st
|
|
where
|
|
isRecursive (A.Function _ (_, A.Recursive) _ _ _) = True
|
|
isRecursive (A.Proc _ (_, A.Recursive) _ _) = True
|
|
isRecursive (A.ChanBundleType _ A.Recursive _) = True
|
|
isRecursive _ = False
|
|
|
|
scopeOutSpec :: A.Specification -> OccParser ()
|
|
scopeOutSpec (A.Specification _ n _) = scopeOut n
|
|
|
|
-- | A formal, along with the 'NameType' of the name it defines.
|
|
type NameFormal = (A.Formal, NameType)
|
|
|
|
scopeInFormal :: NameFormal -> OccParser A.Formal
|
|
scopeInFormal (A.Formal am t n, nt)
|
|
= do n' <- scopeIn n nt (A.Declaration (A.nameMeta n) t) am normalName
|
|
return (A.Formal am t n')
|
|
|
|
scopeInFormals :: [NameFormal] -> OccParser [A.Formal]
|
|
scopeInFormals fs = mapM scopeInFormal fs
|
|
|
|
scopeOutFormals :: [A.Formal] -> OccParser ()
|
|
scopeOutFormals fs = sequence_ [scopeOut n | (A.Formal am t n) <- reverse 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.
|
|
--
|
|
-- Productions with an "-- AMBIGUITY" comment match something that's ambiguous
|
|
-- in the occam grammar, and may thus produce incorrect AST fragments. The
|
|
-- ambiguities will be resolved later.
|
|
|
|
--{{{ names
|
|
anyName :: NameType -> OccParser A.Name
|
|
anyName nt
|
|
= do m <- md
|
|
s <- identifier
|
|
return $ A.Name m s
|
|
<?> show nt
|
|
|
|
name :: NameType -> OccParser A.Name
|
|
name nt
|
|
= do n <- anyName nt
|
|
findName n nt
|
|
|
|
newName :: NameType -> OccParser A.Name
|
|
newName nt = anyName nt
|
|
|
|
channelName, chanBundleName, dataTypeName, functionName, portName, procName, protocolName,
|
|
recordName, timerName, variableName
|
|
:: OccParser A.Name
|
|
|
|
channelName = name ChannelName
|
|
chanBundleName = name ChanBundleName
|
|
dataTypeName = name DataTypeName
|
|
functionName = name FunctionName
|
|
portName = name PortName
|
|
procName = name ProcName
|
|
protocolName = name ProtocolName
|
|
recordName = name RecordName
|
|
timerName = name TimerName
|
|
variableName = name VariableName
|
|
|
|
newChannelName, newChanBundleName, newDataTypeName, newFunctionName, _newPortName,
|
|
newProcName, newProtocolName, newRecordName, _newTimerName, newUDOName,
|
|
newVariableName
|
|
:: OccParser A.Name
|
|
|
|
newChannelName = newName ChannelName
|
|
newChanBundleName = newName ChanBundleName
|
|
newDataTypeName = newName DataTypeName
|
|
newFunctionName = newName FunctionName
|
|
_newPortName = newName PortName
|
|
newProcName = newName ProcName
|
|
newProtocolName = newName ProtocolName
|
|
newRecordName = newName RecordName
|
|
_newTimerName = newName TimerName
|
|
newVariableName = newName VariableName
|
|
|
|
newUDOName = do m <- md
|
|
s <- genToken test
|
|
let chs = splitStringLiteral m s
|
|
return $ A.Name m $ concat [cs | A.ByteLiteral _ cs <- chs]
|
|
where
|
|
test (Token _ (TokStringLiteral s)) = Just (chop 1 1 s)
|
|
test _ = Nothing
|
|
|
|
-- | 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 :: NameType -> OccParser A.Name
|
|
unscopedName nt
|
|
= do n <- anyName nt
|
|
findUnscopedName n
|
|
<?> show nt
|
|
|
|
fieldName, tagName, newFieldName, newTagName :: OccParser A.Name
|
|
|
|
fieldName = unscopedName FieldName
|
|
tagName = unscopedName TagName
|
|
newFieldName = unscopedName FieldName
|
|
newTagName = unscopedName TagName
|
|
--}}}
|
|
--{{{ types
|
|
-- | A sized array of a production.
|
|
arrayType :: OccParser A.Type -> OccParser A.Type
|
|
arrayType element
|
|
= do (s, t) <- tryXVXV sLeft expression sRight element
|
|
return $ addDimensions [A.Dimension s] 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 (specArrayType element <|> 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
|
|
-- Mobile arrays can lack dimensions:
|
|
<|> do { tryXV sMOBILE (specArrayType dataType) >>* A.Mobile }
|
|
<|> do { tryXV sMOBILE dataType >>* A.Mobile }
|
|
<|> do { (n, dir) <- tryVV chanBundleName direction; return $ A.ChanDataType dir A.Unshared n }
|
|
<|> do { (n, dir) <- tryXVV sSHARED chanBundleName direction; return $ A.ChanDataType dir A.Shared n }
|
|
<|> 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.ChanAttributes {A.caWritingShared = A.Unshared, A.caReadingShared = A.Unshared} p }
|
|
<|> do { tryXX sSHARED sCHAN; optional sOF; p <- protocol; return $ A.Chan A.ChanAttributes
|
|
{A.caWritingShared = A.Shared, A.caReadingShared = A.Shared} p }
|
|
<|> do { tryXXX sSHARED sBang sCHAN; optional sOF; p <- protocol; return $ A.Chan A.ChanAttributes
|
|
{A.caWritingShared = A.Shared, A.caReadingShared = A.Unshared} p }
|
|
<|> do { tryXXX sSHARED sQuest sCHAN; optional sOF; p <- protocol; return $ A.Chan A.ChanAttributes
|
|
{A.caWritingShared = A.Unshared, A.caReadingShared = A.Shared} p }
|
|
<|> arrayType channelType
|
|
<?> "channel type"
|
|
|
|
timerType :: OccParser A.Type
|
|
timerType
|
|
= do { sTIMER; return $ A.Timer A.OccamTimer }
|
|
<|> arrayType timerType
|
|
<?> "timer type"
|
|
|
|
portType :: OccParser A.Type
|
|
portType
|
|
= do { sPORT; optional sOF; p <- dataType; return $ A.Port p }
|
|
<|> arrayType portType
|
|
<?> "port type"
|
|
--}}}
|
|
--{{{ literals
|
|
|
|
typeDecorator :: OccParser A.Type
|
|
typeDecorator
|
|
= do sLeftR
|
|
t <- dataType
|
|
sRightR
|
|
return t
|
|
<|> return A.Infer
|
|
<?> "literal type decorator"
|
|
|
|
literal :: OccParser A.Expression
|
|
literal
|
|
= do m <- md
|
|
lr <- untypedLiteral
|
|
t <- typeDecorator
|
|
return $ A.Literal m t lr
|
|
<?> "literal"
|
|
|
|
untypedLiteral :: OccParser A.LiteralRepr
|
|
untypedLiteral
|
|
= real
|
|
<|> integer
|
|
<|> byte
|
|
|
|
real :: OccParser A.LiteralRepr
|
|
real
|
|
= do m <- md
|
|
genToken (test m)
|
|
<?> "real literal"
|
|
where
|
|
test m (Token _ (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 (Token _ (TokIntLiteral s)) = Just $ A.IntLiteral m s
|
|
test m (Token _ (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 (Token _ (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
|
|
= maybeSubscripted "table" table' A.SubscriptedExpr
|
|
|
|
table' :: OccParser A.Expression
|
|
table'
|
|
= do m <- md
|
|
(defT, lr) <- tableElems
|
|
t <- typeDecorator
|
|
let t' = case t of
|
|
A.Infer -> defT
|
|
_ -> t
|
|
return $ A.Literal m t' lr
|
|
<|> maybeSliced (table <|> arrayConstructor) A.SubscriptedExpr
|
|
<?> "table'"
|
|
|
|
tableElems :: OccParser (A.Type, A.LiteralRepr)
|
|
tableElems
|
|
= stringLiteral
|
|
<|> do m <- md
|
|
es <- tryXVX sLeft (sepBy1 expression sComma) sRight
|
|
return (A.Infer, A.ArrayListLiteral m $ A.Several m (map (A.Only m) es))
|
|
<?> "table elements"
|
|
|
|
-- String literals are implicitly typed []BYTE unless otherwise specified, so
|
|
-- we can tell the type of "".
|
|
stringLiteral :: OccParser (A.Type, A.LiteralRepr)
|
|
stringLiteral
|
|
= do m <- md
|
|
cs <- stringCont <|> stringLit
|
|
let aes = A.Several m [A.Only m $ A.Literal m' A.Byte c
|
|
| c@(A.ByteLiteral m' _) <- cs]
|
|
return (A.Array [A.UnknownDimension] A.Byte, A.ArrayListLiteral m aes)
|
|
<?> "string literal"
|
|
where
|
|
stringCont :: OccParser [A.LiteralRepr]
|
|
stringCont
|
|
= do m <- md
|
|
s <- genToken test
|
|
rest <- stringCont <|> stringLit
|
|
return $ (splitStringLiteral m s) ++ rest
|
|
where
|
|
test (Token _ (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 (Token _ (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 ('*':'*':cs)
|
|
= A.ByteLiteral m ['*'] : ssl cs
|
|
ssl ('*':c:cs)
|
|
= (A.ByteLiteral m ['*', c]) : ssl cs
|
|
ssl (c:cs)
|
|
= (A.ByteLiteral m [c]) : ssl cs
|
|
--}}}
|
|
--{{{ expressions
|
|
expressionList :: OccParser A.ExpressionList
|
|
expressionList
|
|
-- AMBIGUITY: this will also match FunctionCallList.
|
|
= do m <- md
|
|
es <- sepBy1 expression sComma
|
|
return $ A.ExpressionList m es
|
|
-- XXX: Value processes are not supported (because nobody uses them and they're hard to parse)
|
|
<|> do m <- md
|
|
n <- tryXV sMOBILE chanBundleName
|
|
return $ A.AllocChannelBundle m n
|
|
<?> "expression list"
|
|
|
|
expression :: OccParser A.Expression
|
|
expression
|
|
= do m <- md
|
|
o <- udOperator ((`elem` [JustMonadic, EitherDyadicMonadic]) . operatorArity)
|
|
v <- operand
|
|
return $ A.FunctionCall 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 }
|
|
<|> do { m <- md; sCLONE; e <- expression; return $ A.CloneMobile m e }
|
|
<|> do { m <- md; t <- tryXV sMOBILE dataType ; return $ A.AllocMobile m (A.Mobile t) Nothing }
|
|
<|> do { m <- md; sDEFINED; e <- expression; return $ A.IsDefined m e }
|
|
<|> sizeExpr
|
|
<|> do m <- md
|
|
(l, o) <- tryVV operand $ udOperator
|
|
(\op -> (operatorArity op `elem` [JustDyadic, EitherDyadicMonadic])
|
|
&& not (isAssocOperator op))
|
|
r <- operand
|
|
return $ A.FunctionCall m o [l, r]
|
|
<|> associativeOpExpression
|
|
<|> conversion
|
|
<|> operand
|
|
<?> "expression"
|
|
|
|
arrayConstructor :: OccParser A.Expression
|
|
arrayConstructor
|
|
= do m <- md
|
|
sLeft
|
|
(n, r) <- replicator
|
|
sBar
|
|
n' <- scopeInRep n
|
|
e <- expression
|
|
scopeOutRep n'
|
|
sRight
|
|
return $ A.Literal m A.Infer $ A.ArrayListLiteral m $ A.Spec m
|
|
(A.Specification m n' (A.Rep m r)) $ A.Only m e
|
|
<?> "array constructor expression"
|
|
|
|
associativeOpExpression :: OccParser A.Expression
|
|
associativeOpExpression
|
|
= do m <- md
|
|
(l, o) <- tryVV operand $ udOperator
|
|
(\op -> (operatorArity op `elem` [JustDyadic, EitherDyadicMonadic])
|
|
&& isAssocOperator op)
|
|
r <- associativeOpExpression <|> operand
|
|
return $ A.FunctionCall 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 <- operand
|
|
return $ A.SizeExpr m v
|
|
<|> do v <- (directedChannel <|> timer <|> port)
|
|
return $ A.ExprVariable m $ specificDimSize 0 v
|
|
<?> "SIZE expression"
|
|
|
|
functionCall :: OccParser A.Expression
|
|
functionCall
|
|
= do m <- md
|
|
n <- tryVX functionName sLeftR
|
|
as <- sepBy expression sComma
|
|
sRightR
|
|
return $ A.FunctionCall m n as
|
|
<|> do m <- md
|
|
s <- tryVX intrinsicFunctionName sLeftR
|
|
as <- sepBy expression sComma
|
|
sRightR
|
|
return $ A.IntrinsicFunctionCall m s as
|
|
<?> "function call"
|
|
where
|
|
intrinsicFunctionName :: OccParser String
|
|
intrinsicFunctionName
|
|
= do s <- anyName FunctionName >>* A.nameName
|
|
case lookup s intrinsicFunctions of
|
|
Just _ -> return s
|
|
Nothing -> pzero
|
|
|
|
data OperatorArity = JustDyadic | JustMonadic | EitherDyadicMonadic | NotOperator
|
|
deriving (Eq)
|
|
|
|
-- Returns the most operands it can take.
|
|
operatorArity :: String -> OperatorArity
|
|
operatorArity "??" = EitherDyadicMonadic
|
|
operatorArity "@@" = EitherDyadicMonadic
|
|
operatorArity "$$" = EitherDyadicMonadic
|
|
operatorArity "%" = EitherDyadicMonadic
|
|
operatorArity "%%" = EitherDyadicMonadic
|
|
operatorArity "&&" = EitherDyadicMonadic
|
|
operatorArity "<%" = EitherDyadicMonadic
|
|
operatorArity "%>" = EitherDyadicMonadic
|
|
operatorArity "<&" = EitherDyadicMonadic
|
|
operatorArity "&>" = EitherDyadicMonadic
|
|
operatorArity "<]" = EitherDyadicMonadic
|
|
operatorArity "[>" = EitherDyadicMonadic
|
|
operatorArity "<@" = EitherDyadicMonadic
|
|
operatorArity "@>" = EitherDyadicMonadic
|
|
operatorArity "@" = EitherDyadicMonadic
|
|
operatorArity "++" = EitherDyadicMonadic
|
|
operatorArity "!!" = EitherDyadicMonadic
|
|
operatorArity "==" = EitherDyadicMonadic
|
|
operatorArity "^" = EitherDyadicMonadic
|
|
operatorArity "-" = EitherDyadicMonadic
|
|
operatorArity "MINUS" = EitherDyadicMonadic
|
|
operatorArity "~" = JustMonadic
|
|
operatorArity "NOT" = JustMonadic
|
|
operatorArity "+" = JustDyadic
|
|
operatorArity "*" = JustDyadic
|
|
operatorArity "/" = JustDyadic
|
|
operatorArity "\\" = JustDyadic
|
|
operatorArity "REM" = JustDyadic
|
|
operatorArity "PLUS" = JustDyadic
|
|
operatorArity "TIMES" = JustDyadic
|
|
operatorArity "AFTER" = JustDyadic
|
|
operatorArity "/\\" = JustDyadic
|
|
operatorArity "\\/" = JustDyadic
|
|
operatorArity "><" = JustDyadic
|
|
operatorArity "BITNOT" = JustMonadic
|
|
operatorArity "BITAND" = JustDyadic
|
|
operatorArity "BITOR" = JustDyadic
|
|
operatorArity "<<" = JustDyadic
|
|
operatorArity ">>" = JustDyadic
|
|
operatorArity "AND" = JustDyadic
|
|
operatorArity "OR" = JustDyadic
|
|
operatorArity "=" = JustDyadic
|
|
operatorArity "<>" = JustDyadic
|
|
operatorArity "<" = JustDyadic
|
|
operatorArity "<=" = JustDyadic
|
|
operatorArity ">" = JustDyadic
|
|
operatorArity ">=" = JustDyadic
|
|
operatorArity _ = NotOperator
|
|
|
|
isAssocOperator :: String -> Bool
|
|
isAssocOperator "AND" = True
|
|
isAssocOperator "OR" = True
|
|
isAssocOperator "PLUS" = True
|
|
isAssocOperator "TIMES" = True
|
|
isAssocOperator _ = False
|
|
|
|
udOperator :: (String -> Bool) -> OccParser A.Name
|
|
udOperator isOp = do m <- md
|
|
n <- genToken test
|
|
return $ A.Name m $ translate n
|
|
where
|
|
test (Token _ (TokReserved name))
|
|
= if isOp name then Just name else Nothing
|
|
test _ = Nothing
|
|
|
|
-- Turn REM into \ now, to save effort later (and similar for some of the other
|
|
-- operators that are synonyms of each other). This does prevent overloading
|
|
-- REM different to \ (for example), but we think this is ok:
|
|
translate :: String -> String
|
|
translate "REM" = "\\"
|
|
translate "BITNOT" = "~"
|
|
translate "BITAND" = "/\\"
|
|
translate "BITOR" = "\\/"
|
|
translate op = op
|
|
|
|
conversion :: OccParser A.Expression
|
|
conversion
|
|
= do m <- md
|
|
t <- dataType
|
|
(c, o) <- conversionMode
|
|
return $ A.Conversion m c t o
|
|
<?> "conversion"
|
|
|
|
conversionMode :: OccParser (A.ConversionMode, A.Expression)
|
|
conversionMode
|
|
= do { sROUND; o <- operand; return (A.Round, o) }
|
|
<|> do { sTRUNC; o <- operand; return (A.Trunc, o) }
|
|
<|> do { o <- operand; return (A.DefaultConversion, o) }
|
|
<?> "conversion mode and operand"
|
|
--}}}
|
|
--{{{ operands
|
|
operand :: OccParser A.Expression
|
|
operand
|
|
= maybeSubscripted "operand" operand' A.SubscriptedExpr
|
|
|
|
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
|
|
<|> functionCall
|
|
<|> do m <- md
|
|
sBYTESIN
|
|
sLeftR
|
|
(try (do { o <- 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
|
|
|
|
variable' :: OccParser A.Variable
|
|
variable'
|
|
= do { m <- md; n <- try variableName; return $ A.Variable m n }
|
|
<|> maybeSliced variable A.SubscriptedVariable
|
|
<?> "variable'"
|
|
|
|
channel :: OccParser A.Variable
|
|
channel
|
|
= maybeSubscripted "channel" channel' A.SubscriptedVariable
|
|
<?> "channel"
|
|
|
|
channel' :: OccParser A.Variable
|
|
channel'
|
|
= do { m <- md; n <- try channelName; return $ A.Variable m n }
|
|
<|> do { m <- md; n <- try variableName; return $ A.Variable m n }
|
|
<|> maybeSliced directedChannel A.SubscriptedVariable
|
|
<?> "channel'"
|
|
|
|
direction :: OccParser A.Direction
|
|
direction
|
|
= (sQuest >> return A.DirInput)
|
|
<|> (sBang >> return A.DirOutput)
|
|
<?> "direction decorator"
|
|
|
|
-- | Parse a production with an optional direction specifier,
|
|
-- returning a function to apply the direction specifier to a type and the
|
|
-- result of the inner production.
|
|
maybeDirected :: OccParser t -> OccParser (A.Type -> OccParser A.Type, t)
|
|
maybeDirected inner
|
|
= do v <- inner
|
|
m <- md
|
|
dirs <- many direction
|
|
return (foldFuncsM $ map (applyDirection m) (reverse dirs), v)
|
|
|
|
-- | Parse a channel followed by an optional direction specifier.
|
|
directedChannel :: OccParser A.Variable
|
|
directedChannel
|
|
= do c <- channel
|
|
m <- md
|
|
dirs <- many direction
|
|
return $ foldFuncs (map (A.DirectedVariable m) (reverse dirs)) c
|
|
|
|
timer :: OccParser A.Variable
|
|
timer
|
|
= maybeSubscripted "timer" timer' A.SubscriptedVariable
|
|
<?> "timer"
|
|
|
|
timer' :: OccParser A.Variable
|
|
timer'
|
|
= do { m <- md; n <- try timerName; return $ A.Variable m n }
|
|
<|> maybeSliced timer A.SubscriptedVariable
|
|
<?> "timer'"
|
|
|
|
port :: OccParser A.Variable
|
|
port
|
|
= maybeSubscripted "port" port' A.SubscriptedVariable
|
|
<?> "port"
|
|
|
|
port' :: OccParser A.Variable
|
|
port'
|
|
= do { m <- md; n <- try portName; return $ A.Variable m n }
|
|
<|> maybeSliced port A.SubscriptedVariable
|
|
<?> "port'"
|
|
--}}}
|
|
--{{{ 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 (addDimensions [A.UnknownDimension] 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.Name, A.Replicator)
|
|
replicator
|
|
= do m <- md
|
|
n <- tryVX newVariableName sEq
|
|
b <- expression
|
|
sFOR
|
|
c <- expression
|
|
st <- tryXV sSTEP expression <|> return (makeConstant m 1)
|
|
return (n, A.For m b c st)
|
|
<?> "replicator"
|
|
--}}}
|
|
--{{{ specifications, declarations, allocations
|
|
allocation :: OccParser ([NameSpec], OccParser ())
|
|
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 ([], return ())
|
|
<?> "allocation"
|
|
|
|
placement :: OccParser A.Placement
|
|
placement
|
|
= do e <- tryXV (optional sAT) expression
|
|
return $ A.PlaceAt e
|
|
<|> do tryXX sIN sWORKSPACE
|
|
return $ A.PlaceInWorkspace
|
|
<|> do tryXX sIN sVECSPACE
|
|
return $ A.PlaceInVecspace
|
|
<?> "placement"
|
|
|
|
specification :: OccParser ([NameSpec], OccParser ())
|
|
specification
|
|
= do m <- md
|
|
(ns, d, nt) <- declaration
|
|
return ([(A.Specification m n d, nt, normalName) | n <- ns], return ())
|
|
<|> do { a <- abbreviation; return (a, return ()) }
|
|
<|> do { d <- definition; return ([d], return ()) }
|
|
<|> do { n <- pragma ; return (maybeToList n, return ()) }
|
|
<?> "specification"
|
|
|
|
declaration :: OccParser ([A.Name], A.SpecType, NameType)
|
|
declaration
|
|
= declOf dataType VariableName
|
|
<|> declOf channelType ChannelName
|
|
<|> declOf timerType TimerName
|
|
<|> declOf portType PortName
|
|
<?> "declaration"
|
|
|
|
declOf :: OccParser A.Type -> NameType -> OccParser ([A.Name], A.SpecType, NameType)
|
|
declOf spec nt
|
|
= do m <- md
|
|
(d, ns) <- tryVVX spec (sepBy1 (newName nt) sComma) sColon
|
|
eol
|
|
return (ns, A.Declaration m d, nt)
|
|
|
|
abbreviation :: OccParser [NameSpec]
|
|
abbreviation
|
|
= valAbbrev
|
|
<|> refAbbrev variable VariableName >>* singleton
|
|
<|> refAbbrev directedChannel ChannelName >>* singleton
|
|
<|> chanArrayAbbrev >>* singleton
|
|
<|> refAbbrev timer TimerName >>* singleton
|
|
<|> refAbbrev port PortName >>* singleton
|
|
<?> "abbreviation"
|
|
|
|
maybeInfer :: OccParser A.Type -> OccParser A.Type
|
|
maybeInfer spec
|
|
= try spec
|
|
<|> return A.Infer
|
|
<?> "optional specifier"
|
|
|
|
valAbbrevMode :: OccParser A.AbbrevMode
|
|
valAbbrevMode
|
|
= (sVAL >> return A.ValAbbrev)
|
|
<|> (sINITIAL >> return A.InitialAbbrev)
|
|
|
|
valAbbrev :: OccParser [NameSpec]
|
|
valAbbrev
|
|
= do m <- md
|
|
(am, t, ns) <-
|
|
tryVVVX valAbbrevMode (maybeInfer dataSpecifier) (sepBy1 newVariableName sComma) sIS
|
|
es <- sepBy1 expression sComma
|
|
sColon
|
|
eol
|
|
when (length ns /= length es) $
|
|
dieP m "Mismatching number of names and expression in abbreviation"
|
|
return [(A.Specification m n $ A.Is m am t (A.ActualExpression e)
|
|
, VariableName, normalName)
|
|
| (n, e) <- zip ns es]
|
|
<?> "abbreviation by value"
|
|
|
|
refAbbrevMode :: OccParser A.AbbrevMode
|
|
refAbbrevMode
|
|
= (sRESULT >> return A.ResultAbbrev)
|
|
<|> return A.Abbrev
|
|
|
|
refAbbrev :: OccParser A.Variable -> NameType -> OccParser NameSpec
|
|
refAbbrev oldVar nt
|
|
= do m <- md
|
|
(am, t, (direct, n), v) <-
|
|
tryVVVXV refAbbrevMode
|
|
(maybeInfer specifier)
|
|
(maybeDirected $ newName nt)
|
|
sIS
|
|
oldVar
|
|
sColon
|
|
eol
|
|
t' <- direct t
|
|
return (A.Specification m n $ A.Is m am t' $ A.ActualVariable v, nt, normalName)
|
|
<?> "abbreviation by reference"
|
|
|
|
chanArrayAbbrev :: OccParser NameSpec
|
|
chanArrayAbbrev
|
|
= do m <- md
|
|
(t, (direct, n), cs) <-
|
|
tryVVXV (maybeInfer channelSpecifier)
|
|
(maybeDirected newChannelName)
|
|
(sIS >> sLeft)
|
|
(sepBy1 directedChannel sComma)
|
|
sRight
|
|
sColon
|
|
eol
|
|
t' <- direct t
|
|
return (A.Specification m n $ A.Is m A.Abbrev t' $ A.ActualChannelArray cs, ChannelName, normalName)
|
|
<?> "channel array abbreviation"
|
|
|
|
specMode :: OccParser a -> OccParser (A.SpecMode, a)
|
|
specMode keyword
|
|
= do x <- tryXV sINLINE keyword
|
|
return (A.InlineSpec, x)
|
|
<|> do x <- keyword
|
|
return (A.PlainSpec, x)
|
|
<?> "specification mode"
|
|
|
|
recMode :: OccParser a -> OccParser (A.RecMode, a)
|
|
recMode keyword
|
|
= do x <- tryXV sREC_RECURSIVE keyword
|
|
return (A.Recursive, x)
|
|
<|> do x <- keyword
|
|
return (A.PlainRec, x)
|
|
<?> "recursion mode"
|
|
|
|
definition :: OccParser NameSpec
|
|
definition
|
|
= do m <- md
|
|
sDATA
|
|
sTYPE
|
|
do { n <- tryVX newDataTypeName sIS; t <- dataType; sColon; eol;
|
|
return (A.Specification m n (A.DataType m t), DataTypeName, normalName) }
|
|
<|> do { n <- newRecordName; eol; indent; record <- structuredType; outdent; sColon; eol;
|
|
return (A.Specification m n record, RecordName, normalName) }
|
|
<|> do m <- md
|
|
rm <- tryVX (recMode sCHAN) sTYPE >>* fst
|
|
n <- newChanBundleName
|
|
eol
|
|
indent
|
|
sMOBILE
|
|
sRECORD
|
|
eol
|
|
indent
|
|
n' <- if rm == A.Recursive
|
|
then scopeIn n ChanBundleName
|
|
(A.ChanBundleType m rm []) A.Original normalName
|
|
else return n
|
|
fs <- many1 chanInBundle
|
|
outdent
|
|
outdent
|
|
sColon
|
|
eol
|
|
return (A.Specification m n' $ A.ChanBundleType m rm fs, ChanBundleName, normalName)
|
|
<|> do m <- md
|
|
sPROTOCOL
|
|
n <- newProtocolName
|
|
do { sIS; p <- sequentialProtocol; sColon; eol; return (A.Specification m n $ A.Protocol m p, ProtocolName, normalName) }
|
|
<|> do { eol; indent; sCASE; eol; ps <- maybeIndentedList m "empty CASE protocol" taggedProtocol; outdent; sColon; eol; return (A.Specification m n $ A.ProtocolCase m ps, ProtocolName, normalName) }
|
|
<|> do m <- md
|
|
(sm, (rm, _)) <- specMode $ recMode sPROC
|
|
n <- newProcName
|
|
fs <- formalList
|
|
eol
|
|
indent
|
|
n' <- if rm == A.Recursive
|
|
then scopeIn n ProcName
|
|
(A.Proc m (sm, rm) (map fst fs) Nothing) A.Original normalName
|
|
else return n
|
|
fs' <- scopeInFormals fs
|
|
p <- process
|
|
scopeOutFormals fs'
|
|
outdent
|
|
sColon
|
|
eol
|
|
return (A.Specification m n' $ A.Proc m (sm, rm) fs' (Just p), ProcName, normalName)
|
|
<|> do m <- md
|
|
(rs, (sm, (rm, _))) <- tryVV (sepBy1 dataType sComma) (specMode $ recMode sFUNCTION)
|
|
n <- newFunctionName <|> newUDOName
|
|
fs <- formalList
|
|
let addScope body
|
|
= do n' <- if rm == A.Recursive
|
|
then scopeIn n FunctionName
|
|
(A.Function m (sm, rm) rs (map fst fs) Nothing)
|
|
A.Original normalName
|
|
else return n
|
|
fs' <- scopeInFormals fs
|
|
x <- body
|
|
scopeOutFormals fs'
|
|
return (x, fs', n')
|
|
do { sIS; (el, fs', n') <- addScope expressionList; sColon; eol;
|
|
return (A.Specification m n' $ A.Function m (sm, rm) rs fs'
|
|
(Just $ Left $ A.Only m el), FunctionName, normalName) }
|
|
<|> do { eol; indent; (vp, fs', n') <- addScope valueProcess; outdent; sColon; eol;
|
|
return (A.Specification m n' $ A.Function m (sm, rm) rs fs'
|
|
(Just $ Left vp), FunctionName, normalName) }
|
|
<|> retypesAbbrev
|
|
<?> "definition"
|
|
where
|
|
chanInBundle :: OccParser (A.Name, A.Type)
|
|
chanInBundle = do sCHAN
|
|
t <- protocol
|
|
n <- newFieldName
|
|
dir <- direction
|
|
sColon
|
|
eol
|
|
return (n, A.ChanEnd dir A.Unshared t)
|
|
|
|
retypesAbbrev :: OccParser NameSpec
|
|
retypesAbbrev
|
|
= do m <- md
|
|
(am, s, n) <- tryVVVX refAbbrevMode dataSpecifier newVariableName retypesReshapes
|
|
v <- variable
|
|
sColon
|
|
eol
|
|
return (A.Specification m n $ A.Retypes m am s v, VariableName, normalName)
|
|
<|> do m <- md
|
|
(s, (d,n)) <- tryVVX channelSpecifier (maybeDirected newChannelName) retypesReshapes
|
|
c <- directedChannel
|
|
sColon
|
|
eol
|
|
s' <- d s
|
|
return (A.Specification m n $ A.Retypes m A.Abbrev s' c, ChannelName, normalName)
|
|
<|> do m <- md
|
|
(am, s, n) <- tryVVVX valAbbrevMode dataSpecifier newVariableName retypesReshapes
|
|
e <- expression
|
|
sColon
|
|
eol
|
|
return (A.Specification m n $ A.RetypesExpr m am s e, VariableName, normalName)
|
|
<?> "RETYPES/RESHAPES abbreviation"
|
|
where
|
|
retypesReshapes :: OccParser ()
|
|
retypesReshapes
|
|
= sRETYPES <|> sRESHAPES
|
|
|
|
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 [NameFormal]
|
|
formalList
|
|
= do m <- md
|
|
sLeftR
|
|
fs <- option [] formalArgSet
|
|
sRightR
|
|
return fs
|
|
<?> "formal list"
|
|
|
|
formalItem :: OccParser (A.AbbrevMode, A.Type) -> NameType -> OccParser [NameFormal]
|
|
formalItem spec nt
|
|
= do (am, t) <- spec
|
|
names am t
|
|
where
|
|
names :: A.AbbrevMode -> A.Type -> OccParser [NameFormal]
|
|
names am t
|
|
= do (direct, n) <- maybeDirected $ newName nt
|
|
fs <- tail am t
|
|
t' <- direct t
|
|
return $ (A.Formal am t' n, nt) : fs
|
|
|
|
tail :: A.AbbrevMode -> A.Type -> OccParser [NameFormal]
|
|
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 [NameFormal]
|
|
formalArgSet
|
|
= formalItem formalVariableType VariableName
|
|
<|> formalItem (aa channelSpecifier) ChannelName
|
|
<|> formalItem (aa timerSpecifier) TimerName
|
|
<|> formalItem (aa portSpecifier) PortName
|
|
where
|
|
aa :: OccParser A.Type -> OccParser (A.AbbrevMode, A.Type)
|
|
aa = liftM (\t -> (A.Abbrev, t))
|
|
|
|
formalVariableType :: OccParser (A.AbbrevMode, A.Type)
|
|
formalVariableType
|
|
= do am <-
|
|
(sVAL >> return A.ValAbbrev)
|
|
<|> (sINITIAL >> return A.InitialAbbrev)
|
|
<|> (sRESULT >> return A.ResultAbbrev)
|
|
<|> return A.Abbrev
|
|
s <- dataSpecifier
|
|
return (am, s)
|
|
<?> "formal variable type"
|
|
|
|
valueProcess :: OccParser (A.Structured A.ExpressionList)
|
|
valueProcess
|
|
= do m <- md
|
|
sVALOF
|
|
eol
|
|
indent
|
|
p <- process
|
|
sRESULT
|
|
el <- expressionList
|
|
eol
|
|
outdent
|
|
return $ A.ProcThen m p (A.Only m el)
|
|
<|> handleSpecs specification valueProcess A.Spec
|
|
<?> "value process"
|
|
--}}}
|
|
--{{{ RECORDs
|
|
structuredType :: OccParser A.SpecType
|
|
structuredType
|
|
= do m <- md
|
|
attr <- recordKeyword
|
|
eol
|
|
indent
|
|
fs <- many1 structuredTypeField
|
|
outdent
|
|
return $ A.RecordType m attr (concat fs)
|
|
<?> "structured type"
|
|
|
|
recordKeyword :: OccParser A.RecordAttr
|
|
recordKeyword
|
|
= do { tryXXX sPACKED sMOBILE sRECORD; return $ A.RecordAttr True True }
|
|
<|> do { tryXXX sMOBILE sPACKED sRECORD; return $ A.RecordAttr True True }
|
|
<|> do { tryXX sPACKED sRECORD; return $ A.RecordAttr True False }
|
|
<|> do { tryXX sMOBILE sRECORD; return $ A.RecordAttr False True }
|
|
<|> do { sRECORD; return $ A.RecordAttr False 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"
|
|
--}}}
|
|
--}}}
|
|
--{{{ pragmas
|
|
pragma :: OccParser (Maybe NameSpec)
|
|
pragma = do m <- getPosition >>* sourcePosToMeta
|
|
Pragma rawP <- genToken isPragma
|
|
let prag :: Maybe (Either (OccParser (Maybe NameSpec))
|
|
(String, OccParser (Maybe NameSpec)))
|
|
prag = join $ find isJust
|
|
[ fmap (f m) (matchRegex (mkRegex pt) rawP)
|
|
| (pt, f) <- pragmas
|
|
]
|
|
ns <- case prag of
|
|
Just (Right (pragStr, prod)) -> do
|
|
let column = metaColumn m + fromMaybe 0 (findIndex (=='\"') rawP)
|
|
toks <- runLexer' (fromMaybe "<unknown(pragma)>" $ metaFile m
|
|
, metaLine m, column) pragStr
|
|
cs <- getState
|
|
case runParser (do {n <- prod; s <- getState; return (n, s)}) cs "" toks of
|
|
Left err -> do warnP m WarnUnknownPreprocessorDirective $
|
|
"Unknown PRAGMA (parse failed): " ++ show err
|
|
return Nothing
|
|
Right (n, st) -> do setState st
|
|
return n
|
|
Just (Left norm) -> norm
|
|
_ -> do warnP m WarnUnknownPreprocessorDirective $
|
|
"Unknown PRAGMA type: " ++ show rawP
|
|
return Nothing
|
|
eol
|
|
return ns
|
|
|
|
where
|
|
-- The Right return expects the given string to be lexed then parsed, whereas
|
|
-- the Left return is just some code to run as normal, that won't consume
|
|
-- any input.
|
|
pragmas :: [ (String, Meta -> [String] -> Either (OccParser (Maybe NameSpec))
|
|
(String, OccParser (Maybe NameSpec)) ) ]
|
|
pragmas = [ ("^SHARED +(.*)", parseContents handleShared)
|
|
, ("^PERMITALIASES +(.*)", parseContents handlePermitAliases)
|
|
, ("^EXTERNAL +\"(.*)\"", parseContents $ handleExternal True)
|
|
, ("^TOCKEXTERNAL +\"(.*)\"", parseContents $ handleExternal False)
|
|
, ("^TOCKUNSCOPE +(.*)", simple handleUnscope)
|
|
, ("^TOCKSIZES +\"(.*)\"", simple handleSizes)
|
|
, ("^TOCKINCLUDE +\"(.*)\"", simple handleInclude)
|
|
, ("^TOCKNATIVELINK +\"(.*)\"", simple handleNativeLink)
|
|
]
|
|
where
|
|
parseContents :: (Meta -> OccParser (Maybe NameSpec))
|
|
-> Meta -> [String] -> Either a (String, OccParser (Maybe NameSpec))
|
|
parseContents p m [s] = Right (s, p m)
|
|
|
|
simple :: (Die m, CSM m) => (Meta -> [String] -> m (Maybe NameSpec))
|
|
-> Meta -> [String] -> Either (m (Maybe NameSpec)) a
|
|
simple p m ss = Left $ p m ss
|
|
|
|
handleShared m
|
|
= do vars <- sepBy1 identifier sComma
|
|
mapM_ (\var ->
|
|
do st <- getState
|
|
A.Name _ n <- case lookup var (csLocalNames st) of
|
|
Nothing -> dieP m $ "name " ++ var ++ " not defined"
|
|
Just (n, _, _) -> return n
|
|
modifyCompState $ \st -> st {csNameAttr = Map.insertWith Set.union
|
|
n (Set.singleton NameShared) (csNameAttr st)})
|
|
vars
|
|
return Nothing
|
|
|
|
handlePermitAliases m
|
|
= do vars <- sepBy1 identifier sComma
|
|
mapM_ (\var ->
|
|
do st <- getState
|
|
A.Name _ n <- case lookup var (csLocalNames st) of
|
|
Nothing -> dieP m $ "name " ++ var ++ " not defined"
|
|
Just (n, _, _) -> return n
|
|
modifyCompState $ \st -> st {csNameAttr = Map.insertWith Set.union
|
|
n (Set.singleton NameAliasesPermitted) (csNameAttr st)})
|
|
vars
|
|
return Nothing
|
|
handleSizes m [pragStr]
|
|
= do case metaFile m of
|
|
Nothing -> dieP m "PRAGMA TOCKSIZES in undeterminable file"
|
|
Just f -> let (f', _) = splitExtension f in
|
|
modifyCompState $ \cs -> cs { csExtraSizes = (f' ++ pragStr) : csExtraSizes cs }
|
|
return Nothing
|
|
handleInclude m [pragStr]
|
|
= do case metaFile m of
|
|
Nothing -> dieP m "PRAGMA TOCKINCLUDE in undeterminable file"
|
|
Just f -> let (f', _) = splitExtension f in
|
|
modifyCompState $ \cs -> cs { csExtraIncludes = (f' ++ pragStr) : csExtraIncludes cs }
|
|
return Nothing
|
|
handleNativeLink m [pragStr]
|
|
= do modifyCompOpts $ \cs -> cs { csCompilerLinkFlags = csCompilerLinkFlags cs ++ " " ++ pragStr}
|
|
return Nothing
|
|
|
|
handleExternal isCExternal m
|
|
= do m <- md
|
|
(n, nt, origN, fs, sp) <-
|
|
if isCExternal
|
|
then do sPROC
|
|
n <- newProcName
|
|
fs <- formalList'
|
|
sEq
|
|
integer
|
|
return (n, ProcName, n, fs, A.Proc m (A.PlainSpec, A.PlainRec) fs Nothing)
|
|
else do sPROC
|
|
origN <- anyName ProcName
|
|
fs <- formalList'
|
|
sEq
|
|
n <- newProcName
|
|
return (n, ProcName, origN, fs, A.Proc m (A.PlainSpec, A.PlainRec) fs Nothing)
|
|
<|> do ts <- tryVX (sepBy1 dataType sComma) sFUNCTION
|
|
origN <- anyName FunctionName <|> newUDOName
|
|
fs <- formalList'
|
|
sEq
|
|
n <- newFunctionName
|
|
return (n, FunctionName, origN, fs, A.Function m (A.PlainSpec, A.PlainRec) ts fs
|
|
Nothing)
|
|
let ext = if isCExternal then ExternalOldStyle else ExternalOccam
|
|
modifyCompState $ \st -> st
|
|
{ csExternals = (A.nameName n, ext) : csExternals st
|
|
}
|
|
return $ Just (A.Specification m origN sp, nt, (Just n, A.NameExternal))
|
|
|
|
handleUnscope _ [unscope]
|
|
= do st <- getState
|
|
setState $ st { csLocalNames = unscopeLatest $ csLocalNames st }
|
|
return Nothing
|
|
where
|
|
unscopeLatest (l@(s, (n, nt, _)): ls)
|
|
| s == unscope
|
|
= (s, (n, nt, False)) : ls
|
|
| otherwise
|
|
= l : unscopeLatest ls
|
|
|
|
isPragma (Token _ p@(Pragma {})) = Just p
|
|
isPragma _ = Nothing
|
|
|
|
formalList' = do fs <- formalList >>= scopeInFormals
|
|
scopeOutFormals fs
|
|
return fs
|
|
|
|
--}}}
|
|
--{{{ 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 <|> claimSpec) process
|
|
(\m s p -> A.Seq m (A.Spec m s (A.Only m p)))
|
|
<|> do m <- md
|
|
sFORKING
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
n <- makeNonce m "fork" >>* A.Name m
|
|
let spec = A.Specification m n $ A.Forking m
|
|
let nd = A.NameDef {
|
|
A.ndMeta = m,
|
|
A.ndName = A.nameName n,
|
|
A.ndOrigName = "FORKING",
|
|
A.ndSpecType = A.Forking m,
|
|
A.ndAbbrevMode = A.Original,
|
|
A.ndNameSource = A.NameNonce,
|
|
A.ndPlacement = A.Unplaced
|
|
}
|
|
defineName n nd
|
|
return $ A.Seq m $ A.Spec m spec $ A.Only m p
|
|
<|> do m <- md
|
|
sFORK
|
|
p <- procInstance
|
|
return $ A.Fork m Nothing p
|
|
<?> "process"
|
|
|
|
claimSpec :: OccParser ([NameSpec], OccParser ())
|
|
claimSpec
|
|
= do m <- md
|
|
v <- tryXV sCLAIM (variable <|> directedChannel)
|
|
n <- getName v >>= getOrigName
|
|
eol
|
|
indent
|
|
return ([(A.Specification m (A.Name m n) $ A.Is m A.Abbrev A.Infer $ A.ActualClaim v, ChannelName, normalName)], outdent)
|
|
where
|
|
getName :: A.Variable -> OccParser A.Name
|
|
getName (A.Variable _ n) = return n
|
|
getName (A.DirectedVariable _ _ v) = getName v
|
|
getName v = dieP (findMeta v) $ "Cannot abbreviate array/dereference"
|
|
|
|
getOrigName :: A.Name -> OccParser String
|
|
getOrigName n
|
|
= do st <- getState
|
|
case lookup n [(munged, orig) | (orig, (munged, _, True)) <- csLocalNames st] of
|
|
Just orig -> return orig
|
|
Nothing -> dieP (A.nameMeta n) $ "Could not find name: " ++ (A.nameName n)
|
|
|
|
--{{{ assignment (:=)
|
|
assignment :: OccParser A.Process
|
|
assignment
|
|
= do m <- md
|
|
vs <- tryVX (sepBy1 variable sComma) sAssign
|
|
es <- expressionList
|
|
eol
|
|
return $ A.Assign m vs es
|
|
<?> "assignment"
|
|
--}}}
|
|
--{{{ input (?)
|
|
inputProcess :: OccParser A.Process
|
|
inputProcess
|
|
= do m <- md
|
|
(c, i, mp) <- input False
|
|
return $ case mp of
|
|
Nothing -> A.Input m c i
|
|
Just p -> A.Seq m $ A.Several m $ map (A.Only m) [A.Input m c i, p]
|
|
<?> "input process"
|
|
|
|
-- True for in-ALT, False for normal
|
|
input :: Bool ->OccParser (A.Variable, A.InputMode, Maybe A.Process)
|
|
input inAlt
|
|
= channelInput inAlt
|
|
<|> (timerInput >>* (\(a, b) -> (a, b, Nothing)))
|
|
<|> do m <- md
|
|
p <- tryVX port sQuest
|
|
v <- variable
|
|
eol
|
|
return (p, A.InputSimple m [A.InVariable m v] Nothing, Nothing)
|
|
<?> "input"
|
|
|
|
channelInput :: Bool -> OccParser (A.Variable, A.InputMode, Maybe A.Process)
|
|
channelInput inAlt
|
|
= do m <- md
|
|
( do c <- tryVX channel sQuest
|
|
caseInput m c <|> plainInput m c
|
|
<|> do c <- tryVX channel sDoubleQuest
|
|
extCaseInput m c <|> extInput m c
|
|
)
|
|
<?> "channel input"
|
|
where
|
|
caseInput m c
|
|
= do sCASE
|
|
tl <- taggedList
|
|
eol
|
|
return (c, A.InputCase m A.InputCaseNormal (A.Only m (tl (A.Skip m) Nothing)), Nothing)
|
|
plainInput m c
|
|
= do is <- sepBy1 inputItem sSemi
|
|
eol
|
|
return (c, A.InputSimple m is Nothing, Nothing)
|
|
|
|
extInput m c
|
|
= do is <- sepBy1 inputItem sSemi
|
|
eol
|
|
indent
|
|
p <- process
|
|
mp <- if inAlt then return (Just $ error "internal ALT error") else (tryVX process outdent >>* Just) <|> (outdent >> return Nothing)
|
|
return (c, A.InputSimple m is (Just p), mp)
|
|
|
|
extCaseInput m c
|
|
= do sCASE
|
|
tl <- taggedList
|
|
eol
|
|
indent
|
|
p <- process
|
|
mp <- if inAlt then return Nothing else (tryVX process outdent >>* Just) <|> (outdent >> return Nothing)
|
|
return (c, A.InputCase m A.InputCaseExtended (A.Only m (tl p mp)),
|
|
if inAlt then Just $ error "internal ALT error" else Nothing)
|
|
|
|
timerInput :: OccParser (A.Variable, A.InputMode)
|
|
timerInput
|
|
= do m <- md
|
|
c <- tryVX timer sQuest
|
|
do { v <- variable; eol; return (c, A.InputTimerRead m (A.InVariable m v)) }
|
|
<|> do { sAFTER; e <- expression; eol; return (c, A.InputTimerAfter m e) }
|
|
<?> "timer input"
|
|
|
|
taggedList :: OccParser (A.Process -> Maybe A.Process -> A.Variant)
|
|
taggedList
|
|
= do m <- md
|
|
tag <- tagName
|
|
is <- many (sSemi >> inputItem)
|
|
return $ A.Variant m tag is
|
|
<?> "tagged list"
|
|
|
|
inputItem :: OccParser A.InputItem
|
|
inputItem
|
|
= do m <- md
|
|
v <- tryVX variable sColons
|
|
w <- variable
|
|
return $ A.InCounted m v w
|
|
<|> do m <- md
|
|
v <- variable
|
|
return $ A.InVariable m v
|
|
<?> "input item"
|
|
--}}}
|
|
--{{{ variant input (? CASE)
|
|
caseInput :: OccParser A.Process
|
|
caseInput
|
|
= do m <- md
|
|
c <- tryVX channel (sQuest >> sCASE >> eol)
|
|
vs <- maybeIndentedList m "empty ? CASE" (variant A.InputCaseNormal)
|
|
return $ A.Input m c (A.InputCase m A.InputCaseNormal (A.Several m vs))
|
|
<|> do m <- md
|
|
c <- tryVX channel (sDoubleQuest >> sCASE >> eol)
|
|
vs <- maybeIndentedList m "empty ? CASE" (variant A.InputCaseExtended)
|
|
return $ A.Input m c (A.InputCase m A.InputCaseExtended (A.Several m vs))
|
|
<?> "case input"
|
|
|
|
variant :: A.InputCaseType -> OccParser (A.Structured A.Variant)
|
|
variant ty
|
|
= do m <- md
|
|
tl <- tryVX taggedList eol
|
|
indent
|
|
p <- process
|
|
case ty of
|
|
A.InputCaseNormal -> do outdent
|
|
return $ A.Only m (tl p Nothing)
|
|
A.InputCaseExtended ->
|
|
do mp <- (tryVX process outdent >>* Just)
|
|
<|> (outdent >> return Nothing)
|
|
return $ A.Only m (tl p mp)
|
|
<|> handleSpecs specification (variant ty) A.Spec
|
|
<?> "variant"
|
|
--}}}
|
|
--{{{ output (!)
|
|
output :: OccParser A.Process
|
|
output
|
|
= channelOutput
|
|
<|> do m <- md
|
|
p <- tryVX port sBang
|
|
e <- expression
|
|
eol
|
|
return $ A.Output m p [A.OutExpression m e]
|
|
<?> "output"
|
|
|
|
channelOutput :: OccParser A.Process
|
|
channelOutput
|
|
= do m <- md
|
|
c <- tryVX channel sBang
|
|
-- AMBIGUITY: in "a ! b", b may be a tag or a variable.
|
|
regularOutput m c <|> caseOutput m c
|
|
<?> "channel output"
|
|
where
|
|
regularOutput m c
|
|
= do o <- try outputItem
|
|
os <- many (sSemi >> outputItem)
|
|
eol
|
|
return $ A.Output m c (o:os)
|
|
caseOutput m c
|
|
= do tag <- tagName
|
|
os <- many (sSemi >> outputItem)
|
|
eol
|
|
return $ A.OutputCase m c tag os
|
|
|
|
outputItem :: OccParser A.OutputItem
|
|
outputItem
|
|
= do m <- md
|
|
a <- tryVX expression sColons
|
|
b <- expression
|
|
return $ A.OutCounted m a b
|
|
<|> do m <- md
|
|
e <- expression
|
|
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 { (n, r) <- replicator; eol; indent;
|
|
n' <- scopeInRep n; p <- process; scopeOutRep n'; outdent;
|
|
return $ A.Seq m (A.Spec m (A.Specification m n' (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 { (n, r) <- replicator; eol; indent;
|
|
n' <- scopeInRep n; c <- ifChoice; scopeOutRep n'; outdent;
|
|
return $ A.Spec m (A.Specification m n' (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 <- tryVX expression 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
|
|
eol
|
|
os <- maybeIndentedList m "empty CASE" caseOption
|
|
return $ A.Case m sel (A.Several m os)
|
|
<?> "CASE process"
|
|
|
|
caseOption :: OccParser (A.Structured A.Option)
|
|
caseOption
|
|
= do m <- md
|
|
ces <- tryVX (sepBy1 expression 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 A.Spec
|
|
<?> "option"
|
|
--}}}
|
|
--{{{ WHILE
|
|
whileProcess :: OccParser A.Process
|
|
whileProcess
|
|
= do m <- md
|
|
sWHILE
|
|
b <- expression
|
|
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 { (n, r) <- replicator; eol; indent;
|
|
n' <- scopeInRep n; p <- process; scopeOutRep n'; outdent;
|
|
return $ A.Par m isPri (A.Spec m (A.Specification m n'
|
|
(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 <- expression
|
|
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 { (n, r) <- replicator; eol; indent;
|
|
n' <- scopeInRep n; a <- alternative; scopeOutRep n'; outdent;
|
|
return (isPri, A.Spec m (A.Specification m n' (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) <- tryVXVX expression sAmp channel (sQuest >> sCASE >> eol)
|
|
guardCaseBody m b c A.InputCaseNormal
|
|
<|> do m <- md
|
|
c <- tryVXX channel sQuest (sCASE >> eol)
|
|
guardCaseBody m (A.True m) c A.InputCaseNormal
|
|
<|> do m <- md
|
|
(b, c) <- tryVXVX expression sAmp channel (sDoubleQuest >> sCASE >> eol)
|
|
guardCaseBody m b c A.InputCaseExtended
|
|
<|> do m <- md
|
|
c <- tryVXX channel sDoubleQuest (sCASE >> eol)
|
|
guardCaseBody m (A.True m) c A.InputCaseExtended
|
|
<|> guardedAlternative
|
|
<|> handleSpecs specification alternative A.Spec
|
|
<?> "alternative"
|
|
where
|
|
guardCaseBody :: Meta -> A.Expression -> A.Variable -> A.InputCaseType -> OccParser (A.Structured A.Alternative)
|
|
guardCaseBody m b c ty
|
|
= do vs <- maybeIndentedList m "empty ? CASE" (variant ty)
|
|
return $ A.Only m (A.Alternative m b c (A.InputCase m ty $ A.Several m vs) (A.Skip m))
|
|
|
|
guardedAlternative :: OccParser (A.Structured A.Alternative)
|
|
guardedAlternative
|
|
= do m <- md
|
|
(makeAlt, alreadyIndentedAfterExt) <- guard
|
|
if alreadyIndentedAfterExt
|
|
-- There may or may not be a further process:
|
|
then (tryVX process outdent >>* (A.Only m . makeAlt))
|
|
<|> (outdent >> return (A.Only m $ makeAlt (A.Skip m)))
|
|
else do indent
|
|
p <- process
|
|
outdent
|
|
return $ A.Only m (makeAlt p)
|
|
<?> "guarded alternative"
|
|
|
|
guard :: OccParser (A.Process -> A.Alternative, Bool)
|
|
guard
|
|
= do m <- md
|
|
(c, im, ext) <- input True
|
|
return (A.Alternative m (A.True m) c im, isJust ext)
|
|
<|> do m <- md
|
|
sSKIP
|
|
eol
|
|
return (A.AlternativeSkip m (A.True m), False)
|
|
<|> do m <- md
|
|
b <- tryVX expression sAmp
|
|
do { (c, im, ext) <- input True; return (A.Alternative m b c im, isJust ext) }
|
|
<|> do { sSKIP; eol; return (A.AlternativeSkip m b, False) }
|
|
<?> "guard"
|
|
--}}}
|
|
--{{{ PROC calls
|
|
-- FIXME: This shouldn't need to look at the definition
|
|
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 -> expression >>* A.ActualExpression
|
|
_ ->
|
|
case stripArrayType t of
|
|
A.Chan {} -> var directedChannel <|> chanArray
|
|
A.ChanEnd {} -> var directedChannel <|> chanArray
|
|
A.ChanDataType {} -> var directedChannel
|
|
A.Timer {} -> var timer
|
|
A.Port _ -> var port
|
|
_ -> var variable
|
|
<?> "actual of type " ++ showOccam t ++ " for " ++ show n
|
|
where
|
|
var inner = inner >>* A.ActualVariable
|
|
chanArray = tryXVX sLeft (sepBy1 directedChannel sComma) sRight
|
|
>>* A.ActualChannelArray
|
|
--}}}
|
|
--{{{ intrinsic PROC call
|
|
intrinsicProcName :: OccParser (String, [A.Formal])
|
|
intrinsicProcName
|
|
= do n <- anyName ProcName
|
|
let s = A.nameName n
|
|
case lookup s intrinsicProcs of
|
|
Just atns -> return (s, [A.Formal am t (A.Name emptyMeta 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.
|
|
locals <- getState >>* csLocalNames
|
|
modifyCompState $ (\ps -> ps { csMainLocals =
|
|
[(s, (n, nt)) | (s, (n, nt, True)) <- locals] })
|
|
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, CompState)
|
|
sourceFile
|
|
= do p <- topLevelItem
|
|
s <- getState
|
|
return (p, compState 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 (OccParserState [] 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
|
|
(msgWs, msg') = unpackWarnings $ show err
|
|
(msgMeta, msg) = unpackMeta msg'
|
|
m = fromMaybe errMeta msgMeta
|
|
in do mapM_ warnReport msgWs
|
|
dieReport (Just m, "Parse error: " ++ msg)
|
|
Right r -> return r
|
|
|
|
-- | Parse an occam program.
|
|
parseOccamProgram :: [Token] -> PassM A.AST
|
|
parseOccamProgram toks
|
|
= do cs <- get
|
|
(p, cs') <- runTockParser (defaultDecl ++ toks) sourceFile cs
|
|
put cs'
|
|
return p
|
|
|
|
defaultDecl :: [Token]
|
|
defaultDecl = concat
|
|
[let params = [showOccam $ A.Formal A.ValAbbrev t (A.Name emptyMeta $ "x" ++
|
|
show i)
|
|
| (t, i :: Integer) <- zip ts [0..]]
|
|
in
|
|
[Token emptyMeta $ Pragma $ "TOCKEXTERNAL \""
|
|
++ showOccam rt
|
|
++ " FUNCTION \"" ++ concatMap doubleStar op ++ "\"("
|
|
++ joinWith "," params
|
|
++ ") = "
|
|
++ occamDefaultOperator op ts
|
|
++ "\""
|
|
,Token emptyMeta EndOfLine
|
|
]
|
|
| (op, rt, ts) <- occamIntrinsicOperators
|
|
]
|
|
where
|
|
doubleStar '*' = "**"
|
|
doubleStar c = [c]
|
|
|
|
--}}}
|
|
|