internally rename lemma to theorem #108

pull/251/merge
Jon Eugster 2 years ago
parent 17d2ba5a2c
commit 1f14ad185f

@ -76,7 +76,7 @@ function DualEditorMain({ worldId, levelId, level, worldSize }: { worldId: strin
// On completion, add the names of all new items to the local storage // On completion, add the names of all new items to the local storage
let newTiles = [ let newTiles = [
...level?.tactics, ...level?.tactics,
...level?.lemmas, ...level?.theorems,
...level?.definitions ...level?.definitions
].filter((tile) => tile.new).map((tile) => tile.name) ].filter((tile) => tile.new).map((tile) => tile.name)

@ -48,7 +48,7 @@ function InventoryItem({item, name, displayName, locked, disabled, newly, showDo
const icon = locked ? <FontAwesomeIcon icon={faLock} /> : const icon = locked ? <FontAwesomeIcon icon={faLock} /> :
disabled ? <FontAwesomeIcon icon={faBan} /> : <></> disabled ? <FontAwesomeIcon icon={faBan} /> : <></>
const className = locked ? "locked" : disabled ? "disabled" : newly ? "new" : "" const className = locked ? "locked" : disabled ? "disabled" : newly ? "new" : ""
// Note: This is somewhat a hack as the statement of lemmas comes currently in the form // Note: This is somewhat a hack as the statement of theorems comes currently in the form
// `Namespace.statement_name (x y : Nat) : some type` // `Namespace.statement_name (x y : Nat) : some type`
const title = locked ? t("Not unlocked yet") : const title = locked ? t("Not unlocked yet") :
disabled ? t("Not available in this level") : (item.altTitle ? item.altTitle.substring(item.altTitle.indexOf(' ') + 1) : '') disabled ? t("Not available in this level") : (item.altTitle ? item.altTitle.substring(item.altTitle.indexOf(' ') + 1) : '')
@ -172,12 +172,12 @@ export function Inventory () {
<div className="inventory"> <div className="inventory">
{ levelInfo.data ? <> { levelInfo.data ? <>
<div className="tab-bar major"> <div className="tab-bar major">
<div className={`tab${(categoryTab == "theorem") ? " active": ""}${containsNew(levelInfo.data?.lemmas) ? " new" : ""}`} onClick={() => { setCategoryTab("theorem") }}>{t("Theorems")}</div> <div className={`tab${(categoryTab == "theorem") ? " active": ""}${containsNew(levelInfo.data?.theorems) ? " new" : ""}`} onClick={() => { setCategoryTab("theorem") }}>{t("Theorems")}</div>
<div className={`tab${(categoryTab == "tactic") ? " active": ""}${containsNew(levelInfo.data?.tactics) ? " new" : ""}`} onClick={() => { setCategoryTab("tactic") }}>{t("Tactics")}</div> <div className={`tab${(categoryTab == "tactic") ? " active": ""}${containsNew(levelInfo.data?.tactics) ? " new" : ""}`} onClick={() => { setCategoryTab("tactic") }}>{t("Tactics")}</div>
<div className={`tab${(categoryTab == "definition") ? " active": ""}${containsNew(levelInfo.data?.definitions) ? " new" : ""}`} onClick={() => { setCategoryTab("definition") }}>{t("Definitions")}</div> <div className={`tab${(categoryTab == "definition") ? " active": ""}${containsNew(levelInfo.data?.definitions) ? " new" : ""}`} onClick={() => { setCategoryTab("definition") }}>{t("Definitions")}</div>
</div> </div>
{ (categoryTab == "theorem") && { (categoryTab == "theorem") &&
<InventoryList items={levelInfo.data?.lemmas} tab={theoremTab} setTab={setTheoremTab} /> <InventoryList items={levelInfo.data?.theorems} tab={theoremTab} setTab={setTheoremTab} />
} }
{ (categoryTab == "tactic") && { (categoryTab == "tactic") &&
<InventoryList items={levelInfo.data?.tactics} /> <InventoryList items={levelInfo.data?.tactics} />
@ -228,7 +228,7 @@ export function Documentation() {
</div> </div>
} }
/** The panel showing the user's inventory with tactics, definitions, and lemmas */ /** The panel showing the user's inventory with tactics, definitions, and theorems */
export function InventoryPanel({visible = true}) { export function InventoryPanel({visible = true}) {
const {gameId, worldId, levelId} = React.useContext(GameIdContext) const {gameId, worldId, levelId} = React.useContext(GameIdContext)
const levelInfo = useLoadLevelQuery({game: gameId, world: worldId, level: levelId}) const levelInfo = useLoadLevelQuery({game: gameId, world: worldId, level: levelId})
@ -240,10 +240,10 @@ export function InventoryPanel({visible = true}) {
const [docTile, setDocTile] = useState<InventoryTile>(null) const [docTile, setDocTile] = useState<InventoryTile>(null)
useEffect(() => { useEffect(() => {
// If the level specifies `LemmaTab "Nat"`, we switch to this tab on loading. // If the level specifies `TheoremTab "Nat"`, we switch to this tab on loading.
// `defaultTab` is `null` or `undefined` otherwise, in which case we don't want to switch. // `defaultTab` is `null` or `undefined` otherwise, in which case we don't want to switch.
if (levelInfo?.data?.lemmaTab) { if (levelInfo?.data?.theoremTab) {
setTheoremTab(levelInfo?.data?.lemmaTab) setTheoremTab(levelInfo?.data?.theoremTab)
}}, [levelInfo]) }}, [levelInfo])
return <div className={`column inventory-panel ${visible ? '' : 'hidden'}`}> return <div className={`column inventory-panel ${visible ? '' : 'hidden'}`}>

@ -47,11 +47,11 @@ export interface LevelInfo {
conclusion: null|string, conclusion: null|string,
index: number, index: number,
tactics: InventoryTile[], tactics: InventoryTile[],
lemmas: InventoryTile[], theorems: InventoryTile[],
definitions: InventoryTile[], definitions: InventoryTile[],
descrText: null|string, descrText: null|string,
descrFormat: null|string, descrFormat: null|string,
lemmaTab: null|string, theoremTab: null|string,
statementName: null|string, statementName: null|string,
displayName: null|string, displayName: null|string,
template: null|string, template: null|string,
@ -61,9 +61,9 @@ export interface LevelInfo {
/** Used to display the inventory on the welcome page */ /** Used to display the inventory on the welcome page */
export interface InventoryOverview { export interface InventoryOverview {
tactics: InventoryTile[], tactics: InventoryTile[],
lemmas: InventoryTile[], theorems: InventoryTile[],
definitions: InventoryTile[], definitions: InventoryTile[],
lemmaTab: null, theoremTab: null,
} }
interface Doc { interface Doc {

@ -48,9 +48,9 @@ only evaluated at the beginning of the proof.
## Attributes ## Attributes
You can add attributes as you would for a `theorem`. Most notably, you can make your named exercise a `simp` lemma: You can add attributes as you would for a `theorem`. Most notably, you can make your named exercise a `simp` theorem:
```lean ```lean
@[simp] @[simp]
Statement my_simp_lemma ... Statement my_simp_theorem ...
``` ```

@ -176,7 +176,7 @@ elab doc:docComment ? "TheoremDoc" name:ident "as" displayName:str "in" category
let doc ← parseDocCommentLegacy doc content let doc ← parseDocCommentLegacy doc content
let doc ← doc.translate let doc ← doc.translate
modifyEnv (inventoryTemplateExt.addEntry · { modifyEnv (inventoryTemplateExt.addEntry · {
type := .Lemma type := .Theorem
name := name.getId name := name.getId
category := category.getString category := category.getString
displayName := displayName.getString displayName := displayName.getString
@ -236,13 +236,13 @@ elab "NewHiddenTactic" args:ident* : command => do
/-- Declare theorems that are introduced by this level. -/ /-- Declare theorems that are introduced by this level. -/
elab "NewTheorem" args:ident* : command => do elab "NewTheorem" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).lemmas.new) "NewTheorem" checkCommandNotDuplicated ((←getCurLevel).theorems.new) "NewTheorem"
for name in ↑args do for name in ↑args do
try let _decl ← getConstInfo name.getId catch try let _decl ← getConstInfo name.getId catch
| _ => logErrorAt name m!"unknown identifier '{name}'." | _ => logErrorAt name m!"unknown identifier '{name}'."
checkInventoryDoc .Lemma name -- TODO: Add (template := "[mathlib]") checkInventoryDoc .Theorem name -- TODO: Add (template := "[mathlib]")
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with new := args.map (·.getId)}} theorems := {level.theorems with new := args.map (·.getId)}}
/-- Declare definitions that are introduced by this level. -/ /-- Declare definitions that are introduced by this level. -/
elab "NewDefinition" args:ident* : command => do elab "NewDefinition" args:ident* : command => do
@ -262,10 +262,10 @@ elab "DisabledTactic" args:ident* : command => do
/-- Declare theorems that are temporarily disabled in this level. /-- Declare theorems that are temporarily disabled in this level.
This is ignored if `OnlyTheorem` is set. -/ This is ignored if `OnlyTheorem` is set. -/
elab "DisabledTheorem" args:ident* : command => do elab "DisabledTheorem" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).lemmas.disabled) "DisabledTheorem" checkCommandNotDuplicated ((←getCurLevel).theorems.disabled) "DisabledTheorem"
for name in ↑args do checkInventoryDoc .Lemma name for name in ↑args do checkInventoryDoc .Theorem name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with disabled := args.map (·.getId)}} theorems := {level.theorems with disabled := args.map (·.getId)}}
/-- Declare definitions that are temporarily disabled in this level -/ /-- Declare definitions that are temporarily disabled in this level -/
elab "DisabledDefinition" args:ident* : command => do elab "DisabledDefinition" args:ident* : command => do
@ -283,10 +283,10 @@ elab "OnlyTactic" args:ident* : command => do
/-- Temporarily disable all theorems except the ones declared here -/ /-- Temporarily disable all theorems except the ones declared here -/
elab "OnlyTheorem" args:ident* : command => do elab "OnlyTheorem" args:ident* : command => do
checkCommandNotDuplicated ((←getCurLevel).lemmas.only) "OnlyTheorem" checkCommandNotDuplicated ((←getCurLevel).theorems.only) "OnlyTheorem"
for name in ↑args do checkInventoryDoc .Lemma name for name in ↑args do checkInventoryDoc .Theorem name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with only := args.map (·.getId)}} theorems := {level.theorems with only := args.map (·.getId)}}
/-- Temporarily disable all definitions except the ones declared here. /-- Temporarily disable all definitions except the ones declared here.
This is ignored if `OnlyDefinition` is set. -/ This is ignored if `OnlyDefinition` is set. -/
@ -296,10 +296,10 @@ elab "OnlyDefinition" args:ident* : command => do
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
definitions := {level.definitions with only := args.map (·.getId)}} definitions := {level.definitions with only := args.map (·.getId)}}
/-- Define which tab of Lemmas is opened by default. Usage: `TheoremTab "Nat"`. /-- Define which tab of Theorems is opened by default. Usage: `TheoremTab "Nat"`.
If omitted, the current tab will remain open. -/ If omitted, the current tab will remain open. -/
elab "TheoremTab" category:str : command => elab "TheoremTab" category:str : command =>
modifyCurLevel fun level => pure {level with lemmaTab := category.getString} modifyCurLevel fun level => pure {level with theoremTab := category.getString}
/-! DEPRECATED -/ /-! DEPRECATED -/
@ -309,7 +309,7 @@ elab doc:docComment ? "LemmaDoc" name:ident "as" displayName:str "in" category:s
logWarning "Deprecated. Has been renamed to `TheoremDoc`" logWarning "Deprecated. Has been renamed to `TheoremDoc`"
let doc ← parseDocCommentLegacy doc content let doc ← parseDocCommentLegacy doc content
modifyEnv (inventoryTemplateExt.addEntry · { modifyEnv (inventoryTemplateExt.addEntry · {
type := .Lemma type := .Theorem
name := name.getId name := name.getId
category := category.getString category := category.getString
displayName := displayName.getString displayName := displayName.getString
@ -317,31 +317,31 @@ elab doc:docComment ? "LemmaDoc" name:ident "as" displayName:str "in" category:s
elab "NewLemma" args:ident* : command => do elab "NewLemma" args:ident* : command => do
logWarning "Deprecated. Has been renamed to `NewTheorem`" logWarning "Deprecated. Has been renamed to `NewTheorem`"
checkCommandNotDuplicated ((←getCurLevel).lemmas.new) "NewLemma" checkCommandNotDuplicated ((←getCurLevel).theorems.new) "NewTheorem"
for name in ↑args do for name in ↑args do
try let _decl ← getConstInfo name.getId catch try let _decl ← getConstInfo name.getId catch
| _ => logErrorAt name m!"unknown identifier '{name}'." | _ => logErrorAt name m!"unknown identifier '{name}'."
checkInventoryDoc .Lemma name -- TODO: Add (template := "[mathlib]") checkInventoryDoc .Theorem name -- TODO: Add (template := "[mathlib]")
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with new := args.map (·.getId)}} theorems := {level.theorems with new := args.map (·.getId)}}
elab "DisabledLemma" args:ident* : command => do elab "DisabledLemma" args:ident* : command => do
logWarning "Deprecated. Has been renamed to `DisabledTheorem`" logWarning "Deprecated. Has been renamed to `DisabledTheorem`"
checkCommandNotDuplicated ((←getCurLevel).lemmas.disabled) "DisabledLemma" checkCommandNotDuplicated ((←getCurLevel).theorems.disabled) "DisabledTheorem"
for name in ↑args do checkInventoryDoc .Lemma name for name in ↑args do checkInventoryDoc .Theorem name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with disabled := args.map (·.getId)}} theorems := {level.theorems with disabled := args.map (·.getId)}}
elab "OnlyLemma" args:ident* : command => do elab "OnlyLemma" args:ident* : command => do
logWarning "Deprecated. Has been renamed to `OnlyTheorem`" logWarning "Deprecated. Has been renamed to `OnlyTheorem`"
checkCommandNotDuplicated ((←getCurLevel).lemmas.only) "OnlyLemma" checkCommandNotDuplicated ((←getCurLevel).theorems.only) "OnlyTheorem"
for name in ↑args do checkInventoryDoc .Lemma name for name in ↑args do checkInventoryDoc .Theorem name
modifyCurLevel fun level => pure {level with modifyCurLevel fun level => pure {level with
lemmas := {level.lemmas with only := args.map (·.getId)}} theorems := {level.theorems with only := args.map (·.getId)}}
elab "LemmaTab" category:str : command => do elab "LemmaTab" category:str : command => do
logWarning "Deprecated. Has been renamed to `TheoremTab`" logWarning "Deprecated. Has been renamed to `TheoremTab`"
modifyCurLevel fun level => pure {level with lemmaTab := category.getString} modifyCurLevel fun level => pure {level with theoremTab := category.getString}
/-! # Exercise Statement -/ /-! # Exercise Statement -/
@ -388,7 +388,7 @@ elab doc:docComment ? attrs:Parser.Term.attributes ?
let defaultDeclName : Ident := mkIdent <| (← getCurGame).name ++ (← getCurWorld).name ++ let defaultDeclName : Ident := mkIdent <| (← getCurGame).name ++ (← getCurWorld).name ++
("level" ++ toString lvlIdx : String) ("level" ++ toString lvlIdx : String)
-- Collect all used tactics/lemmas in the sample proof: -- Collect all used tactics/theorems in the sample proof:
let usedInventory : UsedInventory ← match val with let usedInventory : UsedInventory ← match val with
| `(Parser.Command.declVal| := $proof:term) => do | `(Parser.Command.declVal| := $proof:term) => do
collectUsedInventory proof collectUsedInventory proof
@ -417,12 +417,12 @@ elab doc:docComment ? attrs:Parser.Term.attributes ?
let thmStatement ← `(command| $[$doc]? $[$attrs:attributes]? theorem $defaultDeclName $sig := by {let_intros; $(⟨preambleSeq⟩); $(⟨tacticStx⟩)}) let thmStatement ← `(command| $[$doc]? $[$attrs:attributes]? theorem $defaultDeclName $sig := by {let_intros; $(⟨preambleSeq⟩); $(⟨tacticStx⟩)})
elabCommand thmStatement elabCommand thmStatement
-- Check that statement has a docs entry. -- Check that statement has a docs entry.
checkInventoryDoc .Lemma name (name := fullName) (template := docContent) checkInventoryDoc .Theorem name (name := fullName) (template := docContent)
else else
let thmStatement ← `(command| $[$doc]? $[$attrs:attributes]? theorem $name $sig := by {let_intros; $(⟨preambleSeq⟩); $(⟨tacticStx⟩)}) let thmStatement ← `(command| $[$doc]? $[$attrs:attributes]? theorem $name $sig := by {let_intros; $(⟨preambleSeq⟩); $(⟨tacticStx⟩)})
elabCommand thmStatement elabCommand thmStatement
-- Check that statement has a docs entry. -- Check that statement has a docs entry.
checkInventoryDoc .Lemma name (name := fullName) (template := docContent) checkInventoryDoc .Theorem name (name := fullName) (template := docContent)
| none => | none =>
let thmStatement ← `(command| $[$doc]? $[$attrs:attributes]? theorem $defaultDeclName $sig := by {let_intros; $(⟨preambleSeq⟩); $(⟨tacticStx⟩)}) let thmStatement ← `(command| $[$doc]? $[$attrs:attributes]? theorem $defaultDeclName $sig := by {let_intros; $(⟨preambleSeq⟩); $(⟨tacticStx⟩)})
elabCommand thmStatement elabCommand thmStatement
@ -511,7 +511,7 @@ elab doc:docComment ? attrs:Parser.Term.attributes ?
hints := hints hints := hints
tactics := {level.tactics with used := usedInventory.tactics.toArray} tactics := {level.tactics with used := usedInventory.tactics.toArray}
definitions := {level.definitions with used := usedInventory.definitions.toArray} definitions := {level.definitions with used := usedInventory.definitions.toArray}
lemmas := {level.lemmas with used := usedInventory.lemmas.toArray} theorems := {level.theorems with used := usedInventory.theorems.toArray}
} }
/-! # Hints -/ /-! # Hints -/
@ -644,7 +644,7 @@ elab "Template" tacs:tacticSeq : tactic => do
-- open Lean Meta Elab Command Tactic Simp -- open Lean Meta Elab Command Tactic Simp
-- def Lean.Meta.SimpTheorems.hasAttribute (d : SimpTheorems) (decl : Name) := -- def Lean.Meta.SimpTheorems.hasAttribute (d : SimpTheorems) (decl : Name) :=
-- d.isLemma (.decl decl) || d.isDeclToUnfold decl -- d.isTheorem (.decl decl) || d.isDeclToUnfold decl
-- def isInSimpset (simpAttr decl: Name) : CoreM Bool := do -- def isInSimpset (simpAttr decl: Name) : CoreM Bool := do
-- let .some simpDecl ←getSimpExtension? simpAttr | return false -- let .some simpDecl ←getSimpExtension? simpAttr | return false
@ -662,8 +662,8 @@ def Parser.dependency := Parser.sepBy1Indent Parser.ident "→"
/-- Manually add a dependency between two worlds. /-- Manually add a dependency between two worlds.
Normally, the dependencies are computed automatically by the Normally, the dependencies are computed automatically by the
tactics & lemmas used in the example tactics & theorems used in the example
proof and the ones introduced by `NewLemma`/`NewTactic`. proof and the ones introduced by `NewTheorem`/`NewTactic`.
Use the command `Dependency World₁ → World₂` to add a manual edge to the graph, Use the command `Dependency World₁ → World₂` to add a manual edge to the graph,
for example if the only dependency between the worlds is given by for example if the only dependency between the worlds is given by
the narrative. -/ the narrative. -/
@ -708,10 +708,10 @@ elab "MakeGame" : command => do
s!"[mathlib doc](https://leanprover-community.github.io/mathlib4_docs/find/?pattern={name}#doc)" s!"[mathlib doc](https://leanprover-community.github.io/mathlib4_docs/find/?pattern={name}#doc)"
match item.type with match item.type with
| .Lemma => | .Theorem =>
modifyEnv (inventoryExt.addEntry · { item with modifyEnv (inventoryExt.addEntry · { item with
content := content content := content
-- Add the lemma statement to the doc -- Add the theorem statement to the doc
statement := (← getStatementString name) statement := (← getStatementString name)
}) })
| _ => | _ =>
@ -736,14 +736,14 @@ elab "MakeGame" : command => do
for (worldId, world) in allWorlds do for (worldId, world) in allWorlds do
let mut usedItems : HashSet Name := {} let mut usedItems : HashSet Name := {}
let mut newItems : HashSet Name := {} let mut newItems : HashSet Name := {}
for inventoryType in #[.Tactic, .Definition, .Lemma] do for inventoryType in #[.Tactic, .Definition, .Theorem] do
for (levelId, level) in world.levels.toArray do for (levelId, level) in world.levels.toArray do
usedItems := usedItems.insertMany (level.getInventory inventoryType).used usedItems := usedItems.insertMany (level.getInventory inventoryType).used
newItems := newItems.insertMany (level.getInventory inventoryType).new newItems := newItems.insertMany (level.getInventory inventoryType).new
hiddenItems := hiddenItems.insertMany (level.getInventory inventoryType).hidden hiddenItems := hiddenItems.insertMany (level.getInventory inventoryType).hidden
-- if the previous level was named, we need to add it as a new lemma -- if the previous level was named, we need to add it as a new theorem
if inventoryType == .Lemma then if inventoryType == .Theorem then
match levelId with match levelId with
| 0 => pure () | 0 => pure ()
| 1 => pure () -- level ids start with 1, so we need to skip 1, too | 1 => pure () -- level ids start with 1, so we need to skip 1, too
@ -756,9 +756,9 @@ elab "MakeGame" : command => do
let name := Name.str pre s let name := Name.str pre s
newItems := newItems.insert name newItems := newItems.insert name
if inventoryType == .Lemma then if inventoryType == .Theorem then
-- if the last level was named, we need to add it as a new lemma -- if the last level was named, we need to add it as a new theorem
let i₀ := world.levels.size let i₀ := world.levels.size
let some idx := world.levels.find? (i₀) | throwError s!"Level {i₀} not found for world {worldId}!" let some idx := world.levels.find? (i₀) | throwError s!"Level {i₀} not found for world {worldId}!"
@ -863,27 +863,27 @@ elab "MakeGame" : command => do
let mut allItemsByType : HashMap InventoryType (HashSet Name) := {} let mut allItemsByType : HashMap InventoryType (HashSet Name) := {}
-- Compute which inventory items are available in which level: -- Compute which inventory items are available in which level:
for inventoryType in #[.Tactic, .Definition, .Lemma] do for inventoryType in #[.Tactic, .Definition, .Theorem] do
-- Which items are introduced in which world? -- Which items are introduced in which world?
let mut lemmaStatements : HashMap (Name × Nat) Name := {} let mut theoremStatements : HashMap (Name × Nat) Name := {}
-- TODO: I believe `newItemsInWorld` has way to many elements in it which we iterate over -- TODO: I believe `newItemsInWorld` has way to many elements in it which we iterate over
-- e.g. we iterate over `ring` for `Lemma`s as well, but so far that seems to cause no problems -- e.g. we iterate over `ring` for `Theorem`s as well, but so far that seems to cause no problems
let mut allItems : HashSet Name := {} let mut allItems : HashSet Name := {}
for (worldId, world) in game.worlds.nodes.toArray do for (worldId, world) in game.worlds.nodes.toArray do
let mut newItems : HashSet Name := {} let mut newItems : HashSet Name := {}
for (levelId, level) in world.levels.toArray do for (levelId, level) in world.levels.toArray do
let newLemmas := (level.getInventory inventoryType).new let newTheorems := (level.getInventory inventoryType).new
newItems := newItems.insertMany newLemmas newItems := newItems.insertMany newTheorems
allItems := allItems.insertMany newLemmas allItems := allItems.insertMany newTheorems
if inventoryType == .Lemma then if inventoryType == .Theorem then
-- For levels `2, 3, …` we check if the previous level was named -- For levels `2, 3, …` we check if the previous level was named
-- in which case we add it as available lemma. -- in which case we add it as available theorem.
match levelId with match levelId with
| 0 => pure () | 0 => pure ()
| 1 => pure () -- level ids start with 1, so we need to skip 1, too. | 1 => pure () -- level ids start with 1, so we need to skip 1, too.
| i₀ + 1 => | i₀ + 1 =>
-- add named statement from previous level to the available lemmas. -- add named statement from previous level to the available theorems.
let some idx := world.levels.find? (i₀) | throwError s!"Level {i₀ + 1} not found for world {worldId}!" let some idx := world.levels.find? (i₀) | throwError s!"Level {i₀ + 1} not found for world {worldId}!"
match (idx).statementName with match (idx).statementName with
| .anonymous => pure () | .anonymous => pure ()
@ -892,9 +892,9 @@ elab "MakeGame" : command => do
let name := Name.str pre s let name := Name.str pre s
newItems := newItems.insert name newItems := newItems.insert name
allItems := allItems.insert name allItems := allItems.insert name
lemmaStatements := lemmaStatements.insert (worldId, levelId) name theoremStatements := theoremStatements.insert (worldId, levelId) name
if inventoryType == .Lemma then if inventoryType == .Theorem then
-- if named, add the lemma from the last level of the world to the inventory -- if named, add the theorem from the last level of the world to the inventory
let i₀ := world.levels.size let i₀ := world.levels.size
match i₀ with match i₀ with
| 0 => logWarning m!"World `{worldId}` contains no levels." | 0 => logWarning m!"World `{worldId}` contains no levels."
@ -916,7 +916,7 @@ elab "MakeGame" : command => do
-- Using a match statement because the error message of `Option.get!` is not helpful. -- Using a match statement because the error message of `Option.get!` is not helpful.
match (← getInventoryItem? item inventoryType) with match (← getInventoryItem? item inventoryType) with
| none => | none =>
-- Note: we did have a panic here before because lemma statement and doc entry -- Note: we did have a panic here before because theorem statement and doc entry
-- had mismatching namespaces -- had mismatching namespaces
logError m!"There is no inventory item ({inventoryType}) for: {item}." logError m!"There is no inventory item ({inventoryType}) for: {item}."
panic s!"Inventory item {item} not found!" panic s!"Inventory item {item} not found!"
@ -970,8 +970,8 @@ elab "MakeGame" : command => do
-- add the exercise statement from the previous level -- add the exercise statement from the previous level
-- if it was named -- if it was named
if inventoryType == .Lemma then if inventoryType == .Theorem then
match lemmaStatements.find? (worldId, levelId) with match theoremStatements.find? (worldId, levelId) with
| none => pure () | none => pure ()
| some name => | some name =>
let data := (← getInventoryItem? name inventoryType).get! let data := (← getInventoryItem? name inventoryType).get!
@ -986,7 +986,7 @@ elab "MakeGame" : command => do
altTitle := data.statement altTitle := data.statement
locked := false } locked := false }
-- add marks for `disabled` and `new` lemmas here, so that they only apply to -- add marks for `disabled` and `new` theorems here, so that they only apply to
-- the current level. -- the current level.
let itemsArray := items.toArray let itemsArray := items.toArray
|>.insertionSort (fun a b => a.1.toString < b.1.toString) |>.insertionSort (fun a b => a.1.toString < b.1.toString)
@ -1009,10 +1009,10 @@ elab "MakeGame" : command => do
| throwError "Expected item to exist: {name}" | throwError "Expected item to exist: {name}"
return item.toTile) return item.toTile)
let inventory : InventoryOverview := { let inventory : InventoryOverview := {
lemmas := (← getTiles .Lemma).map (fun tile => {tile with hidden := hiddenItems.contains tile.name}) theorems := (← getTiles .Theorem).map (fun tile => {tile with hidden := hiddenItems.contains tile.name})
tactics := (← getTiles .Tactic).map (fun tile => {tile with hidden := hiddenItems.contains tile.name}) tactics := (← getTiles .Tactic).map (fun tile => {tile with hidden := hiddenItems.contains tile.name})
definitions := (← getTiles .Definition).map (fun tile => {tile with hidden := hiddenItems.contains tile.name}) definitions := (← getTiles .Definition).map (fun tile => {tile with hidden := hiddenItems.contains tile.name})
lemmaTab := none theoremTab := none
} }
saveGameData allItemsByType inventory saveGameData allItemsByType inventory

