Zulip Chat Archive
Stream: lean4
Topic: Accessing splices and joining quoted syntax in macros
Codegoblin (Apr 15 2024 at 13:58):
Hello,
I would like to bundle multiple commands together as a new command.
However, I have problems to compute the splices as identifiers in the attached example. (I get Error : unexpected token '*'; expected ':=', 'where' or '|')
Additionally, I would like to join x and y, so that both are executed when you call the command. Is this even possible?
open Lean
syntax "create" ident "{"
(ident ":" ident),*
"}" : command
set_option hygiene false in
macro_rules
| `(create $name {}) => `(variable ($name : R))
| `(create $name {
$[$relation : $name2],*
}
)
=> show MacroM Command from do
let x : MacroM (TSyntax `command) :=
`(
variable ($name : R)
variable ($name2* : R)
-- rel def ?
def $relation* : Prop := true -- how to access relation as identifier
)
let y : MacroM (TSyntax `command) := `(variable (xx : Nat))
x -- join x and y
create A {
B : C
}
#check A
#check B
#check C
Marcus Rossel (Apr 16 2024 at 04:57):
What is create A { B : C }
supposed to do?
Codegoblin (Apr 16 2024 at 08:37):
@Marcus Rossel
create A { B : C }
It is supposed to execute the commands in the Macro.
Meaning to create the Variables A and C. This and the matching work already.
Then I would also like it to create the definition (or theorem) with name B. However, here I can't (or don't know how to) access the ident "relation".
Since I want the possibility to add more than one in the {}-brackets, I used the *-Operator.
Another possibility would be to use a for loop over relation and name2, but then I would have to join multiple of the Expressions in the `()-Quotation together (which I also do not know how to do).
Maybe there is also an easier way to accomplish my goal?
Jon Eugster (Apr 16 2024 at 09:28):
Is it that you just need to give your definition a name? adding I think I actually don't understand what you try to do, sorry. If def xy $relation* : Prop := True
seems to compile at first glance.B
is a list of names then I think you should create one definition per name. If B
includes hypotheses, then I think you need separate name & declSign as in the link below.
Jon Eugster (Apr 16 2024 at 09:32):
(note that I have no clue, but for reference here I create some theorems on the fly, maybe that helps for comparison?)
Eric Wieser (Apr 16 2024 at 09:57):
You can't access relation
as an identifier, because relation
is an Array
. Did you want to loop over that array?
Codegoblin (Apr 16 2024 at 10:06):
@Eric Wieser Indeed, that's what I want. I want that the def is created as many times as there is something in the array.
@Jon Eugster I think my description was unclear. I want to create the possibility to add *-many entrys in the {}. For example
create A{
B : C
D : E
F : G
H : J
}
should also be possible. (And can already be parsed). I would like to access relation in a manner that the def is done *-times like in the variable creation (
variable ($name2* : R)
E: So the output of the Example above would be.
variable (A:R)
variable (C:R)
variable (E:R)
variable (G:R)
variable (J:R)
def B : Prop := true
def D : Prop := true
def F : Prop := true
def H : Prop := true
Codegoblin (Apr 18 2024 at 12:05):
Here is a minimalistic example of what I'm trying to archive.
While cmd1 works, in cmd2 I get an Error.
If there is another way to achieve this goal (maybe without macro?), I will also gladly hear it.
open Lean
syntax "cmd1" ident,* : command
macro_rules
| `(cmd1 $[$n],*) => do
`(variable ($n* : Prop)) -- Error in this line
cmd1 A, B, C
#check A
#check B
#check C
syntax "cmd2" ident,* : command
set_option hygiene false in
macro_rules
| `(cmd2 $[$n],*) => do
`(def $n* : Prop := True)
cmd2 A, B, C
#check A
#check B
#check C
Marcus Rossel (Apr 18 2024 at 12:25):
I'm not sure where R
comes from, but this implements the example you gave:
import Lean
open Lean
declare_syntax_cat entry
syntax ident " : " ident : entry
private def splitEntries (entries : Array (TSyntax `entry)) : (Array Ident) × (Array Ident) :=
entries.foldl (init := (#[], #[])) fun (ls, rs) => fun
| `(entry|$l:ident : $r) => (ls.push l, rs.push r)
| _ => unreachable!
macro "create" a:ident "{" entries:entry* "}" : command => do
let (ls, rs) := splitEntries entries
let rIdent := mkIdent `R
let vars ← (#[a] ++ rs).mapM fun r => `(variable ($r : $rIdent))
let defs ← ls.mapM fun l => `(def $l : Prop := true)
let cmds := vars ++ defs
return ⟨mkNullNode cmds⟩
create A {
B : C
D : E
F : G
}
#check A -- A : R
#check B -- B : Prop
(For an explanation of the mkNullNode
, see https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Antiquot.20Splice/near/297760730)
Codegoblin (Apr 18 2024 at 13:08):
@Marcus Rossel Thank you VERY much. Not only did you show me the entry "trick", you also showed how to join multiple Command outputs. I could apply it perfectly to my Problem. :smiley:
Last updated: May 02 2025 at 03:31 UTC