786 lines
28 KiB
Haskell
786 lines
28 KiB
Haskell
-- Parse occam code
|
|
|
|
module Parse (readSource, parseSource) where
|
|
|
|
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 qualified Tree as N
|
|
|
|
-- -------------------------------------------------------------
|
|
|
|
mainMarker = "##MAGIC-FCO-MAIN-PROCESS##"
|
|
|
|
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",
|
|
mainMarker
|
|
]
|
|
, P.caseSensitive = True
|
|
}
|
|
|
|
lexer :: P.TokenParser ()
|
|
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
|
|
|
|
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 "="
|
|
|
|
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"
|
|
sMainMarker = reserved mainMarker
|
|
|
|
-- XXX could handle VALOF by translating each step to one { and matching multiple ones?
|
|
indent = symbol "{"
|
|
outdent = symbol "}"
|
|
eol = symbol "@"
|
|
|
|
-- -------------------------------------------------------------
|
|
|
|
-- These productions are based on the syntax in the occam2.1 manual.
|
|
|
|
-- The way productions should work is that each production should only consume input if it's sure that it's unambiguous.
|
|
|
|
abbreviation
|
|
= try (do { n <- name ; sIS ; v <- variable ; sColon ; eol ; return $ N.Is n v })
|
|
<|> try (do { s <- specifier ; n <- name ; sIS ; v <- variable ; sColon ; eol ; return $ N.IsType s n v })
|
|
<|> do { sVAL ;
|
|
try (do { n <- name ; sIS ; e <- expression ; sColon ; eol ; return $ N.ValIs n e })
|
|
<|> do { s <- specifier ; n <- name ; sIS ; e <- expression ; sColon ; eol ; return $ N.ValIsType s n e } }
|
|
<?> "abbreviation"
|
|
|
|
actual
|
|
= expression
|
|
<|> variable
|
|
<|> channel
|
|
<?> "actual"
|
|
|
|
allocation
|
|
= do { sPLACE ; n <- name ; sAT ; e <- expression ; sColon ; eol ; return $ N.Place n e }
|
|
<?> "allocation"
|
|
|
|
alternation
|
|
= do { sALT ;
|
|
do { eol ; indent ; as <- many1 alternative ; outdent ; return $ N.Alt as }
|
|
<|> do { r <- replicator ; eol ; indent ; a <- alternative ; outdent ; return $ N.AltRep r a } }
|
|
<|> do { sPRI ; sALT ;
|
|
do { eol ; indent ; as <- many1 alternative ; outdent ; return $ N.PriAlt as }
|
|
<|> do { r <- replicator ; eol ; indent ; a <- alternative ; outdent ; return $ N.PriAltRep r a } }
|
|
<?> "alternation"
|
|
|
|
alternative
|
|
= guardedAlternative
|
|
<|> alternation
|
|
<|> try (do { b <- boolean ; sAmp ; c <- channel ; sQuest ; sCASE ; eol ; indent ; vs <- many1 variant ; outdent ; return $ N.InCaseGuard b c vs })
|
|
<|> try (do { c <- channel ; sQuest ; sCASE ; eol ; indent ; vs <- many1 variant ; outdent ; return $ N.InCase c vs })
|
|
<|> do { s <- specification ; a <- alternative ; return $ N.Decl s a }
|
|
<?> "alternative"
|
|
|
|
assignment
|
|
= do { vs <- variableList ; sAssign ; es <- expressionList ; eol ; return $ N.Assign vs es }
|
|
<?> "assignment"
|
|
|
|
base
|
|
= expression
|
|
<?> "base"
|
|
|
|
boolean
|
|
= expression
|
|
<?> "boolean"
|
|
|
|
byte
|
|
= lexeme (do { char '\'' ; s <- character ; char '\'' ; return $ N.LitByte s })
|
|
<?> "byte"
|
|
|
|
caseExpression
|
|
= expression
|
|
<?> "caseExpression"
|
|
|
|
caseInput
|
|
= do { c <- channel ; sQuest ; sCASE ; eol ; indent ; vs <- many1 variant ; outdent ; return $ N.InCase c vs }
|
|
<?> "caseInput"
|
|
|
|
-- This is also used for timers and ports, since the syntax is identical (and
|
|
-- the parser really can't tell at this stage which is which).
|
|
channel
|
|
= do { v <- channel' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl N.Sub v es }
|
|
<?> "channel"
|
|
|
|
channel'
|
|
= try name
|
|
<|> try (do { sLeft ; n <- channel ; sFROM ; e <- expression ; sFOR ; f <- expression ; sRight ; return $ N.SubFromFor n e f })
|
|
<|> try (do { sLeft ; n <- channel ; sFROM ; e <- expression ; sRight ; return $ N.SubFrom n e })
|
|
<|> do { sLeft ; n <- channel ; sFOR ; e <- expression ; sRight ; return $ N.SubFor n e }
|
|
<?> "channel'"
|
|
|
|
-- FIXME should probably make CHAN INT work, since that'd be trivial...
|
|
channelType
|
|
= do { sCHAN ; sOF ; p <- protocol ; return $ N.ChanOf p }
|
|
<|> try (do { sLeft ; s <- expression ; sRight ; t <- channelType ; return $ N.Array s t })
|
|
<?> "channelType"
|
|
|
|
-- FIXME this isn't at all the right way to return the character!
|
|
character
|
|
= try (do { char '*' ;
|
|
do { char '#' ; a <- hexDigit ; b <- hexDigit ; return $ ['*', '#', a, b] }
|
|
<|> do { c <- anyChar ; return $ ['*', c] } })
|
|
<|> do { c <- anyChar ; return $ [c] }
|
|
<?> "character"
|
|
|
|
occamChoice
|
|
= guardedChoice
|
|
<|> conditional
|
|
<|> do { s <- try specification ; c <- occamChoice ; return $ N.Decl s c }
|
|
<?> "choice"
|
|
|
|
conditional
|
|
= do { sIF ;
|
|
do { eol ; indent ; cs <- many1 occamChoice ; outdent ; return $ N.If cs }
|
|
<|> do { r <- replicator ; eol ; indent ; c <- occamChoice ; outdent ; return $ N.IfRep r c } }
|
|
<?> "conditional"
|
|
|
|
conversion
|
|
= do t <- dataType
|
|
do { sROUND ; o <- operand ; return $ N.Round t o } <|> do { sTRUNC ; o <- operand ; return $ N.Trunc t o } <|> do { o <- operand ; return $ N.Conv t o }
|
|
<?> "conversion"
|
|
|
|
occamCount
|
|
= expression
|
|
<?> "count"
|
|
|
|
dataType
|
|
= do { sBOOL ; return $ N.Bool }
|
|
<|> do { sBYTE ; return $ N.Byte }
|
|
<|> do { sINT ; return $ N.Int }
|
|
<|> do { sINT16 ; return $ N.Int16 }
|
|
<|> do { sINT32 ; return $ N.Int32 }
|
|
<|> do { sINT64 ; return $ N.Int64 }
|
|
<|> do { sREAL32 ; return $ N.Real32 }
|
|
<|> do { sREAL64 ; return $ N.Real64 }
|
|
<|> try (do { sLeft ; s <- expression ; sRight ; t <- dataType ; return $ N.Array s t })
|
|
<|> name
|
|
<?> "data type"
|
|
|
|
declType
|
|
= dataType
|
|
<|> channelType
|
|
<|> timerType
|
|
<|> portType
|
|
|
|
-- FIXME this originally had four lines like this, one for each of the above;
|
|
-- it might be nicer to generate a different N.Node for each type of declaration
|
|
declaration
|
|
= do { d <- declType ; ns <- sepBy1 name sComma ; sColon ; eol ; return $ N.Vars d ns }
|
|
<?> "declaration"
|
|
|
|
definition
|
|
= do { sDATA ; sTYPE ; n <- name ;
|
|
do {sIS ; t <- dataType ; sColon ; eol ; return $ N.DataType n t }
|
|
<|> do { eol ; indent ; t <- structuredType ; outdent ; sColon ; eol ; return $ N.DataType n t } }
|
|
<|> do { sPROTOCOL ; n <- name ;
|
|
do { sIS ; p <- sequentialProtocol ; sColon ; eol ; return $ N.Protocol n p }
|
|
<|> do { eol ; indent ; sCASE ; eol ; indent ; ps <- many1 taggedProtocol ; outdent ; outdent ; sColon ; eol ; return $ N.TaggedProtocol n ps } }
|
|
<|> do { sPROC ; n <- name ; fs <- formalList ; eol ; indent ; p <- process ; outdent ; sColon ; eol ; return $ N.Proc n fs p }
|
|
<|> try (do { rs <- sepBy1 dataType sComma ; (n, fs) <- functionHeader ;
|
|
do { sIS ; el <- expressionList ; sColon ; eol ; return $ N.FuncIs n rs fs el }
|
|
<|> do { eol ; indent ; vp <- valueProcess ; outdent ; sColon ; eol ; return $ N.Func n rs fs vp } })
|
|
<|> try (do { s <- specifier ; n <- name ;
|
|
do { sRETYPES ; v <- variable ; sColon ; eol ; return $ N.Retypes s n v }
|
|
<|> do { try sRESHAPES ; v <- variable ; sColon ; eol ; return $ N.Reshapes s n v } })
|
|
<|> do { sVAL ; s <- specifier ; n <- name ;
|
|
do { sRETYPES ; v <- variable ; sColon ; eol ; return $ N.ValRetypes s n v }
|
|
<|> do { sRESHAPES ; v <- variable ; sColon ; eol ; return $ N.ValReshapes s n v } }
|
|
<?> "definition"
|
|
|
|
digits
|
|
= many1 digit
|
|
<?> "digits"
|
|
|
|
dyadicOperator
|
|
= do { reservedOp "+" ; return $ N.Add }
|
|
<|> do { reservedOp "-" ; return $ N.Subtr }
|
|
<|> do { reservedOp "*" ; return $ N.Mul }
|
|
<|> do { reservedOp "/" ; return $ N.Div }
|
|
<|> do { reservedOp "\\" ; return $ N.Rem }
|
|
<|> do { sREM ; return $ N.Rem }
|
|
<|> do { sPLUS ; return $ N.Plus }
|
|
<|> do { sMINUS ; return $ N.Minus }
|
|
<|> do { sTIMES ; return $ N.Times }
|
|
<|> do { reservedOp "/\\" ; return $ N.BitAnd }
|
|
<|> do { reservedOp "\\/" ; return $ N.BitOr }
|
|
<|> do { reservedOp "><" ; return $ N.BitXor }
|
|
<|> do { sBITAND ; return $ N.BitAnd }
|
|
<|> do { sBITOR ; return $ N.BitOr }
|
|
<|> do { sAND ; return $ N.And }
|
|
<|> do { sOR ; return $ N.Or }
|
|
<|> do { reservedOp "=" ; return $ N.Eq }
|
|
<|> do { reservedOp "<>" ; return $ N.NEq }
|
|
<|> do { reservedOp "<" ; return $ N.Less }
|
|
<|> do { reservedOp ">" ; return $ N.More }
|
|
<|> do { reservedOp "<=" ; return $ N.LessEq }
|
|
<|> do { reservedOp ">=" ; return $ N.MoreEq }
|
|
<|> do { sAFTER ; return $ N.After }
|
|
<?> "dyadicOperator"
|
|
|
|
occamExponent
|
|
= try (do { c <- oneOf "+-" ; d <- digits ; return $ c : d })
|
|
<?> "exponent"
|
|
|
|
expression :: Parser N.Node
|
|
expression
|
|
= try (do { o <- monadicOperator ; v <- operand ; return $ N.MonadicOp o v })
|
|
<|> do { a <- sMOSTPOS ; t <- dataType ; return $ N.MostPos t }
|
|
<|> do { a <- sMOSTNEG ; t <- dataType ; return $ N.MostNeg t }
|
|
<|> do { a <- sSIZE ; t <- dataType ; return $ N.Size t }
|
|
<|> try (do { a <- operand ; o <- dyadicOperator ; b <- operand ; return $ N.DyadicOp o a b })
|
|
<|> try conversion
|
|
<|> operand
|
|
<?> "expression"
|
|
|
|
expressionList
|
|
= try (do { n <- name ; sLeftR ; as <- sepBy expression sComma ; sRightR ; return $ N.Call n as })
|
|
<|> do { es <- sepBy1 expression sComma ; return $ N.ExpList es }
|
|
-- XXX value process
|
|
<?> "expressionList"
|
|
|
|
fieldName
|
|
= name
|
|
<?> "fieldName"
|
|
|
|
-- This is rather different from the grammar.
|
|
-- FIXME should this lot actually be done in a pass? probably...
|
|
formalList
|
|
= do { sLeftR ; fs <- sepBy formalArg sComma ; sRightR ; return $ markTypes fs }
|
|
<?> "formalList"
|
|
where
|
|
formalArg :: Parser (Maybe N.Node, N.Node)
|
|
formalArg = try (do { sVAL ; s <- specifier ; n <- name ; return $ (Just (N.Val s), n) })
|
|
<|> try (do { s <- specifier ; n <- name ; return $ (Just s, n) })
|
|
<|> try (do { n <- name ; return $ (Nothing, n) })
|
|
|
|
markTypes :: [(Maybe N.Node, N.Node)] -> [N.Node]
|
|
markTypes [] = []
|
|
markTypes ((Nothing, _):_) = error "Formal list must start with a type"
|
|
markTypes ((Just ft,fn):is) = (N.Formal ft fn) : markRest ft is
|
|
|
|
markRest :: N.Node -> [(Maybe N.Node, N.Node)] -> [N.Node]
|
|
markRest _ [] = []
|
|
markRest t ((Nothing, n):is) = (N.Formal t n) : markRest t is
|
|
markRest _ ((Just t, n):is) = (N.Formal t n) : markRest t is
|
|
|
|
functionHeader
|
|
= do { sFUNCTION ; n <- name ; fs <- formalList ; return $ (n, fs) }
|
|
<?> "functionHeader"
|
|
|
|
guard
|
|
= try input
|
|
<|> try (do { b <- boolean ; sAmp ; i <- input ; return $ N.Guarded b i })
|
|
<|> try (do { b <- boolean ; sAmp ; sSKIP ; eol ; return $ N.Guarded b N.Skip })
|
|
<?> "guard"
|
|
|
|
guardedAlternative
|
|
= do { g <- guard ; indent ; p <- process ; outdent ; return $ N.Guarded g p }
|
|
<?> "guardedAlternative"
|
|
|
|
guardedChoice
|
|
= do { b <- boolean ; eol ; indent ; p <- process ; outdent ; return $ N.Guarded b p }
|
|
<?> "guardedChoice"
|
|
|
|
hexDigits
|
|
= do { d <- many1 hexDigit ; return $ N.LitHex d }
|
|
<?> "hexDigits"
|
|
|
|
-- XXX how does the syntax handle multiline regular CASE inputs?
|
|
-- chan ? CASE
|
|
-- foo
|
|
-- ...
|
|
|
|
input
|
|
= do c <- channel
|
|
sQuest
|
|
(do { sCASE ; tl <- taggedList ; eol ; return $ N.InTag c tl }
|
|
<|> do { sAFTER ; e <- expression ; eol ; return $ N.InAfter c e }
|
|
<|> do { is <- sepBy1 inputItem sSemi ; eol ; return $ N.In c is })
|
|
<?> "input"
|
|
|
|
inputItem
|
|
= try (do { v <- variable ; sColons ; w <- variable ; return $ N.Counted v w })
|
|
<|> variable
|
|
<?> "inputItem"
|
|
|
|
integer
|
|
= try (do { d <- lexeme digits ; return $ N.LitInt d })
|
|
<|> do { char '#' ; d <- lexeme hexDigits ; return $ d }
|
|
<?> "integer"
|
|
|
|
literal
|
|
= try real
|
|
<|> try integer
|
|
<|> try byte
|
|
<|> try (do { v <- real ; sLeftR ; t <- dataType ; sRightR ; return $ N.TypedLit t v })
|
|
<|> try (do { v <- integer ; sLeftR ; t <- dataType ; sRightR ; return $ N.TypedLit t v })
|
|
<|> try (do { v <- byte ; sLeftR ; t <- dataType ; sRightR ; return $ N.TypedLit t v })
|
|
<|> try (do { sTRUE ; return $ N.True })
|
|
<|> do { sFALSE ; return $ N.False }
|
|
<?> "literal"
|
|
|
|
loop
|
|
= do { sWHILE ; b <- boolean ; eol ; indent ; p <- process ; outdent ; return $ N.While b p }
|
|
|
|
monadicOperator
|
|
= do { reservedOp "-" ; return $ N.MonSub }
|
|
<|> do { sMINUS ; return $ N.MonSub }
|
|
<|> do { reservedOp "~" ; return $ N.MonBitNot }
|
|
<|> do { sBITNOT ; return $ N.MonBitNot }
|
|
<|> do { sNOT ; return $ N.MonNot }
|
|
<|> do { sSIZE ; return $ N.MonSize }
|
|
<?> "monadicOperator"
|
|
|
|
name
|
|
= do { s <- identifier ; return $ N.Name s }
|
|
<?> "name"
|
|
|
|
occamString
|
|
= lexeme (do { char '"' ; s <- many (noneOf "\"") ; char '"' ; return $ N.LitString s })
|
|
<?> "string"
|
|
|
|
operand
|
|
= do { v <- operand' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl N.Sub v es }
|
|
<?> "operand"
|
|
|
|
operand'
|
|
= try variable
|
|
<|> try literal
|
|
<|> try table
|
|
<|> try (do { sLeftR ; e <- expression ; sRightR ; return e })
|
|
-- XXX value process
|
|
<|> try (do { n <- name ; sLeftR ; as <- sepBy expression sComma ; sRightR ; return $ N.Call n as })
|
|
<|> try (do { sBYTESIN ; sLeftR ; o <- operand ; sRightR ; return $ N.BytesIn o })
|
|
<|> try (do { sBYTESIN ; sLeftR ; o <- dataType ; sRightR ; return $ N.BytesIn o })
|
|
<|> try (do { sOFFSETOF ; sLeftR ; n <- name ; sComma ; f <- fieldName ; sRightR ; return $ N.OffsetOf n f })
|
|
<?> "operand'"
|
|
|
|
occamOption
|
|
= try (do { ces <- sepBy caseExpression sComma ; eol ; indent ; p <- process ; outdent ; return $ N.CaseExps ces p })
|
|
<|> try (do { sELSE ; eol ; indent ; p <- process ; outdent ; return $ N.Else p })
|
|
<|> do { s <- specification ; o <- occamOption ; return $ N.Decl s o }
|
|
<?> "option"
|
|
|
|
-- XXX This can't tell at parse time in "c ! x; y" whether x is a variable or a tag...
|
|
-- ... so this now wants "c ! CASE x" if it's a tag, to match input.
|
|
-- We can fix this with a pass later...
|
|
output
|
|
= do c <- channel
|
|
sBang
|
|
(do { sCASE ; t <- tag ; sSemi ; os <- sepBy1 outputItem sSemi ; eol ; return $ N.OutCase c t os }
|
|
<|> do { sCASE ; t <- tag ; eol ; return $ N.OutCase c t [] }
|
|
<|> do { os <- sepBy1 outputItem sSemi ; eol ; return $ N.Out c os })
|
|
<?> "output"
|
|
|
|
outputItem
|
|
= try (do { a <- expression ; sColons ; b <- expression ; return $ N.Counted a b })
|
|
<|> expression
|
|
<?> "outputItem"
|
|
|
|
parallel
|
|
= do { sPAR ; do { eol ; indent ; ps <- many1 process ; outdent ; return $ N.Par ps } <|> do { r <- replicator ; eol ; indent ; p <- process ; outdent ; return $ N.ParRep r p } }
|
|
<|> do { sPRI ; sPAR ; do { eol ; indent ; ps <- many1 process ; outdent ; return $ N.PriPar ps } <|> do { r <- replicator ; eol ; indent ; p <- process ; outdent ; return $ N.PriParRep r p } }
|
|
<|> placedpar
|
|
<?> "parallel"
|
|
|
|
-- XXX PROCESSOR as a process isn't really legal, surely?
|
|
placedpar
|
|
= do { sPLACED ; sPAR ; do { eol ; indent ; ps <- many1 placedpar ; outdent ; return $ N.PlacedPar ps } <|> do { r <- replicator ; eol ; indent ; p <- placedpar ; outdent ; return $ N.PlacedParRep r p } }
|
|
<|> do { sPROCESSOR ; e <- expression ; eol ; indent ; p <- process ; outdent ; return $ N.Processor e p }
|
|
<?> "placedpar"
|
|
|
|
portType
|
|
= do { sPORT ; sOF ; p <- protocol ; return $ N.PortOf p }
|
|
<|> do { try sLeft ; s <- try expression ; try sRight ; t <- portType ; return $ N.Array s t }
|
|
<?> "portType"
|
|
|
|
procInstance
|
|
= do { n <- name ; sLeftR ; as <- sepBy actual sComma ; sRightR ; eol ; return $ N.ProcCall n as }
|
|
<?> "procInstance"
|
|
|
|
process
|
|
= try assignment
|
|
<|> try input
|
|
<|> try output
|
|
<|> do { sSKIP ; eol ; return $ N.Skip }
|
|
<|> do { sSTOP ; eol ; return $ N.Stop }
|
|
<|> occamSequence
|
|
<|> conditional
|
|
<|> selection
|
|
<|> loop
|
|
<|> try parallel
|
|
<|> alternation
|
|
<|> try caseInput
|
|
<|> try procInstance
|
|
<|> do { sMainMarker ; eol ; return $ N.MainProcess }
|
|
<|> do { a <- allocation ; p <- process ; return $ N.Decl a p }
|
|
<|> do { s <- specification ; p <- process ; return $ N.Decl s p }
|
|
<?> "process"
|
|
|
|
protocol
|
|
= try name
|
|
<|> simpleProtocol
|
|
<?> "protocol"
|
|
|
|
real
|
|
= try (do { l <- digits ; char '.' ; r <- digits ; char 'e' ; e <- lexeme occamExponent ; return $ N.LitReal (l ++ "." ++ r ++ "e" ++ e) })
|
|
<|> do { l <- digits ; char '.' ; r <- lexeme digits ; return $ N.LitReal (l ++ "." ++ r) }
|
|
<?> "real"
|
|
|
|
replicator
|
|
= do { n <- name ; sEq ; b <- base ; sFOR ; c <- occamCount ; return $ N.For n b c }
|
|
<?> "replicator"
|
|
|
|
selection
|
|
= do { sCASE ; s <- selector ; eol ; indent ; os <- many1 occamOption ; outdent ; return $ N.Case s os }
|
|
<?> "selection"
|
|
|
|
selector
|
|
= expression
|
|
<?> "selector"
|
|
|
|
occamSequence
|
|
= do sSEQ
|
|
(do { eol ; indent ; ps <- many1 process ; outdent ; return $ N.Seq ps }
|
|
<|> do { r <- replicator ; eol ; indent ; p <- process ; outdent ; return $ N.SeqRep r p })
|
|
<?> "sequence"
|
|
|
|
sequentialProtocol
|
|
= do { l <- try $ sepBy1 simpleProtocol sSemi ; return $ l }
|
|
<?> "sequentialProtocol"
|
|
|
|
simpleProtocol
|
|
= try (do { l <- dataType ; sColons ; sLeft ; sRight ; r <- dataType ; return $ N.Counted l r })
|
|
<|> dataType
|
|
<|> do { sANY ; return $ N.Any }
|
|
<?> "simpleProtocol"
|
|
|
|
specification
|
|
= try declaration
|
|
<|> try abbreviation
|
|
<|> definition
|
|
<?> "specification"
|
|
|
|
specifier :: Parser N.Node
|
|
specifier
|
|
= try dataType
|
|
<|> try channelType
|
|
<|> try timerType
|
|
<|> try portType
|
|
<|> try (do { sLeft ; sRight ; s <- specifier ; return $ N.ArrayUnsized s })
|
|
<|> do { sLeft ; e <- expression ; sRight ; s <- specifier ; return $ N.Array e s }
|
|
<?> "specifier"
|
|
|
|
structuredType
|
|
= try (do { sRECORD ; eol ; indent ; fs <- many1 structuredTypeField ; outdent ; return $ N.Record fs })
|
|
<|> do { sPACKED ; sRECORD ; eol ; indent ; fs <- many1 structuredTypeField ; outdent ; return $ N.PackedRecord fs }
|
|
<?> "structuredType"
|
|
|
|
-- FIXME this should use the same type-folding code as proc/func definitions
|
|
structuredTypeField
|
|
= do { t <- dataType ; fs <- many1 fieldName ; sColon ; eol ; return $ N.Fields t fs }
|
|
<?> "structuredTypeField"
|
|
|
|
-- i.e. array literal
|
|
table
|
|
= do { v <- table' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl N.Sub v es }
|
|
<?> "table"
|
|
|
|
table'
|
|
= try occamString
|
|
<|> try (do { s <- occamString ; sLeftR ; n <- name ; sRightR ; return $ N.TypedLit n s })
|
|
<|> do { sLeft ;
|
|
try (do { es <- sepBy1 expression sComma ; sRight ; return $ N.LitArray es })
|
|
<|> try (do { n <- table ; sFROM ; e <- expression ; sFOR ; f <- expression ; sRight ; return $ N.SubFromFor n e f })
|
|
<|> try (do { n <- table ; sFROM ; e <- expression ; sRight ; return $ N.SubFrom n e })
|
|
<|> do { n <- table ; sFOR ; e <- expression ; sRight ; return $ N.SubFor n e } }
|
|
<?> "table'"
|
|
|
|
tag
|
|
= name
|
|
<?> "tag"
|
|
|
|
taggedList
|
|
= try (do { t <- tag ; sSemi ; is <- sepBy1 inputItem sSemi ; return $ N.Tag t is })
|
|
<|> do { t <- tag ; return $ N.Tag t [] }
|
|
<?> "taggedList"
|
|
|
|
taggedProtocol
|
|
= try (do { t <- tag ; eol ; return $ N.Tag t [] })
|
|
<|> try (do { t <- tag ; sSemi ; sp <- sequentialProtocol ; eol ; return $ N.Tag t sp })
|
|
|
|
timerType
|
|
= do { sTIMER ; return $ N.Timer }
|
|
<|> do { try sLeft ; s <- try expression ; try sRight ; t <- timerType ; return $ N.Array s t }
|
|
<?> "timerType"
|
|
|
|
valueProcess
|
|
= try (do { sVALOF ; eol ; indent ; p <- process ; sRESULT ; el <- expressionList ; eol ; outdent ; return $ N.ValOf p el })
|
|
<|> do { s <- specification ; v <- valueProcess ; return $ N.Decl s v }
|
|
|
|
variable
|
|
= do { v <- variable' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl N.Sub v es }
|
|
<?> "variable"
|
|
|
|
variable'
|
|
= try name
|
|
<|> try (do { sLeft ; n <- variable ; sFROM ; e <- expression ; sFOR ; f <- expression ; sRight ; return $ N.SubFromFor n e f })
|
|
<|> try (do { sLeft ; n <- variable ; sFROM ; e <- expression ; sRight ; return $ N.SubFrom n e })
|
|
<|> do { sLeft ; n <- variable ; sFOR ; e <- expression ; sRight ; return $ N.SubFor n e }
|
|
<?> "variable'"
|
|
|
|
variableList
|
|
= do { vs <- sepBy1 variable sComma ; return $ vs }
|
|
<?> "variableList"
|
|
|
|
variant
|
|
= try (do { t <- taggedList ; eol ; indent ; p <- process ; outdent ; return $ N.Variant t p })
|
|
<|> do { s <- specification ; v <- variant ; return $ N.Decl s v }
|
|
<?> "variant"
|
|
|
|
-- -------------------------------------------------------------
|
|
|
|
-- 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 = do { whiteSpace ; process }
|
|
|
|
-- -------------------------------------------------------------
|
|
|
|
-- XXX this doesn't handle multi-line strings
|
|
-- XXX or VALOF processes
|
|
|
|
countIndent :: String -> Int
|
|
countIndent (' ':' ':cs) = 1 + (countIndent cs)
|
|
countIndent (' ':cs) = error "Bad indentation"
|
|
countIndent _ = 0
|
|
|
|
stripIndent :: String -> String
|
|
stripIndent (' ':cs) = stripIndent cs
|
|
stripIndent cs = cs
|
|
|
|
stripComment :: String -> String
|
|
stripComment [] = []
|
|
stripComment ('-':'-':s) = []
|
|
stripComment ('"':s) = '"' : stripCommentInString s
|
|
stripComment (c:s) = c : stripComment s
|
|
|
|
stripCommentInString :: String -> String
|
|
stripCommentInString [] = error "In string at end of line"
|
|
stripCommentInString ('"':s) = '"' : stripComment s
|
|
stripCommentInString (c:s) = c : stripCommentInString s
|
|
|
|
flatten :: [String] -> String
|
|
flatten ls = concat $ intersperse "\n" $ flatten' ls 0
|
|
where
|
|
rep n i = take n (repeat i)
|
|
flatten' [] level = [rep level '}']
|
|
flatten' (s:ss) level
|
|
| stripped == "" = "" : flatten' ss level
|
|
| newLevel > level = (rep (newLevel - level) '{' ++ stripped) : rest
|
|
| newLevel < level = (rep (level - newLevel) '}' ++ stripped) : rest
|
|
| otherwise = stripped : rest
|
|
where newLevel = countIndent s
|
|
stripped' = stripIndent $ stripComment s
|
|
stripped = if stripped' == "" then "" else (stripped' ++ "@")
|
|
rest = flatten' ss newLevel
|
|
|
|
-- -------------------------------------------------------------
|
|
|
|
-- XXX Doesn't handle preprocessor instructions.
|
|
|
|
preprocess :: String -> String
|
|
preprocess d = flatten $ 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
|
|
|
|
-- -------------------------------------------------------------
|
|
|
|
parseSource :: String -> N.Node
|
|
parseSource prep
|
|
= case (parse sourceFile "occam" prep) of
|
|
Left err -> error ("Parsing error: " ++ (show err))
|
|
Right defs -> defs
|
|
|