tock-mirror/frontends/ParseRain.hs
Neil Brown 8f767ff0d4 Made all the imports of Data.Generics have an import list
This makes sure that we catch all leftover instances of using SYB to do generic operations that we should be using Polyplate for instead.  Most modules should only import Data, and possibly Typeable.
2009-04-09 15:36:37 +00:00

548 lines
22 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 (Data)
import Data.List
import Data.Maybe
import qualified IO
import Text.ParserCombinators.Parsec
import qualified AST as A
import CompState
import Errors
import Intrinsics
import qualified LexRain as L
import Metadata
import ParseUtils
import Pass
import Utils
type RainState = CompState
type RainParser = GenParser L.Token RainState
instance CSMR (GenParser tok CompState) where
getCompState = getState
-- We can expose only part of the state to make it look like we are only using
-- CompState:
instance MonadState CompState (GenParser tok CompState) where
get = getState
put = setState
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, sReturn,
sWait, sFor, sUntil, sPoison :: 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"
sReturn = reserved "return"
sWait = reserved "wait"
sFor = reserved "for"
sUntil = reserved "until"
sPoison = reserved "poison"
--}}}
--{{{Operators
dyadicArithOp :: RainParser (Meta,A.Name)
dyadicArithOp
= do {m <- reserved "+" ; return (m, A.Name m "+") }
<|> do {m <- reserved "-" ; return (m, A.Name m "-") }
<|> do {m <- reserved "*" ; return (m, A.Name m "*") }
<|> do {m <- reserved "/" ; return (m, A.Name m "/") }
<|> do {m <- reserved "%" ; return (m, A.Name m "%") }
<|> do {m <- reserved "++" ; return (m, A.Name m "++") }
dyadicCompOp :: RainParser (Meta,A.Name)
dyadicCompOp
= do {m <- reserved "<" ; return (m, A.Name m "<") }
<|> do {m <- reserved ">" ; return (m, A.Name m ">") }
<|> do {m <- reserved "<=" ; return (m, A.Name m "<=") }
<|> do {m <- reserved ">=" ; return (m, A.Name m ">=") }
<|> do {m <- reserved "==" ; return (m, A.Name m "=") }
<|> do {m <- reserved "<>" ; return (m, A.Name m "<>") }
-- TODO remove this in future, I think
monadicArithOp :: RainParser (Meta,A.Name)
monadicArithOp
= do {m <- reserved "-" ; return (m, A.Name m "-") }
--}}}
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 s
<?> "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.ChanAttributes {A.caWritingShared = A.Unshared, A.caReadingShared = A.Unshared}) inner}
<|> do {sIn ; inner <- dataType ; return $ A.ChanEnd A.DirInput A.Unshared inner}
<|> do {sOut ; inner <- dataType ; return $ A.ChanEnd A.DirOutput A.Unshared inner}
<|> do {sLeftQ ; inner <- dataType ; sRightQ ; return $ A.List inner}
<|> do {(m,n) <- identifier ; return $ A.UserDataType A.Name {A.nameMeta = m, A.nameName = n}}
<?> "data type"
variable :: RainParser A.Variable
variable = do {v <- name ; return $ A.Variable (A.nameMeta 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
stringLiteral
= do (m,str) <- getToken testToken
let processed = replaceEscapes str
aes = A.Several m [A.Only m $ A.Literal m A.Byte $ A.ByteLiteral m [c] | c <- processed]
return (A.ArrayListLiteral m aes)
<?> "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 (Integer, A.LiteralRepr)
integer
= do (m,d) <- getToken testToken
return $ (read d, A.IntLiteral m d)
where
testToken (L.TokDecimalLiteral d) = Just d
testToken _ = Nothing
integerLiteral :: RainParser A.Expression
integerLiteral = do (val, i) <- integer
u <- getUniqueIdentifer
let m = findMeta i
return $ A.Literal m (A.UnknownNumLitType m u val) i
listLiteral :: RainParser A.Expression
listLiteral
= try $ do m <- sLeftQ
u <- getUniqueIdentifer
let t = A.List $ A.UnknownVarType (A.TypeRequirements False) (Right (m,u))
(do try sRightQ
return $ A.Literal m t $ A.ArrayListLiteral m (A.Several m [])
<|> do e0 <- try expression
(do try sRightQ
return $ A.Literal m t $ A.ArrayListLiteral m $
A.Several m [A.Only m e0]
-- Up until the first comma, this may be a type declaration
-- in a cast expression, so we "try" all the way
-- up until that comma
<|> do try sComma
es <- sepBy1 expression sComma
sRightQ
return $ A.Literal m t $
A.ArrayListLiteral m $ A.Several m $
map (A.Only m) (e0 : es)
)
)
literal :: RainParser A.Expression
literal = do {lr <- 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}
<|> listLiteral
<?> "literal"
maybeParse :: RainParser a -> RainParser (Maybe a)
maybeParse p = option Nothing (p >>* Just)
range :: RainParser A.Expression
range = try $ do m <- sLeftQ
optTy <- maybeParse $ try $ do t <- dataType
m <- sColon
return (t, m)
begin <- literal
sDots
end <- literal
sRightQ
(t, be) <- case optTy of
Just (t, mc) ->
let begin' = A.Conversion mc A.DefaultConversion t begin
end' = A.Conversion mc A.DefaultConversion t end
in return (A.List t, (begin', end'))
Nothing -> do u <- getUniqueIdentifer
let t = A.List $ A.UnknownVarType (A.TypeRequirements
False) (Right (m,u))
return (t, (begin, end))
return $ A.Literal m t $ A.RangeLiteral m (fst be) (snd be)
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.FunctionCall 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.Name,A.Expression) -> A.Expression
foldOps lhs (m,op,rhs) = A.FunctionCall m op [lhs, rhs]
subExpr' :: RainParser A.Expression
subExpr' = try functionCall
<|> do {id <- variable ; return $ A.ExprVariable (findMeta id) id}
<|> literal
<|> range
<|> do {(m,op) <- monadicArithOp ; rhs <- subExpr' ; return $ A.FunctionCall m op [rhs]}
<|> do {sLeftR ; e <- expression ; sRightR ; return e}
functionCall :: RainParser A.Expression
functionCall = do funcName <- name
sLeftR
es <- sepBy expression sComma
sRightR
case lookup (A.nameName funcName) rainIntrinsicFunctions of
Just _ -> return $ A.IntrinsicFunctionCall (A.nameMeta
funcName) (A.nameName funcName) es
Nothing -> return $
A.FunctionCall (A.nameMeta funcName) funcName es
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.Name)
--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.Name m "+")}
<|> do {m <- reserved "-=" ; return (m,Just $ A.Name m "-")}
<|> do {m <- reserved "*=" ; return (m,Just $ A.Name m "*")}
<|> do {m <- reserved "/=" ; return (m,Just $ A.Name m "/")}
<|> do {m <- reserved "%=" ; return (m,Just $ A.Name m "%")}
<|> 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.Spec m (A.Specification m n $
A.Rep m (A.ForEach m exp)) $ A.Only m st }
<|> do { m <- sSeqeach ; sLeftR ; n <- name ; sColon ; exp <- expression ; sRightR ; st <- block ;
return $ A.Seq m $ A.Spec m (A.Specification m n $ A.Rep m (A.ForEach m 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
(A.True m) lv im body
_ -> dieP (findMeta input) $ "communication type not supported in an alt: \"" ++ show input ++ "\""
<|> do (m, wm) <- waitStatement True
body <- block
return $ A.Only m $ A.Alternative m (A.True m)
(A.Variable m rainTimerName) wm 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 (mProcess,processName) <- identifier
items <- tuple
sSemiColon
return $ A.ProcCall mProcess A.Name {A.nameName = processName, A.nameMeta = mProcess} (map convertItem items)
where
convertItem :: A.Expression -> A.Actual
convertItem (A.ExprVariable _ v) = A.ActualVariable v
convertItem e = A.ActualExpression e
waitStatement :: Bool -> RainParser (Meta, A.InputMode)
waitStatement isAlt
= do { m <- sWait ;
do { sFor ; e <- expression ; possSemiColon ;
return (m, A.InputTimerFor m e)}
<|> do { sUntil ; e <- expression ; possSemiColon ;
return (m, A.InputTimerAfter m 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
<|> try runProcess
<|> do {m <- reserved "now" ; dest <- lvalue ; sSemiColon ; return $ A.Input
m (A.Variable m rainTimerName) $ A.InputTimerRead m $ A.InVariable m dest}
<|> do {(m,wm) <- waitStatement False; return $ A.Input m (A.Variable m
rainTimerName) wm}
<|> try (comm False)
<|> alt
<|> do {m <- sPoison ; ch <- lvalue; sSemiColon ; return $ A.InjectPoison m ch}
<|> try (do { lv <- lvalue ; op <- assignOp ; exp <- expression ; sSemiColon ;
case op of
(m', Just dyOp) -> return (A.Assign m' [lv] (A.ExpressionList m' [(A.FunctionCall 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; m <- sColon ; ns <- name `sepBy1` sComma ; sSemiColon ;
return (m, \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 (A.nameMeta n) $ A.Specification (A.nameMeta n) n $ A.Declaration (A.nameMeta n) 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, A.Recursive) (formaliseTuple params) $ Just 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, A.Recursive) [retType] (formaliseTuple params) (Just $ 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)
-- | A ghost variable for Rain's single timer.
-- This is used for all timer operations.
rainTimerName :: A.Name
rainTimerName = A.Name emptyMeta "rain_timer"
rainTimerNameDef :: A.NameDef
rainTimerNameDef
= A.NameDef { A.ndMeta = emptyMeta
, A.ndName = A.nameName rainTimerName
, A.ndOrigName = A.nameName rainTimerName
, A.ndSpecType = A.Declaration emptyMeta (A.Timer A.RainTimer)
, A.ndAbbrevMode = A.Original
, A.ndNameSource = A.NamePredefined
, A.ndPlacement = A.Unplaced
}
-- | Parse Rain source text (with filename for error messages)
parseRainProgram :: FilePath -> String -> PassM A.AST
parseRainProgram filename source
= do lexOut <- liftIO $ L.runLexer filename source
case lexOut of
Left merr -> dieP merr $ "Parse (lexing) error"
Right toks ->
do defineName rainTimerName rainTimerNameDef
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