Zulip Chat Archive

Stream: new members

Topic: Replicate builtin do syntax


Alex M (Nov 24 2024 at 11:13):

Hi all. Trying to understand how to create intrusive DSL with Lean4. Want to create syntax that replicate 'do' builtin syntax into more lightweight 'mm'. My try was:

prelude
import Lean.Parser.Term

namespace Lean
namespace Parser

namespace Category
def mmElem : Category := {}
end Category

builtin_initialize registerBuiltinParserAttribute `builtin_mmElem_parser ``Category.mmElem
builtin_initialize registerBuiltinDynamicParserAttribute `mmElem_parser `mmElem

@[inline] def mmElemParser (rbp : Nat := 0) : Parser :=
  categoryParser `mmElem rbp

namespace Term

def mmSeqItem := leading_parser
  ppLine >> mmElemParser >> optional "; "
def mmSeqIndent := leading_parser
  many1Indent mmSeqItem
def mmSeqBracketed := leading_parser
  "{" >> withoutPosition (many1 mmSeqItem) >> ppLine >> "}"
@[builtin_doc] def mmSeq :=
  withAntiquot (mkAntiquot "mmSeq" decl_name% (isPseudoKind := true)) <|
    mmSeqBracketed <|> mmSeqIndent

attribute [run_builtin_parser_attribute_hooks] mmSeq
builtin_initialize register_parser_alias mmSeq

@[builtin_mmElem_parser] def mmReturn := leading_parser:leadPrec
  withPosition ("return" >> optional (ppSpace >> checkLineEq >> termParser))

@[builtin_term_parser] def «mm»  := leading_parser:argPrec
  ppAllowUngrouped >> "mm " >> mmSeq

@[builtin_term_parser] def termReturn := leading_parser:leadPrec
  withPosition ("return" >> optional (ppSpace >> checkLineEq >> termParser))

end Term
end Parser
end Lean

But that not worked with error "unknown attribute [builtin_mmElem_parser]" on latest Lean 4.13.0. Is that doable? Appreciate some guidance from knowledgeables here.

Daniel Weber (Nov 24 2024 at 12:07):

Have you read Metaprogramming in Lean? In particular https://leanprover-community.github.io/lean4-metaprogramming-book/main/08_dsls.html should be helpful. I don't think you're supposed to use the @[builtin_... attributes, those are for builtin parts of Lean itself

Alex M (Nov 24 2024 at 15:34):

Thank you for reply.

Ok, may be @[builtin.. attributes not for users, but if i get rid of failing attribute mm not considered anyway by Lean. That not worked:

prelude
import Lean.Parser.Term

namespace Lean
namespace Parser

namespace Category
def mmElem : Category := {}
end Category

builtin_initialize registerBuiltinParserAttribute `builtin_mmElem_parser ``Category.mmElem
builtin_initialize registerBuiltinDynamicParserAttribute `mmElem_parser `mmElem

@[inline] def mmElemParser (rbp : Nat := 0) : Parser :=
  categoryParser `mmElem rbp

namespace Term

def mmSeqItem := leading_parser
  ppLine >> mmElemParser >> optional "; "
def mmSeqIndent := leading_parser
  many1Indent mmSeqItem
def mmSeqBracketed := leading_parser
  "{" >> withoutPosition (many1 mmSeqItem) >> ppLine >> "}"
@[builtin_doc] def mmSeq :=
  withAntiquot (mkAntiquot "mmSeq" decl_name% (isPseudoKind := true)) <|
    mmSeqBracketed <|> mmSeqIndent

attribute [run_builtin_parser_attribute_hooks] mmSeq
builtin_initialize register_parser_alias mmSeq

@[builtin_term_parser] def «mm»  := leading_parser:argPrec
  ppAllowUngrouped >> "mm " >> mmSeq

end Term
end Parser
end Lean

def main : IO Unit := mm
  IO.println "Hello"

I will try Embedding DSLs By Elaboration approach, that was referenced by you, thanx.

Daniel Weber (Nov 24 2024 at 15:35):

You should also avoid using prelude and builtin_initialize

Kyle Miller (Nov 24 2024 at 20:11):

In particular, remove prelude and remove builtin_ everywhere, but there's an issue that these are low-level parsing facilities. Maybe the notation will work from within a module that imports this one.

Kyle Miller (Nov 24 2024 at 20:12):

In case it's helpful, a while back I made a little do-like notation of my own: https://gist.github.com/kmill/be21ca7017d4b1d2eadd47ded5168dd1

Here's a variant: https://gist.github.com/kmill/db96b331b9f86579d5dea8e17a49425f

Both of these use syntax instead of leading_parsers, which is a higher-level interface.

Alex M (Nov 25 2024 at 09:51):

@Kyle Miller Cool. Your code works as expected, second variant too (aside from checkMaxHeartbeats). That is exactly what example i needed. Thank you. My try apparently was in wrong direction.


Last updated: May 02 2025 at 03:31 UTC