@ -32,40 +32,40 @@ open Lean
/-! ## Inventory (documentation) /-! ## Inventory (documentation)
The inventory contains documentation that the user can access. The inventory contains documentation that the user can access.
There are three inventory types: Lemma, Tactic, Definition. They vary about in the information There are three inventory types: Theorem, Tactic, Definition. They vary about in the information
they carry. they carry.
The commands `TheoremDoc`, `TacticDoc`, and `DefinitionDoc` add keys and templates to an The commands `TheoremDoc`, `TacticDoc`, and `DefinitionDoc` add keys and templates to an
env. extension called `InventoryTemplateExt`. Commands like `NewLemma`, etc. as well as env. extension called `InventoryTemplateExt`. Commands like `NewTheorem`, etc. as well as
`Statement` check if there is a key registered in this extension and might add a default or `Statement` check if there is a key registered in this extension and might add a default or
print a warning if not. print a warning if not.
Then, `MakeGame` takes the templates from `InventoryTemplateExt` and creates the documentation entries Then, `MakeGame` takes the templates from `InventoryTemplateExt` and creates the documentation entries
that are sent to the client. This allows us to modify them like adding information from that are sent to the client. This allows us to modify them like adding information from
mathlib or from parsing the lemma in question. mathlib or from parsing the theorem in question.
-/ -/
/-- The game knows three different inventory types that contain slightly different information -/ /-- The game knows three different inventory types that contain slightly different information -/
inductive InventoryType := | Tactic | Lemma | Definition inductive InventoryType := | Tactic | Theorem | Definition
deriving ToJson, FromJson, Repr, BEq, Hashable, Inhabited deriving ToJson, FromJson, Repr, BEq, Hashable, Inhabited
-- TODO: golf this? -- TODO: golf this?
instance : ToString InventoryType := ⟨fun t => match t with instance : ToString InventoryType := ⟨fun t => match t with
| .Tactic => "Tactic" | .Tactic => "Tactic"
| .Lemma => "Lemma" | .Theorem => "Theorem"
| .Definition => "Definition"⟩ | .Definition => "Definition"⟩
/-- The keys/templates of the inventory items, stored in `InventoryTemplateExt`. -/ /-- The keys/templates of the inventory items, stored in `InventoryTemplateExt`. -/
structure InventoryTemplate where structure InventoryTemplate where
/-- Lemma, Tactic, or Definition -/ /-- Theorem, Tactic, or Definition -/
type: InventoryType type: InventoryType
/-- Depends on the type: /-- Depends on the type:
* Tactic: the tactic's name * Tactic: the tactic's name
* Lemma: fully qualified lemma name * Theorem: fully qualified theorem name
* Definition: no restrictions (preferably the definitions fully qualified name) * Definition: no restrictions (preferably the definitions fully qualified name)
-/ -/
name: Name name: Name
/-- Only for Lemmas. To sort them into tabs -/ /-- Only for Theorems. To sort them into tabs -/
category: String := default category: String := default
/-- Free-text short name -/ /-- Free-text short name -/
displayName: String := name.toString displayName: String := name.toString
@ -85,13 +85,13 @@ structure InventoryTile where
The name of the item. The restrictions are: The name of the item. The restrictions are:
* for Tactics: The name of the tactic. * for Tactics: The name of the tactic.
* for Lemmas: *Fully qualified* lemma name. * for Theorems: *Fully qualified* theorem name.
* for Definitions: no restrictions. * for Definitions: no restrictions.
-/ -/
name : Name name : Name
/-- The display name shown in the inventory. This can be free-text. -/ /-- The display name shown in the inventory. This can be free-text. -/
displayName : String displayName : String
/-- Category to group inventory items by (currently only used for lemmas). -/ /-- Category to group inventory items by (currently only used for theorems). -/
category : String category : String
/-- The world which introduced this item. -/ /-- The world which introduced this item. -/
world : Option Name := none world : Option Name := none
@ -146,16 +146,16 @@ def getInventoryItem? [Monad m] [MonadEnv m] (n : Name) (type : InventoryType) :
structure InventoryOverview where structure InventoryOverview where
tactics : Array InventoryTile tactics : Array InventoryTile
lemmas : Array InventoryTile theorems : Array InventoryTile
definitions : Array InventoryTile definitions : Array InventoryTile
lemmaTab : Option String theoremTab : Option String
deriving ToJson, FromJson deriving ToJson, FromJson
-- TODO: Reuse the following code for checking available tactics in user code: -- TODO: Reuse the following code for checking available tactics in user code:
structure UsedInventory where structure UsedInventory where
(tactics : HashSet Name := {}) (tactics : HashSet Name := {})
(definitions : HashSet Name := {}) (definitions : HashSet Name := {})
(lemmas : HashSet Name := {}) (theorems : HashSet Name := {})
/-! ## Environment extensions for game specification -/ /-! ## Environment extensions for game specification -/
@ -255,7 +255,7 @@ structure GameLevel where
/-- Introduction text shown all the time. (markdown) -/ /-- Introduction text shown all the time. (markdown) -/
introduction: String := default introduction: String := default
conclusion: String := default conclusion: String := default
/-- The name of the exercise proven. If provided this lemma will be available in /-- The name of the exercise proven. If provided this theorem will be available in
future levels. -/ future levels. -/
statementName: Name := default statementName: Name := default
hints: Array GoalHintEntry := default hints: Array GoalHintEntry := default
@ -265,13 +265,13 @@ structure GameLevel where
/-- The mathematical statement in mathematician-readable form. (markdown) -/ /-- The mathematical statement in mathematician-readable form. (markdown) -/
descrText: Option String := none descrText: Option String := none
descrFormat : String := default descrFormat : String := default
/-- The `category` of lemmas to be open by default -/ /-- The `category` of theorems to be open by default -/
lemmaTab: Option String := none theoremTab: Option String := none
/-- The module to be imported when playing this level -/ /-- The module to be imported when playing this level -/
module : Name := default module : Name := default
tactics: InventoryInfo := default tactics: InventoryInfo := default
definitions: InventoryInfo := default definitions: InventoryInfo := default
lemmas: InventoryInfo := default theorems: InventoryInfo := default
/-- A proof template that is printed in an empty editor. -/ /-- A proof template that is printed in an empty editor. -/
template: Option String := none template: Option String := none
/-- The image for this level. -/ /-- The image for this level. -/
@ -282,20 +282,20 @@ deriving Inhabited, Repr
/-- Json-encodable version of `GameLevel` /-- Json-encodable version of `GameLevel`
Fields: Fields:
- description: Lemma in mathematical language. - description: Theorem in mathematical language.
- descriptionGoal: Lemma printed as Lean-Code. - descriptionGoal: Theorem printed as Lean-Code.
-/ -/
structure LevelInfo where structure LevelInfo where
index : Nat index : Nat
title : String title : String
tactics : Array InventoryTile tactics : Array InventoryTile
lemmas : Array InventoryTile theorems : Array InventoryTile
definitions : Array InventoryTile definitions : Array InventoryTile
introduction : String introduction : String
conclusion : String conclusion : String
descrText : Option String := none descrText : Option String := none
descrFormat : String := "" descrFormat : String := ""
lemmaTab : Option String theoremTab : Option String
module : Name module : Name
displayName : Option String displayName : Option String
statementName : Option String statementName : Option String
@ -307,17 +307,17 @@ def GameLevel.toInfo (lvl : GameLevel) (env : Environment) : LevelInfo :=
{ index := lvl.index, { index := lvl.index,
title := lvl.title, title := lvl.title,
tactics := lvl.tactics.tiles, tactics := lvl.tactics.tiles,
lemmas := lvl.lemmas.tiles, theorems := lvl.theorems.tiles,
definitions := lvl.definitions.tiles, definitions := lvl.definitions.tiles,
descrText := lvl.descrText, descrText := lvl.descrText,
descrFormat := lvl.descrFormat --toExpr <| format (lvl.goal.raw) --toString <| Syntax.formatStx (lvl.goal.raw) --Syntax.formatStx (lvl.goal.raw) , -- TODO descrFormat := lvl.descrFormat --toExpr <| format (lvl.goal.raw) --toString <| Syntax.formatStx (lvl.goal.raw) --Syntax.formatStx (lvl.goal.raw) , -- TODO
introduction := lvl.introduction introduction := lvl.introduction
conclusion := lvl.conclusion conclusion := lvl.conclusion
lemmaTab := match lvl.lemmaTab with theoremTab := match lvl.theoremTab with
| some tab => tab | some tab => tab
| none => | none =>
-- Try to set the lemma tab to the category of the first added lemma -- Try to set the theorem tab to the category of the first added theorem
match lvl.lemmas.tiles.find? (·.new) with match lvl.theorems.tiles.find? (·.new) with
| some tile => tile.category | some tile => tile.category
| none => none | none => none
statementName := lvl.statementName.toString statementName := lvl.statementName.toString
@ -325,11 +325,11 @@ def GameLevel.toInfo (lvl : GameLevel) (env : Environment) : LevelInfo :=
displayName := match lvl.statementName with displayName := match lvl.statementName with
| .anonymous => none | .anonymous => none
| name => match (inventoryExt.getState env).find? | name => match (inventoryExt.getState env).find?
(fun x => x.name == name && x.type == .Lemma) with (fun x => x.name == name && x.type == .Theorem) with
| some n => n.displayName | some n => n.displayName
| none => name.toString | none => name.toString
-- Note: we could call `.find!` because we check in `Statement` that the -- Note: we could call `.find!` because we check in `Statement` that the
-- lemma doc must exist. -- theorem doc must exist.
template := lvl.template template := lvl.template
image := lvl.image image := lvl.image
} }
@ -536,7 +536,7 @@ def modifyLevel (levelId : LevelId) (fn : GameLevel → m GameLevel) [MonadError
-- def getIntroducedInventory (game : Game) [MonadError m] : m (Array Name) := do -- def getIntroducedInventory (game : Game) [MonadError m] : m (Array Name) := do
-- let allItems : Array Name := game.worlds.nodes.fold (fun L _ world => L ++ -- let allItems : Array Name := game.worlds.nodes.fold (fun L _ world => L ++
-- world.levels.fold (fun LL _ level => -- world.levels.fold (fun LL _ level =>
-- LL ++ level.tactics.new ++ level.lemmas.new -- LL ++ level.tactics.new ++ level.theorems.new
-- ) #[]) #[] -- ) #[]) #[]
-- pure allItems -- pure allItems

@ -166,7 +166,7 @@ partial def findForbiddenTactics (inputCtx : Parser.InputContext) (workerState :
-- Forbid the theorem we are proving currently -- Forbid the theorem we are proving currently
addMessage info inputCtx (severity := .error) addMessage info inputCtx (severity := .error)
s!"Structural recursion: you can't use '{n}' to proof itself!" s!"Structural recursion: you can't use '{n}' to proof itself!"
let theoremsAndDefs := levelInfo.lemmas ++ levelInfo.definitions let theoremsAndDefs := levelInfo.theorems ++ levelInfo.definitions
match theoremsAndDefs.find? (·.name == n) with match theoremsAndDefs.find? (·.name == n) with
| none => | none =>
-- Theorem will never be introduced in this game -- Theorem will never be introduced in this game

@ -81,7 +81,7 @@ def getStatement (name : Name) : CommandElabM MessageData := do
-- Note: We use `String` because we can't send `MessageData` as json, but -- Note: We use `String` because we can't send `MessageData` as json, but
-- `MessageData` might be better for interactive highlighting. -- `MessageData` might be better for interactive highlighting.
/-- Get a string of the form `my_lemma (n : ) : n + n = 2 * n`. /-- Get a string of the form `my_theorem (n : ) : n + n = 2 * n`.
Note: A statement like `theorem abc : ∀ x : Nat, x ≥ 0` would be turned into Note: A statement like `theorem abc : ∀ x : Nat, x ≥ 0` would be turned into
`theorem abc (x : Nat) : x ≥ 0` by `PrettyPrinter.ppSignature`. -/ `theorem abc (x : Nat) : x ≥ 0` by `PrettyPrinter.ppSignature`. -/

@ -59,7 +59,7 @@ def getDocstring (env : Environment) (name : Name) (type : InventoryType) :
match type with match type with
-- for tactics it's a lookup following mathlib's `#help`. not guaranteed to be the correct one. -- for tactics it's a lookup following mathlib's `#help`. not guaranteed to be the correct one.
| .Tactic => getTacticDocstring env name | .Tactic => getTacticDocstring env name
| .Lemma => findDocString? env name | .Theorem => findDocString? env name
-- TODO: for definitions not implemented yet, does it work? -- TODO: for definitions not implemented yet, does it work?
| .Definition => findDocString? env name | .Definition => findDocString? env name
@ -99,14 +99,14 @@ def checkInventoryDoc (type : InventoryType) (ref : Ident) (name : Name := ref.g
modifyEnv (inventoryTemplateExt.addEntry · { modifyEnv (inventoryTemplateExt.addEntry · {
type := type type := type
name := name name := name
category := if type == .Lemma then s!"{n.getPrefix}" else "" category := if type == .Theorem then s!"{n.getPrefix}" else ""
content := docstring}) content := docstring})
-- Add the default documentation -- Add the default documentation
| some s => | some s =>
modifyEnv (inventoryTemplateExt.addEntry · { modifyEnv (inventoryTemplateExt.addEntry · {
type := type type := type
name := name name := name
category := if type == .Lemma then s!"{n.getPrefix}" else "" category := if type == .Theorem then s!"{n.getPrefix}" else ""
content := s }) content := s })
logInfoAt ref (m!"Missing {type} Documentation: {name}, used default (e.g. provided " ++ logInfoAt ref (m!"Missing {type} Documentation: {name}, used default (e.g. provided " ++
m!"docstring) instead. If you want to write a different description, add " ++ m!"docstring) instead. If you want to write a different description, add " ++
@ -133,7 +133,7 @@ partial def collectUsedInventory (stx : Syntax) (acc : UsedInventory := {}) : Co
catch | _ => pure [] -- catch "unknown constant" error catch | _ => pure [] -- catch "unknown constant" error
return ← ns.foldlM (fun acc n => do return ← ns.foldlM (fun acc n => do
if let some (.thmInfo ..) := (← getEnv).find? n then if let some (.thmInfo ..) := (← getEnv).find? n then
return {acc with lemmas := acc.lemmas.insertMany ns} return {acc with theorems := acc.theorems.insertMany ns}
else else
return {acc with definitions := acc.definitions.insertMany ns} return {acc with definitions := acc.definitions.insertMany ns}
) acc ) acc
@ -143,10 +143,10 @@ partial def collectUsedInventory (stx : Syntax) (acc : UsedInventory := {}) : Co
def GameLevel.getInventory (level : GameLevel) : InventoryType → InventoryInfo def GameLevel.getInventory (level : GameLevel) : InventoryType → InventoryInfo
| .Tactic => level.tactics | .Tactic => level.tactics
| .Definition => level.definitions | .Definition => level.definitions
| .Lemma => level.lemmas | .Theorem => level.theorems
def GameLevel.setComputedInventory (level : GameLevel) : def GameLevel.setComputedInventory (level : GameLevel) :
InventoryType → Array InventoryTile → GameLevel InventoryType → Array InventoryTile → GameLevel
| .Tactic, v => {level with tactics := {level.tactics with tiles := v}} | .Tactic, v => {level with tactics := {level.tactics with tiles := v}}
| .Definition, v => {level with definitions := {level.definitions with tiles := v}} | .Definition, v => {level with definitions := {level.definitions with tiles := v}}
| .Lemma, v => {level with lemmas := {level.lemmas with tiles := v}} | .Theorem, v => {level with theorems := {level.theorems with tiles := v}}

@ -51,7 +51,7 @@ def saveGameData (allItemsByType : HashMap InventoryType (HashSet Name))
IO.FS.writeFile (path / gameFileName) (toString (getGameJson game)) IO.FS.writeFile (path / gameFileName) (toString (getGameJson game))
for inventoryType in [InventoryType.Lemma, .Tactic, .Definition] do for inventoryType in [InventoryType.Theorem, .Tactic, .Definition] do
for name in allItemsByType.findD inventoryType {} do for name in allItemsByType.findD inventoryType {} do
let some item ← getInventoryItem? name inventoryType let some item ← getInventoryItem? name inventoryType
| throwError "Expected item to exist: {name}" | throwError "Expected item to exist: {name}"

@ -53,9 +53,9 @@ def getTacticDocstring (name: Name) : CommandElabM String := do
-- TODO: Things we want: -- TODO: Things we want:
-- 1) Getting docstring this way is a problem if we want to "reprove" a mathlib lemma because -- 1) Getting docstring this way is a problem if we want to "reprove" a mathlib theorem because
-- either it would not be imported from mathlib or have a different name in `Statement` -- either it would not be imported from mathlib or have a different name in `Statement`
-- 3) is the lemma a simp lemma? (are there other attributes on it? --> hard/impossible) -- 3) is the theorem a simp theorem? (are there other attributes on it? --> hard/impossible)
-- 4) which mathlib file is it imported from? -- 4) which mathlib file is it imported from?
-- 5) namespace -- 5) namespace
-- 6) tactics: are there alternative variations like `ext`, `ext?`, `ext1?`, … -- 6) tactics: are there alternative variations like `ext`, `ext?`, `ext1?`, …

@ -22,7 +22,7 @@ Statement foo.bar2 : 3 ≤ 7 := by
simp simp
NewLemma foo.bar NewTheorem foo.bar
DisabledTactic tauto DisabledTactic tauto
@ -39,7 +39,7 @@ end myNamespace
/- Other tests -/ /- Other tests -/
LemmaDoc add_zero as "add_zero" in "Nat" "(nothing)" TheoremDoc add_zero as "add_zero" in "Nat" "(nothing)"
/-- test -/ /-- test -/
Statement add_zero (n : Nat) : n + 0 = n := by Statement add_zero (n : Nat) : n + 0 = n := by
@ -58,7 +58,7 @@ Statement (n : Nat) : 0 + n = n := by
simp simp
NewLemma add_zero NewTheorem add_zero
--attribute [simp] add_zero --attribute [simp] add_zero

Loading…
Cancel
Save