packages feed

dedukti-1.0.0: Dedukti/Parser.hs

-- |
-- Copyright : © 2009 CNRS - École Polytechnique - INRIA
-- License   : GPL

{-# OPTIONS_GHC -fno-warn-unused-binds #-}
module Dedukti.Parser (Pa, Dedukti.Parser.parse, parseIface) where

import Dedukti.Core
import Dedukti.Module
import Text.Parsec hiding (ParseError, parse)
import qualified Text.Parsec.Token as Token
import Control.Applicative hiding ((<|>), many)
import Control.Monad.Identity
import qualified Control.Exception as Exception
import qualified Data.Text.Lazy as T
import Data.Typeable (Typeable)


-- The AST type as returned by the Parser.
type Pa t = t Qid Unannot

-- The parsing monad.
type P = Parsec String [Pa TyRule]

newtype ParseError = ParseError String
    deriving Typeable

instance Show ParseError where
    show (ParseError e) = e

instance Exception.Exception ParseError

newtype IfaceError = IfaceError String
    deriving Typeable

instance Show IfaceError where
    show (IfaceError f) = "Broken interface file " ++ f ++ "."

instance Exception.Exception IfaceError

parse :: SourceName -> T.Text -> Pa Module
parse name input =
    -- At the toplevel, a source file is a list of declarations and rule
    -- definitions. Here rules are accumulated by side-effect, added to the
    -- parser state as we encounter them.
    case runParser ((,) <$> toplevel <*> allRules) [] name (T.unpack input) of
      Left e -> Exception.throw (ParseError (show e))
      Right x -> x

-- | Parser for interface files.
parseIface :: SourceName -> T.Text -> [Qid]
parseIface _ = map qid . T.lines

addRule :: Pa TyRule -> P ()
addRule rule = modifyState (rule:)

-- | Retrieve all rules encountered so far from the parser state.
allRules :: P [Pa TyRule]
allRules = liftM reverse getState

lexDef = Token.LanguageDef
         { Token.commentStart = "(;"
         , Token.commentEnd = ";)"
         , Token.commentLine = ";"
         , Token.nestedComments = False
         , Token.identStart = alphaNum <|> char '_' <|> char '\''
         , Token.identLetter = alphaNum <|> char '_' <|> char '\''
         , Token.opStart = parserFail "No user defined operators yet."
         , Token.opLetter = parserFail "No user defined operators yet."
         , Token.reservedNames = ["Type", "Kind"]
         , Token.reservedOpNames = [":", "=>", "->", "-->"]
         , Token.caseSensitive = True
         }

Token.LanguageDef{..} = lexDef
Token.TokenParser{..} = Token.makeTokenParser lexDef

-- | Qualified or unqualified name.
--
-- > qid ::= id.id | id
qident = ident <?> "qid" where
    ident = do
      c <- identStart
      cs <- many identLetter
      x <- (do let qualifier = T.pack (c:cs)
               c <- try $ do char '.'; identStart
               cs <- many identLetter
               let name = T.pack (c:cs)
               return $ Qid (Root :. qualifier) name Root)
           <|> return (qid (T.pack (c:cs)))
      whiteSpace
      return (Var x nann)

-- | Unqualified name.
ident = qid . T.pack <$> identifier

-- | Root production rule of the grammar.
--
-- > toplevel ::= declaration toplevel
-- >            | rule toplevel
-- >            | eof
toplevel =
    whiteSpace *>
    (    (rule *> toplevel) -- Rules are accumulated by side-effect.
     <|> ((:) <$> declaration <*> toplevel)
     <|> (eof *> return []))

-- | Binding construct.
--
-- > binding ::= id : term
binding = ((:::) <$> ident <* reservedOp ":" <*> term)
          <?> "binding"

-- | Top-level declarations.
--
-- > declaration ::= id ":" term "."
declaration = (binding <* dot)
              <?> "declaration"

-- | Left hand side of an abstraction or a product.
--
-- > domain ::= id ":" applicative
-- >          | applicative
domain = (    ((:::) <$> try (ident <* reservedOp ":") <*> applicative)
          <|> (Hole <$> applicative))
         <?> "domain"

-- |
-- > sort ::= Type
sort = Type <$ reserved "Type"

-- | Terms and types.
--
-- We first try to parse as the domain of a lambda or pi. If we
-- later find out there was no arrow after the domain, then we take
-- the domain to be an expression, and return that.
--
-- > term ::= domain "->" term
-- >        | domain "=>" term
-- >        | applicative
term = do
  d <- domain
  choice [ pi d <?> "pi"
         , lambda d <?> "lambda"
         , return (bind_type d)]
    where pi d = Pi <$> pure d <* reservedOp "->" <*> term <%%> nann
          lambda d = Lam <$> pure d <* reservedOp "=>" <*> term <%%> nann

-- | Constituents of an applicative form.
--
-- > simple ::= sort
-- >          | qid
-- >          | "(" term ")"
simple = sort <|> qident <|> parens term

-- | Expressions that are either a name or an application of a
-- expression to one or more arguments.
--
-- > applicative ::= simple
-- >               | applicative simple
-- >
applicative = (\xs -> case xs of
                        [t] -> t
                        (f:ts) -> apply f ts (repeat nann))
              <$> many1 simple
              <?> "applicative"

-- | A rule.
--
-- > rule ::= env term "-->" term
-- > env ::= "[]"
-- >       | "[" env2 "]"
-- > env2 ::= binding
-- >        | binding "," env2
rule = ((\env lhs rhs -> foldr (&) emptyEnv env :@ lhs :--> rhs)
        <$> brackets (sepBy binding comma)
        <*> term
        <*  reservedOp "-->"
        <*> term
        <*  dot) >>= addRule
       <?> "rule"