diff --git a/server/GameServer/FileWorker.lean b/server/GameServer/FileWorker.lean index 305487f..d66c2a2 100644 --- a/server/GameServer/FileWorker.lean +++ b/server/GameServer/FileWorker.lean @@ -18,7 +18,7 @@ private def mkEOI (pos : String.Pos) : Syntax := mkNode ``Command.eoi #[atom] partial def parseTactic (inputCtx : InputContext) (pmctx : ParserModuleContext) - (mps : ModuleParserState) (messages : MessageLog) (couldBeEndSnap : Bool) : + (mps : ModuleParserState) (messages : MessageLog) : Syntax × ModuleParserState × MessageLog × String.Pos := Id.run do let mut pos := mps.pos let mut recovering := mps.recovering @@ -66,8 +66,7 @@ structure GameWorkerState := 2: give errors -/ difficulty : Nat - -- /-- The name of the theorem to be proven in this level. -/ - -- statementName : Name + levelInfo : LevelInfo deriving ToJson, FromJson abbrev GameWorkerM := StateT GameWorkerState Server.FileWorker.WorkerM @@ -89,20 +88,21 @@ def addErrorMessage (info : SourceInfo) (inputCtx : Parser.InputContext) (s : Me /-- Find all tactics in syntax object that are forbidden according to a set `allowed` of allowed tactics. -/ partial def findForbiddenTactics (inputCtx : Parser.InputContext) - (gameWorkerState : GameWorkerState) (level : GameLevel) (stx : Syntax) : + (gameWorkerState : GameWorkerState) (stx : Syntax) : Elab.Command.CommandElabM Unit := do + let levelInfo := gameWorkerState.levelInfo match stx with | .missing => return () | .node _info _kind args => for arg in args do - findForbiddenTactics inputCtx gameWorkerState level arg + findForbiddenTactics inputCtx gameWorkerState arg | .atom info val => -- ignore syntax elements that do not start with a letter -- and ignore "with" keyword let allowed := ["with", "fun", "at", "only", "by", "to"] if 0 < val.length ∧ val.data[0]!.isAlpha ∧ not (allowed.contains val) then let val := val.dropRightWhile (fun c => c == '!' || c == '?') -- treat `simp?` and `simp!` like `simp` - match level.tactics.tiles.find? (·.name.toString == val) with + match levelInfo.tactics.find? (·.name.toString == val) with | none => -- Note: This case means that the tactic will never be introduced in the game. match gameWorkerState.inventory.find? (· == val) with @@ -125,10 +125,10 @@ partial def findForbiddenTactics (inputCtx : Parser.InputContext) let some (.thmInfo ..) := (← getEnv).find? n | return () -- not a theorem -> ignore -- Forbid the theorem we are proving currently - if n = level.statementName then + if some n = levelInfo.statementName then addErrorMessage info inputCtx s!"Structural recursion: you can't use '{n}' to proof itself!" - let lemmasAndDefs := level.lemmas.tiles ++ level.definitions.tiles + let lemmasAndDefs := levelInfo.lemmas ++ levelInfo.definitions match lemmasAndDefs.find? (fun l => l.name == n) with | none => addWarningMessage info s!"You have not unlocked the lemma/definition '{n}' yet!" | some lem => @@ -202,12 +202,12 @@ def compileProof (inputCtx : Parser.InputContext) (snap : Snapshot) (hasWidgets currNamespace := scope.currNamespace, openDecls := scope.openDecls } let (tacticStx, cmdParserState, msgLog, endOfWhitespace) := - MyModule.parseTactic inputCtx pmctx snap.mpState snap.msgLog couldBeEndSnap + MyModule.parseTactic inputCtx pmctx snap.mpState snap.msgLog modify (fun s => { s with messages := msgLog }) parseResultRef.set (tacticStx, cmdParserState) -- Check for forbidden tactics - findForbiddenTactics inputCtx gameWorkerState level tacticStx + findForbiddenTactics inputCtx gameWorkerState tacticStx -- Insert invisible `skip` command to make sure we always display the initial goal let skip := Syntax.node (.original default 0 default endOfWhitespace) ``Lean.Parser.Tactic.skip #[] @@ -468,16 +468,11 @@ section Initialization return (headerSnap, srcSearchPath) def initializeWorker (meta : DocumentMeta) (i o e : FS.Stream) (initParams : InitializeParams) (opts : Options) - (gameDir : String) (game : Game) (gameWorkerState : GameWorkerState) : IO (WorkerContext × WorkerState) := do - -- TODO: We misuse the `rootUri` field to the gameName - let rootUri? : Option String := some (toString game.name) - let initParams := {initParams with rootUri?} + (gameDir : String) (gameWorkerState : GameWorkerState) : IO (WorkerContext × WorkerState) := do let clientHasWidgets := initParams.initializationOptions?.bind (·.hasWidgets?) |>.getD false - let some (levelId : LevelId) := GameServer.levelIdFromFileName? initParams meta.mkInputContext.fileName - | throwServerError "Could not determine level ID" - let level ← loadLevelData gameDir levelId.world levelId.level + let (headerStx, headerTask) ← compileHeader meta o opts (hasWidgets := clientHasWidgets) - gameDir level.module + gameDir gameWorkerState.levelInfo.module let cancelTk ← CancelToken.new let ctx := { hIn := i @@ -609,13 +604,21 @@ def initAndRunWorker (i o e : FS.Stream) (opts : Options) (gameDir : String) : I let _ ← IO.setStderr e try let game ← loadGameData gameDir + -- TODO: We misuse the `rootUri` field to the gameName + let rootUri? : Option String := some (toString game.name) + let initParams := {initRequest.param.toLeanInternal with rootUri?} + let some (levelId : LevelId) := GameServer.levelIdFromFileName? + initParams meta.mkInputContext.fileName + | throwServerError s!"Could not determine level ID: {meta.mkInputContext.fileName}" + let levelInfo ← loadLevelData gameDir levelId.world levelId.level let some initializationOptions := initRequest.param.initializationOptions? | throwServerError "no initialization options found" let gameWorkerState : GameWorkerState:= { inventory := initializationOptions.inventory difficulty := initializationOptions.difficulty + levelInfo } - let (ctx, st) ← initializeWorker meta i o e initRequest.param.toLeanInternal opts gameDir game gameWorkerState + let (ctx, st) ← initializeWorker meta i o e initParams opts gameDir gameWorkerState let _ ← StateRefT'.run (s := st) <| ReaderT.run (r := ctx) <| StateT.run (s := gameWorkerState) <| (mainLoop) return (0 : UInt32)