1470 lines
51 KiB
Haskell
1470 lines
51 KiB
Haskell
-- | Parse occam code into an AST.
|
|
module Parse where
|
|
|
|
import Control.Monad.State (StateT, execStateT, liftIO, modify, get)
|
|
import Data.List
|
|
import Data.Maybe
|
|
import qualified IO
|
|
import Numeric (readHex)
|
|
import Text.ParserCombinators.Parsec
|
|
import Text.ParserCombinators.Parsec.Language (emptyDef)
|
|
import qualified Text.ParserCombinators.Parsec.Token as P
|
|
import Text.Regex
|
|
|
|
import qualified AST as A
|
|
import Errors
|
|
import EvalConstants
|
|
import Indentation
|
|
import Metadata
|
|
import ParseState
|
|
import Pass
|
|
import Types
|
|
import Utils
|
|
|
|
--{{{ setup stuff for Parsec
|
|
type OccParser = GenParser Char ParseState
|
|
|
|
occamStyle
|
|
= emptyDef
|
|
{ P.commentLine = "--"
|
|
, P.nestedComments = False
|
|
, P.identStart = letter
|
|
, P.identLetter = alphaNum <|> char '.'
|
|
, P.opStart = oneOf "+-*/\\>=<~"
|
|
, P.opLetter = oneOf "/\\>=<"
|
|
, P.reservedOpNames= [
|
|
"+",
|
|
"-",
|
|
"*",
|
|
"/",
|
|
"\\",
|
|
"/\\",
|
|
"\\/",
|
|
"><",
|
|
"=",
|
|
"<>",
|
|
"<",
|
|
">",
|
|
">=",
|
|
"<=",
|
|
"-",
|
|
"~"
|
|
]
|
|
, P.reservedNames = [
|
|
"AFTER",
|
|
"ALT",
|
|
"AND",
|
|
"ANY",
|
|
"AT",
|
|
"BITAND",
|
|
"BITNOT",
|
|
"BITOR",
|
|
"BOOL",
|
|
"BYTE",
|
|
"BYTESIN",
|
|
"CASE",
|
|
"CHAN",
|
|
"DATA",
|
|
"ELSE",
|
|
"FALSE",
|
|
"FOR",
|
|
"FROM",
|
|
"FUNCTION",
|
|
"IF",
|
|
"INT",
|
|
"INT16",
|
|
"INT32",
|
|
"INT64",
|
|
"IS",
|
|
"MINUS",
|
|
"MOSTNEG",
|
|
"MOSTPOS",
|
|
"NOT",
|
|
"OF",
|
|
"OFFSETOF",
|
|
"OR",
|
|
"PACKED",
|
|
"PAR",
|
|
"PLACE",
|
|
"PLACED",
|
|
"PLUS",
|
|
"PORT",
|
|
"PRI",
|
|
"PROC",
|
|
"PROCESSOR",
|
|
"PROTOCOL",
|
|
"REAL32",
|
|
"REAL64",
|
|
"RECORD",
|
|
"REM",
|
|
"RESHAPES",
|
|
"RESULT",
|
|
"RETYPES",
|
|
"ROUND",
|
|
"SEQ",
|
|
"SIZE",
|
|
"SKIP",
|
|
"STOP",
|
|
"TIMER",
|
|
"TIMES",
|
|
"TRUE",
|
|
"TRUNC",
|
|
"TYPE",
|
|
"VAL",
|
|
"VALOF",
|
|
"WHILE",
|
|
"#INCLUDE",
|
|
"#USE",
|
|
indentMarker,
|
|
outdentMarker,
|
|
eolMarker,
|
|
mainMarker
|
|
]
|
|
, P.caseSensitive = True
|
|
}
|
|
|
|
lexer :: P.TokenParser ParseState
|
|
lexer = P.makeTokenParser occamStyle
|
|
|
|
-- XXX replace whitespace with something that doesn't eat \ns
|
|
|
|
whiteSpace = P.whiteSpace lexer
|
|
lexeme = P.lexeme lexer
|
|
symbol = P.symbol lexer
|
|
natural = P.natural lexer
|
|
parens = P.parens lexer
|
|
semi = P.semi lexer
|
|
identifier = P.identifier lexer
|
|
reserved = P.reserved lexer
|
|
reservedOp = P.reservedOp lexer
|
|
--}}}
|
|
|
|
--{{{ symbols
|
|
sLeft = try $ symbol "["
|
|
sRight = try $ symbol "]"
|
|
sLeftR = try $ symbol "("
|
|
sRightR = try $ symbol ")"
|
|
sAssign = try $ symbol ":="
|
|
sColon = try $ symbol ":"
|
|
sColons = try $ symbol "::"
|
|
sComma = try $ symbol ","
|
|
sSemi = try $ symbol ";"
|
|
sAmp = try $ symbol "&"
|
|
sQuest = try $ symbol "?"
|
|
sBang = try $ symbol "!"
|
|
sEq = try $ symbol "="
|
|
sApos = try $ symbol "'"
|
|
sQuote = try $ symbol "\""
|
|
sHash = try $ symbol "#"
|
|
--}}}
|
|
--{{{ keywords
|
|
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"
|
|
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"
|
|
sppINCLUDE = reserved "#INCLUDE"
|
|
sppUSE = reserved "#USE"
|
|
--}}}
|
|
--{{{ markers inserted by the preprocessor
|
|
-- XXX could handle VALOF by translating each step to one { and matching multiple ones?
|
|
mainMarker = "__main"
|
|
|
|
sMainMarker = do { whiteSpace; reserved mainMarker } <?> "end of input (top-level process)"
|
|
|
|
indent = do { whiteSpace; reserved indentMarker } <?> "indentation increase"
|
|
outdent = do { whiteSpace; reserved outdentMarker } <?> "indentation decrease"
|
|
eol = do { whiteSpace; reserved eolMarker } <?> "end of line"
|
|
--}}}
|
|
|
|
--{{{ helper functions
|
|
md :: OccParser Meta
|
|
md
|
|
= do pos <- getPosition
|
|
return Meta {
|
|
metaFile = Just $ sourceName pos,
|
|
metaLine = sourceLine pos,
|
|
metaColumn = sourceColumn pos
|
|
}
|
|
|
|
tryVX :: OccParser a -> OccParser b -> OccParser a
|
|
tryVX p q = try (do { v <- p; q; return v })
|
|
|
|
tryXV :: OccParser a -> OccParser b -> OccParser b
|
|
tryXV p q = try (do { p; q })
|
|
|
|
tryXVV :: OccParser a -> OccParser b -> OccParser c -> OccParser (b, c)
|
|
tryXVV a b c = try (do { a; bv <- b; cv <- c; return (bv, cv) })
|
|
|
|
tryXVX :: OccParser a -> OccParser b -> OccParser c -> OccParser b
|
|
tryXVX a b c = try (do { a; bv <- b; c; return bv })
|
|
|
|
tryVXV :: OccParser a -> OccParser b -> OccParser c -> OccParser (a, c)
|
|
tryVXV a b c = try (do { av <- a; b; cv <- c; return (av, cv) })
|
|
|
|
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' <- pSubscriptType sub t
|
|
rest <- postSubscripts t'
|
|
return $ sub : rest)
|
|
<|> return []
|
|
|
|
postSubscript :: A.Type -> OccParser A.Subscript
|
|
postSubscript t
|
|
= do m <- md
|
|
case t of
|
|
A.UserDataType _ ->
|
|
do f <- tryXV sLeft fieldName
|
|
sRight
|
|
return $ A.SubscriptField m f
|
|
A.Array _ _ ->
|
|
do e <- tryXV sLeft intExpr
|
|
sRight
|
|
return $ A.Subscript m e
|
|
_ ->
|
|
fail $ "subscript of non-array/record type " ++ show t
|
|
|
|
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
|
|
case t of
|
|
(A.Array _ _) -> return ()
|
|
_ -> fail $ "slice of non-array type " ++ show 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")
|
|
|
|
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'
|
|
|
|
-- | Like sepBy1, but not eager: it won't consume the separator unless it finds
|
|
-- another item after it.
|
|
sepBy1NE :: OccParser a -> OccParser b -> OccParser [a]
|
|
sepBy1NE item sep
|
|
= do i <- item
|
|
rest <- option [] $ try (do sep
|
|
sepBy1NE item sep)
|
|
return $ i : rest
|
|
|
|
-- | 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
|
|
|
|
listType :: Meta -> [A.Type] -> OccParser A.Type
|
|
listType m l = listType' m (length l) l
|
|
where
|
|
listType' m len [] = fail "expected non-empty list"
|
|
listType' m len [t] = return $ makeArrayType (A.Dimension $ makeConstant m len) t
|
|
listType' m len (t1 : rest@(t2 : _))
|
|
= if t1 == t2 then listType' m len rest
|
|
else fail "multiple types in list"
|
|
|
|
matchType :: A.Type -> A.Type -> OccParser ()
|
|
matchType et rt
|
|
= case (et, rt) of
|
|
((A.Array ds t), (A.Array ds' t')) ->
|
|
if length ds == length ds' then return () else bad
|
|
_ -> if rt == et then return () else bad
|
|
where
|
|
bad = fail $ "type mismatch (got " ++ show rt ++ "; expected " ++ show et ++ ")"
|
|
|
|
checkMaybe :: String -> Maybe a -> OccParser a
|
|
checkMaybe msg op
|
|
= case op of
|
|
Just t -> return t
|
|
Nothing -> fail msg
|
|
|
|
pTypeOf :: (ParseState -> a -> Maybe b) -> a -> OccParser b
|
|
pTypeOf f item
|
|
= do st <- getState
|
|
checkMaybe "cannot compute type" $ f st item
|
|
|
|
pTypeOfVariable = pTypeOf typeOfVariable
|
|
pTypeOfLiteral = pTypeOf typeOfLiteral
|
|
pTypeOfExpression = pTypeOf typeOfExpression
|
|
pSpecTypeOfName = pTypeOf specTypeOfName
|
|
|
|
pSubscriptType :: A.Subscript -> A.Type -> OccParser A.Type
|
|
pSubscriptType sub t
|
|
= do st <- getState
|
|
checkMaybe "cannot subscript type" $ subscriptType st sub t
|
|
--}}}
|
|
|
|
--{{{ name scoping
|
|
findName :: A.Name -> OccParser A.Name
|
|
findName thisN
|
|
= do st <- getState
|
|
origN <- case lookup (A.nameName thisN) (psLocalNames st) of
|
|
Nothing -> fail $ "name " ++ A.nameName thisN ++ " not defined"
|
|
Just n -> return n
|
|
if A.nameType thisN /= A.nameType origN
|
|
then fail $ "expected " ++ show (A.nameType thisN) ++ " (" ++ A.nameName origN ++ " is " ++ show (A.nameType origN) ++ ")"
|
|
else return $ thisN { A.nameName = A.nameName origN }
|
|
|
|
scopeIn :: A.Name -> A.SpecType -> A.AbbrevMode -> OccParser A.Name
|
|
scopeIn n@(A.Name m nt s) t am
|
|
= do st <- getState
|
|
let s' = s ++ "_u" ++ (show $ psNameCounter st)
|
|
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
|
|
}
|
|
setState $ psDefineName n' nd $ st {
|
|
psNameCounter = (psNameCounter st) + 1,
|
|
psLocalNames = (s, n') : (psLocalNames st)
|
|
}
|
|
return n'
|
|
|
|
scopeOut :: A.Name -> OccParser ()
|
|
scopeOut n@(A.Name m nt s)
|
|
= do st <- getState
|
|
let lns' = case psLocalNames st of
|
|
(s, _):ns -> ns
|
|
otherwise -> dieInternal "scopeOut trying to scope out the wrong name"
|
|
setState $ st { psLocalNames = lns' }
|
|
|
|
-- FIXME: Do these with generics? (going carefully to avoid nested code blocks)
|
|
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
|
|
|
|
-- This one's more complicated because we need to check if we're introducing a constant.
|
|
scopeInSpec :: A.Specification -> OccParser A.Specification
|
|
scopeInSpec (A.Specification m n st)
|
|
= do ps <- getState
|
|
let (st', isConst) = case st of
|
|
(A.IsExpr m A.ValAbbrev t e) ->
|
|
case simplifyExpression ps e of
|
|
Left _ -> (st, False)
|
|
Right e' -> (A.IsExpr m A.ValAbbrev t e', True)
|
|
_ -> (st, False)
|
|
n' <- scopeIn n st' (abbrevModeOfSpec st')
|
|
if isConst
|
|
then updateState (\ps -> ps { psConstants = (A.nameName n', case st' of A.IsExpr _ _ _ e' -> e') : psConstants ps })
|
|
else return ()
|
|
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 = name A.ChannelName
|
|
dataTypeName = name A.DataTypeName
|
|
functionName = name A.FunctionName
|
|
portName = name A.PortName
|
|
procName = name A.ProcName
|
|
protocolName = name A.ProtocolName
|
|
timerName = name A.TimerName
|
|
variableName = name A.VariableName
|
|
|
|
newChannelName = newName A.ChannelName
|
|
newDataTypeName = newName A.DataTypeName
|
|
newFunctionName = newName A.FunctionName
|
|
newPortName = newName A.PortName
|
|
newProcName = newName A.ProcName
|
|
newProtocolName = newName A.ProtocolName
|
|
newTimerName = newName A.TimerName
|
|
newVariableName = newName A.VariableName
|
|
|
|
-- These are special because their scope is only valid within the particular
|
|
-- record or protocol they're used in.
|
|
fieldName = anyName A.FieldName
|
|
tagName = anyName A.TagName
|
|
newFieldName = anyName A.FieldName
|
|
newTagName = anyName A.TagName
|
|
--}}}
|
|
--{{{ types
|
|
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 }
|
|
<|> try (do { sLeft; s <- constIntExpr; sRight; t <- dataType; return $ makeArrayType (A.Dimension s) t })
|
|
<|> do { n <- dataTypeName; return $ A.UserDataType n }
|
|
<?> "dataType"
|
|
|
|
-- FIXME should probably make CHAN INT work, since that'd be trivial...
|
|
channelType :: OccParser A.Type
|
|
channelType
|
|
= do { sCHAN; sOF; p <- protocol; return $ A.Chan p }
|
|
<|> try (do { sLeft; s <- constIntExpr; sRight; t <- channelType; return $ makeArrayType (A.Dimension s) t })
|
|
<?> "channelType"
|
|
|
|
timerType :: OccParser A.Type
|
|
timerType
|
|
= do { sTIMER; return $ A.Timer }
|
|
<|> try (do { sLeft; s <- constIntExpr; sRight; t <- timerType; return $ makeArrayType (A.Dimension s) t })
|
|
<?> "timerType"
|
|
|
|
portType :: OccParser A.Type
|
|
portType
|
|
= do { sPORT; sOF; p <- dataType; return $ A.Port p }
|
|
<|> do { m <- md; try sLeft; s <- try constIntExpr; try sRight; t <- portType; return $ makeArrayType (A.Dimension s) t }
|
|
<?> "portType"
|
|
--}}}
|
|
--{{{ literals
|
|
literal :: OccParser A.Literal
|
|
literal
|
|
= try (do { m <- md; v <- real; sLeftR; t <- dataType; sRightR; return $ A.Literal m t v })
|
|
<|> try (do { m <- md; v <- integer; sLeftR; t <- dataType; sRightR; return $ A.Literal m t v })
|
|
<|> try (do { m <- md; v <- byte; sLeftR; t <- dataType; sRightR; return $ A.Literal m t v })
|
|
<|> try (do { m <- md; r <- real; return $ A.Literal m A.Real32 r })
|
|
<|> try (do { m <- md; r <- integer; return $ A.Literal m A.Int r })
|
|
<|> try (do { m <- md; r <- byte; return $ A.Literal m A.Byte r })
|
|
<?> "literal"
|
|
|
|
real :: OccParser A.LiteralRepr
|
|
real
|
|
= try (do m <- md
|
|
l <- digits
|
|
char '.'
|
|
r <- digits
|
|
char 'e'
|
|
e <- lexeme occamExponent
|
|
return $ A.RealLiteral m (l ++ "." ++ r ++ "e" ++ e))
|
|
<|> do m <- md
|
|
l <- digits
|
|
char '.'
|
|
r <- lexeme digits
|
|
return $ A.RealLiteral m (l ++ "." ++ r)
|
|
<?> "real"
|
|
|
|
occamExponent :: OccParser String
|
|
occamExponent
|
|
= try (do { c <- oneOf "+-"; d <- digits; return $ c : d })
|
|
<?> "exponent"
|
|
|
|
integer :: OccParser A.LiteralRepr
|
|
integer
|
|
= try (do { m <- md; d <- lexeme digits; return $ A.IntLiteral m d })
|
|
<|> do { m <- md; sHash; d <- many1 hexDigit; return $ A.HexLiteral m d }
|
|
<?> "integer"
|
|
|
|
digits :: OccParser String
|
|
digits
|
|
= many1 digit
|
|
<?> "digits"
|
|
|
|
byte :: OccParser A.LiteralRepr
|
|
byte
|
|
= do { m <- md; char '\''; s <- character; sApos; return $ A.ByteLiteral m s }
|
|
<?> "byte"
|
|
|
|
-- i.e. array literal
|
|
table :: OccParser A.Literal
|
|
table
|
|
= maybeSubscripted "table" table' A.SubscriptedLiteral pTypeOfLiteral
|
|
|
|
table' :: OccParser A.Literal
|
|
table'
|
|
-- FIXME Check dimensions match
|
|
= try (do { m <- md; (s, dim) <- stringLiteral; sLeftR; t <- dataType; sRightR; return $ A.Literal m t s })
|
|
<|> try (do { m <- md; (s, dim) <- stringLiteral; return $ A.Literal m (A.Array [dim] A.Byte) s })
|
|
<|> do m <- md
|
|
es <- tryXVX sLeft (sepBy1 expression sComma) sRight
|
|
ps <- getState
|
|
ets <- mapM (\e -> checkMaybe "can't type expression" $ typeOfExpression ps e) es
|
|
t <- listType m ets
|
|
return $ A.Literal m t (A.ArrayLiteral m es)
|
|
<|> maybeSliced table A.SubscriptedLiteral pTypeOfLiteral
|
|
<?> "table'"
|
|
|
|
stringLiteral :: OccParser (A.LiteralRepr, A.Dimension)
|
|
stringLiteral
|
|
= do { m <- md; char '"'; cs <- manyTill character sQuote; return $ (A.StringLiteral m $ concat cs, A.Dimension $ makeConstant m $ length cs) }
|
|
<?> "stringLiteral"
|
|
|
|
character :: OccParser String
|
|
character
|
|
= try (do { char '*' ;
|
|
do char '#'
|
|
a <- hexDigit
|
|
b <- hexDigit
|
|
return $ ['*', '#', a, b]
|
|
-- FIXME: Handle *\n, which is just a line continuation?
|
|
<|> do { c <- anyChar; return ['*', c] } })
|
|
<|> do { c <- anyChar; return [c] }
|
|
<?> "character"
|
|
--}}}
|
|
--{{{ expressions
|
|
functionNameSingle :: OccParser A.Name
|
|
= do n <- functionName
|
|
rts <- (pTypeOf returnTypesOfFunction) n
|
|
case rts of
|
|
[_] -> return n
|
|
_ -> pzero
|
|
<?> "function with single return value"
|
|
|
|
functionNameMulti :: OccParser A.Name
|
|
= do n <- functionName
|
|
rts <- (pTypeOf returnTypesOfFunction) n
|
|
case rts of
|
|
[_] -> pzero
|
|
_ -> return n
|
|
<?> "function with multiple return values"
|
|
|
|
expressionList :: OccParser A.ExpressionList
|
|
expressionList
|
|
= try (do { m <- md; n <- functionNameMulti; sLeftR; as <- sepBy expression sComma; sRightR; return $ A.FunctionCallList m n as })
|
|
<|> 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)
|
|
<?> "expressionList"
|
|
|
|
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; sTRUE; return $ A.True m }
|
|
<|> do { m <- md; sFALSE; return $ A.False m }
|
|
<|> try (do { m <- md; l <- operand; o <- dyadicOperator; r <- operand; return $ A.Dyadic m o l r })
|
|
<|> try conversion
|
|
<|> operand
|
|
<?> "expression"
|
|
|
|
sizeExpr :: OccParser A.Expression
|
|
sizeExpr
|
|
= do m <- md
|
|
sSIZE
|
|
(try (do { t <- dataType; return $ A.SizeType m t })
|
|
<|> do { v <- operand; return $ A.SizeExpr m v }
|
|
<|> do { v <- channel <|> timer <|> port; return $ A.SizeVariable m v })
|
|
<?> "sizeExpr"
|
|
|
|
exprOfType :: A.Type -> OccParser A.Expression
|
|
exprOfType wantT
|
|
= do e <- expression
|
|
t <- pTypeOfExpression e
|
|
matchType wantT t
|
|
return e
|
|
|
|
intExpr = exprOfType A.Int <?> "integer expression"
|
|
booleanExpr = exprOfType A.Bool <?> "boolean expression"
|
|
|
|
constExprOfType :: A.Type -> OccParser A.Expression
|
|
constExprOfType wantT
|
|
= do e <- exprOfType wantT
|
|
ps <- getState
|
|
case simplifyExpression ps e of
|
|
Left err -> fail $ "expected constant expression (" ++ err ++ ")"
|
|
Right e' -> return e'
|
|
|
|
constIntExpr = constExprOfType A.Int <?> "constant integer expression"
|
|
|
|
monadicOperator :: OccParser A.MonadicOp
|
|
monadicOperator
|
|
= do { reservedOp "-" <|> sMINUS; return A.MonadicSubtr }
|
|
<|> do { reservedOp "~" <|> sBITNOT; return A.MonadicBitNot }
|
|
<|> do { sNOT; return A.MonadicNot }
|
|
<?> "monadicOperator"
|
|
|
|
dyadicOperator :: OccParser A.DyadicOp
|
|
dyadicOperator
|
|
= do { reservedOp "+"; return A.Add }
|
|
<|> do { reservedOp "-"; return A.Subtr }
|
|
<|> do { reservedOp "*"; return A.Mul }
|
|
<|> do { reservedOp "/"; return A.Div }
|
|
<|> do { reservedOp "\\"; return A.Rem }
|
|
<|> do { sREM; return A.Rem }
|
|
<|> do { sPLUS; return A.Plus }
|
|
<|> do { sMINUS; return A.Minus }
|
|
<|> do { sTIMES; return A.Times }
|
|
<|> do { reservedOp "/\\" <|> sBITAND; return A.BitAnd }
|
|
<|> do { reservedOp "\\/" <|> sBITOR; return A.BitOr }
|
|
<|> do { reservedOp "><"; return A.BitXor }
|
|
<|> do { sAND; return A.And }
|
|
<|> do { sOR; return A.Or }
|
|
<|> do { reservedOp "="; return A.Eq }
|
|
<|> do { reservedOp "<>"; return A.NotEq }
|
|
<|> do { reservedOp "<"; return A.Less }
|
|
<|> do { reservedOp ">"; return A.More }
|
|
<|> do { reservedOp "<="; return A.LessEq }
|
|
<|> do { reservedOp ">="; return A.MoreEq }
|
|
<|> do { sAFTER; return A.After }
|
|
<?> "dyadicOperator"
|
|
|
|
conversion :: OccParser A.Expression
|
|
conversion
|
|
= try (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) }
|
|
-- This uses operandNotTable to resolve the "x[y]" ambiguity.
|
|
<|> do { o <- operandNotTable; return (A.DefaultConversion, o) }
|
|
<?> "conversionMode"
|
|
--}}}
|
|
--{{{ operands
|
|
operand :: OccParser A.Expression
|
|
operand
|
|
= maybeSubscripted "operand" operand' A.SubscriptedExpr pTypeOfExpression
|
|
|
|
operand' :: OccParser A.Expression
|
|
operand'
|
|
= try (do { m <- md; l <- table; return $ A.ExprLiteral m l })
|
|
<|> operandNotTable'
|
|
<?> "operand'"
|
|
|
|
operandNotTable :: OccParser A.Expression
|
|
operandNotTable
|
|
= maybeSubscripted "operandNotTable" operandNotTable' A.SubscriptedExpr pTypeOfExpression
|
|
|
|
operandNotTable' :: OccParser A.Expression
|
|
operandNotTable'
|
|
= try (do { m <- md; v <- variable; return $ A.ExprVariable m v })
|
|
<|> try (do { m <- md; l <- literal; return $ A.ExprLiteral m l })
|
|
<|> try (do { sLeftR; e <- expression; sRightR; return e })
|
|
-- XXX value process
|
|
<|> try (do { m <- md; n <- functionNameSingle; sLeftR; as <- sepBy expression sComma; sRightR; return $ A.FunctionCall m n as })
|
|
<|> try (do { m <- md; sBYTESIN; sLeftR; o <- operand; sRightR; return $ A.BytesInExpr m o })
|
|
<|> try (do { m <- md; sBYTESIN; sLeftR; t <- dataType; sRightR; return $ A.BytesInType m t })
|
|
<|> try (do { m <- md; sOFFSETOF; sLeftR; t <- dataType; sComma; f <- fieldName; sRightR; return $ A.OffsetOf m t f })
|
|
<?> "operandNotTable'"
|
|
--}}}
|
|
--{{{ variables, channels, timers, ports
|
|
variable :: OccParser A.Variable
|
|
variable
|
|
= maybeSubscripted "variable" variable' A.SubscriptedVariable pTypeOfVariable
|
|
|
|
variable' :: OccParser A.Variable
|
|
variable'
|
|
= try (do { m <- md; n <- variableName; return $ A.Variable m n })
|
|
<|> try (maybeSliced variable A.SubscriptedVariable pTypeOfVariable)
|
|
<?> "variable'"
|
|
|
|
channel :: OccParser A.Variable
|
|
channel
|
|
= maybeSubscripted "channel" channel' A.SubscriptedVariable pTypeOfVariable
|
|
<?> "channel"
|
|
|
|
channel' :: OccParser A.Variable
|
|
channel'
|
|
= try (do { m <- md; n <- channelName; return $ A.Variable m n })
|
|
<|> try (maybeSliced channel A.SubscriptedVariable pTypeOfVariable)
|
|
<?> "channel'"
|
|
|
|
timer :: OccParser A.Variable
|
|
timer
|
|
= maybeSubscripted "timer" timer' A.SubscriptedVariable pTypeOfVariable
|
|
<?> "timer"
|
|
|
|
timer' :: OccParser A.Variable
|
|
timer'
|
|
= try (do { m <- md; n <- timerName; return $ A.Variable m n })
|
|
<|> try (maybeSliced timer A.SubscriptedVariable pTypeOfVariable)
|
|
<?> "timer'"
|
|
|
|
port :: OccParser A.Variable
|
|
port
|
|
= maybeSubscripted "port" port' A.SubscriptedVariable pTypeOfVariable
|
|
<?> "port"
|
|
|
|
port' :: OccParser A.Variable
|
|
port'
|
|
= try (do { m <- md; n <- portName; return $ A.Variable m n })
|
|
<|> try (maybeSliced port A.SubscriptedVariable pTypeOfVariable)
|
|
<?> "port'"
|
|
--}}}
|
|
--{{{ protocols
|
|
protocol :: OccParser A.Type
|
|
protocol
|
|
= try (do { n <- protocolName ; return $ A.UserProtocol n })
|
|
<|> simpleProtocol
|
|
<?> "protocol"
|
|
|
|
simpleProtocol :: OccParser A.Type
|
|
simpleProtocol
|
|
= try (do { l <- dataType; sColons; sLeft; sRight; r <- dataType; return $ A.Counted l r })
|
|
<|> dataType
|
|
<|> do { sANY; return $ A.Any }
|
|
<?> "simpleProtocol"
|
|
|
|
sequentialProtocol :: OccParser [A.Type]
|
|
sequentialProtocol
|
|
= do { l <- try $ sepBy1 simpleProtocol sSemi; return l }
|
|
<?> "sequentialProtocol"
|
|
|
|
taggedProtocol :: OccParser (A.Name, [A.Type])
|
|
taggedProtocol
|
|
= try (do { t <- newTagName; eol; return (t, []) })
|
|
<|> try (do { t <- newTagName; sSemi; sp <- sequentialProtocol; eol; return (t, sp) })
|
|
<?> "taggedProtocol"
|
|
--}}}
|
|
--{{{ 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 <- variableName; sAT; e <- intExpr; sColon; eol; return [A.Specification m n (A.Place m e)] }
|
|
<?> "allocation"
|
|
|
|
specification :: OccParser [A.Specification]
|
|
specification
|
|
= try (do { m <- md; (ns, d) <- declaration; return [A.Specification m n d | n <- ns] })
|
|
<|> try (do { a <- abbreviation; return [a] })
|
|
<|> do { d <- definition; return [d] }
|
|
<?> "specification"
|
|
|
|
declaration :: OccParser ([A.Name], A.SpecType)
|
|
declaration
|
|
= do { m <- md; d <- dataType; ns <- sepBy1 newVariableName sComma; sColon; eol; return (ns, A.Declaration m d) }
|
|
<|> do { m <- md; d <- channelType; ns <- sepBy1 newChannelName sComma; sColon; eol; return (ns, A.Declaration m d) }
|
|
<|> do { m <- md; d <- timerType; ns <- sepBy1 newTimerName sComma; sColon; eol; return (ns, A.Declaration m d) }
|
|
<|> do { m <- md; d <- portType; ns <- sepBy1 newPortName sComma; sColon; eol; return (ns, A.Declaration m d) }
|
|
<?> "declaration"
|
|
|
|
abbreviation :: OccParser A.Specification
|
|
abbreviation
|
|
= do m <- md
|
|
(do { (n, v) <- tryVXV newVariableName sIS variable; sColon; eol; t <- pTypeOfVariable v; return $ A.Specification m n $ A.Is m A.Abbrev t v }
|
|
<|> do { (s, n, v) <- try (do { s <- specifier; n <- newVariableName; sIS; v <- variable; return (s, n, v) }); sColon; eol; t <- pTypeOfVariable v; matchType s t; return $ A.Specification m n $ A.Is m A.Abbrev s v }
|
|
<|> valIsAbbrev
|
|
<|> try (do { n <- newChannelName; sIS; c <- channel; sColon; eol; t <- pTypeOfVariable c; return $ A.Specification m n $ A.Is m A.Abbrev t c })
|
|
<|> try (do { n <- newTimerName; sIS; c <- timer; sColon; eol; t <- pTypeOfVariable c; return $ A.Specification m n $ A.Is m A.Abbrev t c })
|
|
<|> try (do { n <- newPortName; sIS; c <- port; sColon; eol; t <- pTypeOfVariable c; return $ A.Specification m n $ A.Is m A.Abbrev t c })
|
|
<|> try (do { s <- specifier; n <- newChannelName; sIS; c <- channel; sColon; eol; t <- pTypeOfVariable c; matchType s t; return $ A.Specification m n $ A.Is m A.Abbrev s c })
|
|
<|> try (do { s <- specifier; n <- newTimerName; sIS; c <- timer; sColon; eol; t <- pTypeOfVariable c; matchType s t; return $ A.Specification m n $ A.Is m A.Abbrev s c })
|
|
<|> try (do { s <- specifier; n <- newPortName; sIS; c <- port; sColon; eol; t <- pTypeOfVariable c; matchType s t; return $ A.Specification m n $ A.Is m A.Abbrev s c })
|
|
<|> try (do { n <- newChannelName; sIS; sLeft; cs <- sepBy1 channel sComma; sRight; sColon; eol; ts <- mapM pTypeOfVariable cs; t <- listType m ts; return $ A.Specification m n $ A.IsChannelArray m t cs })
|
|
<|> try (do { s <- specifier; n <- newChannelName; sIS; sLeft; cs <- sepBy1 channel sComma; sRight; sColon; eol; ts <- mapM pTypeOfVariable cs; t <- listType m ts; matchType s t; return $ A.Specification m n $ A.IsChannelArray m s cs }))
|
|
<?> "abbreviation"
|
|
|
|
valIsAbbrev :: OccParser A.Specification
|
|
valIsAbbrev
|
|
= do m <- md
|
|
sVAL
|
|
(n, t, e) <- do { (n, e) <- tryVXV newVariableName sIS expression; sColon; eol; t <- pTypeOfExpression e; return (n, t, e) }
|
|
<|> do { s <- specifier; n <- newVariableName; sIS; e <- expression; sColon; eol; t <- pTypeOfExpression e; matchType s t; return (n, t, e) }
|
|
return $ A.Specification m n $ A.IsExpr m A.ValAbbrev t e
|
|
<?> "VAL IS abbreviation"
|
|
|
|
definition :: OccParser A.Specification
|
|
definition
|
|
= do { m <- md; sDATA; sTYPE; n <- newDataTypeName ;
|
|
do {sIS; t <- dataType; sColon; eol; return $ A.Specification m n (A.DataType m t) }
|
|
<|> do { 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; indent; ps <- many1 taggedProtocol; outdent; outdent; sColon; eol; return $ A.Specification m n $ A.ProtocolCase m ps } }
|
|
<|> do { m <- md; 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 fs' p }
|
|
<|> try (do { m <- md; rs <- sepBy1 dataType sComma; (n, fs) <- functionHeader ;
|
|
do { sIS; fs' <- scopeInFormals fs; el <- expressionList; scopeOutFormals fs'; sColon; eol; return $ A.Specification m n $ A.Function m rs fs' (A.ValOf m (A.Skip m) el) }
|
|
<|> do { eol; indent; fs' <- scopeInFormals fs; vp <- valueProcess; scopeOutFormals fs'; outdent; sColon; eol; return $ A.Specification m n $ A.Function m rs fs' vp } })
|
|
<|> try (do { m <- md; s <- specifier; n <- newVariableName ;
|
|
sRETYPES <|> sRESHAPES; v <- variable; sColon; eol; return $ A.Specification m n $ A.Retypes m A.Abbrev s v })
|
|
<|> try (do { m <- md; sVAL; s <- specifier; n <- newVariableName ;
|
|
sRETYPES <|> sRESHAPES; e <- expression; sColon; eol; return $ A.Specification m n $ A.RetypesExpr m A.ValAbbrev s e })
|
|
<?> "definition"
|
|
|
|
dataSpecifier :: OccParser A.Type
|
|
dataSpecifier
|
|
= try dataType
|
|
<|> try (do { sLeft; sRight; s <- dataSpecifier; return $ makeArrayType A.UnknownDimension s })
|
|
<?> "dataSpecifier"
|
|
|
|
specifier :: OccParser A.Type
|
|
specifier
|
|
= try dataType
|
|
<|> try channelType
|
|
<|> try timerType
|
|
<|> try portType
|
|
<|> try (do { sLeft; sRight; s <- specifier; return $ makeArrayType A.UnknownDimension s })
|
|
<?> "specifier"
|
|
|
|
--{{{ PROCs and FUNCTIONs
|
|
formalList :: OccParser [A.Formal]
|
|
formalList
|
|
= do m <- md
|
|
sLeftR
|
|
fs <- sepBy formalArgSet sComma
|
|
sRightR
|
|
return $ concat fs
|
|
<?> "formalList"
|
|
|
|
formalArgSet :: OccParser [A.Formal]
|
|
formalArgSet
|
|
= try (do (am, t) <- formalVariableType
|
|
ns <- sepBy1NE newVariableName sComma
|
|
return [A.Formal am t n | n <- ns])
|
|
<|> do t <- specifier
|
|
ns <- sepBy1NE newChannelName sComma
|
|
return [A.Formal A.Abbrev t n | n <- ns]
|
|
<?> "formalArgSet"
|
|
|
|
formalVariableType :: OccParser (A.AbbrevMode, A.Type)
|
|
= try (do { sVAL; s <- dataSpecifier; return (A.ValAbbrev, s) })
|
|
<|> do { s <- dataSpecifier; return (A.Abbrev, s) }
|
|
<?> "formalVariableType"
|
|
|
|
functionHeader :: OccParser (A.Name, [A.Formal])
|
|
functionHeader
|
|
= do { sFUNCTION; n <- newFunctionName; fs <- formalList; return $ (n, fs) }
|
|
<?> "functionHeader"
|
|
|
|
valueProcess :: OccParser A.ValueProcess
|
|
valueProcess
|
|
= try (do { m <- md; sVALOF; eol; indent; p <- process; sRESULT; el <- expressionList; eol; outdent; return $ A.ValOf m p el })
|
|
<|> handleSpecs specification valueProcess A.ValOfSpec
|
|
--}}}
|
|
--{{{ RECORDs
|
|
structuredType :: OccParser A.SpecType
|
|
structuredType
|
|
= do m <- md
|
|
isPacked <- recordKeyword
|
|
eol
|
|
indent
|
|
fs <- many1 structuredTypeField
|
|
outdent
|
|
return $ A.DataTypeRecord m isPacked (concat fs)
|
|
<?> "structuredType"
|
|
|
|
recordKeyword :: OccParser Bool
|
|
recordKeyword
|
|
= do { sPACKED; sRECORD; return True }
|
|
<|> do { sRECORD; return False }
|
|
<?> "recordKeyword"
|
|
|
|
structuredTypeField :: OccParser [(A.Name, A.Type)]
|
|
structuredTypeField
|
|
= do { t <- dataType; fs <- many1 newFieldName; sColon; eol; return [(f, t) | f <- fs] }
|
|
<?> "structuredTypeField"
|
|
--}}}
|
|
--}}}
|
|
--{{{ 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
|
|
<|> mainProcess
|
|
<|> handleSpecs (allocation <|> specification) process A.ProcSpec
|
|
<|> preprocessorDirective
|
|
<?> "process"
|
|
|
|
--{{{ assignment (:=)
|
|
assignment :: OccParser A.Process
|
|
assignment
|
|
= do { m <- md; vs <- tryVX variableList sAssign; es <- expressionList; eol; return $ A.Assign m vs es }
|
|
<?> "assignment"
|
|
|
|
variableList :: OccParser [A.Variable]
|
|
variableList
|
|
= do { vs <- sepBy1 variable sComma; return $ vs }
|
|
<?> "variableList"
|
|
--}}}
|
|
--{{{ input (?)
|
|
inputProcess :: OccParser A.Process
|
|
inputProcess
|
|
= do m <- md
|
|
(c, i) <- input
|
|
return $ A.Input m c i
|
|
|
|
input :: OccParser (A.Variable, A.InputMode)
|
|
input
|
|
= channelInput
|
|
<|> timerInput
|
|
<|> do { m <- md; p <- tryVX port sQuest; v <- variable; eol; return (p, A.InputSimple m [A.InVariable m v]) }
|
|
<?> "input"
|
|
|
|
channelInput :: OccParser (A.Variable, A.InputMode)
|
|
= do m <- md
|
|
c <- tryVX channel sQuest
|
|
(do { tl <- try (do { sCASE; taggedList }); eol; return (c, A.InputCase m (A.OnlyV m (tl (A.Skip m)))) }
|
|
<|> do { sAFTER; e <- intExpr; eol; return (c, A.InputAfter m e) }
|
|
<|> do { is <- sepBy1 inputItem sSemi; eol; return (c, A.InputSimple m is) })
|
|
<?> "channelInput"
|
|
|
|
timerInput :: OccParser (A.Variable, A.InputMode)
|
|
= do m <- md
|
|
c <- tryVX timer sQuest
|
|
(do { v <- variable; eol; return (c, A.InputSimple m [A.InVariable m v]) }
|
|
<|> do { sAFTER; e <- intExpr; eol; return (c, A.InputAfter m e) })
|
|
<?> "timerInput"
|
|
|
|
taggedList :: OccParser (A.Process -> A.Variant)
|
|
taggedList
|
|
= try (do { m <- md; t <- tagName; sSemi; is <- sepBy1 inputItem sSemi; return $ A.Variant m t is })
|
|
<|> do { m <- md; t <- tagName; return $ A.Variant m t [] }
|
|
<?> "taggedList"
|
|
|
|
inputItem :: OccParser A.InputItem
|
|
inputItem
|
|
= try (do { m <- md; v <- variable; sColons; w <- variable; return $ A.InCounted m v w })
|
|
<|> do { m <- md; v <- variable; return $ A.InVariable m v }
|
|
<?> "inputItem"
|
|
--}}}
|
|
--{{{ variant input (? CASE)
|
|
caseInput :: OccParser A.Process
|
|
caseInput
|
|
= do m <- md
|
|
c <- tryVX channel (do {sQuest; sCASE; eol})
|
|
indent
|
|
vs <- many1 variant
|
|
outdent
|
|
return $ A.Input m c (A.InputCase m (A.Several m vs))
|
|
<?> "caseInput"
|
|
|
|
variant :: OccParser A.Structured
|
|
variant
|
|
= try (do { m <- md; tl <- taggedList; eol; indent; p <- process; outdent; return $ A.OnlyV m (tl p) })
|
|
<|> handleSpecs specification variant 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
|
|
-- 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.
|
|
st <- getState
|
|
isCase <- case typeOfVariable st c of
|
|
Just t -> return $ isCaseProtocolType st t
|
|
Nothing -> fail $ "cannot figure out the type of " ++ show c
|
|
if isCase
|
|
then
|
|
(try (do { t <- tagName; sSemi; os <- sepBy1 outputItem sSemi; eol; return $ A.OutputCase m c t os })
|
|
<|> do { t <- tagName; eol; return $ A.OutputCase m c t [] })
|
|
else
|
|
do { os <- sepBy1 outputItem sSemi; eol; return $ A.Output m c os }
|
|
<?> "channelOutput"
|
|
|
|
outputItem :: OccParser A.OutputItem
|
|
outputItem
|
|
= try (do { m <- md; a <- intExpr; sColons; b <- expression; return $ A.OutCounted m a b })
|
|
<|> do { m <- md; e <- expression; return $ A.OutExpression m e }
|
|
<?> "outputItem"
|
|
--}}}
|
|
--{{{ SEQ
|
|
seqProcess :: OccParser A.Process
|
|
seqProcess
|
|
= do m <- md
|
|
sSEQ
|
|
(do { eol; indent; ps <- many1 process; outdent; return $ A.Seq m ps }
|
|
<|> do { r <- replicator; eol; indent; r' <- scopeInRep r; p <- process; scopeOutRep r'; outdent; return $ A.SeqRep m r' p })
|
|
<?> "seqProcess"
|
|
--}}}
|
|
--{{{ IF
|
|
ifProcess :: OccParser A.Process
|
|
ifProcess
|
|
= do m <- md
|
|
c <- conditional
|
|
return $ A.If m c
|
|
<?> "ifProcess"
|
|
|
|
conditional :: OccParser A.Structured
|
|
conditional
|
|
= do { m <- md; sIF ;
|
|
do { eol; indent; cs <- many1 ifChoice; outdent; 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
|
|
ifChoice
|
|
= guardedChoice
|
|
<|> conditional
|
|
<|> handleSpecs specification ifChoice A.Spec
|
|
<?> "choice"
|
|
|
|
guardedChoice :: OccParser A.Structured
|
|
guardedChoice
|
|
= do m <- md
|
|
b <- booleanExpr
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.OnlyC m (A.Choice m b p)
|
|
<?> "guardedChoice"
|
|
--}}}
|
|
--{{{ CASE
|
|
caseProcess :: OccParser A.Process
|
|
caseProcess
|
|
= do m <- md
|
|
sCASE
|
|
s <- caseSelector
|
|
eol
|
|
indent
|
|
os <- many1 caseOption
|
|
outdent
|
|
return $ A.Case m s (A.Several m os)
|
|
<?> "caseProcess"
|
|
|
|
caseSelector :: OccParser A.Expression
|
|
caseSelector
|
|
-- FIXME Should constrain to things that can be CASEd over.
|
|
= expression
|
|
<?> "caseSelector"
|
|
|
|
caseOption :: OccParser A.Structured
|
|
caseOption
|
|
= try (do { m <- md; ces <- sepBy caseExpression sComma; eol; indent; p <- process; outdent; return $ A.OnlyO m (A.Option m ces p) })
|
|
<|> try (do { m <- md; sELSE; eol; indent; p <- process; outdent; return $ A.OnlyO m (A.Else m p) })
|
|
<|> handleSpecs specification caseOption A.Spec
|
|
<?> "option"
|
|
|
|
caseExpression :: OccParser A.Expression
|
|
caseExpression
|
|
-- FIXME: Check the type is something constant that CASE can deal with
|
|
= expression
|
|
<?> "caseExpression"
|
|
--}}}
|
|
--{{{ WHILE
|
|
whileProcess :: OccParser A.Process
|
|
whileProcess
|
|
= do m <- md
|
|
sWHILE
|
|
b <- booleanExpr
|
|
eol
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.While m b p
|
|
<?> "whileProcess"
|
|
--}}}
|
|
--{{{ PAR
|
|
parallel :: OccParser A.Process
|
|
parallel
|
|
= do m <- md
|
|
isPri <- parKeyword
|
|
(do { eol; indent; ps <- many1 process; outdent; return $ A.Par m isPri ps }
|
|
<|> do { r <- replicator; eol; indent; r' <- scopeInRep r; p <- process; scopeOutRep r'; outdent; return $ A.ParRep m isPri r' p })
|
|
<|> placedpar
|
|
<?> "parallel"
|
|
|
|
parKeyword :: OccParser A.ParMode
|
|
parKeyword
|
|
= do { sPAR; return A.PlainPar }
|
|
<|> try (do { sPRI; sPAR; return A.PriPar })
|
|
<?> "parKeyword"
|
|
|
|
-- XXX PROCESSOR as a process isn't really legal, surely?
|
|
placedpar :: OccParser A.Process
|
|
placedpar
|
|
= do m <- md
|
|
sPLACED
|
|
sPAR
|
|
(do { eol; indent; ps <- many1 placedpar; outdent; return $ A.Par m A.PlacedPar ps }
|
|
<|> do { r <- replicator; eol; indent; r' <- scopeInRep r; p <- placedpar; scopeOutRep r'; outdent; return $ A.ParRep m A.PlacedPar r' p })
|
|
<|> do { m <- md; sPROCESSOR; e <- intExpr; eol; indent; p <- process; outdent; return $ A.Processor m e p }
|
|
<?> "placedpar"
|
|
--}}}
|
|
--{{{ ALT
|
|
altProcess :: OccParser A.Process
|
|
altProcess
|
|
= do m <- md
|
|
(isPri, a) <- alternation
|
|
return $ A.Alt m isPri a
|
|
<?> "altProcess"
|
|
|
|
alternation :: OccParser (Bool, A.Structured)
|
|
alternation
|
|
= do { m <- md; isPri <- altKeyword ;
|
|
do { eol; indent; as <- many1 alternative; outdent; 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 }
|
|
<|> try (do { sPRI; sALT; return True })
|
|
<?> "altKeyword"
|
|
|
|
-- 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
|
|
alternative
|
|
= guardedAlternative
|
|
-- FIXME: Check we don't have PRI ALT inside ALT.
|
|
<|> do { (isPri, a) <- alternation; return a }
|
|
<|> try (do m <- md
|
|
b <- booleanExpr
|
|
sAmp
|
|
c <- channel
|
|
sQuest
|
|
sCASE
|
|
eol
|
|
indent
|
|
vs <- many1 variant
|
|
outdent
|
|
return $ A.OnlyA m (A.AlternativeCond m b c (A.InputCase m $ A.Several m vs) (A.Skip m)))
|
|
<|> try (do m <- md
|
|
c <- channel
|
|
sQuest
|
|
sCASE
|
|
eol
|
|
indent
|
|
vs <- many1 variant
|
|
outdent
|
|
return $ A.OnlyA m (A.Alternative m c (A.InputCase m $ A.Several m vs) (A.Skip m)))
|
|
<|> handleSpecs specification alternative A.Spec
|
|
<?> "alternative"
|
|
|
|
guardedAlternative :: OccParser A.Structured
|
|
guardedAlternative
|
|
= do m <- md
|
|
makeAlt <- guard
|
|
indent
|
|
p <- process
|
|
outdent
|
|
return $ A.OnlyA m (makeAlt p)
|
|
<?> "guardedAlternative"
|
|
|
|
guard :: OccParser (A.Process -> A.Alternative)
|
|
guard
|
|
= try (do { m <- md; (c, im) <- input; return $ A.Alternative m c im })
|
|
<|> try (do { m <- md; b <- booleanExpr; sAmp; (c, im) <- input; return $ A.AlternativeCond m b c im })
|
|
<|> try (do { m <- md; b <- booleanExpr; sAmp; sSKIP; eol; return $ A.AlternativeSkip m b })
|
|
<?> "guard"
|
|
--}}}
|
|
--{{{ PROC calls
|
|
procInstance :: OccParser A.Process
|
|
procInstance
|
|
= do m <- md
|
|
n <- tryVX procName sLeftR
|
|
st <- pSpecTypeOfName n
|
|
let fs = case st of A.Proc _ fs _ -> fs
|
|
as <- actuals fs
|
|
sRightR
|
|
eol
|
|
return $ A.ProcCall m n as
|
|
<?> "procInstance"
|
|
|
|
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 <- expression; et <- pTypeOfExpression e; matchType t et; return $ A.ActualExpression t e } <?> "actual expression for " ++ an
|
|
_ -> if isChannelType t
|
|
then do { c <- channel; ct <- pTypeOfVariable c; matchType t ct; return $ A.ActualVariable am t c } <?> "actual channel for " ++ an
|
|
else do { v <- variable; vt <- pTypeOfVariable v; matchType t vt; return $ A.ActualVariable am t v } <?> "actual variable for " ++ an
|
|
where
|
|
an = A.nameName n
|
|
--}}}
|
|
--{{{ preprocessor directives
|
|
preprocessorDirective :: OccParser A.Process
|
|
preprocessorDirective
|
|
= ppInclude
|
|
<|> ppUse
|
|
<?> "preprocessor directive"
|
|
|
|
ppInclude :: OccParser A.Process
|
|
ppInclude
|
|
= do sppINCLUDE
|
|
char '"'
|
|
file <- manyTill character sQuote
|
|
eol
|
|
includeFile $ concat file
|
|
|
|
ppUse :: OccParser A.Process
|
|
ppUse
|
|
= do sppUSE
|
|
char '"'
|
|
mod <- manyTill character sQuote
|
|
eol
|
|
let file = mangleModName $ concat mod
|
|
|
|
-- Check whether it's been included already.
|
|
ps <- getState
|
|
if file `elem` psLoadedFiles ps
|
|
then process
|
|
else includeFile file
|
|
|
|
-- | Invoke the parser recursively to handle an included file.
|
|
includeFile :: String -> OccParser A.Process
|
|
includeFile file
|
|
= do ps <- getState
|
|
(f, ps') <- parseFile file ps
|
|
setState ps' { psLocalNames = psMainLocals ps' }
|
|
p <- process
|
|
return $ f p
|
|
--}}}
|
|
--{{{ main process
|
|
mainProcess :: OccParser A.Process
|
|
mainProcess
|
|
= do m <- md
|
|
sMainMarker
|
|
eol
|
|
-- 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.
|
|
updateState $ (\ps -> ps { psMainLocals = psLocalNames ps })
|
|
return $ A.Main m
|
|
--}}}
|
|
--}}}
|
|
--{{{ top-level forms
|
|
-- This is only really true once we've tacked a process onto the bottom; a
|
|
-- source file is really a series of specifications, but the later ones need to
|
|
-- have the earlier ones in scope, so we can't parse them separately.
|
|
sourceFile :: OccParser (A.Process, ParseState)
|
|
sourceFile
|
|
= do whiteSpace
|
|
p <- process
|
|
s <- getState
|
|
return (p, s)
|
|
--}}}
|
|
--}}}
|
|
|
|
--{{{ preprocessor
|
|
-- XXX Doesn't handle conditionals.
|
|
|
|
preprocess :: String -> String
|
|
preprocess d = parseIndentation $ lines (d ++ "\n" ++ mainMarker)
|
|
|
|
readSource :: String -> IO String
|
|
readSource file
|
|
= do f <- IO.openFile file IO.ReadMode
|
|
d <- IO.hGetContents f
|
|
return $ preprocess d
|
|
|
|
-- | Find (via a nasty regex search) all the files that this source file includes.
|
|
preFindIncludes :: String -> [String]
|
|
preFindIncludes source
|
|
= concat [case matchRegex incRE l of
|
|
Just [_, fn] -> [fn]
|
|
Nothing -> []
|
|
| l <- lines source]
|
|
where
|
|
incRE = mkRegex "^#(INCLUDE|USE) +\"([^\"]*)\""
|
|
|
|
-- | If a module name doesn't already have a suffix, add one.
|
|
mangleModName :: String -> String
|
|
mangleModName mod
|
|
= if ".occ" `isSuffixOf` mod || ".inc" `isSuffixOf` mod
|
|
then mod
|
|
else mod ++ ".occ"
|
|
|
|
type LoaderM a = StateT ParseState IO a
|
|
|
|
-- | Load all the source files necessary for a program.
|
|
-- We have to do this now, before entering the parser, because the parser
|
|
-- doesn't run in the IO monad. If there were a monad transformer version of
|
|
-- Parsec then we could just open files as we need them.
|
|
loadSource :: String -> ParseState -> IO ParseState
|
|
loadSource file ps = execStateT (load file file) ps
|
|
where
|
|
load :: String -> String -> LoaderM ()
|
|
load file realName
|
|
= do ps <- get
|
|
case lookup file (psSourceFiles ps) of
|
|
Just _ -> return ()
|
|
Nothing ->
|
|
do progress $ "Loading source file " ++ realName
|
|
source <- liftIO $ readSource realName
|
|
modify $ (\ps -> ps { psSourceFiles = (file, source) : psSourceFiles ps })
|
|
let deps = map mangleModName $ preFindIncludes source
|
|
sequence_ [load dep (joinPath file dep) | dep <- deps]
|
|
--}}}
|
|
|
|
--{{{ entry points for the parser itself
|
|
-- | Test a parser production (for use from ghci while debugging the parser).
|
|
testParse :: Show a => OccParser a -> String -> IO ()
|
|
testParse prod text
|
|
= do let r = runParser prod emptyState "" text
|
|
putStrLn $ "Result: " ++ show r
|
|
|
|
-- | Parse a file, returning a function you can apply to make all its
|
|
-- definitions available to a process.
|
|
parseFile :: Monad m => String -> ParseState -> m (A.Process -> A.Process, ParseState)
|
|
parseFile file ps
|
|
= do let source = fromJust $ lookup file (psSourceFiles ps)
|
|
let ps' = ps { psLoadedFiles = file : psLoadedFiles ps }
|
|
case runParser sourceFile ps' file source of
|
|
Left err -> die $ "Parse error: " ++ show err
|
|
Right (p, ps'') -> return (replaceMain p, ps'')
|
|
where
|
|
replaceMain :: A.Process -> A.Process -> A.Process
|
|
replaceMain (A.ProcSpec m s p) np = A.ProcSpec m s (replaceMain p np)
|
|
replaceMain (A.Main _) np = np
|
|
|
|
-- | Parse the top level source file in a program.
|
|
parseProgram :: Monad m => String -> ParseState -> m (A.Process, ParseState)
|
|
parseProgram file ps
|
|
= do (f, ps') <- parseFile file ps
|
|
return (f $ A.Main emptyMeta, ps')
|
|
--}}}
|
|
|