Zulip Chat Archive
Stream: lean4
Topic: Unexpander can't match expression with nested parentheses
Chris Wong (Feb 16 2025 at 02:24):
Hi all, I ran into a problem that I've minimized here:
import Mathlib
open Lean
open Lean.PrettyPrinter
def Board := Array (Option (Option Bool))
def Board.mk (array : Array (Option (Option Bool))) : Board := array
declare_syntax_cat cell
syntax "- " : cell
syntax "# " : cell
syntax "I " : cell
syntax "board " cell* : term
open Lean.Macro in
macro_rules
| `(term| board $cells:cell*) => do
let cells := ← cells.mapM fun
| `(cell| -) => ``(none)
| `(cell| #) => ``(some none)
| `(cell| I) => ``(some (some true))
| _ => throwUnsupported
``(Board.mk #[ $[$cells],* ])
@[app_unexpander Board.mk]
def unexpandBoard : Unexpander
| `($_boardMk #[$cells,*]) => do
let cells := ← (cells : TSyntaxArray `term).mapM fun
| `(none) => `(cell| -)
| `(some none) => `(cell| #)
| `(some (some true)) => `(cell| I)
| _ => throw ()
`(board $cells*)
| _ => throw ()
def board1 : Board := board - #
def board2 : Board := board - # I
#print board1
#print board2
board1
pretty-prints successfully, but board2
doesn't, and it seems like the difference is that the latter has a nested constructor.
Any ideas why this happens?
Context: I'm formalizing Tetris, and as part of that, I'm making a DSL for constructing game boards. Here's the unminimized code.
Kyle Miller (Feb 16 2025 at 02:27):
Unexpanders get run before parenthesization, so you can't have parentheses in your match
patterns.
A way to work around this limitation is to nest match
es
Kyle Miller (Feb 16 2025 at 02:28):
@[app_unexpander Board.mk]
def unexpandBoard : Unexpander
| `($_boardMk #[$cells,*]) => do
let cells := ← (cells : TSyntaxArray `term).mapM fun
| `(none) => `(cell| -)
| `(some none) => `(cell| #)
| `(some $v) =>
match v with
| `(some true) => `(cell| I)
| _ => throw ()
| _ => throw ()
`(board $cells*)
| _ => throw ()
Chris Wong (Feb 16 2025 at 02:29):
Thanks Kyle, that makes sense!
Chris Wong (Feb 16 2025 at 03:53):
I wonder if it's worth calling this out in the Metaprogramming in Lean book.
Kyle Miller (Feb 16 2025 at 04:48):
Maybe match
could also have some widget to make this more convenient, like
| stripParens `(some (some true)) => `(cell| I)
Chris Wong (Feb 16 2025 at 05:43):
Chris Wong said:
I wonder if it's worth calling this out in the Metaprogramming in Lean book.
https://github.com/leanprover-community/lean4-metaprogramming-book/pull/162
Last updated: May 02 2025 at 03:31 UTC