Zulip Chat Archive

Stream: lean4

Topic: using syntax parsers at runtime


Joachim Breitner (Aug 24 2023 at 20:14):

Can I use lean’s syntax declaration to define parsers that I want to use at _runtime_ of my program?

syntax test := "some" ident

unsafe def main : IO Unit := do
  let input := "some test"
  let parsed_input : Lean.Syntax := _ -- what do I put here to apply `test` to `input`

Mac Malone (Aug 24 2023 at 22:08):

@Joachim Breitner This was the motivating use case behind my package, Partax.

import Partax
open scoped Partax

syntax test := "some" ident

namespace Parser
compile_parser test
end Parser

def main : IO Unit := do
  let input := "some test"
  let parsedInput  IO.ofExcept <| Parser.test.run' input
  -- ...
  IO.println parsedInput

#eval main -- (test "some" `test)

Joachim Breitner (Aug 25 2023 at 02:00):

Great, will try that tomorrow! I'm a bit relieved that there isn't a simple way that I failed to figure out :-)

Joachim Breitner (Aug 29 2023 at 16:02):

Kyle Miller said:

Could you say a little bit about why find_patterns is a syntax category in the PR? I don't really understand "so that it can be used by external tools".

(moving this question to a separate topic)

This was the only way so far I managed to access this parser at runtime, which I need in the loogle tool, using this code:
https://github.com/nomeata/loogle/blob/ac0b5ff07276449df091bd2f30ef31a589547391/Loogle.lean#L31
I was mostly guessing and cargo-culting, and am happy to learn of better ways.

I did not yet investigate Partax, it seems then I wouldn’t need a syntax category. Let me do that right away. (Although I wish lean would just allow me to use parsers at runtime without extra tools…)

Kyle Miller (Aug 29 2023 at 16:20):

@Joachim Breitner I've never tried this before and I might be doing it wrong, but here's a runParser function that takes a declaration name for the syntax.

import Lean

/-- The turnstyle for conclusion patterns, unicode or ascii allowed -/
syntax turnstyle := patternIgnore(" ⊢ " <|> " |- ")

/-- `#find` patterns -/
syntax find_patterns := term,* (turnstyle term)?

open Lean Parser

def runParser (env : Environment) (declName : Name) (input : String) (fileName := "<input>") : Except String Syntax :=
  let p := andthenFn whitespace (evalParserConst declName)
  let ictx := mkInputContext input fileName
  let s := p.run ictx { env, options := {} } (getTokenTable env) (mkParserState input)
  if s.hasError then
    Except.error (s.toErrorMsg ictx)
  else if input.atEnd s.pos then
    Except.ok s.stxStack.back
  else
    Except.error ((s.mkError "end of input").toErrorMsg ictx)

#eval do
  let env  (getEnv : Elab.TermElabM _)
  match runParser env ``find_patterns "1 + 1 ⊢ x" with
  | .ok s => logInfo m!"ok: {s}"
  | .error e => logError m!"error: {e}"

Kyle Miller (Aug 29 2023 at 16:20):

(The particular syntax declarations are just for testing, and I don't mean to suggest anything about how #find should work.)

Joachim Breitner (Aug 29 2023 at 16:33):

Hmm, using partax to compile the term parsers certainly adds noticably friction to the development process. It seems cleaner if loogle doesn't use the environment of the loaded .olean, but not sure if the extra build time is worth it. Hmm.

Joachim Breitner (Aug 29 2023 at 16:39):

Thanks, Kyle, that code seems to work. Using it now in loogle and removed the syntax category from the #find PR

Mac Malone (Aug 29 2023 at 18:03):

Joachim Breitner said:

Hmm, using partax to compile the term parsers certainly adds noticably friction to the development process.

Yeah, compiling the whole Lean grammar is unfortunately currently slow in Partax.


Last updated: Dec 20 2023 at 11:08 UTC