alms-0.6.0: src/Syntax/Parser.hs
-- | Parser
module Syntax.Parser (
-- * The parsing monad
P, parse,
-- ** Errors
ParseError,
-- ** Quasiquote parsing
parseQuasi,
-- ** File and REPL command parsing
parseFile,
REPLCommand(..), parseCommand,
-- ** Parsers
parseProg, parseRepl, parseDecls, parseDecl, parseModExp,
parseTyDec, parseAbsTy, parseType, parseTyPat,
parseQExp, parseExpr, parsePatt,
parseCaseAlt, parseBinding, parseField,
parseSigExp, parseSigItem,
-- ** For parsing with row dots
withDots,
-- * Convenience parsers (quick and dirty)
pp, pds, pd, pme, ptd, pt, ptp, pqe, pe, px
) where
import Util hiding (before, lift)
import Paths
import AST
import Syntax.Prec
import Syntax.Lexer as Lexer
import Error (AlmsError(..), Phase(ParserPhase))
import qualified Message.AST as Msg
import Alt.Parsec hiding (parse)
import Prelude ()
import qualified Data.Map as M
import qualified Data.List as L
import qualified Language.Haskell.TH as TH
import qualified Text.ParserCombinators.Parsec.Error as PE
import System.IO.Unsafe (unsafePerformIO)
data St = St {
stAnti :: !Bool,
stPos :: !SourcePos,
stDots :: !Bool
}
instance TokenEnd St where
saveTokenEnd = do
pos <- getPosition
updateState $ \st -> st { stPos = pos }
-- | A 'Parsec' character parser, with abstract state
type P a = CharParser St a
state0 :: St
state0 = St {
stAnti = False,
stPos = toSourcePos bogus,
stDots = False
}
-- | Run a parser, given the source file name, on a given string
parse :: P a -> SourceName -> String -> Either ParseError a
parse p = runParser p state0
-- | Run a parser on the given string in quasiquote mode
parseQuasi :: String ->
(String -> String -> Maybe TH.Name -> P a) ->
TH.Q a
parseQuasi str p = do
loc <- fromTHLoc <$> TH.location
let parser = do
setPosition (toSourcePos loc)
iflag <- (++) <$> option "" (string "+")
<*> option "" (string "'")
lflag <- choice [
do char '@'
choice [ char '=' >> identp_no_ws >>! Just,
char '!' >> return Nothing ],
char '!' >> return Nothing,
return (Just "_loc")
]
p (file loc) iflag (fmap TH.mkName lflag)
either (fail . show) return $
runParser parser state0 { stAnti = True } "<quasi>" str
-- | Given a file name and source, parse it
parseFile :: Tag i => String -> String -> Either AlmsError (Prog i)
parseFile = (almsParseError +++ id) <$$> parse parseProg
almsParseError :: ParseError -> AlmsError
almsParseError e =
AlmsError ParserPhase (fromSourcePos (errorPos e)) message
where
message =
Msg.Stack Msg.Broken [
flow ";" messages,
(if null messages then id else Msg.Indent)
(Msg.Table (unlist ++ explist))
]
unlist = case unexpects of
[] -> []
s:_ -> [("unexpected:", Msg.Words s)]
explist = case expects of
[] -> []
_ -> [("expected:", flow "," expects)]
messages = [ s | PE.Message s <- PE.errorMessages e, not $ null s ]
unexpects = [ s | PE.UnExpect s <- PE.errorMessages e, not $ null s ]
++ [ s | PE.SysUnExpect s <- PE.errorMessages e, not $ null s ]
expects = [ s | PE.Expect s <- PE.errorMessages e, not $ null s ]
flow c = Msg.Flow . map Msg.Words . punct c . L.nub
punct _ [] = []
punct _ [s] = [s]
punct c (s:ss) = (s++c) : punct c ss
-- | REPL-level commands
data REPLCommand
= GetInfoCmd [Ident Raw]
| GetPrecCmd [String]
| GetConstraintCmd
| QuitCmd
| DeclsCmd [Decl Raw]
| ParseError AlmsError
-- | Parse a line typed into the REPL
parseCommand :: Int -> String -> String -> REPLCommand
parseCommand row line syntax =
case parsePragma line of
Just cmd -> cmd
_ -> case parseInteractive row syntax of
Right ast -> DeclsCmd ast
Left err -> ParseError (almsParseError err)
-- | Parse a #-style REPL command
parsePragma :: String -> Maybe REPLCommand
parsePragma = (const Nothing ||| Just) . runParser parser state0 "-"
where
parser = finish $
GetInfoCmd <$
pragma "info" <*>
many1 (identp
<|> J [] . Var . ident <$> (operator <|> qjoin))
<|> GetPrecCmd <$
pragma "precedence" <*>
many1 (operator <|> qjoin)
<|> GetConstraintCmd <$
pragma "constraints"
<|> QuitCmd <$
pragma "quit"
-- | Parse a declaration or expression in the REPL
parseInteractive :: Tag i => Int -> String -> Either ParseError [Decl i]
parseInteractive line src = parse p "-" src where
p = do
pos <- getPosition
setPosition (pos `setSourceLine` line)
optional whiteSpace
r <- replp
eof
return r
-- | Get the ending position of the last token, before trailing whitespace
getEndPosition :: P SourcePos
getEndPosition = stPos <$> getState
-- | Parse something and return the span of its location
withLoc :: P a -> P (a, Loc)
withLoc p = do
before <- getPosition
a <- p
after <- getEndPosition
return (a, fromSourcePosSpan before after)
addLoc :: Relocatable a => P a -> P a
addLoc = uncurry (<<@) <$$> withLoc
class Nameable a where
(@@) :: String -> a -> a
infixr 0 @@
instance Relocatable a => Nameable (P a) where
s @@ p = addLoc p <?> s
instance Nameable r => Nameable (a -> r) where
s @@ p = \x -> s @@ p x
punit :: P ()
punit = pure ()
delimList :: P pre -> (P [a] -> P [a]) -> P sep -> P a -> P [a]
delimList before around delim each =
choice [
before >> choice [
around (each `sepBy` delim),
each >>! \x -> [x]
],
return []
]
chainl1last :: P a -> P (a -> a -> a) -> P a -> P a
chainl1last each sep final = start where
start = each >>= loop
loop a = option a $ do
build <- sep
choice
[ each >>= loop . build a,
final >>= return . build a ]
chainr1last :: P a -> P (a -> a -> a) -> P a -> P a
chainr1last each sep final = start where
start = do
a <- each
builder <- loop
return (builder a)
loop = option id $ do
build <- sep
choice
[ do
b <- each
builder <- loop
return (\a -> a `build` builder b),
do
b <- final
return (\a -> a `build` b) ]
foldlp :: (a -> b -> a) -> P a -> P b -> P a
foldlp make start follow = foldl make <$> start <*> many follow
-- Antiquote
antip :: AntiDict -> P Anti
antip dict = antilabels . lexeme . try $ do
char '$' <?> ""
(s1, s2) <- (,) <$> option "" (try (option "" identp_no_ws <* char ':'))
<*> identp_no_ws
assertAnti
case M.lookup s1 dict of
Just _ -> return (Anti s1 s2)
Nothing -> unexpected $ "antiquote tag: `" ++ s1 ++ "'"
where
antilabels p = do
st <- getState
if (stAnti st)
then labels p [ "antiquote `" ++ key ++ "'"
| key <- M.keys dict, key /= "" ]
else p
identp_no_ws :: P String
identp_no_ws = do
c <- lower <|> char '_'
cs <- many (alphaNum <|> oneOf "_'")
return (c:cs)
-- Fail if we should not recognize antiquotes
assertAnti :: P ()
assertAnti = do
st <- getState
unless (stAnti st) (unexpected "antiquote")
-- | Parse an antiquote and inject into syntax
antiblep :: forall a. Antible a => P a
antiblep = antip (dictOf (undefined::a)) >>! injAnti
antioptp :: Antible a => P a -> P (Maybe a)
antioptp = antioptaroundp id
antioptaroundp :: Antible a =>
(P (Maybe a) -> P (Maybe a)) ->
P a -> P (Maybe a)
antioptaroundp wrap p = wrap present <|> pure Nothing
where present = antiblep
<|> Just <$> antiblep
<|> Just <$> p
antilist1p :: Antible a => P b -> P a -> P [a]
antilist1p sep p = antiblep
<|> sepBy1 (antiblep <|> p) sep
antilistp :: Antible a => P b -> P a -> P [a]
antilistp = option [] <$$> antilist1p
-- | Accept or don't accept row type dots.
withDots :: Bool -> P a -> P a
withDots b p = do
saved <- getState
setState saved { stDots = b }
result <- p
state <- getState
setState state { stDots = stDots saved }
return result
-- | Assert that we are accepting dots.
assertDots :: P ()
assertDots = do
state <- getState
if stDots state
then return ()
else pzero
-- Just uppercase identifiers
uidp :: Tag i => P (Uid i)
uidp = ident <$> Lexer.uid
<|> antiblep
<?> "uppercase identifier"
-- Just lowercase identifiers
lidp :: Tag i => P (Lid i)
lidp = ident <$> Lexer.lid
<|> antiblep
<?> "lowercase identifier"
-- Just uppercase row labels
ulabelp :: Tag i => P (Uid i)
ulabelp = ident <$> Lexer.ulabel
<|> antiblep
<?> "variant constructor label"
-- Just lowercase row labels
llabelp :: Tag i => P (Uid i)
llabelp = ident <$> Lexer.llabel
<|> antiblep
<?> "record field label"
-- Infix operator at the given precdence
oplevelp :: Tag i => Prec -> P (Lid i)
oplevelp p = ident <$> opP p
<?> "infix operator"
-- Just parenthesized operators
operatorp :: Tag i => P (Lid i)
operatorp = ident <$> try (parens (operator <|> semis))
<?> "operator name"
-- Lowercase identifiers or naturals
-- - tycon declarations
lidnatp :: Tag i => P (Lid i)
lidnatp = ident <$> (Lexer.lid <|> show <$> natural)
<|> operatorp
<|> antiblep
<?> "type name"
-- Type identifiers (unqualified)
typidp :: Tag i => P (TypId i)
typidp = antiblep
<|> TypId <$> (lidnatp <|> operatorp)
<?> "type constructor"
-- Infix type identifiers
typopp :: Tag i => Prec → P (TypId i)
typopp p = TypId <$> oplevelp p
<?> "infix type operator"
-- Variable bindings
varidp :: Tag i => P (VarId i)
varidp = antiblep
<|> VarId <$> (lidp <|> operatorp)
<?> "variable name"
-- Infix variable occurrences
varopp :: Tag i => Prec → P (VarId i)
varopp p = VarId <$> oplevelp p
<?> "infix operator"
-- Data constructor names
conidp :: Tag i => P (ConId i)
conidp = antiblep
<|> ConId <$> uidp
<|> ConId (ident "::") <$ parens cons
<?> "data constructor"
-- Module names
modidp :: Tag i => P (ModId i)
modidp = antiblep
<|> ModId <$> uidp
<?> "module name"
-- Module type names
sigidp :: Tag i => P (SigId i)
sigidp = antiblep
<|> SigId <$> uidp
<?> "module type (signature) name"
-- Add a path before something
pathp :: (Tag i, Antible b) => P ([ModId i] -> b) -> P b
pathp p = (antiblep <|>) . try $ do
path <- many $ try $ modidp <* dot
make <- p
return (make path)
-- Qualified type identifiers
qtypidp :: Tag i => P (QTypId i)
qtypidp = pathp (flip J <$> typidp)
<|> qident tnAf <$ qualA
<|> qident tnUn <$ qualU
<?> "(qualified) type constructor"
-- Qualified variable occurrences
qvaridp :: Tag i => P (QVarId i)
qvaridp = pathp (flip J <$> varidp)
<?> "(qualified) variable name"
-- Qualified data constructor names
qconidp :: Tag i => P (QConId i)
qconidp = pathp (flip J <$> conidp)
<?> "(qualified) data constructor"
-- Qualified module names
qmodidp :: Tag i => P (QModId i)
qmodidp = pathp (flip J <$> modidp)
<?> "(qualified) module name"
-- Qualified module type names
qsigidp :: Tag i => P (QSigId i)
qsigidp = pathp (flip J <$> sigidp)
<?> "(qualified) module type name"
-- Identifiers
identp :: Tag i => P (Ident i)
identp = pathp (flip J <$> (Var . unTypId <$> typidp <|> Con <$> uidp))
<?> "identifier"
-- Type variables
tyvarp :: Tag i => P (TyVar i)
tyvarp = try $ "type variable" @@
sigilU *> tv Qu
<|> sigilA *> tv Qa
where tv q = antiblep <|> TV <$> lidp <*> pure q <*> pure bogus
-- open variant injection constructor
varinjp ∷ Tag i ⇒ P (Uid i)
varinjp = try (variantInj *> ulabelp)
<?> "open variant constructor"
-- open variant embedding constructor
varembp ∷ Tag i ⇒ P (Uid i)
varembp = try (variantEmb *> ulabelp)
<?> "open variant constructor"
quantp :: P Quant
quantp = Forall <$ forall
<|> Exists <$ exists
<|> antiblep
<?> "quantifier"
typep :: Tag i => P (Type i)
typep = typepP precStart
typepP :: Tag i => Int -> P (Type i)
typepP p = "type" @@ case () of
_ | p == precStart
-> tyrowp1 <|> next
| p == precDot
-> do
tc <- tyQu <$> quantp
<|> tyMu <$ mu
tvs <- many tyvarp
dot
t <- typepP p
return (foldr tc t tvs)
<|> next
| p == precArr
-> chainr1last
next
(choice
[ tyArr <$ arrow,
tyLol <$ lolli,
funbraces (flip tyFun <$> (antiblep <|> Just <$> qExpp)),
tybinopp (Right precArr) ])
(typepP precStart)
| p == precTySemi
-> chainr1last next
(tyAppN <$> (semis <|> qjoin))
(typepP precStart)
| Just (Left _) <- fixities p
-> chainl1last next
(tybinopp (Left p))
(typepP precStart)
| Just (Right _) <- fixities p
-> chainr1last next
(tybinopp (Right p))
(typepP precStart)
| p == precApp -- this case ensures termination
-> tyarg >>= tyapp'
| p < precApp
-> next
| otherwise
-> typepP precStart
where
tyarg :: Tag i => P [Type i]
tyarg = parens (antiblep <|> commaSep1 (typepP precMin))
<|> (:[]) <$> tyatom
--
tyatom :: Tag i => P (Type i)
tyatom = tyVar <$> tyvarp
<|> tyApp <$> qtypidp <*> pure []
<|> antiblep
<|> varianttyp
<|> recordtyp
<|> parens (typepP precMin)
<|> do
ops <- many1 $ addLoc $
typopp (Right precBang) >>! tyApp . J []
arg <- tyatom
return (foldr (\op t -> op [t]) arg ops)
--
tyapp' :: Tag i => [Type i] -> P (Type i)
tyapp' [t] = option t $
do
tc <- qtypidp
tyapp' [tyApp tc [t]]
<|>
do
assertDots
ellipsis
tyapp' [tyRowDots t]
tyapp' ts = do
tc <- qtypidp
tyapp' [tyApp tc ts]
--
next = typepP (p + 1)
-- A variant type
varianttyp ∷ Tag i ⇒ P (Type i)
varianttyp = AST.tyVariant <$> brackets tyrowp
-- A non-empty type row, in variant style
tyrowp1 ∷ Tag i ⇒ P (Type i)
tyrowp1 = AST.tyRow <$> varinjp
<*> option AST.tyUnit
(reserved "of" *> typepP precStart)
<*> option AST.tyRowEnd
(reservedOp "|" *> tyrowp)
-- A possibly empty type row, in variant style
tyrowp ∷ Tag i ⇒ P (Type i)
tyrowp = "row type" @@
antiblep
<|> tyrowp1
<|> extensionp
<|> AST.tyRowEnd <$ whiteSpace
-- A record type
recordtyp ∷ Tag i ⇒ P (Type i)
recordtyp = AST.tyRecordAdditive <$>
plusbraces (recrowp <|> pure AST.tyRowEnd)
<|> AST.tyRecordMultiplicative <$>
braces (recrowp <|> pure AST.tyRowEnd)
-- A type row in record style
recrowp ∷ Tag i ⇒ P (Type i)
recrowp = antiblep
<|> do
labs ← commaSep1 llabelp
colon
t ← typepP precStart
rest ← option AST.tyRowEnd $
comma *> recrowp
<|> reservedOp "|" *> extensionp
return (foldr (AST.tyRow <-> t) rest labs)
<|> extensionp
-- A row extension variable or dot form
extensionp ∷ Tag i ⇒ P (Type i)
extensionp = try (tyRowDots <$> withDots False (typepP precApp) <* ellipsis)
<|> AST.tyVar <$> tyvarp
tybinopp :: Tag i => Prec -> P (Type i -> Type i -> Type i)
tybinopp p = try $ do
op <- typopp p
when (idName op == "-") pzero
return (\t1 t2 -> tyApp (J [] op) [t1, t2])
progp :: Tag i => P (Prog i)
progp = choice [
do ds <- declsp
when (null ds) pzero
e <- antioptaroundp (reserved "in" `between` punit) exprp
return (prog ds e),
antioptp exprp >>! prog []
]
replp :: Tag i => P [Decl i]
replp = choice [
try $ do
ds <- declsp
when (null ds) pzero
eof
return ds,
exprp >>! (prog2decls . prog [] . Just)
]
declsp :: Tag i => P [Decl i]
declsp = antiblep <|> loop
where loop =
choice [
do
d <- declp
ds <- loop
return (d : ds),
(<?> "#load") $ do
pragma "load"
name <- stringLiteral
rel <- sourceName `liftM` getPosition
let mcontents = unsafePerformIO $ do
mfile <- findAlmsLibRel name rel
traverse readFile mfile
contents <- case mcontents of
Just contents -> return contents
Nothing -> fail $ "Could not load: " ++ name
ds <- case parse parseProg name contents of
Left e -> fail (show e)
Right p -> return (prog2decls p)
ds' <- loop
return (ds ++ ds'),
return []
]
declp :: Tag i => P (Decl i)
declp = "declaration" @@ choice [
do
tid ← try $
reserved "type" *> typidp <* reservedOp "=" <* reserved "type"
rhs ← qtypidp
return (dcAli tid rhs),
do
reserved "type"
tyDecsp >>! dcTyp,
letp,
do
reserved "open"
modexpp >>! dcOpn,
do
reserved "module"
choice [
do
reserved "type"
n <- sigidp
reservedOp "="
s <- sigexpp
return (dcSig n s),
do
n <- modidp
asc <- option id $ do
colon
sigexpp >>! flip meAsc
reservedOp "="
b <- modexpp >>! asc
return (dcMod n b)
],
do
reserved "local"
ds0 <- declsp
reserved "with"
ds1 <- declsp
reserved "end"
return (dcLoc ds0 ds1),
do
reserved "abstype"
at <- absTysp
reserved "with"
ds <- declsp
reserved "end"
return (dcAbs at ds),
do
reserved "exception"
n <- conidp
t <- antioptaroundp (reserved "of" `between` punit) typep
return (dcExn n t),
antiblep
]
modexpp :: Tag i => P (ModExp i)
modexpp = "structure" @@ foldlp meAsc body ascription where
body = choice [
meStr <$> between (reserved "struct") (reserved "end") declsp,
meName <$> qmodidp
<*> antilistp comma qvaridp,
antiblep
]
ascription = colon *> sigexpp
sigexpp :: Tag i => P (SigExp i)
sigexpp = "signature" @@ do
se <- choice [
seSig <$> between (reserved "sig") (reserved "end")
(antiblep <|> many sigitemp),
seName <$> qsigidp
<*> antilistp comma qvaridp,
antiblep
]
specs <- many $ do
reserved "with"
reserved "type"
flip sepBy1 (reserved "and") $ "signature specialization" @@ do
(tvs, tc) <- tyAppp (antiblep <|>) tyvarp (J []) qtypidp
reservedOp "="
t <- typep
return (\sig -> seWith sig tc tvs t)
return (foldl (flip ($)) se (concat specs))
sigitemp :: Tag i => P (SigItem i)
sigitemp = "signature item" @@ choice [
do
reserved "val"
n <- varidp
colon
t <- typep
return (sgVal n t),
do
tid ← try $
reserved "type" *> typidp <* reservedOp "=" <* reserved "type"
rhs ← qtypidp
return (sgAli tid rhs),
do
reserved "type"
sgTyp <$> tyDecsp,
do
reserved "module"
choice [
do
reserved "type"
n <- sigidp
reservedOp "="
s <- sigexpp
return (sgSig n s),
do
n <- modidp
colon
s <- sigexpp
return (sgMod n s)
],
do
reserved "include"
sgInc <$> sigexpp,
do
reserved "exception"
n <- conidp
t <- antioptaroundp (reserved "of" `between` punit) typep
return (sgExn n t),
antiblep
]
tyDecsp :: Tag i => P [TyDec i]
tyDecsp = antilist1p (reserved "and") tyDecp
tyDecp :: Tag i => P (TyDec i)
tyDecp = "type declaration" @@ addLoc $ choice
[ antiblep
, do
optional (reservedOp "|")
tp <- typatp
(name, ps) <- checkHead tp
case checkTVs ps of
-- Could be a data type, a synonym, or an abstract type
Just (True, tvs, arity) ->
reservedOp "=" *>
(tdDat name tvs <$> altsp
<|> tryTySyn name ps)
<|> finishTyAbs name tvs arity
-- Must be a synonym or an abstract type
Just (_, tvs, arity) ->
reservedOp "=" *> tryTySyn name ps
<|> finishTyAbs name tvs arity
-- Must be a type function
Nothing ->
reservedOp "=" *> tryTySyn name ps
]
where
-- Try to parse the right-hand side of a type synonym
tryTySyn name ps = do
t <- withDots True typep
alts <- many $ do
reservedOp "|"
tp <- typatp
(name', ps') <- checkHead tp
unless (name == name') $
unexpected $
"non-matching type operators ‘" ++ show name' ++
"’ and ‘" ++ show name ++ "’ in type pattern"
reservedOp "="
ti <- withDots True typep
return (ps', ti)
return (tdSyn name ((ps,t):alts))
--
finishTyAbs name tvs arity = do
let guardsp = option [] $ reserved "rec" *> commaSep1 tyvarp
guards1 ← guardsp
qual ← qualsp
guards2 ← guardsp
return (tdAbs name tvs arity (guards1 ++ guards2) qual)
--
-- A type declaration needs to give an unqualified name for the type
-- being defined. This checks that and splits into the name and the
-- parameter patterns.
checkHead tp = case dataOf tp of
TpApp (J [] name) ps -> return (name, ps)
TpApp _ _ -> unexpected "qualified identifier"
TpVar _ _ -> unexpected "type variable"
TpRow _ _ -> unexpected "row type"
TpAnti _ -> unexpected "antiquote"
--
-- Look at the parameters and determine what kind of type declaration
-- this might be. Returns @Just (allInv, tvs, vars)@ if all the
-- parameters are type variables, where @allInv@ tells whether all the
-- variances are 'Invariant', and @tvs@ and @vars@ are the lists of
-- type variables and variances. Otherwise, we're defining a type
-- function and it returns @Nothing@.
checkTVs [] = return (True, [], [])
checkTVs (N _ (TpVar tv var):rest) = do
(b, tvs, vars) <- checkTVs rest
return (b && var == Invariant, tv:tvs, var:vars)
checkTVs _ = Nothing
-- | Generic parser for things in the shape of type constructor
-- applications.
tyAppp :: Tag i =>
-- | Wrapper for parsing the parameter(s) of a normal suffix
-- type application
(P [a] -> P [a]) ->
-- | Parser for a type parameter
P a ->
-- | Injection to lift a type operator
(TypId i -> b) ->
-- | Parser for postfix constructor
P b ->
P ([a], b)
tyAppp wrap param oper suffix = choice [
-- prefix operator
do
l <- typopp (Right precBang)
p1 <- param
return ([p1], oper l),
-- infix operator
try $ do
p1 <- param
n <- choice [ semis, operator ]
when (n == "-" || precOp n == Right precBang) pzero
p2 <- param
return ([p1, p2], oper (ident n)),
-- normal postfix application
do
ps <- wrap (delimList punit parens comma param)
name <- suffix
return (ps, name)
]
-- | Left-hand side of a type declaration, which looks like a
-- type constructor applied to parameters
tyProtp :: Tag i => P ([(Variance, TyVar i)], TypId i)
tyProtp = tyAppp id paramVp id typidp
-- | A type pattern
typatp :: Tag i => P (TyPat i)
typatp = typatpP precStart
typatpP :: Tag i => Int -> P (TyPat i)
typatpP p = "type pattern" @@ case () of
_ | p == precTySemi
-> chainr1last (typatpP (p + 1))
(tpBinOp . J [] . ident <$> semis)
(typatpP precStart)
| Just e <- fixities p -> case e of
Left _ ->
chainl1last (typatpP (p + 1))
(tpBinOp . J [] <$> typopp (Left p))
(typatpP precStart)
Right _ ->
chainr1last (typatpP (p + 1))
(tpBinOp . J [] <$> typopp (Right p))
(typatpP precStart)
| p == precApp -- this case ensures termination
-> tparg >>= tpapp'
| p < precApp
-> typatpP (p + 1)
| otherwise
-> typatpP precStart
where
tpBinOp ql tp1 tp2 = tpApp ql [tp1, tp2]
--
tparg = parens (antiblep <|> commaSep1 (typatpP precMin))
<|> (:[]) <$> tpatom
--
tpatom = tpvar
<|> tpApp <$> qtypidp <*> pure []
<|> antiblep
<|> tpApp (qident tnUn) [] <$ qualU
<|> tpApp (qident tnAf) [] <$ qualA
<|> tpvariant
<|> tprecord
<|> parens (typatpP precMin)
<|> do
ops <- many1 $ addLoc $
typopp (Right precBang) >>! tpApp . J []
arg <- tpatom
return (foldr (\op t -> op [t]) arg ops)
tpapp' [t] = option t $ do
tc <- qtypidp
tpapp' [tpApp tc [t]]
tpapp' ts = do
tc <- qtypidp
tpapp' [tpApp tc ts]
--
tpvar = do
(v,tv) <- paramVp
con <- option tpVar (tpRow <$ ellipsis)
return (con tv v)
--
tpvariant = brackets $
tpApp (qident tnVariant) . (:[]) <$> (antiblep <|> tpvar)
tprecord = braces $
tpApp (qident tnRecord) . (:[]) <$> (antiblep <|> tpvar)
-- | A let or let rec declaration
letp :: Tag i => P (Decl i)
letp = do
reserved "let"
choice [
do
reserved "rec"
dcLetRec <$> antilist1p (reserved "and") bindingp,
do
f <- varidp
args <- buildargsp
annot <- buildannotp
reservedOp "="
e <- args . annot <$> exprp
return (dcLet (paVar f) e),
dcLet <$> pattp
<* reservedOp "="
<*> exprp
]
-- An abstype group
absTysp :: Tag i => P [AbsTy i]
absTysp = antilist1p (reserved "and") $ absTyp
-- A single abstype
absTyp :: Tag i => P (AbsTy i)
absTyp = addLoc $ antiblep <|> do
((arity, tvs), name) <- tyProtp >>! first unzip
quals <- qualsp
reservedOp "="
alts <- altsp
return (absTy arity quals (tdDat name tvs alts))
-- A type declaration parameter, consisting of a variance and a tyvar
paramVp :: Tag i => P (Variance, TyVar i)
paramVp = try $ (,) <$> variancep <*> tyvarp
-- A variance mark
variancep :: P Variance
variancep = do
qvariance ← option Invariant (QInvariant <$ markQVariant)
sign ← option Invariant $ choice
[ Covariant <$ markCovariant
, Contravariant <$ markContravariant
, Omnivariant <$ markOmnivariant
, Invariant <$ markInvariant ]
return (qvariance ⊓ sign)
<?> "variance marker"
-- A qualifier annotation for a type declaration
qualsp :: Tag i => P (QExp i)
qualsp = option minBound $
(reserved "qualifier" <|> reservedOp ":") *> qExpp
-- A qualifier expression
qExpp :: Tag i => P (QExp i)
qExpp = "qualifier expression" @@ qexp where
qexp = addLoc $
chainl1 qatom (addLoc $ qeJoin <$ (void comma <|> qjoinArr))
qatom = addLoc $
qeLit Qu <$ qualU
<|> qeLit Qa <$ qualA
<|> clean <$> tyvarp
<|> qeLid <$> lidp
<|> antiblep
<|> parens qexp
qeLid = qeVar . (TV <-> Qa <-> bogus)
clean (TV _ Qu _) = minBound
clean tv = qeVar tv
altsp :: Tag i => P [(ConId i, Maybe (Type i))]
altsp = sepBy1 altp (reservedOp "|")
altp :: Tag i => P (ConId i, Maybe (Type i))
altp = do
k <- try $ conidp <* try (dot *> pzero <|> punit)
t <- optionMaybe $ do
reserved "of"
typep
return (k, t)
exprp :: Tag i => P (Expr i)
exprp = exprpP precStart
exprpP :: Tag i => Int -> P (Expr i)
exprpP p = mark $ case () of
_ | p == precStart → choice
[ do reserved "let"
choice
[ exLetRec <$ reserved "rec"
<*> antilist1p (reserved "and") bindingp
<* reserved "in"
<*> exprp
, exLet <$> (paVar <$> varidp)
<*> (buildargsp <*>
(buildannotp <* reservedOp "=" <*> exprp))
<* reserved "in"
<*> exprp
, exLet <$> pattp
<* reservedOp "="
<*> exprp
<* reserved "in"
<*> exprp
, reserved "let" *> unexpected "let"
, exLetDecl <$> declp
<* reserved "in"
<*> exprp ],
do reserved "if"
ec <- exprp
clt <- addLoc $ do
reserved "then"
caClause (paCon idTrueValue Nothing)
<$> exprp
clf <- addLoc $ do
reserved "else"
caClause (paCon idFalseValue Nothing)
<$> exprp
return (exCase ec [clt, clf]),
do reserved "match"
e1 <- exprp
reserved "with"
choice [
exCase e1 <$> antiblep,
do
optional (reservedOp "|")
clauses <- flip sepBy1 (reservedOp "|") casealtp
return (exCase e1 clauses) ],
do reserved "try"
e1 <- exprp
reserved "with"
optional (reservedOp "|")
clauses <- sepBy1 <-> reservedOp "|" $ addLoc $ do
caClause . paCon (qident "Left") . Just
<$> pattp
<* arrow
<*> exprp
let tryQ = qident $
"INTERNALS.Exn.tryfun"
return $
exCase (exApp (exVar tryQ)
(exAbs paWild e1)) $
caClause (paCon (qident "Right")
(Just (paVar (ident "x"))))
(exVar (qident "x"))
:
clauses ++
[caClause
(paCon (qident "Left")
(Just (paVar (ident "e"))))
(exApp (exVar (qident "INTERNALS.Exn.raise"))
(exVar (qident "e")))
],
lambda *> buildargsp <* arrow <*> exprp,
next ]
| p == precExSemi → do
e1 <- next
choice
[ do semi
e2 <- exprp
return (exSeq e1 e2),
return e1 ]
| p == precCast → do
e1 <- next
anns <- many $ do
b <- False <$ colon
<|> True <$ reservedOp ":>"
t2 <- typep
return (t2, b)
return (foldl (uncurry . exCast) e1 anns)
| p == precTySemi →
next
| p == precApp →
choice [
exCon <$> qconidp <*> antioptp next,
exInj <$> varinjp <*> antioptp next,
exEmb <$> varembp <*> next,
chainl1 next (addLoc (return exApp))
]
| p == precSel → do
foldl' exSel <$> next <*> many (dot *> llabelp)
| p == precBang → do
ops <- many $ addLoc $ exBVar <$> varopp (Right precBang)
arg <- next
return (foldr exApp arg ops)
| p == precCom →
foldl1 exPair <$> commaSep1 next
| p > precMax → choice
[
exVar <$> qvaridp,
exCon <$> qconidp <*> pure Nothing,
exLit <$> litp,
antiblep,
brackets (listexp <|> pure exNil),
parens (exprpP precMin <|> pure exUnit),
recordp
]
| p == precCaret
-> chainr1last next
(opappp (Right p) <|> opconsp exCon exPair)
exprp
| Just (Left _) <- fixities p ->
chainl1last next (opappp (Left p)) exprp
| Just (Right _) <- fixities p ->
chainr1last next (opappp (Right p)) exprp
| otherwise → next
where
next = exprpP (p + 1)
mark = ("expression" @@)
-- | The body of a list.
listexp ∷ Tag i ⇒ P (Expr i)
listexp = foldr exCons exNil <$> commaSep1 exprp
-- | Parse a record expression
recordp :: Tag i ⇒ P (Expr i)
recordp = plusbraces (recordbodyp True <|> pure exNilRecord)
<|> braces (recordbodyp False <|> pure exNilRecord)
-- | Parse a record expression body
recordbodyp :: Tag i => Bool → P (Expr i)
recordbodyp additive = "record field" @@ do
fields ← antilist1p comma fieldp
end ← reservedOp "|" *> exprp
<|> pure exNilRecord
return (exRec additive fields end)
-- | Parse a record field
fieldp :: Tag i ⇒ P (Field i)
fieldp = "record field" @@ antiblep <|> do
lab ← llabelp
e ← option (exBVar (VarId (uidToLid lab))) $
buildargsp <*> (buildannotp <* reservedOp "=" <*> exprp)
return (fdField lab e)
-- Parse a match clause
casealtp :: Tag i => P (CaseAlt i)
casealtp = "match clause" @@ antiblep <|>
caClause <$> pattp <* arrow <*> exprp
<|> caPrj <$> varembp <*> antioptp pattp <* arrow <*> exprp
-- Parse a single let rec binding
bindingp :: Tag i => P (Binding i)
bindingp = "let rec binding" @@ antiblep <|>
bnBind <$> varidp
<*> (buildargsp
<*> (buildannotp
<* reservedOp "="
<*> exprp))
-- Parse an infix operator at given precedence
opappp :: Tag i => Prec -> P (Expr i -> Expr i -> Expr i)
opappp p = do
op <- addLoc (exBVar <$> varopp p)
return (\e1 e2 -> op `exApp` e1 `exApp` e2)
-- Parse list cons
opconsp :: Tag i => (QConId i -> Maybe a -> a) → (a -> a -> a) →
P (a -> a -> a)
opconsp con pair = cons *> return (con idConsList . Just <$$> pair)
-- Parse some number of argument patterns and return the function
-- that adds them to a body expression to build a lambda.
buildargsp :: Tag i => P (Expr i -> Expr i)
buildargsp = (foldr exAbs <->) <$> many (pattpP (precApp + 1))
-- Parse an optional type annotation and return the function that
-- adds it as an ascription on an expression.
buildannotp :: Tag i => P (Expr i -> Expr i)
buildannotp = do
mt <- antioptaroundp (colon *>) typep
return $ case mt of
Nothing → id
Just t → \e → exCast e t False
-- A pattern
pattp :: Tag i => P (Patt i)
pattp = pattpP precStart
pattpP ∷ Tag i ⇒ Int → P (Patt i)
pattpP p = mark $ case () of
_ | p == precCast →
foldl paAnn <$> next <*> many (colon *> typep)
| p == precEq → do
x <- next
choice
[ do
reserved "as"
y <- varidp
return (paAs x y),
return x
]
| p == precApp →
choice [
paCon <$> qconidp <*> antioptp next,
paInj <$> varinjp <*> antioptp next,
next
]
| p == precBang →
option id (paBang <$ bang) <*> next
| p == precCom →
foldl1 paPair <$> commaSep1 next
| p == precCaret →
chainr1last next (opconsp paCon paPair) pattp
| p > precMax → choice
[
paWild <$ reserved "_",
paVar <$> varidp,
paCon <$> qconidp <*> pure Nothing,
paInj <$> varinjp <*> pure Nothing,
paLit <$> litp,
antiblep,
brackets (listpap <|> pure paNil),
braces recordpap,
parens (pattpP precMin <|> pure paUnit)
]
| otherwise → next
where
next = pattpP (p + 1)
mark = ("pattern" @@)
-- | The body of a list.
listpap ∷ Tag i ⇒ P (Patt i)
listpap = foldr paCons paNil <$> commaSep1 pattp
recordpap ∷ Tag i ⇒ P (Patt i)
recordpap = do
flds ← commaSep1 $ do
lab ← llabelp
π ← option (paVar (VarId (uidToLid lab))) (reservedOp "=" *> pattp)
return (lab, π)
ext ← option paWild (reservedOp "|" *> pattp)
return (foldr (uncurry paRec) ext flds)
litp :: P Lit
litp = (<?> "literal") $ choice [
integerOrFloat >>! either LtInt LtFloat,
charLiteral >>! LtChar,
stringLiteral >>! LtStr,
antiblep
]
finish :: P a -> P a
finish p = do
optional whiteSpace
r <- p
eof
return r
-- | Parse a program
parseProg :: Tag i => P (Prog i)
-- | Parse a REPL line
parseRepl :: Tag i => P [Decl i]
-- | Parse a sequence of declarations
parseDecls :: Tag i => P [Decl i]
-- | Parse a declaration
parseDecl :: Tag i => P (Decl i)
-- | Parse a module expression
parseModExp :: Tag i => P (ModExp i)
-- | Parse a type declaration
parseTyDec :: Tag i => P (TyDec i)
-- | Parse a abstype declaration
parseAbsTy :: Tag i => P (AbsTy i)
-- | Parse a type
parseType :: Tag i => P (Type i)
-- | Parse a type pattern
parseTyPat :: Tag i => P (TyPat i)
-- | Parse a qualifier expression
parseQExp :: Tag i => P (QExp i)
-- | Parse an expression
parseExpr :: Tag i => P (Expr i)
-- | Parse a pattern
parsePatt :: Tag i => P (Patt i)
-- | Parse a case alternative
parseCaseAlt :: Tag i => P (CaseAlt i)
-- | Parse a let rec binding
parseBinding :: Tag i => P (Binding i)
-- | Parse a record field
parseField :: Tag i => P (Field i)
-- | Parse a signature
parseSigExp :: Tag i => P (SigExp i)
-- | Parse a signature item
parseSigItem :: Tag i => P (SigItem i)
parseProg = finish progp
parseRepl = finish replp
parseDecls = finish declsp
parseDecl = finish declp
parseModExp = finish modexpp
parseTyDec = finish tyDecp
parseAbsTy = finish absTyp
parseType = finish typep
parseTyPat = finish typatp
parseQExp = finish qExpp
parseExpr = finish exprp
parsePatt = finish pattp
parseCaseAlt = finish casealtp
parseBinding = finish bindingp
parseField = finish fieldp
parseSigExp = finish sigexpp
parseSigItem = finish sigitemp
-- Convenience functions for quick-and-dirty parsing:
-- | Parse a program
pp :: String -> Prog Renamed
pp = makeQaD parseProg
-- | Parse a sequence of declarations
pds :: String -> [Decl Renamed]
pds = makeQaD parseDecls
-- | Parse a declaration
pd :: String -> Decl Renamed
pd = makeQaD parseDecl
pme :: String -> ModExp Renamed
pme = makeQaD parseModExp
-- | Parse a type declaration
ptd :: Tag i => String -> TyDec i
ptd = makeQaD parseTyDec
-- | Parse a type
pt :: String -> Type Renamed
pt = makeQaD parseType
-- | Parse a type pattern
ptp :: String -> TyPat Renamed
ptp = makeQaD parseTyPat
-- | Parse a qualifier expression
pqe :: String -> QExp Renamed
pqe = makeQaD parseQExp
-- | Parse an expression
pe :: String -> Expr Renamed
pe = makeQaD parseExpr
-- | Parse a pattern
px :: String -> Patt Renamed
px = makeQaD parsePatt
{-
deriving instance Show (Expr' i)
deriving instance Show (CaseAlt' i)
deriving instance Show (Decl' i)
deriving instance Show (Binding' i)
deriving instance Show (AbsTy' i)
deriving instance Show (ModExp' i)
deriving instance Show (SigExp' i)
deriving instance Show (TyDec' i)
deriving instance Show (TyPat' i)
deriving instance Show (SigItem' i)
deriving instance Show (Patt' i)
deriving instance Show (Type' i)
deriving instance Show (QExp' i)
deriving instance Show (Prog' i)
deriving instance Show Lit
instance Show a ⇒ Show (N i a) where showsPrec = showsPrec <$.> view
-}
makeQaD :: P a -> String -> a
makeQaD parser =
either (error . show) id . runParser parser state0 "<string>"