lol-calculus-1.20160822: Language/LOL/Calculus/Read.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Language.LOL.Calculus.Read where
import Control.Applicative (Alternative(..), Applicative(..), (<$>), (<$))
import qualified Control.Applicative as Applicative
import Control.Monad
import Data.Bool
import Data.Char (Char)
import qualified Data.Char as Char
import Data.Either (Either(..))
import Data.Eq (Eq(..))
import Data.Foldable (Foldable(..))
import Data.Function (($), (.), flip)
import qualified Data.List as List
import Data.Maybe (Maybe(..), catMaybes)
import Data.Text (Text)
import Data.String (String)
import qualified Data.Text as Text
import Text.Parsec ((<?>))
import qualified Text.Parsec as R
import Text.Show (Show(..))
-- import Debug.Trace
import Language.LOL.Calculus
-- * Type 'Lexer'
type Lexer s m a
= R.Stream s m Char
=> R.ParsecT s Lexer_State m a
type Lexer_State = ()
-- ** Type 'Token'
-- | A lexed token.
data Token
= Token_Arrow
| Token_Type_0
| Token_Equal
| Token_Colon
| Token_Lambda
| Token_Name Text
| Token_Paren_Close
| Token_Paren_Open
| Token_Pi
deriving (Eq, Show)
-- | A lexed token with location information attached
data Lexed_Token
= Lexed_Token Token R.SourcePos
deriving (Show)
-- | Remove location information from a 'Lexed_Token'
lexed_token :: Lexed_Token -> Token
lexed_token (Lexed_Token tok _) = tok
lex
:: R.Stream s m Char
=> Lexer s m a
-> s -> m (Either R.ParseError a)
lex l = R.runParserT l () ""
lex_all :: Lexer s m [Lexed_Token]
lex_all =
catMaybes
<$> R.many lex_token_or_whitespace
<* R.eof
-- | Lex either one token or some whitespace
lex_token_or_whitespace :: Lexer s m (Maybe Lexed_Token)
lex_token_or_whitespace =
(<|>)
(Nothing <$ R.try lex_whitespace)
(Just <$> lex_token)
lex_whitespace :: Lexer s m ()
lex_whitespace =
R.choice
[ () <$ Applicative.some R.space
, lex_comment_block
, lex_comment_line
]
-- | Lex a @{- ... -}@ comment (perhaps nested).
lex_comment_block :: Lexer s m ()
lex_comment_block = R.try (R.string "{-") *> lex_comment_body
-- | Lex a block comment, without the opening.
lex_comment_body :: Lexer s m ()
lex_comment_body =
R.choice
[ lex_comment_block *> lex_comment_body
, R.try (R.string "-}") *> return ()
, R.anyChar *> lex_comment_body
]
lex_comment_line :: Lexer s m ()
lex_comment_line =
R.try (R.string "--")
*> R.manyTill R.anyChar (R.eof <|> (() <$ R.newline))
*> return ()
lex_token :: Lexer s m Lexed_Token
lex_token =
Lexed_Token
<$> R.choice
[ lex_symbol
, lex_word
-- , Token_Int . fromInteger <$> R.natural R.haskell
]
<*> R.getPosition
-- | Lex one non-alphanumeric token
lex_symbol :: Lexer s m Token
lex_symbol =
R.choice
[ Token_Arrow <$ (R.try (R.string "->") <|> R.string "→")
, Token_Colon <$ R.char ':'
, Token_Equal <$ R.char '='
, Token_Lambda <$ (R.char '\\' <|> R.char 'λ')
, Token_Type_0 <$ R.char '*'
, Token_Paren_Close <$ R.char ')'
, Token_Paren_Open <$ R.char '('
, Token_Pi <$ (R.string "\\/" <|> R.string "Π" <|> R.string "∀")
]
lex_word :: Lexer s m Token
lex_word = tokenify <$> lex_name
where
tokenify name = Token_Name (Text.pack name)
lex_name :: Lexer s m String
lex_name = (:)
<$> (R.letter <|> R.char '_')
<*> R.many (R.alphaNum <|> R.char '_')
-- * Type 'Parser'
type Parser m a
= R.Stream [Lexed_Token] m Lexed_Token
=> R.ParsecT [Lexed_Token] Parser_State m a
type Parser_State = ()
read
:: (R.Stream s m Char, Monad m)
=> Parser m a -> s -> m (Either R.ParseError a)
read p s = do
toks <- lex lex_all s
case toks of
Left err -> return $ Left err
Right ts -> parse p ts
parse
:: R.Stream [Lexed_Token] m Lexed_Token
=> Parser m a
-> [Lexed_Token]
-> m (Either R.ParseError a)
parse p = R.runParserT (p <* R.eof) () ""
-- | Like 'R.updatePosChar' but working on 'Lexed_Token'.
updatePosToken
:: R.SourcePos -- ^ position of the current token
-> Lexed_Token -- ^ current token
-> [Lexed_Token] -- ^ remaining tokens
-> R.SourcePos -- ^ position of the next token
updatePosToken pos _ [] = pos
updatePosToken _ _ (Lexed_Token _ pos : _) = pos
-- | Parse a nullary token.
parse_token
:: Token -> Parser m ()
parse_token t =
R.tokenPrim
show updatePosToken
(guard . (t ==) . lexed_token)
-- | Parse an unary token.
parse_token_1
:: (Token -> Maybe a)
-> Parser m a
parse_token_1 untoken =
R.tokenPrim
show updatePosToken
(untoken . lexed_token)
-- | 'parse_term' @::=@ 'parse_term_abst' @|@ 'parse_type_abst'
parse_term :: Parser m (Term Var_Name)
parse_term =
R.try parse_term_abst
<|> parse_type_abst
<?> "term"
parse_sort :: Parser m (Type Var_Name)
parse_sort =
Type_Sort . (Type_Level_0,)
<$ parse_token Token_Type_0
<*> R.option Type_Morphism_Mono
(parse_token_1 $ \tok -> case tok of
Token_Name "m" -> Just Type_Morphism_Mono
Token_Name "p" -> Just Type_Morphism_Poly
_ -> Nothing)
<?> "sort"
parse_var_name :: Parser m Var_Name
parse_var_name =
parse_token_1 (\tok ->
case tok of
Token_Name "_" -> Nothing
Token_Name n -> Just n
_ -> Nothing)
<?> "variable-name"
parse_var_def :: Parser m Var_Name
parse_var_def =
parse_token_1 (\tok ->
case tok of
Token_Name "_" -> Just ""
Token_Name n -> Just n
_ -> Nothing)
<?> "variable-definition"
parse_var :: Parser m (Term Var_Name)
parse_var =
TeTy_Var
<$> parse_var_name
<?> "variable"
-- | 'parse_app' @::=@ 'parse_atom'@+@
parse_app :: Parser m (Term Var_Name)
parse_app =
List.foldl1 TeTy_App
<$> R.many1 (R.try parse_atom)
<?> "application"
-- | 'parse_atom' @::=@ 'parse_sort' @|@ 'parse_var' @|@ @"("@ 'parse_term @")"@
parse_atom :: Parser m (Term Var_Name)
parse_atom =
R.try parse_sort
<|> R.try parse_var
<|> R.between
(parse_token Token_Paren_Open)
(parse_token Token_Paren_Close)
parse_term
<?> "atom"
-- | 'parse_term_abst' @::=@ @"\"@ 'parse_term_abst_decl'@+@ @"->"@ 'parse_term'
parse_term_abst :: Parser m (Term Var_Name)
parse_term_abst =
flip (foldr (\(x, f_in) t ->
Term_Abst (Suggest x) f_in ((x =?) `abstract` t)))
<$ parse_token Token_Lambda
<*> R.many1 parse_term_abst_decl
<* parse_token Token_Arrow
<*> parse_term
<?> "term_abst"
-- | 'parse_term_abst_decl' @::=@ @"("@ 'parse_var_def' @":"@ 'parse_type' @")"@
parse_term_abst_decl :: Parser m (Var_Name, Type Var_Name)
parse_term_abst_decl =
(,)
<$ parse_token Token_Paren_Open
<*> parse_var_def
<* parse_token Token_Colon
<*> parse_type
<* parse_token Token_Paren_Close
<?> "term_abst_decl"
-- | 'parse_type_abst_decl' @::=@ @"("@ @(@ 'parse_var_def' @":"@ @)?@ 'parse_type' @")"@
parse_type_abst_decl :: Parser m (Var_Name, Type Var_Name)
parse_type_abst_decl =
(,)
<$ parse_token Token_Paren_Open
<*> R.option "" (R.try parse_var_def <* parse_token Token_Colon)
<*> parse_type
<* parse_token Token_Paren_Close
<?> "type_abst_decl"
-- | 'parse_type_abst' @::=@ @(@ 'parse_app' @|@ 'parse_type_abst_decl' @)@ @"->"@ 'parse_type_abst' @|@ 'parse_app'
parse_type_abst :: Parser m (Term Var_Name)
parse_type_abst =
R.try
((\(x, f_in) f_out ->
Type_Abst (Suggest x) f_in ((x =?) `abstract` f_out))
<$> (
R.try (("",) <$> parse_app)
<|>
(R.option () (parse_token Token_Pi)
*> parse_type_abst_decl)
)
<* parse_token Token_Arrow
<*> parse_type_abst)
<|> parse_app
<?> "type_abst"
-- | 'parse_type' @::=@ 'parse_term'
parse_type :: Parser m (Term Var_Name)
parse_type =
parse_term <?> "type"
parse_let_or_term :: Parser m (Either (Var_Name, Maybe (Type Var_Name), Term Var_Name) (Term Var_Name))
parse_let_or_term =
R.option
Right
(R.try ((\name args maybe_ty te ->
Left
( name
, (\ty -> foldr (\(x, x_ty) t ->
Type_Abst (Suggest x) x_ty ((x =?) `abstract` t)) ty args
) <$> maybe_ty
, foldr (\(x, x_ty) t ->
Term_Abst (Suggest x) x_ty ((x =?) `abstract` t)) te args
)
)<$> parse_var_name
<*> R.many parse_term_abst_decl
<*> (<|>)
(R.try (Just
<$ parse_token Token_Colon
<*> parse_type))
(pure Nothing)
<* parse_token Token_Equal
))
<*> parse_term
-- | 'parse_let' @::=@ 'parse_var_name' @(@'parse_term_abst_decl'@)*@ @"="@ 'parse_term'
parse_let :: Parser m (Var_Name, Maybe (Type Var_Name), Term Var_Name)
parse_let =
(\name args maybe_ty te ->
( name
, (\ty -> foldr (\(x, x_ty) t ->
Type_Abst (Suggest x) x_ty ((x =?) `abstract` t)) ty args
) <$> maybe_ty
, foldr (\(x, x_ty) t ->
Term_Abst (Suggest x) x_ty ((x =?) `abstract` t)) te args
)
)<$> parse_var_name
<*> R.many parse_term_abst_decl
<*> (<|>)
(R.try (Just
<$ parse_token Token_Colon
<*> parse_type))
(pure Nothing)
<* parse_token Token_Equal
<*> parse_term
<?> "let"
-- | 'parse_assume' @::=@ 'parse_var_name' @":"@ 'parse_type'
parse_assume :: Parser m (Var_Name, Type Var_Name)
parse_assume =
(,)
<$> parse_var_name
<* parse_token Token_Colon
<*> parse_type
<?> "axiom"
parse_command
:: R.Stream s m Char
=> R.ParsecT s () m (String, String)
parse_command =
(,)
<$> R.option "" (R.try (
R.char ':'
*> R.many (R.satisfy Char.isLetter <|> R.char '_')
<* R.option () (void $ R.many (void $ R.char ' '))
))
<*> R.many (R.try (
(<|>)
(R.try R.newline <* R.lookAhead (R.char ' ' <|> R.char '\t'))
(R.satisfy is_horizontal)
))
where
is_horizontal c = case c of {'\n' -> False; '\r' -> False; _ -> True}
parse_commands
:: R.Stream s m Char
=> R.ParsecT s () m [(String, String)]
parse_commands =
R.many $ do
_ <- R.many $ do
_ <- R.many (R.satisfy is_horizontal_space)
R.newline
(c, s) <- parse_command
_ <- R.many1 $ do
_ <- R.try (R.many (R.satisfy is_horizontal_space))
R.newline
return (c,s)
where
is_horizontal_space c = case c of {' ' -> True; '\t' -> True; _ -> False}