File: .lake/packages/Qq/Qq/Match.lean

full_name: Lean.Syntax.stripPos
code: partial def Lean.Syntax.stripPos : Syntax → Syntax
  
goal: Lean.Syntax → Lean.Syntax

full_name: Qq.Impl.PatVarDecl.ty
code: structure PatVarDecl where
  ty : Option Q(Expr)
  fvarId : FVarId
  userName : Name
goal: Option Q(Lean.Expr)

full_name: Qq.Impl.PatVarDecl.fvarId
code: structure PatVarDecl where
  ty : Option Q(Expr)
  fvarId : FVarId
  userName : Name
goal: Lean.FVarId

full_name: Qq.Impl.PatVarDecl.userName
code: structure PatVarDecl where
  ty : Option Q(Expr)
  fvarId : FVarId
  userName : Name
goal: Lean.Name

full_name: Qq.Impl.PatVarDecl
code: structure PatVarDecl where
  ty : Option Q(Expr)
  fvarId : FVarId
  userName : Name
goal: Type

full_name: Qq.Impl.PatVarDecl.fvarTy
code: def PatVarDecl.fvarTy : PatVarDecl → Q(Type)
  
goal: Qq.Impl.PatVarDecl → Q(Type)

full_name: Qq.Impl.PatVarDecl.fvar
code: def PatVarDecl.fvar (decl : PatVarDecl) : Q($((decl.fvarTy))) 
goal: let a := decl.fvarTy;
let match_1_1 :=
  match decl with
  | { ty := none, fvarId := fvarId, userName := userName } => q(Lean.Level)
  | { ty := some val, fvarId := fvarId, userName := userName } => q(Lean.Expr);
Q(«$match_1_1»)

full_name: Qq.Impl.mkIsDefEqType
code: def mkIsDefEqType : List PatVarDecl → Q(Type)
  
goal: List Qq.Impl.PatVarDecl → Q(Type)

full_name: Qq.Impl.mkIsDefEqResult
code: def mkIsDefEqResult (val : Bool) : (decls : List PatVarDecl) → Q($(mkIsDefEqType decls))
  
goal: let a := Qq.Impl.mkIsDefEqType decls;
let mkIsDefEqType_1 := Qq.Impl.mkIsDefEqType decls;
Q(«$mkIsDefEqType_1»)

full_name: Qq.Impl.mkIsDefEqResultVal
code: def mkIsDefEqResultVal : (decls : List PatVarDecl) → Q($(mkIsDefEqType decls)) → Q(Bool)
  
goal: (let a := Qq.Impl.mkIsDefEqType decls;
  let mkIsDefEqType_1 := Qq.Impl.mkIsDefEqType decls;
  Q(«$mkIsDefEqType_1»)) →
  Q(Bool)

full_name: Qq.Impl.mkLambda'
code: def mkLambda' (n : Name) (fvar : Expr) (ty : Expr) (body : Expr) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Qq.Impl.mkLet'
code: def mkLet' (n : Name) (fvar : Expr) (ty : Expr) (val : Expr) (body : Expr) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Qq.Impl.mkLambdaQ
code: def mkLambdaQ (n : Name) (fvar : Quoted α) (body : Quoted β) : MetaM (Quoted (mkForall n BinderInfo.default α β)) 

full_name: Qq.Impl.mkInstantiateMVars
code: def mkInstantiateMVars (decls : List PatVarDecl) : List PatVarDecl → MetaM Q(MetaM $(mkIsDefEqType decls))
  
goal: List Qq.Impl.PatVarDecl →
  Lean.MetaM
    (let a := Qq.Impl.mkIsDefEqType decls;
    let mkIsDefEqType_1 := Qq.Impl.mkIsDefEqType decls;
    Q(Lean.MetaM «$mkIsDefEqType_1»))

full_name: Qq.Impl.mkIsDefEqCore
code: def mkIsDefEqCore (decls : List PatVarDecl) (pat discr : Q(Expr)) :
    List PatVarDecl → MetaM Q(MetaM $(mkIsDefEqType decls))
  
