tock-mirror/fco2/Parse.hs

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')
--}}}