Zulip Chat Archive

Stream: lean4

Topic: Parsing expression in the context of a goal


Leni Aniva (Apr 06 2024 at 23:27):

Programatically, how can I parse an expression that refers to variables in the local context? For example, suppose the goal (with a TermElabM context and MVarId looks like this:

p q : Prop
h : p
 (p  q)  p  q

and I want to type the expression Or.inl h. It seems like its not sufficient to restore the TermElabM context and then run Lean.Elab.Term.elabTerm:

state.term.restore
let expr <- Elab.Term.elabTerm stx
...

Below is a MWE showing what I want to do. The program crashes when the expression Or.inl h is elaborated.

import Lean

open Lean

def filename := "<example>"

structure Context where
  env: Environment
  coreContext : Core.Context := {
    currNamespace := Name.anonymous,
    openDecls := [],
    fileName := "<stdin>",
    fileMap := { source := "", positions := #[0] }
  }
  elabContext : Elab.Term.Context := {
    declName? := .none,
    errToSorry := false
  }

abbrev M (m: Type  Type) := ReaderT Context m

def parse_expr (s: String): Elab.TermElabM Lean.Expr := do
  let syn := Parser.runParserCategory
    (env :=  MonadEnv.getEnv)
    (catName := `term)
    (input := s)
    (fileName := filename) |>.toOption |>.get!
  let expr  Elab.Term.elabType syn
  Elab.Term.synthesizeSyntheticMVarsNoPostponing
  let expr  instantiateMVars expr
  return expr

def start_tactic_state (expr: Expr): Elab.TermElabM Elab.Tactic.SavedState := do
  let mvar  Meta.mkFreshExprMVar (some expr) (kind := .synthetic)
  let termState : Elab.Term.SavedState  Elab.Term.saveState
  let tacticState : Elab.Tactic.SavedState := { term := termState, tactic := { goals := [mvar.mvarId!] }}
  IO.println "Tactic state started"
  return tacticState

def execute_tactic (state: Elab.Tactic.SavedState) (goalId: Nat) (tactic: String):
  Elab.TermElabM Elab.Tactic.SavedState := do
  let stx := Parser.runParserCategory
    (env :=  MonadEnv.getEnv)
    (catName := `tactic)
    (input := tactic)
    (fileName := filename) |>.toOption |>.get!
  state.term.restore
  let tac : Elab.Tactic.TacticM Unit := set state.tactic *> Elab.Tactic.evalTactic stx
  let mvarId := state.tactic.goals.get! goalId
  Elab.Term.synthesizeSyntheticMVarsNoPostponing
  let unsolvedGoals  Elab.Tactic.run mvarId tac
  if ( getThe Core.State).messages.hasErrors then
    let messages := ( getThe Core.State).messages |>.toList.toArray
    let errors  (messages.map Message.data).mapM fun md => md.toString
    IO.println s!"{errors}"
    return state
  else
    unsolvedGoals.forM instantiateMVarDeclMVars
    let nextState : Elab.Tactic.SavedState := {
      term := ( Elab.Term.saveState),
      tactic := { goals := unsolvedGoals }
    }
    let goals  unsolvedGoals.mapM fun g => do pure $ toString ( Meta.ppGoal g)
    IO.println s!"Tactic '{tactic}' succeeded."
    IO.println s!"{goals}"
    return nextState
def try_assign (state: Elab.Tactic.SavedState) (goalId: Nat) (expr: String):
  Elab.TermElabM Elab.Tactic.SavedState := do
  state.term.restore
  let mvarId := state.tactic.goals.get! goalId

  let expr := Parser.runParserCategory
    (env :=  MonadEnv.getEnv)
    (catName := `term)
    (input := expr)
    (fileName := filename) |>.toOption |>.get!
  let goalType  mvarId.getType
  let expr  Elab.Term.elabTerm (stx := expr) (expectedType? := .some goalType)

  mvarId.checkNotAssigned `try_assign
  mvarId.assign expr

  if ( getThe Lean.Core.State).messages.hasErrors then
    let messages := ( getThe Lean.Core.State).messages |>.toList.toArray
    let errors  (messages.map Lean.Message.data).mapM fun md => md.toString
    IO.println s!"{errors}"
    return state

  let prevMCtx := state.term.meta.meta.mctx
  let nextMCtx  getMCtx
  -- Generate a list of mvarIds that exist in the parent state; Also test the
  -- assertion that the types have not changed on any mvars.
  let newMVars  nextMCtx.decls.foldlM (fun acc mvarId mvarDecl => do
    if let .some prevMVarDecl := prevMCtx.decls.find? mvarId then
      assert! prevMVarDecl.type == mvarDecl.type
      return acc
    else
      return mvarId :: acc
    ) []
  let nextGoals  newMVars.filterM (λ mvar => do pure !( mvar.isAssigned))
  let nextState : Lean.Elab.Tactic.SavedState := {
    term := ( Lean.Elab.Term.saveState),
    tactic := { goals := nextGoals }
  }
  return nextState

def execute_term_elab (termElabM: Lean.Elab.TermElabM α): M IO (α × Lean.Core.State) := do
  let context  read
  let metaM : Lean.MetaM α := termElabM.run' (ctx := context.elabContext)
  let coreM : Lean.CoreM α := metaM.run'
  let coreState : Lean.Core.State := { env := context.env }
  coreM.toIO context.coreContext coreState

def proof: M IO Unit := do
  let context  read
  let (_, _)  execute_term_elab <| do
    let expr  parse_expr "∀ (p q: Prop), p → ((p ∨ q) ∨ (p ∨ q))"
    let state  start_tactic_state expr
    let state  execute_tactic state 0 "intro p q h"
    let branch  try_assign state 0 "Or.inl (Or.inl h)"
  IO.println "Completed"


def main := do
  let env: Lean.Environment  Lean.importModules
    (imports := #[`Init])
    (opts := {})
    (trustLevel := 1)
  proof |>.run { env := env }

The error is

unknown identifier 'h'

Leni Aniva (Apr 07 2024 at 03:22):

I found the solution: Lean.Meta.withLCtx


Last updated: May 02 2025 at 03:31 UTC