You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Lean的UnexpandM语法引用中包含≅等特殊字符?

解决Lean中Congruent反展开器无法正确显示≅符号的问题

问题原因

核心问题在于Lean的语法引用(`(...))无法直接识别未注册的符号≅——默认情况下Lean的term语法范畴不包含该符号,导致反展开器里的$ab ≅ $cd被判定为非法语法。

解决方案

要让反展开器正确生成ab ≅ cd的美化输出,需先将≅注册为Lean语法系统中合法的term级符号,再调整反展开器实现。

完整代码示例

import Lean

inductive Point : Type

inductive Congruent : Point -> Point -> Point -> Point -> Prop

-- 第一步:注册 `≅` 相关语法,让Lean识别 `ident ≅ ident` 为合法term
declare_syntax_cat congruence_expr
syntax ident " ≅ " ident : congruence_expr
syntax congruence_expr : term

-- 第二步:定义宏,将 `ab ≅ cd` 展开为 `Congruent a b c d`
macro ab:ident " ≅ " cd:ident : term =>
  match ab.getId.toString.toList with
    | [a, b] =>
      match cd.getId.toString.toList with
      | [c, d] =>
        let a := Lean.mkIdent (Lean.Name.mkStr1 ("".push a))
        let b := Lean.mkIdent (Lean.Name.mkStr1 ("".push b))
        let c := Lean.mkIdent (Lean.Name.mkStr1 ("".push c))
        let d := Lean.mkIdent (Lean.Name.mkStr1 ("".push d))
        `(Congruent $a $b $c $d)
      | _ => Lean.Macro.throwUnsupported
    | _ => Lean.Macro.throwUnsupported

-- 第三步:定义反展开器,将 `Congruent a b c d` 还原为 `ab ≅ cd`
open Lean PrettyPrinter Delaborator SubExpr in
@[app_unexpander Congruent]
def unexpandCongruent : Unexpander
  | `(Congruent $a:ident $b:ident $c:ident $d:ident) => do
    let ab := mkIdent (Name.mkStr1 (a.getId.toString ++ b.getId.toString))
    let cd := mkIdent (Name.mkStr1 (c.getId.toString ++ d.getId.toString))
    `($ab ≅ $cd)
  | _ => throw ()

variable (a b c d : Point)

#check ab ≅ cd -- 现在输出为:ab ≅ cd : Prop

关键说明

  1. 语法注册:通过declare_syntax_cat创建自定义语法范畴congruence_expr,将ident " ≅ " ident纳入该范畴后并入term语法,让Lean解析器认可ab ≅ cd是合法表达式。
  2. 反展开器兼容:注册语法后,语法引用$ab ≅ $cd可被正确解析,反展开器能生成目标美化格式,#check命令会显示ab ≅ cd而非原始的Congruent a b c d。

若不想额外注册语法范畴,也可手动构造Syntax节点替代语法引用,示例如下:

open Lean PrettyPrinter Delaborator SubExpr in
@[app_unexpander Congruent]
def unexpandCongruent : Unexpander
  | `(Congruent $a:ident $b:ident $c:ident $d:ident) => do
    let ab := mkIdent (Name.mkStr1 (a.getId.toString ++ b.getId.toString))
    let cd := mkIdent (Name.mkStr1 (c.getId.toString ++ d.getId.toString))
    -- 手动构造包含≅的Syntax节点
    return Syntax.node `term #[ab.raw, Syntax.atom " ≅ ", cd.raw]
  | _ => throw ()

这种方式跳过语法注册,直接生成符合要求的语法树,同样能实现美化输出。

内容的提问来源于stack exchange,提问作者owlattack22

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 22:42:15