cryptol-3.3.0: src/Cryptol/Parser/ParserUtils.hs
-- |
-- Module : Cryptol.Parser.ParserUtils
-- Copyright : (c) 2013-2016 Galois, Inc.
-- License : BSD3
-- Maintainer : cryptol@galois.com
-- Stability : provisional
-- Portability : portable
{-# LANGUAGE Safe #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
-- See Note [-Wincomplete-uni-patterns and irrefutable patterns] in Cryptol.TypeCheck.TypePat
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
module Cryptol.Parser.ParserUtils where
import Data.Char(isAlphaNum, isSpace)
import Data.Maybe(fromMaybe, mapMaybe)
import Data.Bits(testBit,setBit)
import Data.List(foldl')
import Data.List.NonEmpty ( NonEmpty(..) )
import qualified Data.List.NonEmpty as NE
import Control.Monad(liftM,ap,unless,guard,msum)
import qualified Control.Monad.Fail as Fail
import Data.Text(Text)
import qualified Data.Text as T
import qualified Data.Map as Map
import Text.Read(readMaybe)
import Data.Foldable (for_)
import GHC.Generics (Generic)
import Control.DeepSeq
import Prelude ()
import Prelude.Compat
import Cryptol.Parser.AST
import Cryptol.Parser.Lexer
import Cryptol.Parser.Token(SelectorType(..))
import Cryptol.Parser.Position
import Cryptol.Parser.Utils (translateExprToNumT,widthIdent)
import Cryptol.Utils.Ident( packModName,packIdent,modNameChunks
, identAnonArg, identAnonIfaceMod, identAnonInstImport
, modNameArg, modNameIfaceMod
, mainModName, modNameIsNormal
, modNameToNormalModName
, unpackIdent, isUpperIdent
)
import Cryptol.Utils.PP
import Cryptol.Utils.Panic
import Cryptol.Utils.RecordMap
parseString :: Config -> ParseM a -> String -> Either ParseError a
parseString cfg p cs = parse cfg p (T.pack cs)
parse :: Config -> ParseM a -> Text -> Either ParseError a
parse cfg p cs = case unP p cfg eofPos S { sPrevTok = Nothing
, sTokens = toks
, sNextTyParamNum = 0
} of
Left err -> Left err
Right (a,_) -> Right a
where (toks,eofPos) = lexer cfg cs
{- The parser is parameterized by the pozition of the final token. -}
newtype ParseM a =
P { unP :: Config -> Position -> S -> Either ParseError (a,S) }
askConfig :: ParseM Config
askConfig = P \cfg _ s -> Right (cfg, s)
lexerP :: (Located Token -> ParseM a) -> ParseM a
lexerP k = P $ \cfg p s ->
case sTokens s of
t : _ | Err e <- tokenType it ->
Left $ HappyErrorMsg (srcRange t) $
[case e of
UnterminatedComment -> "unterminated comment"
UnterminatedString -> "unterminated string"
UnterminatedChar -> "unterminated character"
InvalidString -> "invalid string literal: " ++
T.unpack (tokenText it)
InvalidChar -> "invalid character literal: " ++
T.unpack (tokenText it)
LexicalError -> "unrecognized character: " ++
T.unpack (tokenText it)
MalformedLiteral -> "malformed literal: " ++
T.unpack (tokenText it)
MalformedSelector -> "malformed selector: " ++
T.unpack (tokenText it)
InvalidIndentation c -> "invalid indentation, unmatched " ++
case c of
Sym CurlyR -> "{ ... } "
Sym ParenR -> "( ... )"
Sym BracketR -> "[ ... ]"
_ -> show c -- basically panic
]
where it = thing t
t : more -> unP (k t) cfg p s { sPrevTok = Just t, sTokens = more }
[] -> Left (HappyOutOfTokens (cfgSource cfg) p)
data ParseError = HappyError FilePath {- Name of source file -}
(Located Token) {- Offending token -}
| HappyErrorMsg Range [String]
| HappyUnexpected FilePath (Maybe (Located Token)) String
| HappyOutOfTokens FilePath Position
deriving (Show, Generic, NFData)
data S = S { sPrevTok :: Maybe (Located Token)
, sTokens :: [Located Token]
, sNextTyParamNum :: !Int
-- ^ Keep track of the type parameters as they appear in the input
}
ppError :: ParseError -> Doc
ppError (HappyError path ltok)
| Err _ <- tokenType tok =
text "Parse error at" <+>
text path <.> char ':' <.> pp pos <.> comma <+>
pp tok
| White DocStr <- tokenType tok =
"Unexpected documentation (/**) comment at" <+>
text path <.> char ':' <.> pp pos <.> colon $$
indent 2
"Documentation comments need to be followed by something to document."
| otherwise =
text "Parse error at" <+>
text path <.> char ':' <.> pp pos <.> comma $$
indent 2 (text "unexpected:" <+> pp tok)
where
pos = from (srcRange ltok)
tok = thing ltok
ppError (HappyOutOfTokens path pos) =
text "Unexpected end of file at:" <+>
text path <.> char ':' <.> pp pos
ppError (HappyErrorMsg p xs) = text "Parse error at" <+> pp p $$ indent 2 (vcat (map text xs))
ppError (HappyUnexpected path ltok e) =
nest 2 $ vcat $
[ text "Parse error at" <+> text path <.> char ':' <.> pp pos <.> comma ]
++ unexp
++ ["expected:" <+> text e]
where
(unexp,pos) =
case ltok of
Nothing -> ( [] ,start)
Just t -> ( ["unexpected:" <+> text (T.unpack (tokenText (thing t)))]
, from (srcRange t)
)
instance Functor ParseM where
fmap = liftM
instance Applicative ParseM where
pure a = P (\_ _ s -> Right (a,s))
(<*>) = ap
instance Monad ParseM where
return = pure
m >>= k = P (\cfg p s1 -> case unP m cfg p s1 of
Left e -> Left e
Right (a,s2) -> unP (k a) cfg p s2)
instance Fail.MonadFail ParseM where
fail s = panic "[Parser] fail" [s]
happyError :: ParseM a
happyError = P $ \cfg _ s ->
case sPrevTok s of
Just t -> Left (HappyError (cfgSource cfg) t)
Nothing ->
Left (HappyErrorMsg emptyRange ["Parse error at the beginning of the file"])
errorMessage :: Range -> [String] -> ParseM a
errorMessage r xs = P $ \_ _ _ -> Left (HappyErrorMsg r xs)
customError :: String -> Located Token -> ParseM a
customError x t = P $ \_ _ _ -> Left (HappyErrorMsg (srcRange t) [x])
expected :: String -> ParseM a
expected x = P $ \cfg _ s ->
Left (HappyUnexpected (cfgSource cfg) (sPrevTok s) x)
mkModName :: [Text] -> ModName
mkModName = packModName
-- | This is how we derive the name of a module parameter from the
-- @import source@ declaration.
mkModParamName :: Located (ImpName PName) -> Maybe (Located ModName) -> Ident
mkModParamName lsig qual =
case qual of
Nothing ->
case thing lsig of
ImpTop t
| modNameIsNormal t -> packIdent (last (modNameChunks t))
| otherwise -> identAnonIfaceMod
$ packIdent
$ last
$ modNameChunks
$ modNameToNormalModName t
ImpNested nm ->
case nm of
UnQual i -> i
Qual _ i -> i
NewName {} -> panic "mkModParamName" ["Unexpected NewName",show lsig]
Just m -> packIdent (last (modNameChunks (thing m)))
-- Note that type variables are not resolved at this point: they are tcons.
mkSchema :: [TParam PName] -> [Prop PName] -> Type PName -> Schema PName
mkSchema xs ps t = Forall xs ps t Nothing
getName :: Located Token -> PName
getName l = case thing l of
Token (Ident [] x) _ -> mkUnqual (mkIdent x)
_ -> panic "[Parser] getName" ["not an Ident:", show l]
getNum :: Located Token -> Integer
getNum l = case thing l of
Token (Num x _ _) _ -> x
Token (ChrLit x) _ -> toInteger (fromEnum x)
_ -> panic "[Parser] getNum" ["not a number:", show l]
getChr :: Located Token -> Char
getChr l = case thing l of
Token (ChrLit x) _ -> x
_ -> panic "[Parser] getChr" ["not a char:", show l]
getStr :: Located Token -> String
getStr l = case thing l of
Token (StrLit x) _ -> x
_ -> panic "[Parser] getStr" ["not a string:", show l]
numLit :: Token -> Expr PName
numLit Token { tokenText = txt, tokenType = Num x base digs }
| base == 2 = ELit $ ECNum x (BinLit txt digs)
| base == 8 = ELit $ ECNum x (OctLit txt digs)
| base == 10 = ELit $ ECNum x (DecLit txt)
| base == 16 = ELit $ ECNum x (HexLit txt digs)
numLit x = panic "[Parser] numLit" ["invalid numeric literal", show x]
fracLit :: Token -> Expr PName
fracLit tok =
case tokenType tok of
Frac x base
| base == 2 -> ELit $ ECFrac x $ BinFrac $ tokenText tok
| base == 8 -> ELit $ ECFrac x $ OctFrac $ tokenText tok
| base == 10 -> ELit $ ECFrac x $ DecFrac $ tokenText tok
| base == 16 -> ELit $ ECFrac x $ HexFrac $ tokenText tok
_ -> panic "[Parser] fracLit" [ "Invalid fraction", show tok ]
intVal :: Located Token -> ParseM Integer
intVal tok =
case tokenType (thing tok) of
Num x _ _ -> return x
_ -> errorMessage (srcRange tok) ["Expected an integer"]
mkFixity :: Assoc -> Located Token -> [LPName] -> ParseM (Decl PName)
mkFixity assoc tok qns =
do l <- intVal tok
unless (l >= 1 && l <= 100)
(errorMessage (srcRange tok) ["Fixity levels must be between 1 and 100"])
return (DFixity (Fixity assoc (fromInteger l)) qns)
fromStrLit :: Located Token -> ParseM (Located String)
fromStrLit loc = case tokenType (thing loc) of
StrLit str -> return loc { thing = str }
_ -> errorMessage (srcRange loc) ["Expected a string literal"]
validDemotedType :: Range -> Type PName -> ParseM (Type PName)
validDemotedType rng ty =
case ty of
TLocated t r -> validDemotedType r t
TRecord {} -> bad "Record types"
TTyApp {} -> bad "Explicit type application"
TTuple {} -> bad "Tuple types"
TFun {} -> bad "Function types"
TSeq {} -> bad "Sequence types"
TBit -> bad "Type bit"
TNum {} -> ok
TChar {} -> ok
TWild -> bad "Wildcard types"
TUser {} -> ok
TParens t mb -> case mb of
Nothing -> validDemotedType rng t
Just _ -> bad "kind annotation"
TInfix{} -> ok
where bad x = errorMessage rng [x ++ " cannot be demoted."]
ok = return $ at rng ty
-- | Input fields are reversed!
mkRecord :: AddLoc b => Range -> (RecordMap Ident (Range, a) -> b) -> [Named a] -> ParseM b
mkRecord rng f xs =
case res of
Left (nm,(nmRng,_)) -> errorMessage nmRng ["Record has repeated field: " ++ show (pp nm)]
Right r -> pure $ at rng (f r)
where
res = recordFromFieldsErr ys
ys = map (\ (Named (Located r nm) x) -> (nm,(r,x))) (reverse xs)
-- | Input expression are reversed
mkEApp :: NonEmpty (Expr PName) -> ParseM (Expr PName)
mkEApp es@(eLast :| _) =
do f :| xs <- cvtTypeParams eFirst rest
pure (at (eFirst,eLast) $ foldl EApp f xs)
where
eFirst :| rest = NE.reverse es
{- Type applications are parsed as `ETypeVal (TTyApp fs)` expressions.
Here we associate them with their corresponding functions,
converting them into `EAppT` constructs. For example:
[ f, x, `{ a = 2 }, y ]
becomes
[ f, x ` { a = 2 }, y ]
The parser associates field and tuple projectors that follow an
explicit type application onto the TTyApp term, so we also
have to unwind those projections and reapply them. For example:
[ f, x, `{ a = 2 }.f.2, y ]
becomes
[ f, (x`{ a = 2 }).f.2, y ]
-}
cvtTypeParams e [] = pure (e :| [])
cvtTypeParams e (p : ps) =
case toTypeParam p Nothing of
Nothing -> NE.cons e <$> cvtTypeParams p ps
Just (fs,ss,rng) ->
if checkAppExpr e then
let e' = foldr (flip ESel) (EAppT e fs) ss
e'' = case rCombMaybe (getLoc e) rng of
Just r -> ELocated e' r
Nothing -> e'
in cvtTypeParams e'' ps
else
errorMessage (fromMaybe emptyRange (getLoc e))
[ "Explicit type applications can only be applied to named values."
, "Unexpected: " ++ show (pp e)
]
{- Check if the given expression is a legal target for explicit type application.
This is basically only variables, but we also allow the parenthesis and
the phantom "located" AST node.
-}
checkAppExpr e =
case e of
ELocated e' _ -> checkAppExpr e'
EParens e' -> checkAppExpr e'
EVar{} -> True
_ -> False
{- Look under a potential chain of selectors to see if we have a TTyApp.
If so, return the ty app information and the collected selectors
to reapply.
-}
toTypeParam e mr =
case e of
ELocated e' rng -> toTypeParam e' (rCombMaybe mr (Just rng))
ETypeVal t -> toTypeParam' t mr
ESel e' s -> ( \(fs,ss,r) -> (fs,s:ss,r) ) <$> toTypeParam e' mr
_ -> Nothing
toTypeParam' t mr =
case t of
TLocated t' rng -> toTypeParam' t' (rCombMaybe mr (Just rng))
TTyApp fs -> Just (map mkTypeInst fs, [], mr)
_ -> Nothing
unOp :: Expr PName -> Expr PName -> Expr PName
unOp f x = at (f,x) $ EApp f x
-- Use defaultFixity as a placeholder, it will be fixed during renaming.
binOp :: Expr PName -> Located PName -> Expr PName -> Expr PName
binOp x f y = at (x,y) $ EInfix x f defaultFixity y
-- An element type ascription is allowed to appear on one of the arguments.
eFromTo :: Range -> Expr PName -> Maybe (Expr PName) -> Expr PName -> ParseM (Expr PName)
eFromTo r e1 e2 e3 =
case (asETyped e1, asETyped =<< e2, asETyped e3) of
(Just (e1', t), Nothing, Nothing) -> eFromToType r e1' e2 e3 (Just t)
(Nothing, Just (e2', t), Nothing) -> eFromToType r e1 (Just e2') e3 (Just t)
(Nothing, Nothing, Just (e3', t)) -> eFromToType r e1 e2 e3' (Just t)
(Nothing, Nothing, Nothing) -> eFromToType r e1 e2 e3 Nothing
_ -> errorMessage r ["A sequence enumeration may have at most one element type annotation."]
eFromToBy :: Range -> Expr PName -> Expr PName -> Expr PName -> Bool -> ParseM (Expr PName)
eFromToBy r e1 e2 e3 isStrictBound =
case (asETyped e1, asETyped e2, asETyped e3) of
(Just (e1', t), Nothing, Nothing) -> eFromToByTyped r e1' e2 e3 (Just t) isStrictBound
(Nothing, Just (e2', t), Nothing) -> eFromToByTyped r e1 e2' e3 (Just t) isStrictBound
(Nothing, Nothing, Just (e3', t)) -> eFromToByTyped r e1 e2 e3' (Just t) isStrictBound
(Nothing, Nothing, Nothing) -> eFromToByTyped r e1 e2 e3 Nothing isStrictBound
_ -> errorMessage r ["A sequence enumeration may have at most one element type annotation."]
eFromToByTyped :: Range -> Expr PName -> Expr PName -> Expr PName -> Maybe (Type PName) -> Bool -> ParseM (Expr PName)
eFromToByTyped r e1 e2 e3 t isStrictBound =
EFromToBy isStrictBound
<$> exprToNumT r e1
<*> exprToNumT r e2
<*> exprToNumT r e3
<*> pure t
eFromToDownBy ::
Range -> Expr PName -> Expr PName -> Expr PName -> Bool -> ParseM (Expr PName)
eFromToDownBy r e1 e2 e3 isStrictBound =
case (asETyped e1, asETyped e2, asETyped e3) of
(Just (e1', t), Nothing, Nothing) -> eFromToDownByTyped r e1' e2 e3 (Just t) isStrictBound
(Nothing, Just (e2', t), Nothing) -> eFromToDownByTyped r e1 e2' e3 (Just t) isStrictBound
(Nothing, Nothing, Just (e3', t)) -> eFromToDownByTyped r e1 e2 e3' (Just t) isStrictBound
(Nothing, Nothing, Nothing) -> eFromToDownByTyped r e1 e2 e3 Nothing isStrictBound
_ -> errorMessage r ["A sequence enumeration may have at most one element type annotation."]
eFromToDownByTyped ::
Range -> Expr PName -> Expr PName -> Expr PName -> Maybe (Type PName) -> Bool -> ParseM (Expr PName)
eFromToDownByTyped r e1 e2 e3 t isStrictBound =
EFromToDownBy isStrictBound
<$> exprToNumT r e1
<*> exprToNumT r e2
<*> exprToNumT r e3
<*> pure t
asETyped :: Expr n -> Maybe (Expr n, Type n)
asETyped (ELocated e _) = asETyped e
asETyped (ETyped e t) = Just (e, t)
asETyped _ = Nothing
eFromToType ::
Range -> Expr PName -> Maybe (Expr PName) -> Expr PName -> Maybe (Type PName) -> ParseM (Expr PName)
eFromToType r e1 e2 e3 t =
EFromTo <$> exprToNumT r e1
<*> mapM (exprToNumT r) e2
<*> exprToNumT r e3
<*> pure t
eFromToLessThan ::
Range -> Expr PName -> Expr PName -> ParseM (Expr PName)
eFromToLessThan r e1 e2 =
case asETyped e2 of
Just _ -> errorMessage r ["The exclusive upper bound of an enumeration may not have a type annotation."]
Nothing ->
case asETyped e1 of
Nothing -> eFromToLessThanType r e1 e2 Nothing
Just (e1',t) -> eFromToLessThanType r e1' e2 (Just t)
eFromToLessThanType ::
Range -> Expr PName -> Expr PName -> Maybe (Type PName) -> ParseM (Expr PName)
eFromToLessThanType r e1 e2 t =
EFromToLessThan
<$> exprToNumT r e1
<*> exprToNumT r e2
<*> pure t
exprToNumT :: Range -> Expr PName -> ParseM (Type PName)
exprToNumT r expr =
case translateExprToNumT expr of
Just t -> return t
Nothing -> bad
where
bad = errorMessage (fromMaybe r (getLoc expr))
[ "The boundaries of .. sequences should be valid numeric types."
, "The expression `" ++ show (pp expr) ++ "` is not."
]
-- | WARNING: This is a bit of a hack.
-- It is used to represent anonymous type applications.
anonTyApp :: Maybe Range -> [Type PName] -> Type PName
anonTyApp ~(Just r) ts = TLocated (TTyApp (map toField ts)) r
where noName = Located { srcRange = r, thing = mkIdent (T.pack "") }
toField t = Named { name = noName, value = t }
exportDecl :: Maybe (Located Text) -> ExportType -> Decl PName -> TopDecl PName
exportDecl mbDoc e d = Decl TopLevel { tlExport = e
, tlDoc = mbDoc
, tlValue = d }
exportNewtype :: ExportType -> Maybe (Located Text) -> Newtype PName ->
TopDecl PName
exportNewtype e d n = TDNewtype TopLevel { tlExport = e
, tlDoc = d
, tlValue = n }
exportEnum ::
ExportType -> Maybe (Located Text) -> EnumDecl PName -> TopDecl PName
exportEnum e d n = TDEnum TopLevel { tlExport = e
, tlDoc = d
, tlValue = n }
exportModule :: Maybe (Located Text) -> NestedModule PName -> TopDecl PName
exportModule mbDoc m = DModule TopLevel { tlExport = Public
, tlDoc = mbDoc
, tlValue = m }
mkParFun :: Maybe (Located Text) ->
Located PName ->
Schema PName ->
ParamDecl PName
mkParFun mbDoc n s = DParameterFun ParameterFun { pfName = n
, pfSchema = s
, pfDoc = mbDoc
, pfFixity = Nothing
}
mkParType :: Maybe (Located Text) ->
Located PName ->
Located Kind ->
ParseM (ParamDecl PName)
mkParType mbDoc n k =
do num <- P $ \_ _ s -> let nu = sNextTyParamNum s
in Right (nu, s { sNextTyParamNum = nu + 1 })
return (DParameterType
ParameterType { ptName = n
, ptKind = thing k
, ptDoc = mbDoc
, ptFixity = Nothing
, ptNumber = num
})
changeExport :: ExportType -> [TopDecl PName] -> [TopDecl PName]
changeExport e = map change
where
change decl =
case decl of
Decl d -> Decl d { tlExport = e }
DPrimType t -> DPrimType t { tlExport = e }
TDNewtype n -> TDNewtype n { tlExport = e }
TDEnum n -> TDEnum n { tlExport = e }
DModule m -> DModule m { tlExport = e }
DModParam {} -> decl
Include{} -> decl
DImport{} -> decl
DParamDecl{} -> decl
DInterfaceConstraint {} -> decl
addDeclDocstring :: Located Text -> TopDecl name -> ParseM (TopDecl name)
addDeclDocstring doc decl =
case decl of
Decl d -> Decl <$> topLevel d
DPrimType t -> DPrimType <$> topLevel t
TDNewtype n -> TDNewtype <$> topLevel n
TDEnum n -> TDEnum <$> topLevel n
DModule m -> DModule <$> topLevel m
DModParam p -> pure (DModParam p { mpDoc = Just doc })
Include _ -> failure "Docstring on include"
DImport i -> DImport <$> traverse imp i
DInterfaceConstraint Nothing x -> pure (DInterfaceConstraint (Just doc) x)
DInterfaceConstraint Just{} _ -> failure "Overlapping docstring"
DParamDecl{} -> failure "Docstring on parameter declarations"
where
failure e = errorMessage (fromMaybe emptyRange (getLoc decl)) [e]
imp i =
case iDoc i of
Nothing -> pure i { iDoc = Just doc }
Just{} -> failure "Overlapping docstring"
topLevel x =
case tlDoc x of
Just _ -> failure "Overlapping docstring"
Nothing -> pure x { tlDoc = Just doc }
privateDocedDecl :: Located Text -> [TopDecl PName] -> ParseM [TopDecl PName]
privateDocedDecl doc (decl:decls) = fmap (: decls) (addDeclDocstring doc decl)
privateDocedDecl doc [] = errorMessage (srcRange doc) ["Docstring on empty private section"]
mkTypeInst :: Named (Type PName) -> TypeInst PName
mkTypeInst x | nullIdent (thing (name x)) = PosInst (value x)
| otherwise = NamedInst x
mkTParam :: Located Ident -> Maybe Kind -> ParseM (TParam PName)
mkTParam Located { srcRange = rng, thing = n } k
| n == widthIdent = errorMessage rng ["`width` is not a valid type parameter name."]
| otherwise = return (TParam (mkUnqual n) k (Just rng))
mkTySyn :: Type PName -> Type PName -> ParseM (Decl PName)
mkTySyn thead tdef =
do (nm,params) <- typeToDecl thead
pure (DType (TySyn nm Nothing params tdef))
mkPropSyn :: Type PName -> Type PName -> ParseM (Decl PName)
mkPropSyn thead tdef =
do (nm,params) <- typeToDecl thead
ps <- thing <$> mkProp tdef
pure (DProp (PropSyn nm Nothing params ps))
mkNewtype ::
Type PName ->
Located (RecordMap Ident (Range, Type PName)) ->
ParseM (Newtype PName)
mkNewtype thead def =
do (nm,params) <- typeToDecl thead
pure (Newtype nm params (thing nm) (thing def))
mkEnumDecl ::
Type PName ->
[ TopLevel (EnumCon PName) ] {- ^ Reversed -} ->
ParseM (EnumDecl PName)
mkEnumDecl thead def =
do (nm,params) <- typeToDecl thead
mapM_ reportRepeated
(Map.toList (Map.fromListWith (++) [ (thing k,[srcRange k])
| k <- map (ecName . tlValue) def ]))
pure EnumDecl
{ eName = nm
, eParams = params
, eCons = reverse def
}
where
reportRepeated (i,xs) =
case xs of
l : ls@(_ : _) ->
errorMessage l
( ("Multiple declarations for " ++ show (backticks (pp i)))
: [ "Other declaration: " ++ show (pp x) | x <- ls ]
)
_ -> pure ()
mkConDecl ::
Maybe (Located Text) -> ExportType ->
Type PName -> ParseM (TopLevel (EnumCon PName))
mkConDecl mbDoc expT ty =
do con <- go Nothing ty
pure TopLevel { tlExport = expT, tlDoc = mbDoc, tlValue = con }
where
go mbLoc t =
case t of
TLocated t1 r -> go (Just r) t1
TUser n ts ->
case n of
UnQual i
| isUpperIdent i ->
pure EnumCon { ecName = Located (getL mbLoc) (UnQual i)
, ecFields = ts
}
| otherwise ->
errorMessage (getL mbLoc)
[ "Malformed constructor declaration."
, "The constructor name should start with a capital letter."
]
_ -> errorMessage (getL mbLoc)
[ "Malformed constructor declaration."
, "The constructor name may not be qualified."
]
_ -> errorMessage (getL mbLoc) [ "Malformed constructor declaration." ]
getL mb =
case mb of
Just r -> r
Nothing -> panic "mkConDecl" ["Missing type location"]
typeToDecl :: Type PName -> ParseM (Located PName, [TParam PName])
typeToDecl ty0 =
case ty0 of
TLocated ty loc -> goD loc ty
_ -> panic "typeToDecl" ["Type location is missing."]
where
bad loc = errorMessage loc ["Invalid type declaration"]
badP loc = errorMessage loc ["Invalid declaration parameter"]
goN loc n =
case n of
UnQual {} -> pure ()
_ -> errorMessage loc ["Invalid declaration name"]
goP loc ty =
case ty of
TLocated ty1 loc1 -> goP loc1 ty1
TUser f [] ->
do goN loc f
pure TParam { tpName = f, tpKind = Nothing, tpRange = Just loc }
TParens t mb ->
case mb of
Nothing -> badP loc
Just k ->
do p <- goP loc t
case tpKind p of
Nothing -> pure p { tpKind = Just k }
Just {} -> badP loc
TInfix {} -> badP loc
TUser {} -> badP loc
TFun {} -> badP loc
TSeq {} -> badP loc
TBit {} -> badP loc
TNum {} -> badP loc
TChar {} -> badP loc
TRecord {} -> badP loc
TWild {} -> badP loc
TTyApp {} -> badP loc
TTuple {} -> badP loc
goD loc ty =
case ty of
TLocated ty1 loc1 -> goD loc1 ty1
TUser f ts ->
do goN loc f
ps <- mapM (goP loc) ts
pure (Located { thing = f, srcRange = loc },ps)
TInfix l f _ r ->
do goN (srcRange f) (thing f)
a <- goP loc l
b <- goP loc r
pure (f,[a,b])
TFun {} -> bad loc
TSeq {} -> bad loc
TBit {} -> bad loc
TNum {} -> bad loc
TChar {} -> bad loc
TRecord {} -> bad loc
TWild {} -> bad loc
TTyApp {} -> bad loc
TTuple {} -> bad loc
TParens {} -> bad loc
polyTerm :: Range -> Integer -> Integer -> ParseM (Bool, Integer)
polyTerm rng k p
| k == 0 = return (False, p)
| k == 1 = return (True, p)
| otherwise = errorMessage rng ["Invalid polynomial coefficient"]
mkPoly :: Range -> [ (Bool,Integer) ] -> ParseM (Expr PName)
mkPoly rng terms
| w <= toInteger (maxBound :: Int) = mk 0 (map fromInteger bits)
| otherwise = errorMessage rng ["Polynomial literal too large: " ++ show w]
where
w = case terms of
[] -> 0
_ -> 1 + maximum (map snd terms)
bits = [ n | (True,n) <- terms ]
mk :: Integer -> [Int] -> ParseM (Expr PName)
mk res [] = return $ ELit $ ECNum res (PolyLit (fromInteger w :: Int))
mk res (n : ns)
| testBit res n = errorMessage rng
["Polynomial contains multiple terms with exponent " ++ show n]
| otherwise = mk (setBit res n) ns
-- NOTE: The list of patterns is reversed!
mkProperty :: LPName -> [Pattern PName] -> Expr PName -> Decl PName
mkProperty f ps e = at (f,e) $
DBind Bind { bName = f
, bParams = PatternParams (reverse ps)
, bDef = at e (Located emptyRange (exprDef e))
, bSignature = Nothing
, bPragmas = [PragmaProperty]
, bMono = False
, bInfix = False
, bFixity = Nothing
, bDoc = Nothing
, bExport = Public
}
-- NOTE: The lists of patterns are reversed!
mkIndexedDecl ::
LPName -> ([Pattern PName], [Pattern PName]) -> Expr PName -> Decl PName
mkIndexedDecl f (ps, ixs) e =
DBind Bind { bName = f
, bParams = PatternParams (reverse ps)
, bDef = at e (Located emptyRange (exprDef rhs))
, bSignature = Nothing
, bPragmas = []
, bMono = False
, bInfix = False
, bFixity = Nothing
, bDoc = Nothing
, bExport = Public
}
where
rhs :: Expr PName
rhs = mkGenerate (reverse ixs) e
-- NOTE: The lists of patterns are reversed!
mkPropGuardsDecl ::
LPName ->
([Pattern PName], [Pattern PName]) ->
[PropGuardCase PName] ->
ParseM (Decl PName)
mkPropGuardsDecl f (ps, ixs) guards =
do unless (null ixs) $
errorMessage (srcRange f)
["Indexed sequence definitions may not use constraint guards"]
let gs = reverse guards
pure $
DBind Bind { bName = f
, bParams = PatternParams (reverse ps)
, bDef = Located (srcRange f) (DImpl (DPropGuards gs))
, bSignature = Nothing
, bPragmas = []
, bMono = False
, bInfix = False
, bFixity = Nothing
, bDoc = Nothing
, bExport = Public
}
mkConstantPropGuardsDecl ::
LPName -> [PropGuardCase PName] -> ParseM (Decl PName)
mkConstantPropGuardsDecl f guards =
mkPropGuardsDecl f ([],[]) guards
-- NOTE: The lists of patterns are reversed!
mkIndexedExpr :: ([Pattern PName], [Pattern PName]) -> Expr PName -> Expr PName
mkIndexedExpr (ps, ixs) body
| null ps = mkGenerate (reverse ixs) body
| otherwise = EFun emptyFunDesc (reverse ps) (mkGenerate (reverse ixs) body)
mkGenerate :: [Pattern PName] -> Expr PName -> Expr PName
mkGenerate pats body =
foldr (\pat e -> EGenerate (EFun emptyFunDesc [pat] e)) body pats
mkIf :: [(Expr PName, Expr PName)] -> Expr PName -> Expr PName
mkIf ifThens theElse = foldr addIfThen theElse ifThens
where
addIfThen (cond, doexpr) elseExpr = EIf cond doexpr elseExpr
mkPVar :: Located PName -> Pattern PName
mkPVar p =
case thing p of
UnQual i | isInfixIdent i || not (isUpperIdent i) -> PVar p
_ -> PCon p []
mkIPat :: Pattern PName -> ParseM (Pattern PName)
mkIPat pat =
case pat of
PVar {} -> pure pat
PWild -> pure pat
PTuple ps -> PTuple <$> traverse mkIPat ps
PRecord rp -> PRecord <$> traverseRecordMap upd rp
where upd _ (x,y) = (,) x <$> mkIPat y
PList ps -> PList <$> traverse mkIPat ps
PTyped p t -> (`PTyped` t) <$> mkIPat p
PSplit p1 p2 -> PSplit <$> mkIPat p1 <*> mkIPat p2
PLocated p r -> (`PLocated` r) <$> mkIPat p
PCon n ps ->
case ps of
[] | UnQual {} <- thing n -> pure (PVar n)
_ -> errorMessage (srcRange n)
[ "Unexpected constructor pattern."
, "Constructors patterns may be used only in `case` expressions."
]
mkPrimDecl :: Maybe (Located Text) -> LPName -> Schema PName -> [TopDecl PName]
mkPrimDecl = mkNoImplDecl DPrim
mkForeignDecl ::
Maybe (Located Text) -> LPName -> Schema PName -> ParseM [TopDecl PName]
mkForeignDecl mbDoc nm ty =
do let txt = unpackIdent (getIdent (thing nm))
unless (all isOk txt)
(errorMessage (srcRange nm)
[ "`" ++ txt ++ "` is not a valid foreign name."
, "The name should contain only alpha-numeric characters or '_'."
])
-- We do allow optional cryptol implementations of foreign functions, these
-- will be merged with this binding in the NoPat pass. In the parser they
-- are just treated as a completely separate (non-foreign) binding with the
-- same name.
pure (mkNoImplDecl (DForeign Nothing) mbDoc nm ty)
where
isOk c = c == '_' || isAlphaNum c
-- | Generate a signature and a binding for value declarations with no
-- implementation (i.e. primitive or foreign declarations). The reason for
-- generating both instead of just adding the signature at this point is that it
-- means the declarations don't need to be treated differently in the noPat
-- pass. This is also the reason we add the doc to the TopLevel constructor,
-- instead of just place it on the binding directly. A better solution might be
-- to just have a different constructor for primitives and foreigns.
mkNoImplDecl :: BindDef PName
-> Maybe (Located Text) -> LPName -> Schema PName -> [TopDecl PName]
mkNoImplDecl def mbDoc ln sig =
[ exportDecl Nothing Public
$ DBind Bind { bName = ln
, bParams = noParams
, bDef = at sig (Located emptyRange def)
, bSignature = Nothing
, bPragmas = []
, bMono = False
, bInfix = isInfixIdent (getIdent (thing ln))
, bFixity = Nothing
, bDoc = Nothing
, bExport = Public
}
, exportDecl mbDoc Public
$ DSignature [ln] sig
]
mkPrimTypeDecl ::
Maybe (Located Text) ->
Schema PName ->
Located Kind ->
ParseM [TopDecl PName]
mkPrimTypeDecl mbDoc (Forall as qs st ~(Just schema_rng)) finK =
case splitT schema_rng st of
Just (n,xs) ->
do vs <- mapM tpK as
unless (distinct (map fst vs)) $
errorMessage schema_rng ["Repeated parameters."]
let kindMap = Map.fromList vs
lkp v = case Map.lookup (thing v) kindMap of
Just (k,tp) -> pure (k,tp)
Nothing ->
errorMessage
(srcRange v)
["Undefined parameter: " ++ show (pp (thing v))]
(as',ins) <- unzip <$> mapM lkp xs
unless (length vs == length xs) $
errorMessage schema_rng ["All parameters should appear in the type."]
let ki = finK { thing = foldr KFun (thing finK) ins }
pure [ DPrimType TopLevel
{ tlExport = Public
, tlDoc = mbDoc
, tlValue = PrimType { primTName = n
, primTKind = ki
, primTCts = (as',qs)
, primTFixity = Nothing
}
}
]
Nothing -> errorMessage schema_rng ["Invalid primitive signature"]
where
splitT r ty = case ty of
TLocated t r1 -> splitT r1 t
TUser n ts -> mkT r Located { srcRange = r, thing = n } ts
TInfix t1 n _ t2 -> mkT r n [t1,t2]
_ -> Nothing
mkT r n ts = do ts1 <- mapM (isVar r) ts
guard (distinct (map thing ts1))
pure (n,ts1)
isVar r ty = case ty of
TLocated t r1 -> isVar r1 t
TUser n [] -> Just Located { srcRange = r, thing = n }
_ -> Nothing
-- inefficient, but the lists should be small
distinct xs = case xs of
[] -> True
x : ys -> not (x `elem` ys) && distinct ys
tpK tp = case tpKind tp of
Just k -> pure (tpName tp, (tp,k))
Nothing ->
case tpRange tp of
Just r -> errorMessage r ["Parameters need a kind annotation"]
Nothing -> panic "mkPrimTypeDecl"
[ "Missing range on schema parameter." ]
-- | Fix-up the documentation strings by removing the comment delimiters on each
-- end, and stripping out common prefixes on all the remaining lines.
mkDoc :: Located Text -> Located Text
mkDoc ltxt = ltxt { thing = docStr }
where
docStr = T.unlines
$ handlePrefixes
$ T.lines
$ T.dropWhileEnd commentChar
$ thing ltxt
commentChar :: Char -> Bool
commentChar x = x `elem` ("/*" :: String) || isSpace x
-- Prefix dropping with a special case for the first line and common
-- prefix dropping for the following lines. The first line and following
-- lines are treated independently
handlePrefixes :: [Text] -> [Text]
handlePrefixes [] = []
handlePrefixes (l:ls)
| T.all commentChar l = ls'
| otherwise = T.dropWhile commentChar l : ls'
where ls' = dropPrefix ls
dropPrefix :: [Text] -> [Text]
dropPrefix ts =
case startDropPrefixChar ts of
Nothing -> ts -- done dropping
Just ts' -> dropPrefix ts' -- keep dropping
-- At the beginning of a prefix stripping operation we check the
-- first character of the first line. If that first character is
-- droppable we use it as the prefix to check for, otherwise we
-- continue searching for whitespace. Return Nothing if there
-- was no prefix to drop.
startDropPrefixChar :: [Text] -> Maybe [Text]
startDropPrefixChar [] = Nothing
startDropPrefixChar (l:ls) =
case T.uncons l of
Nothing -> (l:) <$> searchWhitePrefixChar ls
Just (c, l')
| c == '*' || isSpace c -> (l':) <$> checkPrefixChar c ls
| otherwise -> Nothing
-- So far we've only seen empty lines, so we accept empty
-- lines and lines starting with whitespace.
searchWhitePrefixChar :: [Text] -> Maybe [Text]
searchWhitePrefixChar [] = Just []
searchWhitePrefixChar (l:ls) =
case T.uncons l of
Nothing -> (l:) <$> searchWhitePrefixChar ls
Just (c, l')
| isSpace c -> (l':) <$> checkPrefixChar c ls
| otherwise -> Nothing
-- So far we've seen a non-empty line and we know what character
-- we're looking for. If that character is whitespace then we also
-- will accept empty lines as matching the prefix
checkPrefixChar :: Char -> [Text] -> Maybe [Text]
checkPrefixChar _ [] = Just []
checkPrefixChar p (l:ls) =
case T.uncons l of
Nothing
| isSpace p -> (l:) <$> checkPrefixChar p ls
Just (c,l')
| c == p -> (l':) <$> checkPrefixChar p ls
_ -> Nothing
distrLoc :: Located [a] -> [Located a]
distrLoc x = [ Located { srcRange = r, thing = a } | a <- thing x ]
where r = srcRange x
mkPropGuards :: Type PName -> ParseM [Located (Prop PName)]
mkPropGuards ty =
do lp <- mkProp ty
pure [ lp { thing = p } | p <- thing lp ]
mkProp :: Type PName -> ParseM (Located [Prop PName])
mkProp ty =
case ty of
TLocated t r -> Located r `fmap` props r t
_ -> panic "Parser" [ "Invalid type given to mkProp"
, "expected a location"
, show ty ]
where
props r t =
case t of
TInfix{} -> return [CType t]
TUser{} -> return [CType t]
TTuple ts -> concat `fmap` mapM (props r) ts
TParens t' mb -> case mb of
Nothing -> props r t'
Just _ -> err
TLocated t' r' -> props r' t'
TFun{} -> err
TSeq{} -> err
TBit{} -> err
TNum{} -> err
TChar{} -> err
TWild -> err
TRecord{} -> err
TTyApp{} -> err
where
err = errorMessage r ["Invalid constraint"]
-- | Make an ordinary module
mkModule :: Located ModName -> [TopDecl PName] -> Module PName
mkModule nm ds = Module { mName = nm
, mDef = NormalModule ds
, mInScope = mempty
, mDocTop = Nothing
}
mkNested :: Module PName -> ParseM (NestedModule PName)
mkNested m =
case modNameChunks (thing nm) of
[c] -> pure (NestedModule m { mName = nm { thing = mkUnqual (packIdent c)}})
_ -> errorMessage r
["Nested modules names should be a simple identifier."]
where
nm = mName m
r = srcRange nm
mkSigDecl :: Maybe (Located Text) -> (Located PName,Signature PName) -> TopDecl PName
mkSigDecl doc (nm,sig) =
DModule
TopLevel { tlExport = Public
, tlDoc = doc
, tlValue = NestedModule
Module { mName = nm
, mDef = InterfaceModule sig
, mInScope = mempty
, mDocTop = Nothing
}
}
mkInterfaceConstraint ::
Maybe (Located Text) -> Type PName -> ParseM [TopDecl PName]
mkInterfaceConstraint mbDoc ty =
do ps <- mkProp ty
pure [DInterfaceConstraint mbDoc ps]
mkParDecls :: [ParamDecl PName] -> TopDecl PName
mkParDecls ds = DParamDecl loc (mkInterface' [] ds)
where loc = rCombs (mapMaybe getLoc ds)
onlySimpleImports :: [Located (ImportG (ImpName PName))] -> ParseM ()
onlySimpleImports = mapM_ check
where
check i =
case iInst (thing i) of
Nothing -> pure ()
Just _ ->
errorMessage (srcRange i)
[ "Functor instantiations are not supported in this context."
, "The imported entity needs to be just the name of a module."
, "A workaround would be to do the instantion in the outer context."
]
mkInterface' :: [Located (ImportG (ImpName PName))] ->
[ParamDecl PName] -> Signature PName
mkInterface' is =
foldl' add
Signature { sigImports = is
, sigTypeParams = []
, sigDecls = []
, sigConstraints = []
, sigFunParams = []
}
where
add s d =
case d of
DParameterType pt -> s { sigTypeParams = pt : sigTypeParams s }
DParameterConstraint ps -> s { sigConstraints = pcProps ps ++ sigConstraints s }
DParameterDecl pd -> s { sigDecls = pd : sigDecls s }
DParameterFun pf -> s { sigFunParams = pf : sigFunParams s }
mkInterface :: [Located (ImportG (ImpName PName))] ->
[ParamDecl PName] -> ParseM (Signature PName)
mkInterface is ps =
do onlySimpleImports is
pure (mkInterface' is ps)
mkIfacePropSyn :: Maybe Text -> Decl PName -> ParamDecl PName
mkIfacePropSyn mbDoc d =
case d of
DLocated d1 _ -> mkIfacePropSyn mbDoc d1
DType ts -> DParameterDecl (SigTySyn ts mbDoc)
DProp ps -> DParameterDecl (SigPropSyn ps mbDoc)
_ -> panic "mkIfacePropSyn" [ "Unexpected declaration", show (pp d) ]
-- | Make an unnamed module---gets the name @Main@.
mkAnonymousModule :: [TopDecl PName] -> ParseM [Module PName]
mkAnonymousModule ds =
do for_ ds \case
DParamDecl l _ -> mainParamError l
DModParam p -> mainParamError (srcRange (mpSignature p))
DInterfaceConstraint _ ps -> mainParamError (srcRange ps)
_ -> pure ()
src <- cfgSource <$> askConfig
mkTopMods Nothing $
mkModule Located
{ srcRange = emptyRange
, thing = mainModName src
}
ds
where
mainParamError l = errorMessage l
["Unnamed module cannot be parameterized"]
-- | Make a module which defines a functor instance.
mkModuleInstanceAnon :: Located ModName ->
Located (ImpName PName) ->
[TopDecl PName] ->
Module PName
mkModuleInstanceAnon nm fun ds =
Module { mName = nm
, mDef = FunctorInstance fun (DefaultInstAnonArg ds) mempty
, mInScope = mempty
, mDocTop = Nothing
}
mkModuleInstance ::
Located ModName ->
Located (ImpName PName) ->
ModuleInstanceArgs PName ->
Module PName
mkModuleInstance m f as =
Module { mName = m
, mDef = FunctorInstance f as emptyModuleInstance
, mInScope = mempty
, mDocTop = Nothing
}
ufToNamed :: UpdField PName -> ParseM (Named (Expr PName))
ufToNamed (UpdField h ls e) =
case (h,ls) of
(UpdSet, [l]) | RecordSel i Nothing <- thing l ->
pure Named { name = l { thing = i }, value = e }
_ -> errorMessage (srcRange lab)
["Invalid record field. Perhaps you meant to update a record?"]
where
-- The list of field updates in an UpdField should always be non-empty.
lab = case ls of
lab':_ -> lab'
[] -> panic "ufToNamed" ["UpdField with empty labels"]
-- | The returned list of 'Selector's will be non-empty.
exprToFieldPath :: Expr PName -> ParseM [Located Selector]
exprToFieldPath e0 = reverse <$> go noLoc e0
where
noLoc = panic "selExprToSels" ["Missing location?"]
go loc expr =
case expr of
ELocated e1 r -> go r e1
ESel e2 s ->
do ls <- go loc e2
let l =
case ls of
l':_ -> l'
[] -> panic "exprToFieldPath" ["empty list of selectors"]
let rng = loc { from = to (srcRange l) }
pure (Located { thing = s, srcRange = rng } : ls)
EVar (UnQual l) ->
pure [ Located { thing = RecordSel l Nothing, srcRange = loc } ]
ELit (ECNum n (DecLit {})) ->
pure [ Located { thing = TupleSel (fromInteger n) Nothing
, srcRange = loc } ]
ELit (ECFrac _ (DecFrac txt))
| (as,bs') <- T.break (== '.') txt
, Just a <- readMaybe (T.unpack as)
, Just (_,bs) <- T.uncons bs'
, Just b <- readMaybe (T.unpack bs)
, let fromP = from loc
, let midP = fromP { col = col fromP + T.length as + 1 } ->
-- these are backward because we reverse above
pure [ Located { thing = TupleSel b Nothing
, srcRange = loc { from = midP }
}
, Located { thing = TupleSel a Nothing
, srcRange = loc { to = midP }
}
]
_ -> errorMessage loc ["Invalid label in record update."]
mkSelector :: Token -> Selector
mkSelector tok =
case tokenType tok of
Selector (TupleSelectorTok n) -> TupleSel n Nothing
Selector (RecordSelectorTok t) -> RecordSel (mkIdent t) Nothing
_ -> panic "mkSelector" [ "Unexpected selector token", show tok ]
mkBacktickImport ::
Range ->
Located (ImpName PName) ->
Maybe (Located ModName) ->
Maybe (Located ImportSpec) ->
Maybe (Located Text) ->
ParseM (Located (ImportG (ImpName PName)))
mkBacktickImport loc impName mbAs mbImportSpec =
mkImport loc impName (Just inst) mbAs mbImportSpec Nothing
where
inst = DefaultInstArg (fmap (const AddParams) impName)
mkImport ::
Range ->
Located (ImpName PName) ->
Maybe (ModuleInstanceArgs PName) ->
Maybe (Located ModName) ->
Maybe (Located ImportSpec) ->
Maybe (Located [Decl PName]) ->
Maybe (Located Text) ->
ParseM (Located (ImportG (ImpName PName)))
mkImport loc impName optInst mbAs mbImportSpec optImportWhere doc =
do i <- getInst
let end = fromMaybe (srcRange impName)
$ msum [ srcRange <$> optImportWhere
, srcRange <$> mbImportSpec
, srcRange <$> mbAs
]
pure Located { srcRange = rComb loc end
, thing = Import
{ iModule = thing impName
, iAs = thing <$> mbAs
, iSpec = thing <$> mbImportSpec
, iInst = i
, iDoc = doc
}
}
where
getInst =
case (optInst,optImportWhere) of
(Just _, Just _) ->
errorMessage loc [ "Invalid instantiating import."
, "Import should have at most one of:"
, " * { } instantiation, or"
, " * where instantiation"
]
(Just a, Nothing) -> pure (Just a)
(Nothing, Just a) ->
pure (Just (DefaultInstAnonArg (map instTop (thing a))))
where
instTop d = Decl TopLevel
{ tlExport = Public
, tlDoc = Nothing
, tlValue = d
}
(Nothing, Nothing) -> pure Nothing
mkTopMods :: Maybe (Located Text) -> Module PName -> ParseM [Module PName]
mkTopMods doc m =
do (m', ms) <- desugarMod m { mDocTop = doc }
pure (ms ++ [m'])
mkTopSig :: Maybe (Located Text) -> Located ModName -> Signature PName -> [Module PName]
mkTopSig doc nm sig =
[ Module { mName = nm
, mDef = InterfaceModule sig
, mInScope = mempty
, mDocTop = doc
}
]
class MkAnon t where
mkAnon :: AnonThing -> t -> t
toImpName :: t -> ImpName PName
data AnonThing = AnonArg Int Int
-- ^ The ints are line, column used for disambiguation
| AnonIfaceMod
instance MkAnon ModName where
mkAnon what = case what of
AnonArg l c -> modNameArg l c
AnonIfaceMod -> modNameIfaceMod
toImpName = ImpTop
instance MkAnon PName where
mkAnon what = mkUnqual
. case what of
AnonArg l c -> const (identAnonArg l c)
AnonIfaceMod -> identAnonIfaceMod
. getIdent
toImpName = ImpNested
-- | Desugar a module returning first the updated original module and a
-- list of any new modules generated by desugaring.
desugarMod :: MkAnon name => ModuleG name PName -> ParseM (ModuleG name PName, [ModuleG name PName])
desugarMod mo =
case mDef mo of
FunctorInstance f as _ | DefaultInstAnonArg lds <- as ->
do (ms,lds') <- desugarTopDs (mName mo) lds
case ms of
m : _ | InterfaceModule si <- mDef m
, l : _ <- map (srcRange . ptName) (sigTypeParams si) ++
map (srcRange . pfName) (sigFunParams si) ++
[ srcRange (mName mo) ] ->
errorMessage l
[ "Instantiation of a parameterized module may not itself be "
++ "parameterized" ]
_ -> pure ()
let i = mkAnon (AnonArg (line pos) (col pos)) (thing (mName mo))
pos = from (srcRange nm)
nm = Located { srcRange = srcRange (mName mo), thing = i }
as' = DefaultInstArg (ModuleArg . toImpName <$> nm)
pure ( mo { mDef = FunctorInstance f as' mempty }
, [ Module
{ mName = nm
, mDef = NormalModule lds'
, mInScope = mempty
, mDocTop = Nothing
}]
)
NormalModule ds ->
do (newMs, newDs) <- desugarTopDs (mName mo) ds
pure (mo {mDef = NormalModule newDs }, newMs)
_ -> pure (mo, [])
desugarTopDs ::
MkAnon name =>
Located name ->
[TopDecl PName] ->
ParseM ([ModuleG name PName], [TopDecl PName])
desugarTopDs ownerName = go emptySig
where
isEmpty s =
null (sigTypeParams s) && null (sigConstraints s) && null (sigFunParams s)
emptySig = Signature
{ sigImports = []
, sigTypeParams = []
, sigDecls = []
, sigConstraints = []
, sigFunParams = []
}
jnSig s1 s2 = Signature { sigImports = j sigImports
, sigTypeParams = j sigTypeParams
, sigDecls = j sigDecls
, sigConstraints = j sigConstraints
, sigFunParams = j sigFunParams
}
where
j f = f s1 ++ f s2
addI i s = s { sigImports = i : sigImports s }
go sig ds =
case ds of
[]
| isEmpty sig -> pure ([],[])
| otherwise ->
do let nm = mkAnon AnonIfaceMod <$> ownerName
pure ( [ Module { mName = nm
, mDef = InterfaceModule sig
, mInScope = mempty
, mDocTop = Nothing
}
]
, [ DModParam
ModParam
{ mpSignature = toImpName <$> nm
, mpAs = Nothing
, mpName = mkModParamName (toImpName <$> nm)
Nothing
, mpDoc = Nothing
, mpRenaming = mempty
}
]
)
d : more ->
let cont emit sig' =
do (ms,ds') <- go sig' more
pure (ms, emit ++ ds')
in
case d of
DImport i
| ImpTop _ <- iModule (thing i)
, Nothing <- iInst (thing i) ->
cont [d] (addI i sig)
DImport i
| Just inst <- iInst (thing i) ->
do newDs <- desugarInstImport i inst
cont newDs sig
DParamDecl _ ds' -> cont [] (jnSig ds' sig)
DModule tl | NestedModule mo <- tlValue tl ->
do (mo', ms) <- desugarMod mo
cont ([ DModule TopLevel
{ tlExport = tlExport tl
, tlValue = NestedModule m
, tlDoc = Nothing -- generated modules have no docstrings
}
| m <- ms] ++ [DModule tl { tlValue = NestedModule mo' }])
sig
_ -> cont [d] sig
desugarInstImport ::
Located (ImportG (ImpName PName)) {- ^ The import -} ->
ModuleInstanceArgs PName {- ^ The insantiation -} ->
ParseM [TopDecl PName]
desugarInstImport i inst =
do (m, ms) <- desugarMod
Module { mName = i { thing = iname }
, mDef = FunctorInstance
(iModule <$> i) inst emptyModuleInstance
, mInScope = mempty
, mDocTop = Nothing
}
pure (DImport (newImp <$> i) : map modTop (ms ++ [m]))
where
iname = mkUnqual
$ let pos = from (srcRange i)
in identAnonInstImport (line pos) (col pos)
newImp d = d { iModule = ImpNested iname
, iInst = Nothing
}
modTop m = DModule TopLevel
{ tlExport = Private
, tlDoc = Nothing
, tlValue = NestedModule m
}