Zulip Chat Archive

Stream: lean4

Topic: macro_rules not apply


Remy Citerin (Aug 06 2023 at 13:04):

Hello, I tried to use macro_rules to make DSL's but I have the impression that in some cases the macros are not applied, I don't understand

declare_syntax_cat foo
syntax "[bar|" foo "]" : term
syntax num : foo
syntax " ( " foo " ) " : foo

macro_rules
| `(foo| ( $f:foo )) => `(foo| $f)
| `([bar|$n:num]) => `($n)

#eval [bar|0]
#eval  [bar|(0)]

#eval [bar|(0)] return an error, the elaboration function is not implemented according to Lean

Marcus Rossel (Aug 06 2023 at 13:52):

You're not handling the case for [bar|f]where f is a foo but not a num. This fixes it:

declare_syntax_cat foo
syntax "[bar|" foo "]" : term
syntax num : foo
syntax " ( " foo " ) " : foo

macro_rules
| `(foo| ( $f:foo ))    => `(foo| $f)
| `([bar|$n:num])       => `($n)
| `([bar| ( $f:foo ) ]) => `([bar|$f]) -- add this

#eval [bar|0]
#eval [bar|(0)]

Remy Citerin (Aug 06 2023 at 14:28):

yes but normally it should just reduce (0) to 0 in [bar|(0)]? And that doesn't solve the problem I want to solve (here is just a minimal example), because in fact I'm not really using $[bar|$f]$ but a custom elaborator that I want to keep simple: for example having a general notation for binary operators like [BINOP| +, $e1, $e2] then have a set of macros to have +, *... with the right associativity...

Marcus Rossel (Aug 06 2023 at 14:45):

Does this serve your use case better?:

macro_rules
  | `(foo|$n:num)      => `($n)
  | `(foo| ( $f:foo )) => `(foo| $f)
  | `([bar|$f:foo])    => `(foo| $f)

Remy Citerin (Aug 07 2023 at 12:16):

no it doesn't work, like I said, I actually want to be able to use custom elaborators:

declare_syntax_cat foo
syntax "[bar|" foo "]" : term
syntax num : foo
syntax " ( " foo " ) " : foo

macro_rules
  | `(foo|$n:num)      => `($n)
  | `(foo| ( $f:foo )) => `(foo| $f)
  | `([bar|$f:foo])    => `(foo| $f)
-- my goal: elabFoo does not contain the rule `($f:foo)` because this case is handled by macros
-- so the user of my library can add macros in my DSL
open Qq in
def elabFoo : TSyntax `foo  TermElabM Q()
  | `(foo| $n:num) => do
    have n:  := n.getNat
    return q($n)
  | s => throw <| .error s "unsupported syntax"

elab "elab_foo " e:foo : term => elabFoo e
#eval [bar|0]
#eval [bar|(0)]
#eval elab_foo (0) -- error: unsupported syntax

Mac Malone (Aug 07 2023 at 22:54):

@Remy Citerin You can use docs#Lean.expandMacros to apply any macros to a syntax before match on it. For example:

def elabFoo (foo : TSyntax `foo) : TermElabM Q() :=
  let foo <- liftMacroM <| expandMacros foo
  match foo with
  | `(foo| $n:num) => do
    have n:  := n.getNat
    return q($n)
  | s => throw <| .error s "unsupported syntax"

Sebastian Ullrich (Aug 08 2023 at 07:22):

The first choice in an elaborator should be to recursively elaborate nested syntax and then match on the Expr because that will deal with both macros and elaborators. It depends on the specific use case of course.

Sebastian Ullrich (Aug 08 2023 at 07:24):

To answer the underlying question:
Remy Citerin said:

yes but normally it should just reduce (0) to 0 in [bar|(0)]?

No, macro expansion works outside-in. This is important for providing the outer macro with as much structure as possible as well as to unify expansion and elaboration.

Remy Citerin (Aug 08 2023 at 13:44):

using Lean.expandMacros works very well, thanks for the explanation


Last updated: Dec 20 2023 at 11:08 UTC