Zulip Chat Archive

Stream: lean4

Topic: What to pass to `categoryParserFn` for it to work?


nrs (Nov 25 2024 at 20:27):

I'm digging around Lean.Parser and trying to get individual parsers to work. Some of these work with empty contexts and environments, but not categoryParserFn. What do I need to initialize to, e.g., have ParserFn.run (categoryParserFn ``command) parse def mynat : Nat := 2?

Mario Carneiro (Nov 25 2024 at 21:28):

you need to run the initializer for Lean

Mario Carneiro (Nov 25 2024 at 21:29):

this populates the global tables used by the parser

Mario Carneiro (Nov 25 2024 at 21:29):

the way you run the initializer depends on how you got yourself in a situation where they haven't been run yet

nrs (Nov 25 2024 at 21:36):

Mario Carneiro said:

the way you run the initializer depends on how you got yourself in a situation where they haven't been run yet

I see thanks a lot for the answer! what parts of source should I look at to learn how the initializer is ran or how it works? (the easy way out is to use the fact that the initializer has in fact been ran and that I have a working environment I'm importing Lean.Parser in, but I want to understand the internals better)

Mario Carneiro (Nov 25 2024 at 21:40):

The parser table is not stored in the environment, so once you have run the initializer it will work even in the empty environment

Mario Carneiro (Nov 25 2024 at 21:40):

this is important, because otherwise lean would not be able to parse Prelude.lean, the first lean file

Mario Carneiro (Nov 25 2024 at 21:44):

Because these are "builtin parsers", they are run as part of the function initialize_Lean which runs all the initializers. The way these function calls ended up in this function is a bit convoluted, but goes via the @[builtin_init] attribute and a bootstrap stage

Mario Carneiro (Nov 25 2024 at 21:45):

but the starting point is the use of @[builtin_command_parser] e.g. here

nrs (Nov 25 2024 at 21:47):

Mario Carneiro said:

The parser table is not stored in the environment, so once you have run the initializer it will work even in the empty environment

hm, I am however getting a kernel panic from the following (crude I know) code. Other parsers that do not depend on something else than the context's input, such as strLitFnAux, do successfully parse when called in this way

import Lean.Parser
import Lean.Data
open Lean.Parser Lean

def mkInputContext' (str : String) : InputContext := .mk str "" (.mk "" #[])

def tempmain : IO Unit := do
  let emptyenv : Environment := (<- mkEmptyEnvironment)
  let emptyParserModuleContext : ParserModuleContext := .mk emptyenv (.mk []) .anonymous []
  let emptyTrie : Lean.Data.Trie Token := Lean.Data.Trie.empty
  let emptyTokenCacheEntry : TokenCacheEntry := .mk 0 0 .missing
  let emptyParserCache : ParserCache := .mk emptyTokenCacheEntry .empty
  let emptyParserState : ParserState := .mk .empty 0 0 emptyParserCache .none #[]

  let result := ParserFn.run (categoryParserFn `command) (mkInputContext' "def mynat : Nat := 2") emptyParserModuleContext emptyTrie emptyParserState
  dbg_trace result.stxStack.toSubarray

#eval tempmain

nrs (Nov 25 2024 at 22:16):

Using the current env instead (from /tests):

def withCurrentEnv : CoreM Unit := do
  let stx <- ofExcept <| Parser.runParserCategory (<- getEnv) `command "def mynat : Nat := 2"
  dbg_trace stx

nrs (Nov 25 2024 at 22:26):

I will be removing each piece of the environment until I figure out the critical objects for this to work, will report back (but do let me know if I should be focusing on something!)

nrs (Nov 26 2024 at 00:18):

The following works and seems to be the minimal initialization necessary to make categoryParserFn work

def tempmain : IO Unit := do
  let env : Environment := (<- makeEmptyEnvironment)
  let emptyParserModuleContext : ParserModuleContext := .mk env (.mk []) .anonymous []
  let pcache : ParserCache := initCacheForInput ""
  let emptyParserState : ParserState := .mk .empty 0 0 pcache .none #[]
  let result := ParserFn.run (categoryParserFn `command) (Parser.mkInputContext "def mynat : Nat := 2" "") emptyParserModuleContext (getTokenTable env) emptyParserState
  dbg_trace result.stxStack.toSubarray

Mario Carneiro (Nov 26 2024 at 10:39):

oh yes, the initial token table comes from the global state I mentioned, but the parser itself carries (non-global) state so it can handle files that change the parser in the middle of the file


Last updated: May 02 2025 at 03:31 UTC