tock-mirror/fco2/Parse.hs

1339 lines
47 KiB
Haskell

-- | Parse occam code into an AST.
module Parse where
-- FIXME: Need to:
-- - insert type checks
-- - remove as many trys as possible; every production should consume input
-- when it's unambiguous
import Data.List
import Text.ParserCombinators.Parsec
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language (emptyDef)
import qualified IO
import Numeric (readHex)
import qualified AST as A
import Metadata
import ParseState
import Errors
import Indentation
import Types
--{{{ 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",
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"
--}}}
--{{{ 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
getSourcePos :: OccParser OccSourcePos
getSourcePos
= do pos <- getPosition
return $ OccSourcePos (sourceName pos) (sourceLine pos) (sourceColumn pos)
md :: OccParser Meta
md
= do pos <- getSourcePos
return [MdSourcePos 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
scopeInSpec :: A.Specification -> OccParser A.Specification
scopeInSpec (A.Specification m n st)
= do n' <- scopeIn n st (abbrevModeOfSpec st)
return $ A.Specification m n' st
scopeOutSpec :: A.Specification -> OccParser ()
scopeOutSpec (A.Specification _ n _) = scopeOut n
scopeInFormal :: A.Formal -> OccParser A.Formal
scopeInFormal (A.Formal am t n)
= do n' <- scopeIn n (A.Declaration (A.nameMeta n) t) am
return (A.Formal am t n')
scopeInFormals :: [A.Formal] -> OccParser [A.Formal]
scopeInFormals fs = mapM scopeInFormal fs
scopeOutFormals :: [A.Formal] -> OccParser ()
scopeOutFormals fs = sequence_ [scopeOut n | (A.Formal am t n) <- fs]
--}}}
--{{{ grammar productions
-- These productions are (now rather loosely) based on the ordered syntax in
-- the occam2.1 manual.
--
-- Each production is allowed to consume the thing it's trying to match.
--{{{ names
anyName :: A.NameType -> OccParser A.Name
anyName nt
= do m <- md
s <- identifier
return $ A.Name m nt s
<?> show nt
name :: A.NameType -> OccParser A.Name
name nt
= do n <- anyName nt
findName n
newName :: A.NameType -> OccParser A.Name
newName nt = anyName nt
channelName = 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
expressionList :: OccParser A.ExpressionList
expressionList
= try (do { m <- md; n <- functionName; 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 })
<?> "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
if isConstExpression ps e
then return e
else fail "expected constant expression"
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 <- functionName; 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 }
<|> do { sVAL ;
do { (n, e) <- try (do { n <- newVariableName; sIS; e <- expression; return (n, e) }); sColon; eol; t <- pTypeOfExpression e; return $ A.Specification m n $ A.IsExpr m A.ValAbbrev t e }
<|> do { s <- specifier; n <- newVariableName; sIS; e <- expression; sColon; eol; t <- pTypeOfExpression e; matchType s t; return $ A.Specification m n $ A.IsExpr m A.ValAbbrev s e } }
<|> 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"
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
<?> "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
--}}}
--{{{ main process
mainProcess :: OccParser A.Process
mainProcess
= do m <- md
sMainMarker
eol
-- Find the last thing that was defined; it should be a PROC of the right type.
-- FIXME We should check that it's using a valid TLP interface.
updateState $ (\ps -> ps { psMainName = Just $ snd $ head $ 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 preprocessor instructions.
preprocess :: String -> String
preprocess d = parseIndentation $ lines (d ++ "\n" ++ mainMarker)
readSource :: String -> IO String
readSource fn = do
f <- IO.openFile fn IO.ReadMode
d <- IO.hGetContents f
let prep = preprocess d
return prep
--}}}
--{{{ interface for other modules
testParse :: Show a => OccParser a -> String -> IO ()
testParse prod text
= do let r = runParser prod emptyState "" text
putStrLn $ "Result: " ++ show r
parseSource :: String -> String -> IO (A.Process, ParseState)
parseSource prep sourceFileName
= case runParser sourceFile emptyState sourceFileName prep of
Left err -> die $ "Parse error: " ++ show err
Right result -> return result
--}}}