{- 

   Parser.hs: Parser for the Flounder interface definition language

   Part of Flounder: a strawman device definition DSL for Barrelfish

  Copyright (c) 2009, ETH Zurich.

  All rights reserved.

  This file is distributed under the terms in the attached LICENSE file.
  If you do not find this file, copies can be found by writing to:
  ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
-}

module Parser where

import Syntax

import Prelude
import Text.ParserCombinators.Parsec as Parsec
import Text.ParserCombinators.Parsec.Expr
import Text.ParserCombinators.Parsec.Pos
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language( javaStyle )
import Data.Char
import Numeric
import Data.List
import Text.Printf

parse_intf predefDecls filename = parseFromFile (intffile predefDecls) filename
parse_include predefDecls filename = parseFromFile (includefile predefDecls) filename

lexer = P.makeTokenParser (javaStyle
                           { P.reservedNames = [ "interface",
                                                 "message",
                                                 "rpc",
                                                 "in",
                                                 "out"
                                               ]
                           , P.reservedOpNames = ["*","/","+","-"]
                           , P.commentStart = "/*"
                           , P.commentEnd = "*/"
                           })

whiteSpace = P.whiteSpace lexer
reserved   = P.reserved lexer
identifier = P.identifier lexer
stringLit  = P.stringLiteral lexer
comma      = P.comma lexer
commaSep   = P.commaSep lexer
commaSep1  = P.commaSep1 lexer
parens     = P.parens lexer
braces     = P.braces lexer
squares    = P.squares lexer
semiSep    = P.semiSep lexer
symbol     = P.symbol lexer
natural    = P.natural lexer

builtinTypes = map show [UInt8 ..] ++ ["errval"] ++ ["int"] -- int is legacy -AB

-- identifyBuiltin :: [(String, Declaration)] -> String -> TypeRef
identifyBuiltin typeDcls typeName =
    do {
      if typeName `elem` builtinTypes then
          return $ Builtin $ (read typeName::TypeBuiltin)
      else
          case typeName `lookup` typeDcls of
            Just (Typedef (TAliasT new orig)) -> return $ TypeAlias new orig
            Just _ -> return $ TypeVar typeName
            Nothing ->
                do {
                ; pos <- getPosition
                -- This is ugly, I agree:
                ; return $ error ("Use of undeclared type '" ++ typeName ++ "' in "
                                  ++ show (sourceName pos) ++ " at l. "
                                  ++ show (sourceLine pos) ++ " col. "
                                  ++ show (sourceColumn pos))
                }
    }

intffile predefDecls = do { whiteSpace
             ; i <- iface predefDecls
             ; return i
              }

includefile predefDecls = do { whiteSpace
             ; typeDecls <- typeDeclaration predefDecls
             ; return typeDecls
              }

iface predefDecls = do { reserved "interface"
           ; name <- identifier
           ; descr <- option name stringLit
           ; decls <- braces $ do {
                               ; typeDecls <- typeDeclaration predefDecls
                               ; msgDecls <- many1 $ mesg typeDecls
                               ; return ((map snd typeDecls) ++ msgDecls)
                               }
           ; symbol ";" <?> " ';' missing from end of " ++ name ++ " interface specification"
           ;  return (Interface name (Just descr) decls)
           }


typeDeclaration typeDcls = do {
                           ; decl <- try (do {
                                           ; x <- transparentAlias
                                           ; return $ Just x
                                           })
                                     <|> try (do {
                                               ; x <- typedefinition typeDcls
                                               ; return $ Just x
                                               })
                                    <|> return Nothing
                           ; case decl of
                               Nothing -> return typeDcls
                               Just x -> typeDeclaration (x : typeDcls)
                           }

mesg typeDcls = do { bckArgs <- many backendParams
                   ; def <- msg typeDcls bckArgs <|> rpc typeDcls bckArgs
                   ; return $ Messagedef def
                   }

