| import Lean.Meta |
| import Std.Data.HashMap |
|
|
| namespace Pantograph.Frontend |
|
|
| open Lean |
|
|
| namespace MetaTranslate |
|
|
| structure Context where |
| sourceMCtx : MetavarContext := {} |
| sourceLCtx : LocalContext := {} |
|
|
| abbrev FVarMap := Std.HashMap FVarId FVarId |
|
|
| structure State where |
| -- Stores mapping from old to new mvar/fvars |
| mvarMap: Std.HashMap MVarId MVarId := {} |
| fvarMap: Std.HashMap FVarId FVarId := {} |
|
|
| /- |
| Monadic state for translating a frozen meta state. The underlying `MetaM` |
| operates in the "target" context and state. |
| -/ |
| abbrev MetaTranslateM := ReaderT Context StateRefT State MetaM |
|
|
| def getSourceLCtx : MetaTranslateM LocalContext := do pure (β read).sourceLCtx |
| def getSourceMCtx : MetaTranslateM MetavarContext := do pure (β read).sourceMCtx |
| def addTranslatedFVar (src dst: FVarId) : MetaTranslateM Unit := do |
| modifyGet Ξ» state => ((), { state with fvarMap := state.fvarMap.insert src dst }) |
| def addTranslatedMVar (src dst: MVarId) : MetaTranslateM Unit := do |
| modifyGet Ξ» state => ((), { state with mvarMap := state.mvarMap.insert src dst }) |
|
|
| def saveFVarMap : MetaTranslateM FVarMap := do |
| return (β get).fvarMap |
| def restoreFVarMap (map: FVarMap) : MetaTranslateM Unit := do |
| modifyGet Ξ» state => ((), { state with fvarMap := map }) |
| def resetFVarMap : MetaTranslateM Unit := do |
| modifyGet Ξ» state => ((), { state with fvarMap := {} }) |
|
|
| mutual |
| private partial def translateLevel (srcLevel: Level) : MetaTranslateM Level := do |
| let sourceMCtx β getSourceMCtx |
| let (_, level) := instantiateLevelMVarsImp sourceMCtx srcLevel |
| match level with |
| | .zero => return .zero |
| | .succ inner => do |
| let inner' β translateLevel inner |
| return .succ inner' |
| | .max l1 l2 => do |
| let l1' β translateLevel l1 |
| let l2' β translateLevel l2 |
| return .max l1' l2' |
| | .imax l1 l2 => do |
| let l1' β translateLevel l1 |
| let l2' β translateLevel l2 |
| return .imax l1' l2' |
| | .param p => return .param p |
| | .mvar _ => |
| Meta.mkFreshLevelMVar |
| private partial def translateExpr (srcExpr: Expr) : MetaTranslateM Expr := do |
| let sourceMCtx β getSourceMCtx |
| -- We want to create as few mvars as possible |
| let (srcExpr, _) := instantiateMVarsCore (mctx := sourceMCtx) srcExpr |
| trace[Pantograph.Frontend.MetaTranslate] "Transform src: {srcExpr}" |
| let result β Core.transform srcExpr Ξ» e => do |
| let state β get |
| match e with |
| | .fvar fvarId => |
| let .some fvarId' := state.fvarMap[fvarId]? | panic! s!"FVar id not registered: {fvarId.name}" |
| -- Delegating this to `Meta.check` later |
| --assert! (β getLCtx).contains fvarId' |
| return .done $ .fvar fvarId' |
| | .mvar mvarId => do |
| -- Must not be assigned |
| assert! !(sourceMCtx.eAssignment.contains mvarId) |
| match state.mvarMap[mvarId]? with |
| | .some mvarId' => do |
| return .done $ .mvar mvarId' |
| | .none => do |
| -- Entering another LCtx, must save the current one |
| let fvarMap β saveFVarMap |
| let mvarId' β translateMVarId mvarId |
| restoreFVarMap fvarMap |
| return .done $ .mvar mvarId' |
| | .sort level => do |
| let level' β translateLevel level |
| return .done $ .sort level' |
| | _ => return .continue |
| Meta.check result |
| return result |
|
|
| partial def translateLocalInstance (srcInstance: LocalInstance) : MetaTranslateM LocalInstance := do |
| return { |
| className := srcInstance.className, |
| fvar := β translateExpr srcInstance.fvar |
| } |
| partial def translateLocalDecl (srcLocalDecl: LocalDecl) : MetaTranslateM LocalDecl := do |
| let fvarId β mkFreshFVarId |
| addTranslatedFVar srcLocalDecl.fvarId fvarId |
| match srcLocalDecl with |
| | .cdecl index _ userName type bi kind => do |
| trace[Pantograph.Frontend.MetaTranslate] "[CD] {userName} {toString type}" |
| return .cdecl index fvarId userName (β translateExpr type) bi kind |
| | .ldecl index _ userName type value nonDep kind => do |
| trace[Pantograph.Frontend.MetaTranslate] "[LD] {toString type} := {toString value}" |
| return .ldecl index fvarId userName (β translateExpr type) (β translateExpr value) nonDep kind |
|
|
| partial def translateLCtx : MetaTranslateM LocalContext := do |
| resetFVarMap |
| let lctx β MonadLCtx.getLCtx |
| assert! lctx.isEmpty |
| (β getSourceLCtx).foldlM (Ξ» lctx srcLocalDecl => do |
| -- Prevents circular proofs and panics |
| if srcLocalDecl.isAuxDecl && !(lctx.auxDeclToFullName.contains srcLocalDecl.fvarId) then |
| pure lctx |
| else |
| let localDecl β Meta.withLCtx' lctx do |
| translateLocalDecl srcLocalDecl |
| pure $ lctx.addDecl localDecl |
| ) lctx |
|
|
| partial def translateMVarId (srcMVarId: MVarId) : MetaTranslateM MVarId := do |
| if let .some mvarId' := (β get).mvarMap[srcMVarId]? then |
| trace[Pantograph.Frontend.MetaTranslate] "Existing mvar id {srcMVarId.name} β {mvarId'.name}" |
| return mvarId' |
| let mvarId' β Meta.withLCtx .empty #[] do |
| let srcDecl := (β getSourceMCtx).findDecl? srcMVarId |>.get! |
| withTheReader Context (Ξ» ctx => { ctx with sourceLCtx := srcDecl.lctx }) do |
| let lctx' β translateLCtx |
| let localInstances' β srcDecl.localInstances.mapM translateLocalInstance |
| Meta.withLCtx lctx' localInstances' do |
| let target' β translateExpr srcDecl.type |
| let mvar' β Meta.mkFreshExprMVar target' srcDecl.kind srcDecl.userName |
| let mvarId' := mvar'.mvarId! |
| if let .some { fvars, mvarIdPending }:= (β getSourceMCtx).getDelayedMVarAssignmentExp srcMVarId then |
| -- Map the fvars in the pending context. |
| let mvarIdPending' β translateMVarId mvarIdPending |
| let fvars' β mvarIdPending'.withContext $ fvars.mapM translateExpr |
| assignDelayedMVar mvarId' fvars' mvarIdPending' |
| pure mvarId' |
| trace[Pantograph.Frontend.MetaTranslate] "Translated {srcMVarId.name} β {mvarId'.name}" |
| addTranslatedMVar srcMVarId mvarId' |
| return mvarId' |
| end |
|
|
| def translateMVarFromTermInfo (termInfo : Elab.TermInfo) (context? : Option Elab.ContextInfo) |
| : MetaTranslateM MVarId := do |
| withTheReader Context (Ξ» ctx => { ctx with |
| sourceMCtx := context?.map (Β·.mctx) |>.getD {}, |
| sourceLCtx := termInfo.lctx, |
| }) do |
| let type := termInfo.expectedType?.get! |
| let lctx' β translateLCtx |
| let mvar β Meta.withLCtx lctx' #[] do |
| Meta.withLocalInstances (lctx'.decls.toList.filterMap id) do |
| let type' β translateExpr type |
| trace[Pantograph.Frontend.MetaTranslate] "Translating from term info {β Meta.ppExpr type'}" |
| Meta.mkFreshExprSyntheticOpaqueMVar type' |
| return mvar.mvarId! |
|
|
| def translateMVarFromTacticInfoBefore (tacticInfo : Elab.TacticInfo) (_context? : Option Elab.ContextInfo) |
| : MetaTranslateM (List MVarId) := do |
| withTheReader Context (Ξ» ctx => { ctx with sourceMCtx := tacticInfo.mctxBefore }) do |
| tacticInfo.goalsBefore.mapM translateMVarId |
|
|
| end MetaTranslate |
|
|
| export MetaTranslate (MetaTranslateM) |
|
|
| initialize |
| registerTraceClass `Pantograph.Frontend.MetaTranslate |
|
|