File size: 7,986 Bytes
0115dcf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | import Pantograph.Environment
import Pantograph.Goal
import Pantograph.Protocol
import Pantograph.Delate
namespace Pantograph
open Lean
/-- This is better than the default version since it handles `.` and doesn't
crash the program when it fails. -/
def setOptionFromString' (opts : Options) (entry : String) : ExceptT String IO Options := do
let ps := (entry.splitOn "=").map String.trim
let [key, val] β pure ps | throw "invalid configuration option entry, it must be of the form '<key> = <value>'"
let key := key.toName
let defValue β getOptionDefaultValue key
match defValue with
| DataValue.ofString _ => pure $ opts.setString key val
| DataValue.ofBool _ =>
match val with
| "true" => pure $ opts.setBool key true
| "false" => pure $ opts.setBool key false
| _ => throw s!"invalid Bool option value '{val}'"
| DataValue.ofName _ => pure $ opts.setName key val.toName
| DataValue.ofNat _ =>
match val.toNat? with
| none => throw s!"invalid Nat option value '{val}'"
| some v => pure $ opts.setNat key v
| DataValue.ofInt _ =>
match val.toInt? with
| none => throw s!"invalid Int option value '{val}'"
| some v => pure $ opts.setInt key v
| DataValue.ofSyntax _ => throw s!"invalid Syntax option value"
def runMetaM { Ξ± } (metaM: MetaM Ξ±): CoreM Ξ± :=
metaM.run'
def errorI (type desc: String): Protocol.InteractionError := { error := type, desc := desc }
/-- Adds the given paths to Lean package search path. This must run with at
least an empty string, otherwise Lean will not be able to find any symbols -/
@[export pantograph_init_search]
unsafe def initSearch (sp: String := ""): IO Unit := do
Lean.enableInitializersExecution
Lean.initSearchPath (β Lean.findSysroot) (sp := System.SearchPath.parse sp)
/-- Creates a Core.Context object needed to run all monads -/
@[export pantograph_create_core_context]
def createCoreContext (options : Array String) : IO Core.Context := do
let options? β options.foldlM setOptionFromString' Options.empty |>.run
let options β match options? with
| .ok options => pure options
| .error e => throw $ IO.userError s!"Options cannot be parsed: {e}"
return {
currNamespace := `Cirno,
openDecls := [], -- No 'open' directives needed
fileName := "<Pantograph>",
fileMap := { source := "", positions := #[0] },
options,
maxRecDepth := maxRecDepth.get options,
}
/-- Creates a Core.State object needed to run all monads -/
@[export pantograph_create_core_state]
def createCoreState (env : Environment) : IO Core.State := do
return { env }
@[export pantograph_parse_elab_type_m]
def parseElabType (type : String) : Protocol.FallibleT Elab.TermElabM Expr := do
let env β MonadEnv.getEnv
let syn β match parseTerm env type with
| .error str => Protocol.throw $ errorI "parse" str
| .ok syn => pure syn
match β elabType syn with
| .error str => Protocol.throw $ errorI "elab" str
| .ok expr => return (β instantiateMVars expr)
/-- This must be a TermElabM since the parsed expr contains extra information -/
@[export pantograph_parse_elab_expr_m]
def parseElabExpr (expr : String) (expectedType? : Option String := .none) : Protocol.FallibleT Elab.TermElabM Expr := do
let env β MonadEnv.getEnv
let expectedType? β expectedType?.mapM parseElabType
let syn β match parseTerm env expr with
| .error str => Protocol.throw $ errorI "parse" str
| .ok syn => pure syn
match β elabTerm syn expectedType? with
| .error str => Protocol.throw $ errorI "elab" str
| .ok expr => return (β instantiateMVars expr)
@[export pantograph_expr_echo_m]
def exprEcho (expr: String) (expectedType?: Option String := .none) (options: @&Protocol.Options := {}):
Protocol.FallibleT Elab.TermElabM Protocol.ExprEchoResult := do
let expr β parseElabExpr expr expectedType?
try
let type β unfoldAuxLemmas (β Meta.inferType expr)
return {
type := (β serializeExpression options type),
expr := (β serializeExpression options expr),
}
catch exception =>
Protocol.throw $ errorI "typing" (β exception.toMessageData.toString)
@[export pantograph_goal_start_expr_m]
def goalStartExpr (expr: String) : Protocol.FallibleT Elab.TermElabM GoalState := do
let t β parseElabType expr
Elab.Term.synthesizeSyntheticMVarsUsingDefault
GoalState.create t
@[export pantograph_goal_serialize_m]
def goalSerialize (state: GoalState) (options: @&Protocol.Options): CoreM (Array Protocol.Goal) :=
runMetaM <| state.serializeGoals options
@[export pantograph_goal_print_m]
def goalPrint (state: GoalState) (rootExpr: Bool) (parentExprs: Bool)
(goals: Bool) (extraMVars : Array Name) (options: @&Protocol.Options)
: CoreM Protocol.GoalPrintResult := runMetaM do
state.restoreMetaM
let rootExpr? := state.rootExpr?
let root? β if rootExpr then
rootExpr?.mapM Ξ» expr => state.withRootContext do
serializeExpression options (β instantiateAll expr)
else
pure .none
let parentExprs? β if parentExprs then
.some <$> state.parentMVars.mapM Ξ» parent => parent.withContext do
let val? := state.getMVarEAssignment parent
val?.mapM Ξ» val => do
serializeExpression options (β instantiateAll val)
else
pure .none
let goals β if goals then
goalSerialize state options
else
pure #[]
let extraMVars β extraMVars.mapM Ξ» name => do
let mvarId: MVarId := β¨nameβ©
let .some _ β mvarId.findDecl? | return {}
state.withContext mvarId do
let .some expr β getExprMVarAssignment? mvarId | return {}
serializeExpression options (β instantiateAll expr)
let env β getEnv
return {
root?,
parentExprs?,
goals,
extraMVars,
rootHasSorry := rootExpr?.map (Β·.hasSorry) |>.getD false,
rootHasUnsafe := rootExpr?.map (env.hasUnsafe Β·) |>.getD false,
rootHasMVar := rootExpr?.map (Β·.hasExprMVar) |>.getD false,
}
@[export pantograph_goal_have_m]
protected def GoalState.tryHave (state: GoalState) (site : Site) (binderName: Name) (type: String): Elab.TermElabM TacticResult := do
let type β match (β parseTermM type) with
| .ok syn => pure syn
| .error error => return .parseError error
state.restoreElabM
state.tryTacticM site $ Tactic.evalHave binderName type
protected def GoalState.tryLet (state : GoalState) (site : Site) (binderName : Name) (type : String)
: Elab.TermElabM TacticResult := do
state.restoreElabM
let type β match Parser.runParserCategory
(env := β MonadEnv.getEnv)
(catName := `term)
(input := type)
(fileName := β getFileName) with
| .ok syn => pure syn
| .error error => return .parseError error
state.tryTacticM site $ Tactic.evalLet binderName type
@[export pantograph_goal_try_define_m]
protected def GoalState.tryDefine (state: GoalState) (site : Site) (binderName: Name) (expr: String): Elab.TermElabM TacticResult := do
let expr β match (β parseTermM expr) with
| .ok syn => pure syn
| .error error => return .parseError error
state.restoreElabM
state.tryTacticM site $ Tactic.evalDefine binderName expr
@[export pantograph_goal_try_draft_m]
protected def GoalState.tryDraft (state: GoalState) (site : Site) (expr: String): Elab.TermElabM TacticResult := do
let expr β match (β parseTermM expr) with
| .ok syn => pure syn
| .error error => return .parseError error
state.restoreElabM
state.tryTacticM site $ Tactic.evalDraft expr
-- Cancel the token after a timeout.
@[export pantograph_run_cancel_token_with_timeout_m]
def runCancelTokenWithTimeout (cancelToken : IO.CancelToken) (timeout : UInt32) : IO Unit := do
let _ β EIO.asTask do
IO.sleep timeout
cancelToken.set
return ()
def spawnCancelToken (timeout : UInt32) : IO IO.CancelToken := do
let token β IO.CancelToken.new
runCancelTokenWithTimeout token timeout
return token
|