goal: List Qq.Impl.PatVarDecl →
  Lean.MetaM
    (let a := Qq.Impl.mkIsDefEqType decls;
    let mkIsDefEqType_1 := Qq.Impl.mkIsDefEqType decls;
    Q(Lean.MetaM «$mkIsDefEqType_1»))

full_name: Qq.Impl.mkIsDefEq
code: def mkIsDefEq (decls : List PatVarDecl) (pat discr : Q(Expr)) : MetaM Q(MetaM $(mkIsDefEqType decls)) 
goal: Lean.MetaM
  (let a := Qq.Impl.mkIsDefEqType decls;
  let mkIsDefEqType_1 := Qq.Impl.mkIsDefEqType decls;
  Q(Lean.MetaM «$mkIsDefEqType_1»))

full_name: Qq.Impl.withLetHave
code: def withLetHave [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] [MonadLCtx m]
    (fvarId : FVarId) (userName : Name) (val : (Quoted α)) (k : (Quoted α) → m (Quoted β)) : m (Quoted β) 

full_name: Qq.Impl.mkQqLets
code: def mkQqLets {γ : Q(Type)} : (decls : List PatVarDecl) → Q($(mkIsDefEqType decls)) →
    TermElabM Q($γ) → TermElabM Q($γ)
  
goal: (let a := Qq.Impl.mkIsDefEqType decls;
  let mkIsDefEqType_1 := Qq.Impl.mkIsDefEqType decls;
  Q(«$mkIsDefEqType_1»)) →
  Lean.Elab.TermElabM Q(«$γ») → Lean.Elab.TermElabM Q(«$γ»)

full_name: Qq.Impl.replaceTempExprsByQVars
code: def replaceTempExprsByQVars : List PatVarDecl → Expr → Expr
  
goal: List Qq.Impl.PatVarDecl → Lean.Expr → Lean.Expr

full_name: Qq.Impl.makeMatchCode
code: def makeMatchCode {γ : Q(Type)} {m : Q(Type → Type v)} (_instLift : Q(MonadLiftT MetaM $m)) (_instBind : Q(Bind $m))
    (decls : List PatVarDecl) (uTy : Q(Level)) (ty : Q(Quoted (.sort $uTy)))
    (pat discr : Q(Quoted $ty)) (alt : Q($m $γ)) (expectedType : Expr)
    (k : Expr → TermElabM Q($m $γ)) : TermElabM Q($m $γ) 
goal: Lean.Elab.TermElabM Q(«$m» «$γ»)

full_name: Qq.Impl.unquoteForMatch
code: def unquoteForMatch (et : Expr) : UnquoteM (LocalContext × LocalInstances × Expr) 
goal: Qq.Impl.UnquoteM (Lean.LocalContext × Lean.LocalInstances × Lean.Expr)

full_name: Qq.Impl.mkNAryFunctionType
code: def mkNAryFunctionType : Nat → MetaM Expr
  
goal: Nat → Lean.MetaM Lean.Expr

full_name: Qq.Impl.getPatVars
code: partial def getPatVars (pat : Term) : StateT (Array (Name × Nat × Expr × Term)) TermElabM Term 
goal: StateT (Array (Lean.Name × Nat × Lean.Expr × Lean.Term)) Lean.Elab.TermElabM Lean.Term

full_name: Qq.Impl.getPatVars.mkMVar
code: partial def getPatVars (pat : Term) : StateT (Array (Name × Nat × Expr × Term)) TermElabM Term 
goal: StateT (Array (Lean.Name × Nat × Lean.Expr × Lean.Term)) Lean.Elab.TermElabM Lean.Term

full_name: Qq.Impl.getPatVars.isPatVar
code: partial def getPatVars (pat : Term) : StateT (Array (Name × Nat × Expr × Term)) TermElabM Term 
goal: Bool

full_name: Qq.Impl.elabPat
code: def elabPat (pat : Term) (lctx : LocalContext) (localInsts : LocalInstances) (ty : Expr)
    (levelNames : List Name) : TermElabM (Expr × Array LocalDecl × Array Name) 
