| import Pantograph |
| import Std.Data.HashMap |
|
|
| namespace Pantograph.Repl |
|
|
| open Lean |
|
|
| structure Context where |
| coreContext : Core.Context |
| |
| inheritEnv : Bool := false |
|
|
| / |
| structure State where |
| options : Protocol.Options := {} |
| nextId : Nat := 0 |
| goalStates : Std.HashMap Nat GoalState := Std.HashMap.emptyWithCapacity |
|
|
| env : Environment |
| |
| scope : Elab.Command.Scope := { header := "" } |
|
|
| / |
| abbrev MainM := ReaderT Context $ StateRefT State IO |
| / |
| abbrev EMainM := Protocol.FallibleT $ ReaderT Context $ StateRefT State IO |
|
|
| def getMainState : MainM State := get |
|
|
| instance : MonadBacktrack State MainM where |
| saveState := getMainState |
| restoreState := set |
|
|
| instance : MonadEnv MainM where |
| getEnv := return (β get).env |
| modifyEnv f := modify fun s => { s with env := f s.env } |
|
|
| def withInheritEnv [Monad m] [MonadWithReaderOf Context m] [MonadLift MainM m] { Ξ± } (z : m Ξ±) : m Ξ± := do |
| withTheReader Context ({ Β· with inheritEnv := true }) z |
|
|
| def newGoalState (goalState : GoalState) : MainM Nat := do |
| let state β get |
| let stateId := state.nextId |
| set { state with |
| goalStates := state.goalStates.insert stateId goalState, |
| nextId := state.nextId + 1 |
| } |
| return stateId |
|
|
| def runCoreM { Ξ± } (coreM : CoreM Ξ±) : EMainM Ξ± := do |
| let { currNamespace, openDecls, opts := options, .. }:= (β get).scope |
| let timeout := (β get).options.timeout |
| let cancelTk? β match timeout with |
| | 0 => pure .none |
| | _ => .some <$> IO.CancelToken.new |
| let coreCtx : Core.Context := { |
| (β read).coreContext with |
| currNamespace, |
| openDecls, |
| options, |
| initHeartbeats := β IO.getNumHeartbeats, |
| cancelTk?, |
| } |
| let coreState : Core.State := { |
| env := β getEnv, |
| } |
| |
| let coreM' : CoreM _ := |
| try |
| Except.ok <$> coreM |
| catch ex => |
| let desc β ex.toMessageData.toString |
| return Except.error ({ error := "exception", desc } : Protocol.InteractionError) |
| finally |
| for {msg, ..} in (β getTraceState).traces do |
| IO.eprintln (β msg.format.toIO) |
| resetTraceState |
| if let .some token := cancelTk? then |
| runCancelTokenWithTimeout token (timeout := .ofBitVec timeout) |
| let (result, state') β match β (coreM'.run coreCtx coreState).toIO' with |
| | Except.error (Exception.error _ msg) => Protocol.throw $ .errorIO (β msg.toString) |
| | Except.error (Exception.internal id _) => Protocol.throw $ .errorIO (β id.getName).toString |
| | Except.ok a => pure a |
| if (β read).inheritEnv && result matches .ok _ then |
| setEnv state'.env |
| liftExcept result |
| |
| /-- Executes a fallible `CoreM` -/ |
| def runCoreM' { Ξ± } (coreM : Protocol.FallibleT CoreM Ξ±) : EMainM Ξ± := do |
| liftExcept $ β runCoreM coreM.run |
|
|
| def liftMetaM { Ξ± } (metaM : MetaM Ξ±) : EMainM Ξ± := |
| runCoreM metaM.run' |
| def liftTermElabM { Ξ± } (termElabM : Elab.TermElabM Ξ±) (levelNames : List Name := []) |
| : EMainM Ξ± := do |
| let scope := (β get).scope |
| let context := { |
| errToSorry := false, |
| isNoncomputableSection := scope.isNoncomputable, |
| } |
| let state := { |
| levelNames := scope.levelNames ++ levelNames, |
| } |
| runCoreM $ termElabM.run' context state |>.run' |
| |
| section Environment |
| |
| def env_catalog (args : Protocol.EnvCatalog) : EMainM Protocol.EnvCatalogResult := runCoreM do |
| let env β MonadEnv.getEnv |
| let names := env.constants.fold (init := []) Ξ» acc name info => |
| let moduleAllow := match args.modulePrefix? with |
| | .some pr => |
| let module? := env.getModuleIdxFor? name >>= (env.allImportedModuleNames[Β·.toNat]?) |
| module?.map (Ξ» name => (toString name).startsWith pr) |>.getD false |
| | .none => true |
| if moduleAllow != args.invertFilter then |
| match toFilteredSymbol name info with |
| | .some x => x :: acc |
| | .none => acc |
| else |
| acc |
| IO.FS.writeFile args.filename $ String.join (names.map (Β· ++ "\n")) |
| return { nSymbols := names.length } |
| |
| def env_inspect (args : Protocol.EnvInspect) : EMainM Protocol.EnvInspectResult := do |
| let env β MonadEnv.getEnv |
| let options := (β getMainState).options |
| let name := args.name |
| let info? := env.find? name |
| let .some info := info? |
| | throw $ .errorIndex s!"Symbol not found {args.name}" |
| runCoreM do |
| let module? := env.getModuleIdxFor? name >>= (env.allImportedModuleNames[Β·.toNat]?) |
| let value? := match args.value?, info with |
| | .some true, _ => info.value? |
| | .some false, _ => .none |
| | .none, .defnInfo _ => info.value? |
| | .none, _ => .none |
| let type β unfoldAuxLemmas info.type |
| let value? β value?.mapM (Ξ» v => unfoldAuxLemmas v) |
| -- Information common to all symbols |
| let core := { |
| type := β (serializeExpression options type).run', |
| isUnsafe := info.isUnsafe, |
| value? := β value?.mapM (Ξ» v => serializeExpression options v |>.run'), |
| publicName? := Lean.privateToUserName? name, |
| typeDependency? := if args.dependency?.getD false |
| then .some <| type.getUsedConstants |
| else .none, |
| valueDependency? := if args.dependency?.getD false |
| then value?.map Ξ» e => |
| e.getUsedConstants.filter (!isNameInternal Β·) |
| else .none, |
| module?, |
| } |
| let result β match info with |
| | .inductInfo induct => pure { core with inductInfo? := .some { |
| induct with |
| all := induct.all.toArray, |
| ctors := induct.ctors.toArray, |
| } } |
| | .ctorInfo ctor => pure { core with constructorInfo? := .some { |
| ctor with |
| } } |
| | .recInfo r => pure { core with recursorInfo? := .some { |
| r with |
| all := r.all.toArray, |
| rules := β r.rules.toArray.mapM (Ξ» rule => do |
| pure { |
| ctor := rule.ctor, |
| nFields := rule.nfields, |
| rhs := β (serializeExpression options rule.rhs).run', |
| }) |
| } } |
| | _ => pure core |
| let result β if args.source?.getD false then |
| try |
| let sourceUri? β module?.bindM (Server.documentUriFromModule? Β·) |
| let declRange? β findDeclarationRanges? name |
| let sourceStart? := declRange?.map (Β·.range.pos) |
| let sourceEnd? := declRange?.map (Β·.range.endPos) |
| .pure { |
| result with |
| sourceUri? := sourceUri?.map (toString Β·), |
| sourceStart?, |
| sourceEnd?, |
| } |
| catch _e => |
| .pure result |
| else |
| .pure result |
| return result |
| def env_describe (_ : Protocol.EnvDescribe) : EMainM Protocol.EnvDescribeResult := runCoreM do |
| let env β Lean.MonadEnv.getEnv |
| return { |
| imports := env.header.imports.map (Β·.module), |
| modules := env.header.moduleNames, |
| } |
| def env_module_read (args : Protocol.EnvModuleRead) : EMainM Protocol.EnvModuleReadResult := runCoreM do |
| let env β Lean.MonadEnv.getEnv |
| let .some i := env.header.moduleNames.findIdx? (Β· == args.module) | |
| throwError s!"Module not found {args.module}" |
| let data := env.header.moduleData[i]! |
| return { |
| imports := data.imports.map (Β·.module), |
| constNames := data.constNames, |
| extraConstNames := data.extraConstNames, |
| } |
| / |
| def env_add (args : Protocol.EnvAdd) : EMainM Protocol.EnvAddResult := withInheritEnv <| runCoreM' do |
| let { name, levels?, type?, value, isTheorem } := args |
| let levels := levels?.getD #[] |
| let env β Lean.MonadEnv.getEnv |
| let levelParams := levels.toList |
| let tvM: Elab.TermElabM (Except String (Expr Γ Expr)) := |
| Elab.Term.withLevelNames levelParams do do |
| let expectedType?? : Except String (Option Expr) β ExceptT.run $ type?.mapM Ξ» type => do |
| match parseTerm env type with |
| | .ok syn => elabTerm syn |
| | .error e => MonadExceptOf.throw e |
| let expectedType? β match expectedType?? with |
| | .ok t? => pure t? |
| | .error e => return .error e |
| let value β match parseTerm env value with |
| | .ok syn => do |
| try |
| let expr β Elab.Term.elabTerm (stx := syn) (expectedType? := expectedType?) |
| Lean.Elab.Term.synthesizeSyntheticMVarsNoPostponing |
| let expr β instantiateMVars expr |
| pure $ expr |
| catch ex => return .error (β ex.toMessageData.toString) |
| | .error e => return .error e |
| Elab.Term.synthesizeSyntheticMVarsNoPostponing |
| let type β match expectedType? with |
| | .some t => pure t |
| | .none => Meta.inferType value |
| pure $ .ok (β instantiateMVars type, β instantiateMVars value) |
| let (type, value) β match β tvM.run' (ctx := {}) |>.run' with |
| | .ok t => pure t |
| | .error e => Protocol.throw $ .errorParse e |
| let decl := if isTheorem then |
| Lean.Declaration.thmDecl <| Lean.mkTheoremValEx |
| (name := name) |
| (levelParams := levelParams) |
| (type := type) |
| (value := value) |
| (all := []) |
| else |
| Lean.Declaration.defnDecl <| Lean.mkDefinitionValEx |
| (name := name) |
| (levelParams := levelParams) |
| (type := type) |
| (value := value) |
| (hints := Lean.mkReducibilityHintsRegularEx 1) |
| (safety := Lean.DefinitionSafety.safe) |
| (all := []) |
| Lean.addDecl decl |
| return {} |
| |
| end Environment |
| |
| section Goal |
| |
| def goal_tactic (args : Protocol.GoalTactic) : EMainM Protocol.GoalTacticResult := do |
| let state β getMainState |
| let .some goalState := state.goalStates[args.stateId]? |
| | throw $ .errorIndex s!"Invalid state index {args.stateId}" |
| let unshielded := args.autoResume?.getD state.options.automaticMode |
| let site β match args.goalId?, unshielded with |
| | .some goalId, true => do |
| let .some goal := goalState.goals[goalId]? | |
| Protocol.throw $ .errorIndex s!"Invalid goal index {goalId}" |
| pure (.prefer goal) |
| | .some goalId, false => do |
| let .some goal := goalState.goals[goalId]? | |
| Protocol.throw $ .errorIndex s!"Invalid goal index {goalId}" |
| pure (.focus goal) |
| | .none, true => pure .unfocus |
| | .none, false => do |
| let .some goal := goalState.mainGoal? | |
| Protocol.throw $ .errorIndex s!"No goals to be solved" |
| pure (.focus goal) |
| let nextGoalState?: Except _ TacticResult β liftTermElabM do |
| -- NOTE: Should probably use a macro to handle this... |
| match args.tactic?, args.mode?, args.expr?, args.have?, args.let?, args.draft? with |
| | .some tactic, .none, .none, .none, .none, .none => do |
| pure $ Except.ok $ β goalState.tryTactic site tactic |
| | .none, .some mode, .none, .none, .none, .none => match mode with |
| | "tactic" => do -- Exit from the current fragment |
| pure $ Except.ok $ β goalState.fragmentExit site |
| | "conv" => do |
| pure $ Except.ok $ β goalState.convEnter site |
| | "calc" => do |
| pure $ Except.ok $ β goalState.calcEnter site |
| | _ => pure $ .error $ .errorCommand s!"Invalid mode {mode}" |
| | .none, .none, .some expr, .none, .none, .none => do |
| pure $ Except.ok $ β goalState.tryAssign site expr |
| | .none, .none, .none, .some type, .none, .none => do |
| let binderName := args.binderName?.getD .anonymous |
| pure $ Except.ok $ β goalState.tryHave site binderName type |
| | .none, .none, .none, .none, .some type, .none => do |
| let binderName := args.binderName?.getD .anonymous |
| pure $ Except.ok $ β goalState.tryLet site binderName type |
| | .none, .none, .none, .none, .none, .some draft => do |
| pure $ Except.ok $ β goalState.tryDraft site draft |
| | _, _, _, _, _, _ => |
| pure $ .error $ .errorCommand |
| "Exactly one of {tactic, mode, expr, have, let, draft} must be supplied" |
| match nextGoalState? with |
| | .error error => Protocol.throw error |
| | .ok (.success nextGoalState messages) => do |
| let env β getEnv |
| let nextStateId β newGoalState nextGoalState |
| let parentExprs := nextGoalState.parentExprs |
| let hasSorry := parentExprs.any Ξ» |
| | .ok e => e.hasSorry |
| | .error _ => false |
| let hasUnsafe := parentExprs.any Ξ» |
| | .ok e => env.hasUnsafe e |
| | .error _ => false |
| let goals β runCoreM $ nextGoalState.serializeGoals (options := state.options) |>.run' |
| let messages β messages.mapM (Β·.serialize) |
| return { |
| nextStateId? := .some nextStateId, |
| goals? := .some goals, |
| messages? := .some messages, |
| hasSorry, |
| hasUnsafe, |
| } |
| | .ok (.parseError message) => |
| return { messages? := .none, parseError? := .some message } |
| | .ok (.invalidAction message) => |
| Protocol.throw $ errorI "invalid" message |
| | .ok (.failure messages) => |
| let messages β messages.mapM (Β·.serialize) |
| return { messages? := .some messages } |
|
|
| end Goal |
|
|
| section Frontend |
|
|
| def frontend_distil (args : Protocol.FrontendDistil) : EMainM Protocol.FrontendDistilResult := do |
| let config := { |
| binderName? := args.binderName?, |
| ignoreValues := args.ignoreValues, |
| } |
| let targets β Frontend.distilSearchTargets (β getEnv) args.file config |
| let targets β targets.mapM Ξ» _dst@{ goalState } => do |
| let stateId β newGoalState goalState |
| let goals β runCoreM $ goalState.serializeGoals (options := (β get).options) |>.run' |
| return { stateId, goals } |
| return { targets } |
| |
| structure CompilationUnit where |
| -- Environment immediately before the unit |
| env : Environment |
| boundary : Nat Γ Nat |
| invocations : List Protocol.InvokedTactic |
| messages : Array SerialMessage |
| newConstants : NameSet |
| |
| export Frontend (defaultFileName) |
| |
| def frontend_process (args : Protocol.FrontendProcess) : EMainM Protocol.FrontendProcessResult := do |
| let (fileName, file) β match args.fileName?, args.file? with |
| | .some fileName, .none => do |
| let file β IO.FS.readFile fileName |
| pure (fileName, file) |
| | .none, .some file => |
| pure (defaultFileName, file) |
| | _, _ => Protocol.throw $ .errorCommand "Exactly one of {fileName, file} must be supplied" |
| let env?: Option Environment β if args.readHeader then |
| pure .none |
| else do |
| .some <$> getEnv |
| let (context, state) β do Frontend.createContextStateFromFile file fileName env? {} |
| let frontendM: Frontend.FrontendM (List CompilationUnit) := |
| Frontend.mapCompilationSteps Ξ» step => do |
| let boundary := (step.src.startPos.byteIdx, step.src.stopPos.byteIdx) |
| let invocations: Option (List Protocol.InvokedTactic) β if args.invocations?.isSome then |
| Frontend.collectTacticsFromCompilationStep step |
| else |
| pure [] |
| let messages β step.msgs.toArray.mapM (Β·.serialize) |
| let newConstants β if args.newConstants then |
| step.newConstants |
| else |
| pure .empty |
| return { |
| env := step.before, |
| boundary, |
| invocations, |
| messages, |
| newConstants |
| } |
| let cancelTk? β match (β get).options.timeout with |
| | 0 => pure .none |
| | timeout => .some <$> spawnCancelToken (timeout := .ofBitVec timeout) |
| let (li, state') β frontendM.run { cancelTk? } |>.run context |>.run state |
| if args.inheritEnv then |
| setEnv state'.commandState.env |
| if let .some scope := state'.commandState.scopes.head? then |
| |
| set { β getMainState with scope } |
| if let .some fileName := args.invocations? then |
| let units := li.map Ξ» unit => { invocations? := .some unit.invocations } |
| let data : Protocol.FrontendData := { units } |
| IO.FS.writeFile fileName (toJson data |>.compress) |
| let units β li.mapM Ξ» step => withEnv step.env do |
| let newConstants? := if args.newConstants then |
| .some $ step.newConstants.toArray |
| else |
| .none |
| let nInvocations? := if args.invocations?.isSome then .some step.invocations.length else .none |
| return { |
| boundary := step.boundary, |
| messages := step.messages, |
| nInvocations?, |
| newConstants?, |
| } |
| return { units } |
|
|
| end Frontend |
|
|
| / |
| def execute (command : Protocol.Command) : MainM Json := do |
| let run { Ξ± Ξ² } [FromJson Ξ±] [ToJson Ξ²] (comm : Ξ± β EMainM Ξ²) : MainM Json := do |
| let args β match fromJson? command.payload with |
| | .ok args => pure args |
| | .error error => return toJson $ Protocol.InteractionError.errorCommand s!"Unable to parse json: {error}" |
| try |
| let (out, result) β IO.FS.withIsolatedStreams (isolateStderr := false) do |
| commitIfNoEx $ comm args |
| if !out.isEmpty then |
| IO.eprint s!"stdout: {out}" |
| match result with |
| | .ok result => return toJson result |
| | .error ierror => return toJson ierror |
| catch ex : IO.Error => |
| let error : Protocol.InteractionError := .errorIO ex.toString |
| return toJson error |
| match command.cmd with |
| | "reset" => run reset |
| | "stat" => run stat |
| | "options.set" => run options_set |
| | "options.print" => run options_print |
| | "expr.echo" => run expr_echo |
| | "env.describe" => run env_describe |
| | "env.module_read" => run env_module_read |
| | "env.catalog" => run env_catalog |
| | "env.inspect" => run env_inspect |
| | "env.add" => run env_add |
| | "env.save" => run env_save |
| | "env.load" => run env_load |
| | "env.parse" => run env_parse |
| | "goal.start" => run goal_start |
| | "goal.tactic" => run goal_tactic |
| | "goal.continue" => run goal_continue |
| | "goal.subsume" => run goal_subsume |
| | "goal.delete" => run goal_delete |
| | "goal.print" => run goal_print |
| | "goal.save" => run goal_save |
| | "goal.load" => run goal_load |
| | "frontend.process" => run frontend_process |
| | "frontend.distil" => run frontend_distil |
| | "frontend.track" => run frontend_track |
| | "frontend.refactor" => run frontend_refactor |
| | cmd => |
| let error: Protocol.InteractionError := |
| .errorCommand s!"Unknown command {cmd}" |
| return toJson error |
| where |
| |
| reset (_: Protocol.Reset): EMainM Protocol.StatResult := do |
| let state β getMainState |
| let nGoals := state.goalStates.size |
| set { state with nextId := 0, goalStates := .emptyWithCapacity } |
| return { nGoals } |
| stat (_: Protocol.Stat): EMainM Protocol.StatResult := do |
| let state β getMainState |
| let nGoals := state.goalStates.size |
| return { nGoals } |
| options_set (args: Protocol.OptionsSet): EMainM Protocol.OptionsSetResult := do |
| let state β getMainState |
| let options := state.options |
| set { state with |
| options := { |
| |
| printJsonPretty := args.printJsonPretty?.getD options.printJsonPretty, |
| printExprPretty := args.printExprPretty?.getD options.printExprPretty, |
| printExprAST := args.printExprAST?.getD options.printExprAST, |
| printDependentMVars := args.printDependentMVars?.getD options.printDependentMVars, |
| noRepeat := args.noRepeat?.getD options.noRepeat, |
| printAuxDecls := args.printAuxDecls?.getD options.printAuxDecls, |
| printImplementationDetailHyps := args.printImplementationDetailHyps?.getD options.printImplementationDetailHyps |
| automaticMode := args.automaticMode?.getD options.automaticMode, |
| timeout := args.timeout?.getD options.timeout, |
| } |
| } |
| return { } |
| options_print (_: Protocol.OptionsPrint): EMainM Protocol.Options := do |
| return (β getMainState).options |
| env_save (args: Protocol.EnvSaveLoad): EMainM Protocol.EnvSaveLoadResult := do |
| let env β MonadEnv.getEnv |
| environmentPickle env args.path |
| return {} |
| env_load (args: Protocol.EnvSaveLoad): EMainM Protocol.EnvSaveLoadResult := do |
| let (env, _) β environmentUnpickle args.path |
| setEnv env |
| return {} |
| expr_echo (args: Protocol.ExprEcho): EMainM Protocol.ExprEchoResult := do |
| let state β getMainState |
| let levelNames := (args.levels?.getD #[]).toList |
| liftExcept $ β liftTermElabM (levelNames := levelNames) do |
| (exprEcho args.expr (expectedType? := args.type?) (options := state.options)).run |
| env_parse (args : Protocol.EnvParse) : EMainM Protocol.EnvParseResult := do |
| let category := args.category |
| match runParserCategory' (β getEnv) category args.input with |
| | .ok (_, p) => return { pos := p.byteIdx } |
| | .error desc => throw $ .errorParse desc |
| goal_start (args: Protocol.GoalStart): EMainM Protocol.GoalStartResult := do |
| let levelNames := (args.levels?.getD #[]).toList |
| let expr?: Except _ GoalState β liftTermElabM (levelNames := levelNames) do |
| match args.expr, args.copyFrom with |
| | .some expr, .none => goalStartExpr expr |>.run |
| | .none, .some copyFrom => do |
| (match (β getEnv).find? copyFrom with |
| | .none => return .error <| .errorIndex s!"Symbol not found: {copyFrom}" |
| | .some cInfo => return .ok (β GoalState.create cInfo.type)) |
| | _, _ => |
| return .error <| .errorCommand "Exactly one of {expr, copyFrom} must be supplied" |
| match expr? with |
| | .error error => Protocol.throw error |
| | .ok goalState => |
| let stateId β newGoalState goalState |
| return { stateId, root := goalState.root.name } |
| goal_continue (args: Protocol.GoalContinue): EMainM Protocol.GoalContinueResult := do |
| let state β getMainState |
| let .some target := state.goalStates[args.target]? |
| | throw $ .errorIndex s!"Invalid state index {args.target}" |
| let nextGoalState? : GoalState β match args.branch?, args.goals? with |
| | .some branchId, .none => do |
| match state.goalStates[branchId]? with |
| | .none => Protocol.throw $ .errorIndex s!"Invalid state index {branchId}" |
| | .some branch => pure $ target.continue branch |
| | .none, .some goals => |
| let goals := goals.toList.map (β¨Β·β©) |
| pure $ target.resume goals |
| | _, _ => Protocol.throw $ .errorCommand "Exactly one of {branch, goals} must be supplied" |
| match nextGoalState? with |
| | .error error => Protocol.throw $ errorI "structure" error |
| | .ok nextGoalState => |
| let nextStateId β newGoalState nextGoalState |
| let goals β liftMetaM $ goalSerialize nextGoalState (options := state.options) |
| return { |
| nextStateId, |
| goals, |
| } |
| goal_subsume (args : Protocol.GoalSubsume) : EMainM Protocol.GoalSubsumeResult := do |
| let state β getMainState |
| let .some goalState := state.goalStates[args.stateId]? |
| | throw $ .errorIndex s!"Invalid state index {args.stateId}" |
| let srcGoalState? β match args.srcStateId? with |
| | .some id => do |
| let .some srcGoalState := state.goalStates[args.stateId]? |
| | throw $ .errorIndex s!"Invalid src state index {id}" |
| pure $ .some srcGoalState |
| | .none => pure .none |
| let goal := β¨args.goalβ© |
| let candidates := args.candidates.map (β¨Β·β©) |
| let (result, nextGoalState?, subsumptor?) β runCoreM do |
| goalState.subsume goal candidates srcGoalState? |
| let stateId? β nextGoalState?.mapM (newGoalState Β·) |
| let subsumptor? := subsumptor?.map (Β·.name) |
| return { result, stateId?, subsumptor? } |
| goal_delete (args: Protocol.GoalDelete): EMainM Protocol.GoalDeleteResult := do |
| let state β getMainState |
| let goalStates := args.stateIds.foldl (Ξ» map id => map.erase id) state.goalStates |
| set { state with goalStates } |
| return {} |
| goal_print (args: Protocol.GoalPrint): EMainM Protocol.GoalPrintResult := do |
| let state β getMainState |
| let .some goalState := state.goalStates[args.stateId]? | |
| Protocol.throw $ .errorIndex s!"Invalid state index {args.stateId}" |
| let result β liftMetaM <| goalPrint |
| goalState |
| (rootExpr := args.rootExpr?.getD False) |
| (parentExprs := args.parentExprs?.getD False) |
| (goals := args.goals?.getD False) |
| (extraMVars := args.extraMVars?.getD #[]) |
| (options := state.options) |
| return result |
| goal_save (args: Protocol.GoalSave): EMainM Protocol.GoalSaveResult := do |
| let state β getMainState |
| let .some goalState := state.goalStates[args.id]? | |
| Protocol.throw $ .errorIndex s!"Invalid state index {args.id}" |
| goalStatePickle goalState args.path (background? := .some $ β getEnv) |
| return {} |
| goal_load (args: Protocol.GoalLoad): EMainM Protocol.GoalLoadResult := do |
| let (goalState, _) β goalStateUnpickle args.path (background? := .some $ β getEnv) |
| let id β newGoalState goalState |
| return { id } |
| frontend_track (args : Protocol.FrontendTrack) : EMainM Protocol.FrontendTrackResult := do |
| let env β getEnv |
| let collectOne (source : String) : IO _ := do |
| let (context, state) β do Frontend.createContextStateFromFile source (env? := env) |
| let m := Frontend.collectEndState |
| m.run { } |>.run context |>.run' state |
| let srcState β collectOne args.src |
| let dstState β collectOne args.dst |
| let srcMessages β srcState.messages.toArray.mapM (Β·.serialize) |
| let dstMessages β dstState.messages.toArray.mapM (Β·.serialize) |
| if srcMessages.any (Β·.severity ==.error) β¨ dstMessages.any (Β·.severity ==.error) then |
| return { srcMessages, dstMessages } |
| let result? β show IO _ from ExceptT.run do |
| checkEnvConflicts env srcState.env dstState.env |
| match result? with |
| | .error e => return { failure? := .some e } |
| | .ok _ => return {} |
| frontend_refactor (args : Protocol.FrontendRefactor) : EMainM Protocol.FrontendRefactorResult := do |
| try |
| let coreOptions? β show IO _ from ExceptT.run $ args.coreOptions.foldlM (init := {}) Ξ» acc opt => |
| setOptionFromString' acc opt |
| match coreOptions? with |
| | .ok coreOptions => do |
| let file β Frontend.runRefactor (β getEnv) args.file { coreOptions } |
| return { file } |
| | .error e => |
| throw $ .errorParse e |
| catch ex : IO.Error => |
| let error : Protocol.InteractionError := { error := "frontend", desc := ex.toString } |
| throw error |
| |