451 lines
18 KiB
Haskell
451 lines
18 KiB
Haskell
{-
|
|
Tock: a compiler for parallel languages
|
|
Copyright (C) 2007 University of Kent
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation, either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-}
|
|
|
|
module ParseRain where
|
|
|
|
|
|
import Control.Monad (liftM)
|
|
import Control.Monad.State (MonadState, liftIO, get, put)
|
|
import Data.Generics
|
|
import Data.List
|
|
import Data.Maybe
|
|
import qualified IO
|
|
import Text.ParserCombinators.Parsec
|
|
|
|
import qualified AST as A
|
|
import CompState
|
|
import Errors
|
|
import qualified LexRain as L
|
|
import Metadata
|
|
import ParseUtils
|
|
import Pass
|
|
|
|
|
|
|
|
type RainState = CompState
|
|
type RainParser = GenParser L.Token RainState
|
|
|
|
instance Die (GenParser tok st) where
|
|
dieReport (Just m, err) = fail $ packMeta m err
|
|
dieReport (Nothing, err) = fail err
|
|
|
|
sLeftQ, sRightQ, sLeftR, sRightR, sLeftC, sRightC, sSemiColon, sColon, sComma, sIn, sOut, sDots,
|
|
sPar, sSeq, sAlt, sPri, sSeqeach, sPareach, sChannel, sOne2One, sIf, sElse, sWhile, sProcess, sFunction, sRun, sReturn, sWait, sFor, sUntil
|
|
:: RainParser Meta
|
|
|
|
--{{{ Symbols
|
|
sLeftQ = reserved "["
|
|
sRightQ = reserved "]"
|
|
sLeftR = reserved "("
|
|
sRightR = reserved ")"
|
|
sLeftC = reserved "{"
|
|
sRightC = reserved "}"
|
|
sSemiColon = reserved ";"
|
|
sColon = reserved ":"
|
|
sComma = reserved ","
|
|
sIn = reserved "?"
|
|
sOut = reserved "!"
|
|
sDots = reserved ".."
|
|
--}}}
|
|
|
|
--{{{ Keywords
|
|
|
|
sPar = reserved "par"
|
|
sSeq = reserved "seq"
|
|
sAlt = reserved "alt"
|
|
sPri = reserved "pri"
|
|
sSeqeach = reserved "seqeach"
|
|
sPareach = reserved "pareach"
|
|
sChannel = reserved "channel"
|
|
sOne2One = reserved "one2one"
|
|
sIf = reserved "if"
|
|
sElse = reserved "else"
|
|
sWhile = reserved "while"
|
|
sProcess = reserved "process"
|
|
sFunction = reserved "function"
|
|
sRun = reserved "run"
|
|
sReturn = reserved "return"
|
|
sWait = reserved "wait"
|
|
sFor = reserved "for"
|
|
sUntil = reserved "until"
|
|
--}}}
|
|
|
|
--{{{Operators
|
|
|
|
dyadicArithOp :: RainParser (Meta,A.DyadicOp)
|
|
dyadicArithOp
|
|
= do {m <- reserved "+" ; return (m,A.Plus) }
|
|
<|> do {m <- reserved "-" ; return (m,A.Minus) }
|
|
<|> do {m <- reserved "*" ; return (m,A.Times) }
|
|
<|> do {m <- reserved "/" ; return (m,A.Div) }
|
|
<|> do {m <- reserved "%" ; return (m,A.Rem) }
|
|
|
|
dyadicCompOp :: RainParser (Meta,A.DyadicOp)
|
|
dyadicCompOp
|
|
= do {m <- reserved "<" ; return (m,A.Less) }
|
|
<|> do {m <- reserved ">" ; return (m,A.More) }
|
|
<|> do {m <- reserved "<=" ; return (m,A.LessEq) }
|
|
<|> do {m <- reserved ">=" ; return (m,A.MoreEq) }
|
|
<|> do {m <- reserved "==" ; return (m,A.Eq) }
|
|
|
|
monadicArithOp :: RainParser (Meta,A.MonadicOp)
|
|
monadicArithOp
|
|
= do {m <- reserved "-" ; return (m,A.MonadicMinus) }
|
|
|
|
|
|
|
|
--}}}
|
|
|
|
getToken :: (L.TokenType -> Maybe x) -> RainParser (Meta, x)
|
|
getToken test = token (show) (metaToSourcePos . fst) (wrap test)
|
|
where
|
|
wrap :: (L.TokenType -> Maybe x) -> (Meta,L.TokenType) -> Maybe (Meta,x)
|
|
wrap f (m,t) = case f t of
|
|
Nothing -> Nothing
|
|
Just t' -> Just (m,t')
|
|
|
|
identifier :: RainParser (Meta, String)
|
|
identifier = getToken testToken
|
|
where
|
|
testToken (L.TokIdentifier id) = Just id
|
|
testToken _ = Nothing
|
|
|
|
reserved :: String -> RainParser Meta
|
|
reserved word
|
|
= (liftM fst) (getToken testToken)
|
|
<?> ("reserved word: " ++ word)
|
|
where
|
|
testToken (L.TokReserved r) = if r == word then Just r else Nothing
|
|
testToken _ = Nothing
|
|
|
|
|
|
name :: RainParser A.Name
|
|
name
|
|
= do (m,s) <- identifier
|
|
return $ A.Name m (A.VariableName) s --A.VariableName is a placeholder until a later pass
|
|
<?> "name"
|
|
|
|
|
|
dataType :: RainParser A.Type
|
|
dataType
|
|
= do {reserved "bool" ; return A.Bool}
|
|
<|> do {reserved "int" ; return A.Int}
|
|
<|> do {reserved "uint8" ; return A.Byte}
|
|
<|> do {reserved "uint16" ; return A.UInt16}
|
|
<|> do {reserved "uint32" ; return A.UInt32}
|
|
<|> do {reserved "uint64" ; return A.UInt64}
|
|
<|> do {reserved "sint8" ; return A.Int8}
|
|
<|> do {reserved "sint16" ; return A.Int16}
|
|
<|> do {reserved "sint32" ; return A.Int32}
|
|
<|> do {reserved "sint64" ; return A.Int64}
|
|
<|> do {reserved "time" ; return A.Time}
|
|
<|> do {sChannel ; inner <- dataType ; return $ A.Chan A.DirUnknown (A.ChanAttributes {A.caWritingShared = False, A.caReadingShared = False}) inner}
|
|
<|> do {sIn ; inner <- dataType ; return $ A.Chan A.DirInput (A.ChanAttributes {A.caWritingShared = False, A.caReadingShared = False}) inner}
|
|
<|> do {sOut ; inner <- dataType ; return $ A.Chan A.DirOutput (A.ChanAttributes {A.caWritingShared = False, A.caReadingShared = False}) inner}
|
|
<|> do {(m,n) <- identifier ; return $ A.UserDataType A.Name {A.nameMeta = m, A.nameName = n, A.nameType = A.DataTypeName}}
|
|
<?> "data type"
|
|
|
|
variable :: RainParser A.Variable
|
|
variable = do {v <- name ; return $ A.Variable (findMeta v) v}
|
|
<|> try (do {m <- sIn ; v <- variable ; return $ A.DirectedVariable m A.DirInput v})
|
|
<|> try (do {m <- sOut ; v <- variable ; return $ A.DirectedVariable m A.DirOutput v})
|
|
<?> "variable"
|
|
|
|
lvalue :: RainParser A.Variable
|
|
lvalue = variable
|
|
|
|
stringLiteral :: RainParser (A.LiteralRepr, A.Dimension)
|
|
stringLiteral
|
|
= do (m,str) <- getToken testToken
|
|
let processed = replaceEscapes str
|
|
let aes = [A.ArrayElemExpr $ A.Literal m A.Byte $ A.ByteLiteral m [c] | c <- processed]
|
|
return (A.ArrayLiteral m aes, A.Dimension $ length processed)
|
|
<?> "string literal"
|
|
where
|
|
testToken (L.TokStringLiteral str) = Just str
|
|
testToken _ = Nothing
|
|
|
|
replaceEscapes :: String -> String
|
|
replaceEscapes [] = []
|
|
replaceEscapes ('\\':(c:cs)) = if c == 'n' then ('\n':replaceEscapes cs) else (c:replaceEscapes cs)
|
|
replaceEscapes (c:cs) = (c:replaceEscapes cs)
|
|
|
|
literalCharacter :: RainParser A.LiteralRepr
|
|
literalCharacter
|
|
= do (m,c) <- getToken testToken
|
|
return $ A.ByteLiteral m (replaceEscapes c)
|
|
where
|
|
testToken (L.TokCharLiteral c) = Just c
|
|
testToken _ = Nothing
|
|
|
|
integer :: RainParser A.LiteralRepr
|
|
integer
|
|
= do (m,d) <- getToken testToken
|
|
return $ A.IntLiteral m d
|
|
where
|
|
testToken (L.TokDecimalLiteral d) = Just d
|
|
testToken _ = Nothing
|
|
|
|
integerLiteral :: RainParser A.Expression
|
|
integerLiteral = do {i <- integer ; return $ A.Literal (findMeta i) A.Int i}
|
|
|
|
literal :: RainParser A.Expression
|
|
literal = do {(lr, dim) <- stringLiteral ; return $ A.Literal (findMeta lr) (A.List A.Byte) lr }
|
|
<|> do {c <- literalCharacter ; return $ A.Literal (findMeta c) A.Byte c}
|
|
<|> integerLiteral
|
|
<|> do {m <- reserved "true" ; return $ A.True m}
|
|
<|> do {m <- reserved "false" ; return $ A.False m}
|
|
<?> "literal"
|
|
|
|
range :: RainParser A.Expression
|
|
range = try $ do {m <- sLeftQ ; begin <- integerLiteral; sDots ; end <- integerLiteral ; sRightQ ; return $ A.ExprConstr m $ A.RangeConstr m begin end}
|
|
|
|
expression :: RainParser A.Expression
|
|
expression
|
|
= try compExpression
|
|
<|> try castExpression
|
|
<|> try subExpression
|
|
<?> "expression"
|
|
where
|
|
castExpression :: RainParser A.Expression
|
|
castExpression = (try $ do {ty <- dataType ; m <- sColon ; e <- expression ; return $ A.Conversion m A.DefaultConversion ty e})
|
|
|
|
compExpression :: RainParser A.Expression
|
|
compExpression = do {lhs <- subExpression ; (m,op) <- dyadicCompOp ; rhs <- subExpression ; return $ A.Dyadic m op lhs rhs }
|
|
|
|
subExpression :: RainParser A.Expression
|
|
subExpression
|
|
= do se <- subExpr'
|
|
further <- many (do {(m, op) <- dyadicArithOp ; exp <- subExpr' ; return (m,op,exp)})
|
|
--further :: [(Meta,A.DyadicOp,A.Expression)]
|
|
return $ foldl foldOps se further
|
|
|
|
foldOps :: A.Expression -> (Meta,A.DyadicOp,A.Expression) -> A.Expression
|
|
foldOps lhs (m,op,rhs) = A.Dyadic m op lhs rhs
|
|
|
|
subExpr' :: RainParser A.Expression
|
|
subExpr' = do {id <- variable ; return $ A.ExprVariable (findMeta id) id}
|
|
<|> literal
|
|
<|> range
|
|
<|> do {(m,op) <- monadicArithOp ; rhs <- subExpr' ; return $ A.Monadic m op rhs}
|
|
<|> do {sLeftR ; e <- expression ; sRightR ; return e}
|
|
|
|
data InnerBlockLineState = Decls | NoMoreDecls | Mixed deriving (Eq)
|
|
|
|
|
|
innerBlock :: Bool -> Maybe A.Name -> RainParser (A.Structured A.Process)
|
|
innerBlock declsMustBeFirst funcName
|
|
= do m <- sLeftC
|
|
lines <- linesToEnd (if declsMustBeFirst then Decls else Mixed)
|
|
case lines of
|
|
Left single -> return single
|
|
Right lines -> return $ A.Several m lines
|
|
where
|
|
wrapProc :: A.Process -> A.Structured A.Process
|
|
wrapProc x = A.Only (findMeta x) x
|
|
|
|
makeList :: Either (A.Structured A.Process) [A.Structured A.Process] -> [A.Structured A.Process]
|
|
makeList (Left s) = [s]
|
|
makeList (Right ss) = ss
|
|
|
|
--Returns either a single line (which means the immediate next line is a declaration) or a list of remaining lines
|
|
linesToEnd :: InnerBlockLineState -> RainParser (Either (A.Structured A.Process) [A.Structured A.Process])
|
|
linesToEnd state
|
|
= (if state /= NoMoreDecls then
|
|
do (m,decl) <- declaration
|
|
rest <- linesToEnd state
|
|
case rest of
|
|
Left s -> return $ Left $ decl s
|
|
Right ss -> return $ Left $ decl $ A.Several m ss
|
|
else pzero)
|
|
<|> do {st <- statement ; rest <- linesToEnd nextState ; return $ Right $ (wrapProc st) : (makeList rest)}
|
|
--Although return is technically a statement, we parse it here because it can only occur inside the right kind of block:
|
|
<|> (case funcName of
|
|
Nothing -> pzero
|
|
Just actFuncName ->
|
|
do m <- sReturn
|
|
exp <- expression
|
|
sSemiColon
|
|
rest <- linesToEnd nextState
|
|
return $ Right $ (A.Only m $ A.Assign m [A.Variable m actFuncName] $ A.ExpressionList (findMeta exp) [exp]) : (makeList rest)
|
|
)
|
|
<|> do {sRightC ; return $ Right []}
|
|
<?> "statement, declaration, or end of block"
|
|
where
|
|
nextState = if state == Mixed then Mixed else NoMoreDecls
|
|
|
|
block :: RainParser A.Process
|
|
block = do { optionalSeq ; b <- innerBlock False Nothing ; return $ A.Seq (findMeta b) b}
|
|
<|> do { m <- sPar ; b <- innerBlock True Nothing ; return $ A.Par m A.PlainPar b}
|
|
<?> "seq or par block"
|
|
|
|
optionalSeq :: RainParser ()
|
|
optionalSeq = option () (sSeq >> return ())
|
|
|
|
assignOp :: RainParser (Meta, Maybe A.DyadicOp)
|
|
--consume an optional operator, then an equals sign (so we can handle = += /= etc) This should not handle !=, nor crazy things like ===, <== (nor <=)
|
|
assignOp
|
|
= do {m <- reserved "+=" ; return (m,Just A.Plus)}
|
|
<|> do {m <- reserved "-=" ; return (m,Just A.Minus)}
|
|
<|> do {m <- reserved "*=" ; return (m,Just A.Times)}
|
|
<|> do {m <- reserved "/=" ; return (m,Just A.Div)}
|
|
<|> do {m <- reserved "%=" ; return (m,Just A.Rem)}
|
|
<|> do {m <- reserved "=" ; return (m,Nothing)}
|
|
|
|
|
|
each :: RainParser A.Process
|
|
each = do { m <- sPareach ; sLeftR ; n <- name ; sColon ; exp <- expression ; sRightR ; st <- block ;
|
|
return $ A.Par m A.PlainPar $ A.Rep m (A.ForEach m n exp) $ A.Only m st }
|
|
<|> do { m <- sSeqeach ; sLeftR ; n <- name ; sColon ; exp <- expression ; sRightR ; st <- block ;
|
|
return $ A.Seq m $ A.Rep m (A.ForEach m n exp) $ A.Only m st }
|
|
|
|
comm :: Bool -> RainParser A.Process
|
|
comm isAlt
|
|
= do { lv <- lvalue ;
|
|
(if isAlt
|
|
then pzero
|
|
else do {sOut ; exp <- expression ; possSemiColon ; return $ A.Output (findMeta lv) lv [A.OutExpression (findMeta exp) exp] })
|
|
<|> do {sIn ; rv <- lvalue ; possSemiColon ; return $ A.Input (findMeta lv) lv $ A.InputSimple (findMeta rv) [A.InVariable (findMeta rv) rv] }
|
|
<?> (if isAlt then "input statement" else "input or output statement")
|
|
}
|
|
where
|
|
possSemiColon :: RainParser ()
|
|
possSemiColon = if isAlt then return () else sSemiColon >> return ()
|
|
|
|
alt :: RainParser A.Process
|
|
alt = do {m <- sPri ; sAlt ; m' <- sLeftC ; cases <- many altCase ; optElseCase <- option [] (singleton elseCase) ; sRightC ; return $ A.Alt m True $ A.Several m' (cases ++ optElseCase)}
|
|
where
|
|
singleton :: RainParser a -> RainParser [a]
|
|
singleton p = do {a <- p ; return [a]}
|
|
|
|
altCase :: RainParser (A.Structured A.Alternative)
|
|
altCase = do input <- comm True
|
|
case input of
|
|
A.Input m lv im -> do { body <- block ; return $ A.Only m $ A.Alternative m lv im body }
|
|
_ -> dieP (findMeta input) $ "communication type not supported in an alt: \"" ++ show input ++ "\""
|
|
<|> do (m, wm, e) <- waitStatement True
|
|
body <- block
|
|
return $ A.Only m $ A.AlternativeWait m wm e body
|
|
elseCase :: RainParser (A.Structured A.Alternative)
|
|
elseCase = do m <- sElse
|
|
body <- block
|
|
return $ A.Only m $ A.AlternativeSkip m (A.True m) body
|
|
|
|
tuple :: RainParser [A.Expression]
|
|
tuple = do { sLeftR ; items <- expression `sepBy` sComma ; sRightR ; return items }
|
|
|
|
runProcess :: RainParser A.Process
|
|
runProcess = do m <- sRun
|
|
(mProcess,processName) <- identifier
|
|
items <- tuple
|
|
sSemiColon
|
|
return $ A.ProcCall m A.Name {A.nameName = processName, A.nameMeta = mProcess, A.nameType = A.ProcName} (map convertItem items)
|
|
where
|
|
convertItem :: A.Expression -> A.Actual
|
|
convertItem (A.ExprVariable _ v) = A.ActualVariable A.Original A.Any v
|
|
convertItem e = A.ActualExpression A.Any e
|
|
|
|
waitStatement :: Bool -> RainParser (Meta, A.WaitMode, A.Expression)
|
|
waitStatement isAlt
|
|
= do { m <- sWait ;
|
|
do { sFor ; e <- expression ; possSemiColon ; return (m, A.WaitFor, e)}
|
|
<|> do { sUntil ; e <- expression ; possSemiColon ; return (m, A.WaitUntil, e)}
|
|
<?> "reserved word \"for\" or \"until\" should follow reserved word \"wait\""
|
|
}
|
|
where
|
|
possSemiColon :: RainParser ()
|
|
possSemiColon = if isAlt then return () else sSemiColon >> return ()
|
|
|
|
statement :: RainParser A.Process
|
|
statement
|
|
= do { m <- sWhile ; sLeftR ; exp <- expression ; sRightR ; st <- block ; return $ A.While m exp st}
|
|
<|> do { m <- sIf ; sLeftR ; exp <- expression ; sRightR ; st <- block ;
|
|
option (A.If m $ A.Several m [A.Only m (A.Choice m exp st), A.Only m (A.Choice m (A.True m) (A.Skip m))])
|
|
(do {sElse ; elSt <- block ; return (A.If m $ A.Several m [A.Only m (A.Choice m exp st), A.Only m (A.Choice m (A.True m) elSt)])})
|
|
}
|
|
<|> block
|
|
<|> each
|
|
<|> runProcess
|
|
<|> do {m <- reserved "now" ; dest <- lvalue ; sSemiColon ; return $ A.GetTime m dest}
|
|
<|> do {(m,wm,exp) <- waitStatement False ; return $ A.Wait m wm exp}
|
|
<|> try (comm False)
|
|
<|> alt
|
|
<|> try (do { lv <- lvalue ; op <- assignOp ; exp <- expression ; sSemiColon ;
|
|
case op of
|
|
(m', Just dyOp) -> return (A.Assign m' [lv] (A.ExpressionList m' [(A.Dyadic m' dyOp (A.ExprVariable (findMeta lv) lv) exp)]))
|
|
(m', Nothing) -> return (A.Assign m' [lv] (A.ExpressionList (findMeta exp) [exp]))
|
|
})
|
|
<?> "statement"
|
|
|
|
formaliseTuple :: [(A.Name,A.Type)] -> [A.Formal]
|
|
formaliseTuple = map (\(n,t) -> A.Formal A.ValAbbrev t n)
|
|
|
|
tupleDef :: RainParser [(A.Name,A.Type)]
|
|
tupleDef = do {sLeftR ; tm <- sepBy tupleDefMember sComma ; sRightR ; return tm}
|
|
where
|
|
tupleDefMember :: RainParser (A.Name,A.Type)
|
|
tupleDefMember = do {t <- dataType ; sColon ; n <- name ; return (n,t)}
|
|
|
|
declaration :: Data a => RainParser (Meta, A.Structured a -> A.Structured a)
|
|
declaration = try $ do {t <- dataType; sColon ; ns <- name `sepBy1` sComma ; sSemiColon ;
|
|
return (findMeta t, \x -> foldr (foldSpec t) x ns) }
|
|
where
|
|
foldSpec :: Data a => A.Type -> A.Name -> (A.Structured a -> A.Structured a)
|
|
foldSpec t n = A.Spec (findMeta t) $ A.Specification (findMeta t) n $ A.Declaration (findMeta t) t
|
|
|
|
terminator :: Data a => A.Structured a
|
|
terminator = A.Several emptyMeta []
|
|
|
|
processDecl :: RainParser A.AST
|
|
processDecl = do {m <- sProcess ; procName <- name ; params <- tupleDef ; body <- block ;
|
|
return $ A.Spec m
|
|
(A.Specification m procName (A.Proc m A.PlainSpec (formaliseTuple params) body))
|
|
terminator}
|
|
|
|
functionDecl :: RainParser A.AST
|
|
functionDecl = do {m <- sFunction ; retType <- dataType ; sColon ; funcName <- name ; params <- tupleDef ; body <- innerBlock False (Just funcName) ;
|
|
return $ A.Spec m
|
|
(A.Specification m funcName (A.Function m A.PlainSpec [retType] (formaliseTuple params) (Right $ A.Seq m body)))
|
|
terminator}
|
|
|
|
topLevelDecl :: RainParser A.AST
|
|
topLevelDecl = do decls <- many (processDecl <|> functionDecl <?> "process or function declaration")
|
|
eof
|
|
return $ A.Several emptyMeta decls
|
|
|
|
rainSourceFile :: RainParser (A.AST, CompState)
|
|
rainSourceFile
|
|
= do p <- topLevelDecl
|
|
s <- getState
|
|
return (p, s)
|
|
|
|
-- | Load and parse a Rain source file.
|
|
parseRainProgram :: String -> PassM A.AST
|
|
parseRainProgram filename
|
|
= do source <- liftIO $ readFile filename
|
|
lexOut <- liftIO $ L.runLexer filename source
|
|
case lexOut of
|
|
Left merr -> dieP merr $ "Parse (lexing) error"
|
|
Right toks ->
|
|
do cs <- get
|
|
case runParser rainSourceFile cs filename toks of
|
|
Left err -> dieP (sourcePosToMeta $ errorPos err) $ "Parse error: " ++ show err
|
|
Right (p, cs') ->
|
|
do put cs'
|
|
return p
|