goal: Lean.Elab.TermElabM (Lean.Expr × Array Lean.LocalDecl × Array Lean.Name)

full_name: Qq.Impl.«term_qq_match_←_|_In_»
code: scoped elab "_qq_match" pat:term " ← " e:term " | " alt:term " in " body:term : term <= expectedType => do
  let emr ← extractBind expectedType
  let alt ← elabTermEnsuringType alt expectedType

  let argLvlExpr ← mkFreshExprMVarQ q(Level)
  let argTyExpr ← mkFreshExprMVarQ q(Quoted (.sort $argLvlExpr))
  let e' ← elabTermEnsuringTypeQ e q(Quoted $argTyExpr)
  let argTyExpr ← instantiateMVarsQ argTyExpr

  let ((lctx, localInsts, type), s) ← (unquoteForMatch argTyExpr).run { mayPostpone := (← read).mayPostpone }
  let (pat, patVarDecls, newLevels) ← elabPat pat lctx localInsts type s.levelNames

  let mut s := s
  let mut oldPatVarDecls : List PatVarDecl := []
  for newLevel in newLevels do
    let fvarId := FVarId.mk (← mkFreshId)
    oldPatVarDecls := oldPatVarDecls ++ [{ ty := none, fvarId := fvarId, userName := newLevel }]
    s := { s with levelBackSubst := s.levelBackSubst.insert (.param newLevel) (.fvar fvarId) }

  for ldecl in patVarDecls do
    let qty ← (quoteExpr ldecl.type).run s
    oldPatVarDecls := oldPatVarDecls ++ [{ ty := some qty, fvarId := ldecl.fvarId, userName := ldecl.userName }]
    s := { s with exprBackSubst := s.exprBackSubst.insert ldecl.toExpr (.quoted ldecl.toExpr) }

  have m : Q(Type → Type) := emr.m
  have γ : Q(Type) := emr.returnType
  let inst ← synthInstanceQ q(Bind $m)
  let inst2 ← synthInstanceQ q(MonadLiftT MetaM $m)
  have synthed : Q(Expr) := (← quoteExpr (← instantiateMVars pat) s)
  let alt : Q($m $γ) := alt
  makeMatchCode q($inst2) inst oldPatVarDecls argLvlExpr argTyExpr synthed q($e') alt expectedType fun expectedType =>
    return Quoted.unsafeMk (← elabTerm body expectedType)
goal: Lean.ParserDescr

full_name: Qq.Impl.«_aux_.lake_packages_Qq_Qq_Match___elabRules_Qq_Impl_term_qq_match_←_|_In__1»
code: scoped elab "_qq_match" pat:term " ← " e:term " | " alt:term " in " body:term : term <= expectedType => do
  let emr ← extractBind expectedType
  let alt ← elabTermEnsuringType alt expectedType

  let argLvlExpr ← mkFreshExprMVarQ q(Level)
  let argTyExpr ← mkFreshExprMVarQ q(Quoted (.sort $argLvlExpr))
  let e' ← elabTermEnsuringTypeQ e q(Quoted $argTyExpr)
  let argTyExpr ← instantiateMVarsQ argTyExpr

  let ((lctx, localInsts, type), s) ← (unquoteForMatch argTyExpr).run { mayPostpone := (← read).mayPostpone }
  let (pat, patVarDecls, newLevels) ← elabPat pat lctx localInsts type s.levelNames

  let mut s := s
  let mut oldPatVarDecls : List PatVarDecl := []
  for newLevel in newLevels do
    let fvarId := FVarId.mk (← mkFreshId)
    oldPatVarDecls := oldPatVarDecls ++ [{ ty := none, fvarId := fvarId, userName := newLevel }]
    s := { s with levelBackSubst := s.levelBackSubst.insert (.param newLevel) (.fvar fvarId) }

  for ldecl in patVarDecls do
    let qty ← (quoteExpr ldecl.type).run s
    oldPatVarDecls := oldPatVarDecls ++ [{ ty := some qty, fvarId := ldecl.fvarId, userName := ldecl.userName }]
    s := { s with exprBackSubst := s.exprBackSubst.insert ldecl.toExpr (.quoted ldecl.toExpr) }

  have m : Q(Type → Type) := emr.m
  have γ : Q(Type) := emr.returnType
  let inst ← synthInstanceQ q(Bind $m)
  let inst2 ← synthInstanceQ q(MonadLiftT MetaM $m)
  have synthed : Q(Expr) := (← quoteExpr (← instantiateMVars pat) s)
  let alt : Q($m $γ) := alt
  makeMatchCode q($inst2) inst oldPatVarDecls argLvlExpr argTyExpr synthed q($e') alt expectedType fun expectedType =>
    return Quoted.unsafeMk (← elabTerm body expectedType)
goal: Lean.Elab.Term.TermElab

full_name: Qq.Impl.«term_qq_match_:=_|_»
code: scoped syntax "_qq_match" term " := " term " | " doSeq : term
goal: Lean.ParserDescr

full_name: Qq.Impl.«_aux_.lake_packages_Qq_Qq_Match___macroRules_Lean_Parser_Term_assert_1»
code: macro_rules
  | `(assert! (_qq_match $pat := $e | $alt); $x) => `(_qq_match $pat ← $e | (do $alt) in $x)
goal: Lean.Macro

full_name: Qq.Impl.isIrrefutablePattern
code: partial def isIrrefutablePattern : Term → Bool
  
goal: Lean.Term → Bool

full_name: Qq.Impl.term_comefrom_Do_In_
code: scoped elab "_comefrom" n:ident "do" b:doSeq " in " body:term : term <= expectedType => do
  let _ ← extractBind expectedType
  let ty ← exprToSyntax expectedType
  elabTerm (← `(have $n:ident : $ty := (do $b:doSeq); $body)) expectedType
goal: Lean.ParserDescr

full_name: Qq.Impl.«_aux_.lake_packages_Qq_Qq_Match___elabRules_Qq_Impl_term_comefrom_Do_In__1»
code: scoped elab "_comefrom" n:ident "do" b:doSeq " in " body:term : term <= expectedType => do
  let _ ← extractBind expectedType
  let ty ← exprToSyntax expectedType
  elabTerm (← `(have $n:ident : $ty := (do $b:doSeq); $body)) expectedType
goal: Lean.Elab.Term.TermElab

full_name: Qq.Impl.term_comefrom_Do_
code: scoped syntax "_comefrom" ident "do" doSeq : term
goal: Lean.ParserDescr

full_name: Qq.Impl.«_aux_.lake_packages_Qq_Qq_Match___macroRules_Lean_Parser_Term_assert_2»
code: macro_rules | `(assert! (_comefrom $n do $b); $body) => `(_comefrom $n do $b in $body)
goal: Lean.Macro

full_name: Qq.Impl.doElemComefrom_Do_
code: macro "comefrom" n:ident "do" b:doSeq : doElem =>
  `(doElem| assert! (_comefrom $n do $b))
goal: Lean.ParserDescr

full_name: Qq.Impl.«_aux_.lake_packages_Qq_Qq_Match___macroRules_Qq_Impl_doElemComefrom_Do__1»
code: macro "comefrom" n:ident "do" b:doSeq : doElem =>
  `(doElem| assert! (_comefrom $n do $b))
goal: Lean.Macro

full_name: Qq.Impl.mkLetDoSeqItem
code: def mkLetDoSeqItem [Monad m] [MonadQuotation m] (pat : Term) (rhs : TSyntax `term) (alt : TSyntax ``doSeq) : m (List (TSyntax ``doSeqItem)) 
goal: m (List (Lean.TSyntax `Lean.Parser.Term.doSeqItem))

full_name: Qq.matcher
code: /--
`Qq`s expression matching in `MetaM`, up to reducible defeq.

This syntax is valid in `match`, `let`, and `if let`, but not `fun`.

The usage is very similar to the builtin `Syntax`-matching that uses `` `(<pattern>)`` notation.
As an example, consider matching against a `n : Q(ℕ)`, which can be written

* With a `match` expression,
  ```
  match n with
  | ~q(Nat.gcd $x $y) => handleGcd x y
  | ~q($x + $y) => handleAdd x y
  | _ => throwError "no match"
  ```
* With a `let` expression (if there is a single match)
  ```
  let ~q(Nat.gcd $x $y) := n | throwError "no match"
  handleGcd x y
  ```
* With an `if let` statement
  ```
  if let ~q(Nat.gcd $x $y) := n then
    handleGcd x y
  else if let ~q($x + $y) := n then
    handleAdd x y
  else
    throwError "no match"
  ```

In addition to the obvious `x` and `y` captures,
in the example above `~q` also inserts into the context a term of type `$n =Q Nat.gcd $x $y`.
-/
scoped syntax (name := matcher) "~q(" term ")" : term
goal: Lean.ParserDescr

full_name: Qq.Impl.hasQMatch
code: partial def Impl.hasQMatch : Syntax → Bool
  
goal: Lean.Syntax → Bool

full_name: Qq.Impl.floatQMatch
code: partial def Impl.floatQMatch (alt : TSyntax ``doSeq) : Term → StateT (List (TSyntax ``doSeqItem)) MacroM Term
  
goal: Lean.Term → StateT (List (Lean.TSyntax `Lean.Parser.Term.doSeqItem)) Lean.MacroM Lean.Term

