add defeq-hints #45

pull/251/merge
Jon Eugster 9 months ago
parent c735211cd8
commit a7d746a8e5

@ -4,7 +4,7 @@ import GameServer.Options
import GameServer.SaveData import GameServer.SaveData
import GameServer.Hints import GameServer.Hints
import GameServer.Tactic.LetIntros import GameServer.Tactic.LetIntros
import GameServer.RpcHandlers -- only needed to collect the translations of "level completed" msgs -- import GameServer.RpcHandlers -- only needed to collect the translations of "level completed" msgs
import I18n import I18n
open Lean Meta Elab Command open Lean Meta Elab Command
@ -437,6 +437,7 @@ elab doc:docComment ? attrs:Parser.Term.attributes ?
.tagged `Hint $ .tagged `Hint $
.nest strict $ .nest strict $
.nest hidden $ .nest hidden $
.nest defeq $
.compose (.ofGoal text) (.ofGoal goal) := msg.data then .compose (.ofGoal text) (.ofGoal goal) := msg.data then
let hint ← liftTermElabM $ withMCtx ctx.mctx $ withLCtx ctx.lctx #[] $ withEnv ctx.env do let hint ← liftTermElabM $ withMCtx ctx.mctx $ withLCtx ctx.lctx #[] $ withEnv ctx.env do
@ -467,6 +468,7 @@ elab doc:docComment ? attrs:Parser.Term.attributes ?
rawText := rawText rawText := rawText
strict := strict == 1 strict := strict == 1
hidden := hidden == 1 hidden := hidden == 1
defeq := defeq == 1
} }
-- Note: The current setup for hints is a bit convoluted, but for now we need to -- Note: The current setup for hints is a bit convoluted, but for now we need to
@ -522,6 +524,7 @@ see hints. The tactic does not affect the goal state.
elab (name := GameServer.Tactic.Hint) "Hint" args:hintArg* msg:interpolatedStr(term) : tactic => do elab (name := GameServer.Tactic.Hint) "Hint" args:hintArg* msg:interpolatedStr(term) : tactic => do
let mut strict := false let mut strict := false
let mut hidden := false let mut hidden := false
let mut defeq := false
-- remove spaces at the beginning of new lines -- remove spaces at the beginning of new lines
let msg := TSyntax.mk $ msg.raw.setArgs $ ← msg.raw.getArgs.mapM fun m => do let msg := TSyntax.mk $ msg.raw.setArgs $ ← msg.raw.getArgs.mapM fun m => do
@ -543,6 +546,8 @@ elab (name := GameServer.Tactic.Hint) "Hint" args:hintArg* msg:interpolatedStr(t
| `(hintArg| (strict := false)) => strict := false | `(hintArg| (strict := false)) => strict := false
| `(hintArg| (hidden := true)) => hidden := true | `(hintArg| (hidden := true)) => hidden := true
| `(hintArg| (hidden := false)) => hidden := false | `(hintArg| (hidden := false)) => hidden := false
| `(hintArg| (defeq := true)) => defeq := true
| `(hintArg| (defeq := false)) => defeq := false
| _ => throwUnsupportedSyntax | _ => throwUnsupportedSyntax
let goal ← Tactic.getMainGoal let goal ← Tactic.getMainGoal
@ -566,6 +571,7 @@ elab (name := GameServer.Tactic.Hint) "Hint" args:hintArg* msg:interpolatedStr(t
.tagged `Hint $ .tagged `Hint $
.nest (if strict then 1 else 0) $ .nest (if strict then 1 else 0) $
.nest (if hidden then 1 else 0) $ .nest (if hidden then 1 else 0) $
.nest (if defeq then 1 else 0) $
.compose (.ofGoal textmvar.mvarId!) (.ofGoal goal) .compose (.ofGoal textmvar.mvarId!) (.ofGoal goal)
/-- This tactic allows us to execute an alternative sequence of tactics, but without affecting the /-- This tactic allows us to execute an alternative sequence of tactics, but without affecting the

@ -8,7 +8,7 @@ open Lean Meta Elab
namespace GameServer namespace GameServer
syntax hintArg := atomic(" (" (&"strict" <|> &"hidden") " := " withoutPosition(term) ")") syntax hintArg := atomic(" (" (&"strict" <|> &"hidden" <|> &"defeq") " := " withoutPosition(term) ")")
/-- A hint to help the user with a specific goal state -/ /-- A hint to help the user with a specific goal state -/
structure GoalHintEntry where structure GoalHintEntry where
@ -20,6 +20,7 @@ structure GoalHintEntry where
hidden : Bool := false hidden : Bool := false
/-- If true, then the goal must contain only the assumptions specified in `goal` and no others -/ /-- If true, then the goal must contain only the assumptions specified in `goal` and no others -/
strict : Bool := false strict : Bool := false
defeq : Bool := false
instance : Repr GoalHintEntry := { instance : Repr GoalHintEntry := {
reprPrec := fun a n => reprPrec a.text n reprPrec := fun a n => reprPrec a.text n

@ -79,7 +79,9 @@ partial def matchExpr (pattern : Expr) (e : Expr) (bij : FVarBijection := {}) :
| _, _ => none | _, _ => none
/-- Check if each fvar in `patterns` has a matching fvar in `fvars` -/ /-- Check if each fvar in `patterns` has a matching fvar in `fvars` -/
def matchDecls (patterns : Array Expr) (fvars : Array Expr) (strict := true) (initBij : FVarBijection := {}) : MetaM (Option FVarBijection) := do def matchDecls (patterns : Array Expr) (fvars : Array Expr) (strict := true)
(initBij : FVarBijection := {}) (defeq := false) : MetaM (Option FVarBijection) := do
let reducer := if defeq then whnf else pure
-- We iterate through the array backwards hoping that this will find us faster results -- We iterate through the array backwards hoping that this will find us faster results
-- TODO: implement backtracking -- TODO: implement backtracking
let mut bij := initBij let mut bij := initBij
@ -93,8 +95,8 @@ def matchDecls (patterns : Array Expr) (fvars : Array Expr) (strict := true) (in
continue continue
if let some bij' := matchExpr if let some bij' := matchExpr
(← instantiateMVars $ ← inferType pattern) (← reducer <| ← instantiateMVars $ ← inferType pattern)
(← instantiateMVars $ ← inferType fvar) bij then (← reducer <| ← instantiateMVars $ ← inferType fvar) bij then
-- usedFvars := usedFvars.set! (fvars.size - j - 1) true -- usedFvars := usedFvars.set! (fvars.size - j - 1) true
bij := bij'.insert pattern.fvarId! fvar.fvarId! bij := bij'.insert pattern.fvarId! fvar.fvarId!
break break
@ -112,7 +114,10 @@ def findHints (goal : MVarId) (m : DocumentMeta) (initParams : Lsp.InitializePar
| throwError "Level not found: {m.mkInputContext.fileName}" | throwError "Level not found: {m.mkInputContext.fileName}"
let hints ← level.hints.filterMapM fun hint => do let hints ← level.hints.filterMapM fun hint => do
openAbstractCtxResult hint.goal fun hintFVars hintGoal => do openAbstractCtxResult hint.goal fun hintFVars hintGoal => do
if let some fvarBij := matchExpr (← instantiateMVars $ hintGoal) (← instantiateMVars $ ← inferType $ mkMVar goal) let reducer := if hint.defeq then whnf else pure
if let some fvarBij := matchExpr
(← reducer <| ← instantiateMVars $ hintGoal)
(← reducer <| ← instantiateMVars $ ← inferType $ mkMVar goal)
then then
-- NOTE: This code for `hintFVarsNames` is also duplicated in the -- NOTE: This code for `hintFVarsNames` is also duplicated in the
@ -127,7 +132,7 @@ def findHints (goal : MVarId) (m : DocumentMeta) (initParams : Lsp.InitializePar
let lctx := (← goal.getDecl).lctx -- the player's local context let lctx := (← goal.getDecl).lctx -- the player's local context
if let some bij ← matchDecls hintFVars lctx.getFVars if let some bij ← matchDecls hintFVars lctx.getFVars
(strict := hint.strict) (initBij := fvarBij) (strict := hint.strict) (initBij := fvarBij) (defeq := hint.defeq)
then then
let userFVars := hintFVars.map fun v => bij.forward.findD v.fvarId! v.fvarId! let userFVars := hintFVars.map fun v => bij.forward.findD v.fvarId! v.fvarId!
-- Evaluate the text in the player's context to get the new variable names. -- Evaluate the text in the player's context to get the new variable names.

Loading…
Cancel
Save