msg typeDcls bckArgs = do { t <- msgtype
                          ; i <- identifier
                          ; a <- parens $ commaSep (marg typeDcls)
                          ; symbol ";"
                          ; return $ Message t i a bckArgs
                          }

rpc typeDcls bckArgs= do { _ <- rpctype
                         ; i <- identifier
                         ; a <- parens $ commaSep (rpcArg typeDcls)
                         ; symbol ";"
                         ; return $ RPC i a bckArgs
                         }

rpctype = do { reserved "rpc"
             ; return () }

rpcArg typeDcls = do { reserved "in"
                       ; Arg b n <- marg typeDcls
                       ; return $ RPCArgIn b n
                       }
                       <|> do { reserved "out"
                       ; Arg b n <- marg typeDcls
                       ; return $ RPCArgOut b n
                       }

backendParams = do { char '@'
                   ; i <- identifier
                   ; p <- parens $ commaSep backendParam
                   ; return (i, p)
                   }

backendParam = do { name <- identifier
                  ; symbol "="
                  ;     do { num <- natural ; return $ (name, BackendInt num) }
                    <|> do { arg <- identifier ; return $ (name, BackendMsgArg arg) }
                  }

msgtype = do { reserved "message"; return MMessage }

marg typeDcls = try (marg_array typeDcls)
               <|> (marg_string typeDcls)
               <|> (marg_simple typeDcls)


marg_string typeDcls = do {  symbol "String"
                           ; n <- identifier
                           ; symbol "["
                           ; s <- natural
                           ; symbol "]"
                           ; bType <- identifyBuiltin typeDcls "String"
                           ; return (Arg bType (StringArray n s))
                           }


marg_simple typeDcls = do { t <- identifier
                          ; n <- identifier
                          ; b <- identifyBuiltin typeDcls t
                          ; return (Arg b (Name n))
                          }

marg_array typeDcls  = do { t <- identifier
                          ; n <- identifier
                          ; symbol "["
                          ; l <- identifier
                          ; comma
                          ; s <- natural
                          ; symbol "]"
                          ; bType <- identifyBuiltin typeDcls t
                          ; return (Arg bType (DynamicArray n l s))
                          }

transparentAlias = do { whiteSpace
                      ; reserved "alias"
                      ; newType <- identifier
                      ; originType <- identifier
                      ; symbol ";"
                      ; return (newType, Typedef $ TAliasT newType
                                                           (read originType::TypeBuiltin))
                      }

typedefinition typeDcls = do { whiteSpace
                             ; reserved "typedef"
                             ; (name, typeDef) <- typedef_body typeDcls
                             ; symbol ";"
                             ; return (name, Typedef typeDef)
                             }

typedef_body typeDcls = try (struct_typedef typeDcls)
                        <|> try (array_typedef typeDcls)
                        <|> try enum_typedef
                        <|> (alias_typedef typeDcls)

struct_typedef typeDcls = do { reserved "struct"
                             ; f <- braces $ many1 (struct_field typeDcls)
                             ; i <- identifier
                             ; return (i, (TStruct i f))
                             }

struct_field typeDcls = do { t <- identifier
                           ; i <- identifier
                           ; symbol ";"
                           ; b <- identifyBuiltin typeDcls t
                           ; return (TStructField b i)
                           }

array_typedef typeDcls = do { t <- identifier
                            ; i <- identifier
                            ; symbol "["
                            ; sz <- integer
                            ; symbol "]"
                            ; b <- identifyBuiltin typeDcls t
                            ; return (i, (TArray b i sz))
                            }

enum_typedef = do { reserved "enum"
                  ; v <- braces $ commaSep1 identifier
                  ; i <- identifier
                  ; return (i, (TEnum i v))
                  }

alias_typedef typeDcls = do { t <- identifier
                            ; i <- identifier
                            ; b <- identifyBuiltin typeDcls t
                            ; return (i, (TAlias i b))
                            }

integer = P.integer lexer
