module Idris.Parser.Expr where
import Idris.AbsSyntax
import Idris.Core.TT
import Idris.DSL
import Idris.Options
import Idris.Parser.Helpers
import Idris.Parser.Ops
import Prelude hiding (pi)
import Control.Applicative
import Control.Arrow (left)
import Control.Monad
import Control.Monad.State.Strict
import Data.Function (on)
import Data.List
import Data.Maybe
import Text.Megaparsec ((<?>))
import qualified Text.Megaparsec as P
import qualified Text.Megaparsec.Char as P
import qualified Text.Megaparsec.Expr as P
allowImp :: SyntaxInfo -> SyntaxInfo
allowImp syn = syn { implicitAllowed = True,
constraintAllowed = False }
disallowImp :: SyntaxInfo -> SyntaxInfo
disallowImp = scopedImp
scopedImp :: SyntaxInfo -> SyntaxInfo
scopedImp syn = syn { implicitAllowed = False,
constraintAllowed = False }
allowConstr :: SyntaxInfo -> SyntaxInfo
allowConstr syn = syn { constraintAllowed = True }
fullExpr :: SyntaxInfo -> IdrisParser PTerm
fullExpr syn = do x <- expr syn
P.eof
i <- get
return $ debindApp syn (desugar syn i x)
tryFullExpr :: SyntaxInfo -> IState -> String -> Either Err PTerm
tryFullExpr syn st = left (Msg . show . parseErrorDoc) . runparser (fullExpr syn) st ""
expr :: SyntaxInfo -> IdrisParser PTerm
expr = pi
opExpr :: SyntaxInfo -> IdrisParser PTerm
opExpr syn = do i <- get
P.makeExprParser (expr' syn) (table (idris_infixes i))
expr' :: SyntaxInfo -> IdrisParser PTerm
expr' syn = P.try (externalExpr syn)
<|> internalExpr syn
<?> "expression"
externalExpr :: SyntaxInfo -> IdrisParser PTerm
externalExpr syn = do i <- get
(expr, outerFC@(FC fn _ _)) <- withExtent $ extensions syn (syntaxRulesList $ syntax_rules i)
return (mapPTermFC (fixFC outerFC) (fixFCH fn outerFC) expr)
<?> "user-defined expression"
where
fixFC outer inner | inner `fcIn` outer = inner
| otherwise = outer
fixFCH fn outer inner | inner `fcIn` outer = inner
| otherwise = FileFC fn
simpleExternalExpr :: SyntaxInfo -> IdrisParser PTerm
simpleExternalExpr syn = do i <- get
extensions syn (filter isSimple (syntaxRulesList $ syntax_rules i))
where
isSimple (Rule (Expr x:xs) _ _) = False
isSimple (Rule (SimpleExpr x:xs) _ _) = False
isSimple (Rule [Keyword _] _ _) = True
isSimple (Rule [Symbol _] _ _) = True
isSimple (Rule (_:xs) _ _) = case last xs of
Keyword _ -> True
Symbol _ -> True
_ -> False
isSimple _ = False
extensions :: SyntaxInfo -> [Syntax] -> IdrisParser PTerm
extensions syn rules = extension syn [] (filter isValid rules)
<?> "user-defined expression"
where
isValid :: Syntax -> Bool
isValid (Rule _ _ AnySyntax) = True
isValid (Rule _ _ PatternSyntax) = inPattern syn
isValid (Rule _ _ TermSyntax) = not (inPattern syn)
isValid (DeclRule _ _) = False
data SynMatch = SynTm PTerm | SynBind FC Name
deriving Show
extension :: SyntaxInfo -> [Maybe (Name, SynMatch)] -> [Syntax] -> IdrisParser PTerm
extension syn ns rules =
P.choice $ flip map (groupBy (ruleGroup `on` syntaxSymbols) rules) $ \rs ->
case head rs of
Rule (symb:_) _ _ -> P.try $ do
n <- extensionSymbol symb
extension syn (n : ns) [Rule ss t ctx | (Rule (_:ss) t ctx) <- rs]
Rule [] ptm _ -> return (flatten (updateSynMatch (mapMaybe id ns) ptm))
where
ruleGroup [] [] = True
ruleGroup (s1:_) (s2:_) = s1 == s2
ruleGroup _ _ = False
extensionSymbol :: SSymbol -> IdrisParser (Maybe (Name, SynMatch))
extensionSymbol (Keyword n) = Nothing <$ keyword (show n)
extensionSymbol (Expr n) = do tm <- expr syn
return $ Just (n, SynTm tm)
extensionSymbol (SimpleExpr n) = do tm <- simpleExpr syn
return $ Just (n, SynTm tm)
extensionSymbol (Binding n) = do (b, fc) <- withExtent name
return $ Just (n, SynBind fc b)
extensionSymbol (Symbol s) = Nothing <$ highlight AnnKeyword (symbol s)
flatten :: PTerm -> PTerm
flatten (PApp fc (PApp _ f as) bs) = flatten (PApp fc f (as ++ bs))
flatten t = t
updateSynMatch = update
where
updateB :: [(Name, SynMatch)] -> (Name, FC) -> (Name, FC)
updateB ns (n, fc) = case lookup n ns of
Just (SynBind tfc t) -> (t, tfc)
_ -> (n, fc)
update :: [(Name, SynMatch)] -> PTerm -> PTerm
update ns (PRef fc hls n) = case lookup n ns of
Just (SynTm t) -> t
_ -> PRef fc hls n
update ns (PPatvar fc n) = uncurry (flip PPatvar) $ updateB ns (n, fc)
update ns (PLam fc n nfc ty sc)
= let (n', nfc') = updateB ns (n, nfc)
in PLam fc n' nfc' (update ns ty) (update (dropn n ns) sc)
update ns (PPi p n fc ty sc)
= let (n', nfc') = updateB ns (n, fc)
in PPi (updTacImp ns p) n' nfc'
(update ns ty) (update (dropn n ns) sc)
update ns (PLet fc rc n nfc ty val sc)
= let (n', nfc') = updateB ns (n, nfc)
in PLet fc rc n' nfc' (update ns ty)
(update ns val) (update (dropn n ns) sc)
update ns (PApp fc t args)
= PApp fc (update ns t) (map (fmap (update ns)) args)
update ns (PAppBind fc t args)
= PAppBind fc (update ns t) (map (fmap (update ns)) args)
update ns (PMatchApp fc n) = let (n', nfc') = updateB ns (n, fc)
in PMatchApp nfc' n'
update ns (PIfThenElse fc c t f)
= PIfThenElse fc (update ns c) (update ns t) (update ns f)
update ns (PCase fc c opts)
= PCase fc (update ns c) (map (pmap (update ns)) opts)
update ns (PRewrite fc by eq tm mty)
= PRewrite fc by (update ns eq) (update ns tm) (fmap (update ns) mty)
update ns (PPair fc hls p l r) = PPair fc hls p (update ns l) (update ns r)
update ns (PDPair fc hls p l t r)
= PDPair fc hls p (update ns l) (update ns t) (update ns r)
update ns (PAs fc n t) = PAs fc (fst $ updateB ns (n, NoFC)) (update ns t)
update ns (PAlternative ms a as) = PAlternative ms a (map (update ns) as)
update ns (PHidden t) = PHidden (update ns t)
update ns (PGoal fc r n sc) = PGoal fc (update ns r) n (update ns sc)
update ns (PDoBlock ds) = PDoBlock $ map (upd ns) ds
where upd :: [(Name, SynMatch)] -> PDo -> PDo
upd ns (DoExp fc t) = DoExp fc (update ns t)
upd ns (DoBind fc n nfc t) = DoBind fc n nfc (update ns t)
upd ns (DoLet fc rc n nfc ty t) = DoLet fc rc n nfc (update ns ty) (update ns t)
upd ns (DoBindP fc i t ts)
= DoBindP fc (update ns i) (update ns t)
(map (\(l,r) -> (update ns l, update ns r)) ts)
upd ns (DoLetP fc i t ts)
= DoLetP fc (update ns i) (update ns t)
(map (\(l,r) -> (update ns l, update ns r)) ts)
upd ns (DoRewrite fc h) = DoRewrite fc (update ns h)
update ns (PIdiom fc t) = PIdiom fc $ update ns t
update ns (PMetavar fc n) = uncurry (flip PMetavar) $ updateB ns (n, fc)
update ns (PProof tacs) = PProof $ map (updTactic ns) tacs
update ns (PTactics tacs) = PTactics $ map (updTactic ns) tacs
update ns (PDisamb nsps t) = PDisamb nsps $ update ns t
update ns (PUnifyLog t) = PUnifyLog $ update ns t
update ns (PNoImplicits t) = PNoImplicits $ update ns t
update ns (PQuasiquote tm mty) = PQuasiquote (update ns tm) (fmap (update ns) mty)
update ns (PUnquote t) = PUnquote $ update ns t
update ns (PQuoteName n res fc) = let (n', fc') = (updateB ns (n, fc))
in PQuoteName n' res fc'
update ns (PRunElab fc t nsp) = PRunElab fc (update ns t) nsp
update ns (PConstSugar fc t) = PConstSugar fc $ update ns t
update ns t = t
updTactic :: [(Name, SynMatch)] -> PTactic -> PTactic
updTactic ns (Intro ns') = Intro $ map (fst . updateB ns . (, NoFC)) ns'
updTactic ns (Focus n) = Focus . fst $ updateB ns (n, NoFC)
updTactic ns (Refine n bs) = Refine (fst $ updateB ns (n, NoFC)) bs
updTactic ns (Claim n t) = Claim (fst $ updateB ns (n, NoFC)) (update ns t)
updTactic ns (MatchRefine n) = MatchRefine (fst $ updateB ns (n, NoFC))
updTactic ns (LetTac n t) = LetTac (fst $ updateB ns (n, NoFC)) (update ns t)
updTactic ns (LetTacTy n ty tm) = LetTacTy (fst $ updateB ns (n, NoFC)) (update ns ty) (update ns tm)
updTactic ns (ProofSearch rec prover depth top psns hints) = ProofSearch rec prover depth
(fmap (fst . updateB ns . (, NoFC)) top) (map (fst . updateB ns . (, NoFC)) psns) (map (fst . updateB ns . (, NoFC)) hints)
updTactic ns (Try l r) = Try (updTactic ns l) (updTactic ns r)
updTactic ns (TSeq l r) = TSeq (updTactic ns l) (updTactic ns r)
updTactic ns (GoalType s tac) = GoalType s $ updTactic ns tac
updTactic ns (TDocStr (Left n)) = TDocStr . Left . fst $ updateB ns (n, NoFC)
updTactic ns t = fmap (update ns) t
updTacImp ns (TacImp o st scr r) = TacImp o st (update ns scr) r
updTacImp _ x = x
dropn :: Name -> [(Name, a)] -> [(Name, a)]
dropn n [] = []
dropn n ((x,t) : xs) | n == x = xs
| otherwise = (x,t):dropn n xs
internalExpr :: SyntaxInfo -> IdrisParser PTerm
internalExpr syn =
unifyLog syn
<|> runElab syn
<|> disamb syn
<|> noImplicits syn
<|> recordType syn
<|> if_ syn
<|> lambda syn
<|> quoteGoal syn
<|> let_ syn
<|> rewriteTerm syn
<|> doBlock syn
<|> caseExpr syn
<|> app syn
<?> "expression"
impossible :: IdrisParser PTerm
impossible = PImpossible <$ keyword "impossible"
caseExpr :: SyntaxInfo -> IdrisParser PTerm
caseExpr syn = do keyword "case"
(scr, fc) <- withExtent $ expr syn
keyword "of"
opts <- indentedBlock1 (caseOption syn)
return (PCase fc scr opts)
<?> "case expression"
caseOption :: SyntaxInfo -> IdrisParser (PTerm, PTerm)
caseOption syn = do lhs <- expr (disallowImp (syn { inPattern = True }))
r <- impossible <|> symbol "=>" *> expr syn
return (lhs, r)
<?> "case option"
warnTacticDeprecation :: FC -> IdrisParser ()
warnTacticDeprecation fc =
parserWarning fc (Just NoOldTacticDeprecationWarnings) (Msg "This style of tactic proof is deprecated. See %runElab for the replacement.")
proofExpr :: SyntaxInfo -> IdrisParser PTerm
proofExpr syn = do kw <- extent $ keyword "proof"
ts <- indentedBlock1 (tactic syn)
warnTacticDeprecation kw
return $ PProof ts
<?> "proof block"
tacticsExpr :: SyntaxInfo -> IdrisParser PTerm
tacticsExpr syn = do kw <- extent $ keyword "tactics"
ts <- indentedBlock1 (tactic syn)
warnTacticDeprecation kw
return $ PTactics ts
<?> "tactics block"
simpleExpr :: SyntaxInfo -> IdrisParser PTerm
simpleExpr syn =
P.try (simpleExternalExpr syn)
<|> do (x, FC f (l, c) end) <- P.try (lchar '?' *> withExtent name)
return (PMetavar (FC f (l, c1) end) x)
<|> do lchar '%'; fc <- extent $ reserved "implementation"; return (PResolveTC fc)
<|> do lchar '%'; fc <- extent $ reserved "instance"
parserWarning fc Nothing $ Msg "The use of %instance is deprecated, use %implementation instead."
return (PResolveTC fc)
<|> do reserved "elim_for"; (t, fc) <- withExtent $ fnName; return (PRef fc [] (SN $ ElimN t))
<|> proofExpr syn
<|> tacticsExpr syn
<|> P.try (do fc <- extent (reserved "Type*"); return $ PUniverse fc AllTypes)
<|> do fc <- extent $ reserved "AnyType"; return $ PUniverse fc AllTypes
<|> PType <$> extent (reserved "Type")
<|> do fc <- extent $ reserved "UniqueType"; return $ PUniverse fc UniqueType
<|> do fc <- extent $ reserved "NullType"; return $ PUniverse fc NullType
<|> do (c, cfc) <- withExtent constant
return (modifyConst syn cfc (PConstant cfc c))
<|> do symbol "'"; (str, fc) <- withExtent name
return (PApp fc (PRef fc [] (sUN "Symbol_"))
[pexp (PConstant NoFC (Str (show str)))])
<|> do (x, fc) <- withExtent fnName
if inPattern syn
then P.option (PRef fc [fc] x)
(do reservedOp "@"
(s, fcIn) <- withExtent $ simpleExpr syn
return (PAs fcIn x s))
else return (PRef fc [fc] x)
<|> idiom syn
<|> listExpr syn
<|> alt syn
<|> do reservedOp "!"
(s, fc) <- withExtent $ simpleExpr syn
return (PAppBind fc s [])
<|> bracketed (disallowImp syn)
<|> quasiquote syn
<|> namequote syn
<|> unquote syn
<|> do lchar '_'; return Placeholder
<?> "expression"
bracketed :: SyntaxInfo -> IdrisParser PTerm
bracketed syn = do (FC fn (sl, sc) _) <- extent (lchar '(') <?> "parenthesized expression"
bracketed' (FC fn (sl, sc) (sl, sc+1)) (syn { withAppAllowed = True })
bracketed' :: FC -> SyntaxInfo -> IdrisParser PTerm
bracketed' open syn =
do fc <- extent (addExtent open *> lchar ')')
return $ PTrue fc TypeOrTerm
<|> P.try (dependentPair TypeOrTerm [] open syn)
<|> P.try (do (opName, fc) <- withExtent operatorName
guardNotPrefix opName
e <- expr syn
lchar ')'
return $ PLam fc (sMN 1000 "ARG") NoFC Placeholder
(PApp fc (PRef fc [] opName) [pexp (PRef fc [] (sMN 1000 "ARG")),
pexp e]))
<|> P.try (simpleExpr syn >>= \l ->
P.try (do (opName, fc) <- withExtent operatorName
lchar ')'
return $ PLam fc (sMN 1000 "ARG") NoFC Placeholder
(PApp fc (PRef fc [] opName) [pexp l,
pexp (PRef fc [] (sMN 1000 "ARG"))]))
<|> bracketedExpr syn open l)
<|> do l <- expr (allowConstr syn)
bracketedExpr (allowConstr syn) open l
where
justPrefix :: FixDecl -> Maybe Name
justPrefix (Fix (PrefixN _) opName) = Just (sUN opName)
justPrefix _ = Nothing
guardNotPrefix :: Name -> IdrisParser ()
guardNotPrefix opName = do
guard $ opName /= sUN "-"
guard $ opName /= sUN "!"
ops <- idris_infixes <$> get
guard . not . (opName `elem`) . mapMaybe justPrefix $ ops
dependentPair :: PunInfo -> [(PTerm, Maybe (FC, PTerm), FC)] -> FC -> SyntaxInfo -> IdrisParser PTerm
dependentPair pun prev openFC syn =
if null prev then
nametypePart <|> namePart
else
case pun of
IsType -> nametypePart <|> namePart <|> exprPart True
IsTerm -> exprPart False
TypeOrTerm -> nametypePart <|> namePart <|> exprPart False
where nametypePart = do
(ln, lnfc, colonFC) <- P.try $ do
(ln, lnfc) <- withExtent name
colonFC <- extent (lchar ':')
return (ln, lnfc, colonFC)
lty <- expr' syn
starsFC <- extent $ reservedOp "**"
dependentPair IsType ((PRef lnfc [] ln, Just (colonFC, lty), starsFC):prev) openFC syn
namePart = P.try $ do
(ln, lnfc) <- withExtent name
starsFC <- extent $ reservedOp "**"
dependentPair pun ((PRef lnfc [] ln, Nothing, starsFC):prev) openFC syn
exprPart isEnd = do
e <- expr syn
sepFCE <-
let stars = (Left <$> extent (reservedOp "**"))
ending = (Right <$> extent (lchar ')'))
in if isEnd then ending else stars <|> ending
case sepFCE of
Left starsFC -> dependentPair IsTerm ((e, Nothing, starsFC):prev) openFC syn
Right closeFC ->
return (mkPDPairs pun openFC closeFC (reverse prev) e)
mkPDPairs pun openFC closeFC ((e, cfclty, starsFC):bnds) r =
(PDPair openFC ([openFC] ++ maybe [] ((: []) . fst) cfclty ++ [starsFC, closeFC] ++ (=<<) (\(_,cfclty,sfc) -> maybe [] ((: []) . fst) cfclty ++ [sfc]) bnds)
pun e (maybe Placeholder snd cfclty) (mergePDPairs pun starsFC bnds r))
mergePDPairs pun starsFC' [] r = r
mergePDPairs pun starsFC' ((e, cfclty, starsFC):bnds) r =
PDPair starsFC' [] pun e (maybe Placeholder snd cfclty) (mergePDPairs pun starsFC bnds r)
bracketedExpr :: SyntaxInfo -> FC -> PTerm -> IdrisParser PTerm
bracketedExpr syn openParenFC e =
do lchar ')'; return e
<|> do exprs <- some (do comma <- extent (lchar ',')
r <- expr syn
return (r, comma))
closeParenFC <- extent (lchar ')')
let hilite = [openParenFC, closeParenFC] ++ map snd exprs
return $ PPair openParenFC hilite TypeOrTerm e (mergePairs exprs)
<|> do starsFC <- extent $ reservedOp "**"
dependentPair IsTerm [(e, Nothing, starsFC)] openParenFC syn
<?> "end of bracketed expression"
where mergePairs :: [(PTerm, FC)] -> PTerm
mergePairs [(t, fc)] = t
mergePairs ((t, fc):rs) = PPair fc [] TypeOrTerm t (mergePairs rs)
modifyConst :: SyntaxInfo -> FC -> PTerm -> PTerm
modifyConst syn fc (PConstant inFC (BI x))
| not (inPattern syn)
= PConstSugar inFC $
PAlternative [] FirstSuccess
(PApp fc (PRef fc [] (sUN "fromInteger")) [pexp (PConstant NoFC (BI (fromInteger x)))]
: consts)
| otherwise = PConstSugar inFC $
PAlternative [] FirstSuccess consts
where
consts = [ PConstant inFC (BI x)
, PConstant inFC (I (fromInteger x))
, PConstant inFC (B8 (fromInteger x))
, PConstant inFC (B16 (fromInteger x))
, PConstant inFC (B32 (fromInteger x))
, PConstant inFC (B64 (fromInteger x))
]
modifyConst syn fc x = x
alt :: SyntaxInfo -> IdrisParser PTerm
alt syn = do symbol "(|"; alts <- P.sepBy1 (expr' (syn { withAppAllowed = False })) (lchar ','); symbol "|)"
return (PAlternative [] FirstSuccess alts)
hsimpleExpr :: SyntaxInfo -> IdrisParser PTerm
hsimpleExpr syn =
do lchar '.'
e <- simpleExpr syn
return $ PHidden e
<|> simpleExpr syn
<?> "expression"
unifyLog :: SyntaxInfo -> IdrisParser PTerm
unifyLog syn = do P.try $ highlight AnnKeyword $ lchar '%' *> reserved "unifyLog"
PUnifyLog <$> simpleExpr syn
<?> "unification log expression"
runElab :: SyntaxInfo -> IdrisParser PTerm
runElab syn = do P.try $ highlight AnnKeyword $ lchar '%' *> reserved "runElab"
(tm, fc) <- withExtent $ simpleExpr syn
return $ PRunElab fc tm (syn_namespace syn)
<?> "new-style tactics expression"
disamb :: SyntaxInfo -> IdrisParser PTerm
disamb syn = do keyword "with"
ns <- P.sepBy1 name (lchar ',')
tm <- expr' syn
return (PDisamb (map tons ns) tm)
<?> "namespace disambiguation expression"
where tons (NS n s) = txt (show n) : s
tons n = [txt (show n)]
noImplicits :: SyntaxInfo -> IdrisParser PTerm
noImplicits syn = do P.try (lchar '%' *> reserved "noImplicits")
tm <- simpleExpr syn
return (PNoImplicits tm)
<?> "no implicits expression"
app :: SyntaxInfo -> IdrisParser PTerm
app syn = (appExtent $ do
f <- simpleExpr syn
(do P.try $ reservedOp "<=="
ff <- fnName
return (\fc -> (PLet fc RigW (sMN 0 "match") NoFC
f
(PMatchApp fc ff)
(PRef fc [] (sMN 0 "match"))))
<?> "matching application expression") <|>
(do args <- many (do notEndApp; arg syn)
wargs <- if withAppAllowed syn && not (inPattern syn)
then many (do notEndApp; reservedOp "|"; expr' syn)
else return []
case args of
[] -> return $ \fc -> f
_ -> return $ \fc -> (withApp fc (flattenFromInt fc f args) wargs)))
<?> "function application"
where
flattenFromInt fc (PAlternative _ x alts) args
| Just i <- getFromInt alts
= PApp fc (PRef fc [] (sUN "fromInteger")) (i : args)
flattenFromInt fc f args = PApp fc f args
withApp fc tm [] = tm
withApp fc tm (a : as) = withApp fc (PWithApp fc tm a) as
getFromInt ((PApp _ (PRef _ _ n) [a]) : _) | n == sUN "fromInteger" = Just a
getFromInt (_ : xs) = getFromInt xs
getFromInt _ = Nothing
arg :: SyntaxInfo -> IdrisParser PArg
arg syn = implicitArg syn
<|> constraintArg syn
<|> do e <- simpleExpr syn
return (pexp e)
<?> "function argument"
implicitArg :: SyntaxInfo -> IdrisParser PArg
implicitArg syn = do lchar '{'
(n, nfc) <- withExtent name
v <- P.option (PRef nfc [nfc] n) (do lchar '='
expr syn)
lchar '}'
return (pimp n v True)
<?> "implicit function argument"
constraintArg :: SyntaxInfo -> IdrisParser PArg
constraintArg syn = do symbol "@{"
e <- expr syn
symbol "}"
return (pconst e)
<?> "constraint argument"
quasiquote :: SyntaxInfo -> IdrisParser PTerm
quasiquote syn = (highlight AnnQuasiquote $ do
highlight AnnKeyword $ symbol "`("
e <- expr syn { syn_in_quasiquote = (syn_in_quasiquote syn) + 1 ,
inPattern = False }
g <- optional $
do highlight AnnKeyword $ symbol ":"
expr syn { inPattern = False }
highlight AnnKeyword $ symbol ")"
return $ PQuasiquote e g)
<?> "quasiquotation"
unquote :: SyntaxInfo -> IdrisParser PTerm
unquote syn = (highlight AnnAntiquote $ do
guard (syn_in_quasiquote syn > 0)
highlight AnnKeyword $ symbol "~"
e <- simpleExpr syn { syn_in_quasiquote = syn_in_quasiquote syn 1 }
return $ PUnquote e)
<?> "unquotation"
namequote :: SyntaxInfo -> IdrisParser PTerm
namequote syn = highlight AnnQuasiquote ((P.try $ do
highlight AnnKeyword $ symbol "`{{"
(n, nfc) <- withExtent fnName
highlight AnnKeyword $ symbol "}}"
return (PQuoteName n False nfc))
<|> (do highlight AnnKeyword $ symbol "`{"
(n, nfc) <- withExtent fnName
highlight AnnKeyword $ symbol "}"
return (PQuoteName n True nfc)))
<?> "quoted name"
data SetOrUpdate = FieldSet PTerm | FieldUpdate PTerm
recordType :: SyntaxInfo -> IdrisParser PTerm
recordType syn =
do ((fgs, rec), fc) <- withExtent $ do
keyword "record"
lchar '{'
fgs <- fieldGetOrSet
lchar '}'
rec <- optional (do notEndApp; simpleExpr syn)
return (fgs, rec)
case fgs of
Left fields ->
case rec of
Nothing ->
return (PLam fc (sMN 0 "fldx") NoFC Placeholder
(applyAll fc fields (PRef fc [] (sMN 0 "fldx"))))
Just v -> return (applyAll fc fields v)
Right fields ->
case rec of
Nothing ->
return (PLam fc (sMN 0 "fldx") NoFC Placeholder
(getAll fc (reverse fields)
(PRef fc [] (sMN 0 "fldx"))))
Just v -> return (getAll fc (reverse fields) v)
<?> "record setting expression"
where fieldSet :: IdrisParser ([Name], SetOrUpdate)
fieldSet = do ns <- fieldGet
(do lchar '='
e <- expr syn
return (ns, FieldSet e))
<|> do symbol "$="
e <- expr syn
return (ns, FieldUpdate e)
<?> "field setter"
fieldGet :: IdrisParser [Name]
fieldGet = P.sepBy1 fnName (symbol "->")
fieldGetOrSet :: IdrisParser (Either [([Name], SetOrUpdate)] [Name])
fieldGetOrSet = P.try (Left <$> P.sepBy1 fieldSet (lchar ','))
<|> do f <- fieldGet
return (Right f)
applyAll :: FC -> [([Name], SetOrUpdate)] -> PTerm -> PTerm
applyAll fc [] x = x
applyAll fc ((ns, e) : es) x
= applyAll fc es (doUpdate fc ns e x)
doUpdate fc ns (FieldUpdate e) get
= let get' = getAll fc (reverse ns) get in
doUpdate fc ns (FieldSet (PApp fc e [pexp get'])) get
doUpdate fc [n] (FieldSet e) get
= PApp fc (PRef fc [] (mkType n)) [pexp e, pexp get]
doUpdate fc (n : ns) e get
= PApp fc (PRef fc [] (mkType n))
[pexp (doUpdate fc ns e (PApp fc (PRef fc [] n) [pexp get])),
pexp get]
getAll :: FC -> [Name] -> PTerm -> PTerm
getAll fc [n] e = PApp fc (PRef fc [] n) [pexp e]
getAll fc (n:ns) e = PApp fc (PRef fc [] n) [pexp (getAll fc ns e)]
mkType :: Name -> Name
mkType (UN n) = sUN ("set_" ++ str n)
mkType (MN 0 n) = sMN 0 ("set_" ++ str n)
mkType (NS n s) = NS (mkType n) s
typeExpr :: SyntaxInfo -> IdrisParser PTerm
typeExpr syn = do cs <- if implicitAllowed syn then constraintList syn else return []
sc <- expr (allowConstr syn)
return (bindList (\r -> PPi (constraint { pcount = r })) cs sc)
<?> "type signature"
lambda :: SyntaxInfo -> IdrisParser PTerm
lambda syn = do lchar '\\' <?> "lambda expression"
((do xt <- P.try $ tyOptDeclList (disallowImp syn)
(sc, fc) <- withExtent lambdaTail
return (bindList (\r -> PLam fc) xt sc))
<|>
(do ps <- P.sepBy (do (e, fc) <- withExtent $ simpleExpr (disallowImp (syn { inPattern = True }))
return (fc, e))
(lchar ',')
sc <- lambdaTail
return (pmList (zip [0..] ps) sc)))
<?> "lambda expression"
where pmList :: [(Int, (FC, PTerm))] -> PTerm -> PTerm
pmList [] sc = sc
pmList ((i, (fc, x)) : xs) sc
= PLam fc (sMN i "lamp") NoFC Placeholder
(PCase fc (PRef fc [] (sMN i "lamp"))
[(x, pmList xs sc)])
lambdaTail :: IdrisParser PTerm
lambdaTail = impossible <|> symbol "=>" *> expr syn
rewriteTerm :: SyntaxInfo -> IdrisParser PTerm
rewriteTerm syn = do keyword "rewrite"
(prf, fc) <- withExtent $ expr syn
giving <- optional (do symbol "==>"; expr' syn)
using <- optional (do reserved "using"
n <- name
return n)
keyword "in"; sc <- expr syn
return (PRewrite fc using prf sc giving)
<?> "term rewrite expression"
let_ :: SyntaxInfo -> IdrisParser PTerm
let_ syn = P.try (do keyword "let"
ls <- indentedBlock (let_binding syn)
keyword "in"; sc <- expr syn
return (buildLets ls sc))
<?> "let binding"
where buildLets [] sc = sc
buildLets ((fc, PRef nfc _ n, ty, v, []) : ls) sc
= PLet fc RigW n nfc ty v (buildLets ls sc)
buildLets ((fc, pat, ty, v, alts) : ls) sc
= PCase fc v ((pat, buildLets ls sc) : alts)
let_binding syn = do (pat, fc) <- withExtent $ expr' (syn { inPattern = True })
ty <- P.option Placeholder (do lchar ':'; expr' syn)
lchar '='
v <- expr (syn { withAppAllowed = isVar pat })
ts <- P.option [] (do lchar '|'
P.sepBy1 (do_alt syn) (lchar '|'))
return (fc,pat,ty,v,ts)
where isVar (PRef _ _ _) = True
isVar _ = False
if_ :: SyntaxInfo -> IdrisParser PTerm
if_ syn = (do keyword "if"
(c, fc) <- withExtent $ expr syn
keyword "then"
t <- expr syn
keyword "else"
f <- expr syn
return (PIfThenElse fc c t f))
<?> "conditional expression"
quoteGoal :: SyntaxInfo -> IdrisParser PTerm
quoteGoal syn = do keyword "quoteGoal"; n <- name;
keyword "by"
r <- expr syn
keyword "in"
(sc, fc) <- withExtent $ expr syn
return (PGoal fc r n sc)
<?> "quote goal expression"
bindsymbol opts st syn
= do symbol "->"
return (Exp opts st False RigW)
explicitPi opts st syn
= do xt <- P.try (lchar '(' *> typeDeclList syn <* lchar ')')
binder <- bindsymbol opts st syn
sc <- expr (allowConstr syn)
return (bindList (\r -> PPi (binder { pcount = r })) xt sc)
autoImplicit opts st syn
= do keyword "auto"
when (st == Static) $ fail "auto implicits can not be static"
xt <- typeDeclList syn
lchar '}'
symbol "->"
sc <- expr (allowConstr syn)
return (bindList (\r -> PPi
(TacImp [] Dynamic (PTactics [ProofSearch True True 100 Nothing [] []]) r)) xt sc)
defaultImplicit opts st syn = do
keyword "default"
when (st == Static) $ fail "default implicits can not be static"
ist <- get
script' <- simpleExpr syn
let script = debindApp syn . desugar syn ist $ script'
xt <- typeDeclList syn
lchar '}'
symbol "->"
sc <- expr (allowConstr syn)
return (bindList (\r -> PPi (TacImp [] Dynamic script r)) xt sc)
normalImplicit opts st syn = do
xt <- typeDeclList syn <* lchar '}'
symbol "->"
cs <- constraintList syn
sc <- expr syn
let (im,cl)
= if implicitAllowed syn
then (Imp opts st False (Just (Impl False True False)) True RigW,
constraint)
else (Imp opts st False (Just (Impl False False False)) True RigW,
Imp opts st False (Just (Impl True False False)) True RigW)
return (bindList (\r -> PPi (im { pcount = r })) xt
(bindList (\r -> PPi (cl { pcount = r })) cs sc))
constraintPi opts st syn =
do cs <- constraintList1 syn
sc <- expr syn
if implicitAllowed syn
then return (bindList (\r -> PPi constraint { pcount = r }) cs sc)
else return (bindList (\r -> PPi (Imp opts st False (Just (Impl True False False)) True r))
cs sc)
implicitPi opts st syn =
autoImplicit opts st syn
<|> defaultImplicit opts st syn
<|> normalImplicit opts st syn
unboundPi opts st syn = do
x <- opExpr syn
(do binder <- bindsymbol opts st syn
sc <- expr syn
return (PPi binder (sUN "__pi_arg") NoFC x sc))
<|> return x
unboundPiNoConstraint opts st syn = do
x <- opExpr syn
(do binder <- bindsymbol opts st syn
sc <- expr syn
P.notFollowedBy $ reservedOp "=>"
return (PPi binder (sUN "__pi_arg") NoFC x sc))
<|> do P.notFollowedBy $ reservedOp "=>"
return x
pi :: SyntaxInfo -> IdrisParser PTerm
pi syn =
do opts <- piOpts syn
st <- static
explicitPi opts st syn
<|> P.try (do lchar '{'; implicitPi opts st syn)
<|> if constraintAllowed syn
then P.try (unboundPiNoConstraint opts st syn)
<|> constraintPi opts st syn
else unboundPi opts st syn
<?> "dependent type signature"
piOpts :: SyntaxInfo -> IdrisParser [ArgOpt]
piOpts syn | implicitAllowed syn =
lchar '.' *> return [InaccessibleArg]
<|> return []
piOpts syn = return []
constraintList :: SyntaxInfo -> IdrisParser [(RigCount, Name, FC, PTerm)]
constraintList syn = P.try (constraintList1 syn)
<|> return []
constraintList1 :: SyntaxInfo -> IdrisParser [(RigCount, Name, FC, PTerm)]
constraintList1 syn = P.try (do lchar '('
tys <- P.sepBy1 nexpr (lchar ',')
lchar ')'
reservedOp "=>"
return tys)
<|> P.try (do t <- opExpr (disallowImp syn)
reservedOp "=>"
return [(RigW, defname, NoFC, t)])
<?> "type constraint list"
where nexpr = P.try (do (n, fc) <- withExtent name; lchar ':'
e <- expr (disallowImp syn)
return (RigW, n, fc, e))
<|> do e <- expr (disallowImp syn)
return (RigW, defname, NoFC, e)
defname = sMN 0 "constraint"
typeDeclList :: SyntaxInfo -> IdrisParser [(RigCount, Name, FC, PTerm)]
typeDeclList syn = P.try (P.sepBy1 (do rig <- P.option RigW rigCount
(x, xfc) <- withExtent fnName
lchar ':'
t <- typeExpr (disallowImp syn)
return (rig, x, xfc, t))
(lchar ','))
<|> do ns <- P.sepBy1 (withExtent name) (lchar ',')
lchar ':'
t <- typeExpr (disallowImp syn)
return (map (\(x, xfc) -> (RigW, x, xfc, t)) ns)
<?> "type declaration list"
where
rigCount = do lchar '1'; return Rig1
<|> do lchar '0'; return Rig0
tyOptDeclList :: SyntaxInfo -> IdrisParser [(RigCount, Name, FC, PTerm)]
tyOptDeclList syn = P.sepBy1 (do (x, fc) <- withExtent nameOrPlaceholder
t <- P.option Placeholder (do lchar ':'
expr syn)
return (RigW, x, fc, t))
(lchar ',')
<?> "type declaration list"
where nameOrPlaceholder :: IdrisParser Name
nameOrPlaceholder = fnName
<|> sMN 0 "underscore" <$ reservedOp "_"
<?> "name or placeholder"
listExpr :: SyntaxInfo -> IdrisParser PTerm
listExpr syn = do (FC f (l, c) _) <- extent (lchar '[')
(do (FC _ _ (l', c')) <- extent (lchar ']') <?> "end of list expression"
return (mkNil (FC f (l, c) (l', c'))))
<|> (do (x, fc) <- withExtent (expr (syn { withAppAllowed = False })) <?> "expression"
(do P.try (lchar '|') <?> "list comprehension"
qs <- P.sepBy1 (do_ syn) (lchar ',')
lchar ']'
return (PDoBlock (map addGuard qs ++
[DoExp fc (PApp fc (PRef fc [] (sUN "pure"))
[pexp x])]))) <|>
(do xs <- many (do commaFC <- extent (lchar ',') <?> "list element"
elt <- expr syn
return (elt, commaFC))
rbrackFC <- extent (lchar ']') <?> "end of list expression"
return (mkList fc rbrackFC ((x, (FC f (l, c) (l, c+1))) : xs))))
<?> "list expression"
where
mkNil :: FC -> PTerm
mkNil fc = PRef fc [fc] (sUN "Nil")
mkList :: FC -> FC -> [(PTerm, FC)] -> PTerm
mkList errFC nilFC [] = PRef nilFC [nilFC] (sUN "Nil")
mkList errFC nilFC ((x, fc) : xs) = PApp errFC (PRef fc [fc] (sUN "::")) [pexp x, pexp (mkList errFC nilFC xs)]
addGuard :: PDo -> PDo
addGuard (DoExp fc e) = DoExp fc (PApp fc (PRef fc [] (sUN "guard"))
[pexp e])
addGuard x = x
doBlock :: SyntaxInfo -> IdrisParser PTerm
doBlock syn
= do keyword "do"
PDoBlock <$> indentedBlock1 (do_ syn)
<?> "do block"
do_ :: SyntaxInfo -> IdrisParser PDo
do_ syn
= P.try (do keyword "let"
(i, ifc) <- withExtent name
ty' <- P.optional (do lchar ':'
expr' syn)
reservedOp "="
(e, fc) <- withExtent $ expr (syn { withAppAllowed = False })
P.option (DoLet fc RigW i ifc (fromMaybe Placeholder ty') e)
(do lchar '|'
when (isJust ty') $ fail "a pattern-matching let may not have an explicit type annotation"
ts <- P.sepBy1 (do_alt (syn { withAppAllowed = False })) (lchar '|')
return (DoLetP fc (PRef ifc [ifc] i) e ts)))
<|> P.try (do keyword "let"
i <- expr' syn
reservedOp "="
(e, fc) <- withExtent $ expr (syn { withAppAllowed = False })
P.option (DoLetP fc i e [])
(do lchar '|'
ts <- P.sepBy1 (do_alt (syn { withAppAllowed = False })) (lchar '|')
return (DoLetP fc i e ts)))
<|> P.try (do (sc, fc) <- withExtent (keyword "rewrite" *> expr syn)
return (DoRewrite fc sc))
<|> P.try (do (i, ifc) <- withExtent name
symbol "<-"
(e, fc) <- withExtent $ expr (syn { withAppAllowed = False });
P.option (DoBind fc i ifc e)
(do lchar '|'
ts <- P.sepBy1 (do_alt (syn { withAppAllowed = False })) (lchar '|')
return (DoBindP fc (PRef ifc [ifc] i) e ts)))
<|> P.try (do i <- expr' syn
symbol "<-"
(e, fc) <- withExtent $ expr (syn { withAppAllowed = False });
P.option (DoBindP fc i e [])
(do lchar '|'
ts <- P.sepBy1 (do_alt (syn { withAppAllowed = False })) (lchar '|')
return (DoBindP fc i e ts)))
<|> do (e, fc) <- withExtent $ expr syn
return (DoExp fc e)
<?> "do block expression"
do_alt syn = do l <- expr' syn
P.option (Placeholder, l)
(do symbol "=>"
r <- expr' syn
return (l, r))
idiom :: SyntaxInfo -> IdrisParser PTerm
idiom syn
= do symbol "[|"
(e, fc) <- withExtent $ expr (syn { withAppAllowed = False })
symbol "|]"
return (PIdiom fc e)
<?> "expression in idiom brackets"
constants :: [(String, Idris.Core.TT.Const)]
constants =
[ ("Integer", AType (ATInt ITBig))
, ("Int", AType (ATInt ITNative))
, ("Char", AType (ATInt ITChar))
, ("Double", AType ATFloat)
, ("String", StrType)
, ("prim__WorldType", WorldType)
, ("prim__TheWorld", TheWorld)
, ("Bits8", AType (ATInt (ITFixed IT8)))
, ("Bits16", AType (ATInt (ITFixed IT16)))
, ("Bits32", AType (ATInt (ITFixed IT32)))
, ("Bits64", AType (ATInt (ITFixed IT64)))
]
constant :: Parsing m => m Idris.Core.TT.Const
constant = P.choice [ ty <$ reserved name | (name, ty) <- constants ]
<|> P.try (Fl <$> float)
<|> BI <$> natural
<|> Str <$> verbatimStringLiteral
<|> Str <$> stringLiteral
<|> P.try (Ch <$> charLiteral)
<?> "constant or literal"
verbatimStringLiteral :: Parsing m => m String
verbatimStringLiteral = token $ do P.try $ string "\"\"\""
str <- P.manyTill P.anyChar $ P.try (string "\"\"\"")
moreQuotes <- P.many $ P.char '"'
return $ str ++ moreQuotes
static :: IdrisParser Static
static = Static <$ reserved "%static"
<|> return Dynamic
<?> "static modifier"
data TacticArg = NameTArg
| ExprTArg
| AltsTArg
| StringLitTArg
tactics :: [([String], Maybe TacticArg, SyntaxInfo -> IdrisParser PTactic)]
tactics =
[ (["intro"], Nothing, const $
do ns <- P.sepBy (spaced name) (lchar ','); return $ Intro ns)
, noArgs ["intros"] Intros
, noArgs ["unfocus"] Unfocus
, (["refine"], Just ExprTArg, const $
do n <- spaced fnName
imps <- many imp
return $ Refine n imps)
, (["claim"], Nothing, \syn ->
do n <- indentGt *> name
goal <- indentGt *> expr syn
return $ Claim n goal)
, (["mrefine"], Just ExprTArg, const $
do n <- spaced fnName
return $ MatchRefine n)
, expressionTactic ["rewrite"] Rewrite
, expressionTactic ["case"] CaseTac
, expressionTactic ["induction"] Induction
, expressionTactic ["equiv"] Equiv
, (["let"], Nothing, \syn ->
do n <- (indentGt *> name)
(do indentGt *> lchar ':'
ty <- indentGt *> expr' syn
indentGt *> lchar '='
t <- indentGt *> expr syn
i <- get
return $ LetTacTy n (desugar syn i ty) (desugar syn i t))
<|> (do indentGt *> lchar '='
t <- indentGt *> expr syn
i <- get
return $ LetTac n (desugar syn i t)))
, (["focus"], Just ExprTArg, const $
do n <- spaced name
return $ Focus n)
, expressionTactic ["exact"] Exact
, expressionTactic ["applyTactic"] ApplyTactic
, expressionTactic ["byReflection"] ByReflection
, expressionTactic ["reflect"] Reflect
, expressionTactic ["fill"] Fill
, (["try"], Just AltsTArg, \syn ->
do t <- spaced (tactic syn)
lchar '|'
t1 <- spaced (tactic syn)
return $ Try t t1)
, noArgs ["compute"] Compute
, noArgs ["trivial"] Trivial
, noArgs ["unify"] DoUnify
, (["search"], Nothing, const $
do depth <- P.option 10 natural
return (ProofSearch True True (fromInteger depth) Nothing [] []))
, noArgs ["implementation"] TCImplementation
, noArgs ["solve"] Solve
, noArgs ["attack"] Attack
, noArgs ["state", ":state"] ProofState
, noArgs ["term", ":term"] ProofTerm
, noArgs ["undo", ":undo"] Undo
, noArgs ["qed", ":qed"] Qed
, noArgs ["abandon", ":q"] Abandon
, noArgs ["skip"] Skip
, noArgs ["sourceLocation"] SourceFC
, expressionTactic [":e", ":eval"] TEval
, expressionTactic [":t", ":type"] TCheck
, expressionTactic [":search"] TSearch
, (["fail"], Just StringLitTArg, const $
do msg <- stringLiteral
return $ TFail [Idris.Core.TT.TextPart msg])
, ([":doc"], Just ExprTArg, const $
do whiteSpace
doc <- (Right <$> constant) <|> (Left <$> fnName)
P.eof
return (TDocStr doc))
]
where
expressionTactic names tactic = (names, Just ExprTArg, \syn ->
do t <- spaced (expr syn)
i <- get
return $ tactic (desugar syn i t))
noArgs names tactic = (names, Nothing, const (return tactic))
spaced parser = indentGt *> parser
imp :: IdrisParser Bool
imp = do lchar '?'; return False
<|> do lchar '_'; return True
tactic :: SyntaxInfo -> IdrisParser PTactic
tactic syn = P.choice [ do P.choice (map reserved names); parser syn
| (names, _, parser) <- tactics ]
<|> do lchar '{'
t <- tactic syn;
lchar ';';
ts <- P.sepBy1 (tactic syn) (lchar ';')
lchar '}'
return $ TSeq t (mergeSeq ts)
<|> ((lchar ':' >> empty) <?> "prover command")
<?> "tactic"
where
mergeSeq :: [PTactic] -> PTactic
mergeSeq [t] = t
mergeSeq (t:ts) = TSeq t (mergeSeq ts)
fullTactic :: SyntaxInfo -> IdrisParser PTactic
fullTactic syn = do t <- tactic syn
P.eof
return t