full_name: _private.«.lake».packages.Qq.Qq.Match.0.Qq.push
code: private def push (i : TSyntax ``doSeqItem) : StateT (Array (TSyntax ``doSeqItem)) MacroM Unit 
goal: StateT (Array (Lean.TSyntax `Lean.Parser.Term.doSeqItem)) Lean.MacroM Unit

full_name: Qq.unpackParensIdent
code: partial def unpackParensIdent : Syntax → Option Syntax
  
goal: Lean.Syntax → Option Lean.Syntax

full_name: _private.«.lake».packages.Qq.Qq.Match.0.Qq.floatLevelAntiquot
code: private partial def floatLevelAntiquot (stx : Syntax.Level) : StateT (Array (TSyntax ``doSeqItem)) MacroM Syntax.Level 
goal: StateT (Array (Lean.TSyntax `Lean.Parser.Term.doSeqItem)) Lean.MacroM Lean.Syntax.Level

full_name: _private.«.lake».packages.Qq.Qq.Match.0.Qq.floatExprAntiquot
code: private partial def floatExprAntiquot (depth : Nat) : Term → StateT (Array (TSyntax ``doSeqItem)) MacroM Term
  
goal: Lean.Term → StateT (Array (Lean.TSyntax `Lean.Parser.Term.doSeqItem)) Lean.MacroM Lean.Term

full_name: Qq.«_aux_.lake_packages_Qq_Qq_Match___macroRules_Lean_Parser_Term_doMatch_1»
code: macro_rules
  | `(doElem| let $pat:term := $_) => do
    if !hasQMatch pat then Macro.throwUnsupported
    Macro.throwError "let-bindings with ~q(.) require an explicit alternative"

  | `(doElem| let $pat:term := $rhs:term | $alt:doSeq) => do
    if !hasQMatch pat then Macro.throwUnsupported
    match pat with
      | `(~q($pat)) =>
        let (pat, lifts) ← floatExprAntiquot 0 pat #[]
        let t ← `(doSeqItem| do assert! (_qq_match $pat := $rhs | $alt))
        `(doElem| do $(lifts.push t):doSeqItem*)

      | _ =>
        let (pat', auxs) ← floatQMatch (← `(doSeq| alt)) pat []
        let items :=
          #[← `(doSeqItem| comefrom alt do $alt:doSeq)] ++
          (← mkLetDoSeqItem pat' rhs alt) ++
          auxs
        `(doElem| do $items:doSeqItem*)

  | `(match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported
    `(do match $[$gen]? $[$discrs:term],* with $[| $[$patss:term],* => $rhss:term]*)

  | `(doElem| match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported

    -- only `generalizing := true` (the default) is supported
    if let some stx := gen then
      match stx with
      | `(generalizingParam| (generalizing := true)) => pure ()
      | _ => Macro.throwErrorAt stx "not supported in ~q matching"

    let mut items := #[]
    items := items.push (← `(doSeqItem| comefrom alt do throwError "nonexhaustive match"))
    for pats in patss.reverse, rhs in rhss.reverse do
      let mut subItems : Array (TSyntax ``doSeqItem) := #[]
      for discr in discrs, pat in pats do
        subItems := subItems ++ (← mkLetDoSeqItem pat discr (← `(doSeq| alt)))
      subItems := subItems.push (← `(doSeqItem| do $rhs))
      items := items.push (← `(doSeqItem| comefrom alt do $subItems:doSeqItem*))
    items := items.push (← `(doSeqItem| alt))
    `(doElem| (do $items:doSeqItem*))
goal: Lean.Macro

full_name: Qq.«_aux_.lake_packages_Qq_Qq_Match___macroRules_Lean_Parser_Term_doLetElse_1»
code: macro_rules
  | `(doElem| let $pat:term := $_) => do
    if !hasQMatch pat then Macro.throwUnsupported
    Macro.throwError "let-bindings with ~q(.) require an explicit alternative"

  | `(doElem| let $pat:term := $rhs:term | $alt:doSeq) => do
    if !hasQMatch pat then Macro.throwUnsupported
    match pat with
      | `(~q($pat)) =>
        let (pat, lifts) ← floatExprAntiquot 0 pat #[]
        let t ← `(doSeqItem| do assert! (_qq_match $pat := $rhs | $alt))
        `(doElem| do $(lifts.push t):doSeqItem*)

      | _ =>
        let (pat', auxs) ← floatQMatch (← `(doSeq| alt)) pat []
        let items :=
          #[← `(doSeqItem| comefrom alt do $alt:doSeq)] ++
          (← mkLetDoSeqItem pat' rhs alt) ++
          auxs
        `(doElem| do $items:doSeqItem*)

  | `(match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported
    `(do match $[$gen]? $[$discrs:term],* with $[| $[$patss:term],* => $rhss:term]*)

  | `(doElem| match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported

    -- only `generalizing := true` (the default) is supported
    if let some stx := gen then
      match stx with
      | `(generalizingParam| (generalizing := true)) => pure ()
      | _ => Macro.throwErrorAt stx "not supported in ~q matching"

    let mut items := #[]
    items := items.push (← `(doSeqItem| comefrom alt do throwError "nonexhaustive match"))
    for pats in patss.reverse, rhs in rhss.reverse do
      let mut subItems : Array (TSyntax ``doSeqItem) := #[]
      for discr in discrs, pat in pats do
        subItems := subItems ++ (← mkLetDoSeqItem pat discr (← `(doSeq| alt)))
      subItems := subItems.push (← `(doSeqItem| do $rhs))
      items := items.push (← `(doSeqItem| comefrom alt do $subItems:doSeqItem*))
    items := items.push (← `(doSeqItem| alt))
    `(doElem| (do $items:doSeqItem*))
goal: Lean.Macro

full_name: Qq.«_aux_.lake_packages_Qq_Qq_Match___macroRules_Lean_Parser_Term_match_1»
code: macro_rules
  | `(doElem| let $pat:term := $_) => do
    if !hasQMatch pat then Macro.throwUnsupported
    Macro.throwError "let-bindings with ~q(.) require an explicit alternative"

  | `(doElem| let $pat:term := $rhs:term | $alt:doSeq) => do
    if !hasQMatch pat then Macro.throwUnsupported
    match pat with
      | `(~q($pat)) =>
        let (pat, lifts) ← floatExprAntiquot 0 pat #[]
        let t ← `(doSeqItem| do assert! (_qq_match $pat := $rhs | $alt))
        `(doElem| do $(lifts.push t):doSeqItem*)

      | _ =>
        let (pat', auxs) ← floatQMatch (← `(doSeq| alt)) pat []
        let items :=
          #[← `(doSeqItem| comefrom alt do $alt:doSeq)] ++
          (← mkLetDoSeqItem pat' rhs alt) ++
          auxs
        `(doElem| do $items:doSeqItem*)

  | `(match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported
    `(do match $[$gen]? $[$discrs:term],* with $[| $[$patss:term],* => $rhss:term]*)

  | `(doElem| match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported

    -- only `generalizing := true` (the default) is supported
    if let some stx := gen then
      match stx with
      | `(generalizingParam| (generalizing := true)) => pure ()
      | _ => Macro.throwErrorAt stx "not supported in ~q matching"

    let mut items := #[]
    items := items.push (← `(doSeqItem| comefrom alt do throwError "nonexhaustive match"))
    for pats in patss.reverse, rhs in rhss.reverse do
      let mut subItems : Array (TSyntax ``doSeqItem) := #[]
      for discr in discrs, pat in pats do
        subItems := subItems ++ (← mkLetDoSeqItem pat discr (← `(doSeq| alt)))
      subItems := subItems.push (← `(doSeqItem| do $rhs))
      items := items.push (← `(doSeqItem| comefrom alt do $subItems:doSeqItem*))
    items := items.push (← `(doSeqItem| alt))
    `(doElem| (do $items:doSeqItem*))
goal: Lean.Macro

full_name: Qq.«_aux_.lake_packages_Qq_Qq_Match___macroRules_Lean_Parser_Term_doLet_1»
code: macro_rules
  | `(doElem| let $pat:term := $_) => do
    if !hasQMatch pat then Macro.throwUnsupported
    Macro.throwError "let-bindings with ~q(.) require an explicit alternative"

  | `(doElem| let $pat:term := $rhs:term | $alt:doSeq) => do
    if !hasQMatch pat then Macro.throwUnsupported
    match pat with
      | `(~q($pat)) =>
        let (pat, lifts) ← floatExprAntiquot 0 pat #[]
        let t ← `(doSeqItem| do assert! (_qq_match $pat := $rhs | $alt))
        `(doElem| do $(lifts.push t):doSeqItem*)

      | _ =>
        let (pat', auxs) ← floatQMatch (← `(doSeq| alt)) pat []
        let items :=
          #[← `(doSeqItem| comefrom alt do $alt:doSeq)] ++
          (← mkLetDoSeqItem pat' rhs alt) ++
          auxs
        `(doElem| do $items:doSeqItem*)

  | `(match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported
    `(do match $[$gen]? $[$discrs:term],* with $[| $[$patss:term],* => $rhss:term]*)

  | `(doElem| match $[$gen:generalizingParam]? $[$discrs:term],* with $[| $[$patss],* => $rhss]*) => do
    if !patss.any (·.any (hasQMatch ·)) then Macro.throwUnsupported

    -- only `generalizing := true` (the default) is supported
    if let some stx := gen then
      match stx with
      | `(generalizingParam| (generalizing := true)) => pure ()
      | _ => Macro.throwErrorAt stx "not supported in ~q matching"

    let mut items := #[]
    items := items.push (← `(doSeqItem| comefrom alt do throwError "nonexhaustive match"))
    for pats in patss.reverse, rhs in rhss.reverse do
      let mut subItems : Array (TSyntax ``doSeqItem) := #[]
      for discr in discrs, pat in pats do
        subItems := subItems ++ (← mkLetDoSeqItem pat discr (← `(doSeq| alt)))
      subItems := subItems.push (← `(doSeqItem| do $rhs))
      items := items.push (← `(doSeqItem| comefrom alt do $subItems:doSeqItem*))
    items := items.push (← `(doSeqItem| alt))
    `(doElem| (do $items:doSeqItem*))
goal: Lean.Macro