1- /- # Extra: Pretty Printing
2- The pretty printer is what Lean uses to present terms that have been
3- elaborated to the user. This is done by converting the `Expr`s back into
4- `Syntax` and then even higher level pretty printing datastructures. This means
5- Lean does not actually recall the `Syntax` it used to create some `Expr`:
6- there has to be code that tells it how to do that.
7- In the big picture, the pretty printer consists of three parts run in the
8- order they are listed in:
9-
10- - the **[ delaborator ] (https://github.com/leanprover/lean4/tree/master/src/Lean/PrettyPrinter/Delaborator)**
11- this will be our main interest since we can easily extend it with our own code.
12- Its job is to turn `Expr` back into `Syntax`.
13- - the **[ parenthesizer ] (https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Parenthesizer.lean)**
14- responsible for adding parenthesis into the `Syntax` tree, where it thinks they would be useful
15- - the **[ formatter ] (https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Formatter.lean)**
16- responsible for turning the parenthesized `Syntax` tree into a `Format` object that contains
17- more pretty printing information like explicit whitespaces
18-
19- ## Delaboration
20- As its name suggests, the delaborator is in a sense the opposite of the
21- elaborator. The job of the delaborator is to take an `Expr` produced by
22- the elaborator and turn it back into a `Syntax` which, if elaborated,
23- should produce an `Expr` that behaves equally to the input one.
24-
25- Delaborators have the type `Lean.PrettyPrinter.Delaborator.Delab`. This
26- is an alias for `DelabM Syntax`, where `DelabM` is the delaboration monad.
27- All of this machinery is defined [ here ] (https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Delaborator/Basic.lean).
28- `DelabM` provides us with quite a lot of options you can look up in the documentation
29- (TODO: Docs link). We will merely highlight the most relevant parts here.
30-
31- - It has a `MonadQuotation` instance which allows us to declare `Syntax` objects
32- using the familiar quotation syntax.
33- - It can run `MetaM` code.
34- - It has a `MonadExcept` instance for throwing errors.
35- - It can interact with `pp` options using functions like `whenPPOption`.
36- - You can obtain the current subexpression using `SubExpr.getExpr`. There is
37- also an entire API defined around this concept in the `SubExpr` module.
38-
39- ### Making our own
40- Like so many things in metaprogramming the elaborator is based on an attribute,
41- in this case the `delab` one. `delab` expects a `Name` as an argument,
42- this name has to start with the name of an `Expr` constructor, most commonly
43- `const` or `app`. This constructor name is then followed by the name of the
44- constant we want to delaborate. For example, if we want to delaborate a function
45- `foo` in a special way we would use `app.foo`. Let's see this in action:
1+ /- # 额外内容:美观打印
2+ Lean 的美观打印器用于向用户呈现已繁饰的术语。这是通过将 `Expr` 转换回 `Syntax`,然后再转换为更高级的美观打印数据结构来完成的。这意味着 Lean 实际上不会记住它用来创建某些 `Expr` 的 `Syntax`:必须有代码告诉它如何执行此操作。
3+
4+ 从宏观角度来看,美观打印器由三个部分组成,按列出的顺序运行:
5+
6+ - **[ 反繁饰器 ] (https://github.com/leanprover/lean4/tree/master/src/Lean/PrettyPrinter/Delaborator)**
7+ 这是我们主要感兴趣的部分,因为我们可以轻松地用自己的代码扩展它。它的任务是将 `Expr` 转换回 `Syntax`。
8+ - **[ 括号添加器 ] (https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Parenthesizer.lean)**
9+ 负责在 `Syntax` 树中添加括号,它认为在某些地方括号会有帮助。
10+ - **[ 格式化器 ] (https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Formatter.lean)**
11+ 负责将加了括号的 `Syntax` 树转换为包含更多美观打印信息(如显式空格)的 `Format` 对象。
12+
13+ ## 反繁饰
14+ 顾名思义,反繁饰器在某种意义上是繁饰器的反面。反繁饰器的任务是将由繁饰器生成的 `Expr` 转换回 `Syntax`,如果再次繁饰,应该生成一个与输入行为相同的 `Expr`。
15+
16+ 反繁饰器的类型是 `Lean.PrettyPrinter.Delaborator.Delab`。这是 `DelabM Syntax` 的别名,其中 `DelabM` 是反繁饰 monad。所有这些机制都定义在[ 这里 ] (https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Delaborator/Basic.lean)。`DelabM` 为我们提供了很多选项,您可以在文档中查看(TODO:文档链接)。这里我们只强调最相关的部分。
17+
18+ - 它有一个 `MonadQuotation` 实例,允许我们使用熟悉的引用语法声明 `Syntax` 对象。
19+ - 它可以运行 `MetaM` 代码。
20+ - 它有一个 `MonadExcept` 实例用于抛出错误。
21+ - 它可以使用 `whenPPOption` 等函数与 `pp` 选项交互。
22+ - 您可以使用 `SubExpr.getExpr` 获取当前的子表达式。`SubExpr` 模块中还围绕这个概念定义了整个 API。
23+
24+ ### 创建我们自己的反繁饰器
25+ 像元编程中的许多事情一样,反繁饰器基于属性,在这种情况下是 `delab` 属性。`delab` 期望以 `Name` 作为参数,这个名称必须以 `Expr` 构造函数的名称开头,最常见的是 `const` 或 `app`。此构造函数名称后面跟着我们想要反繁饰的常量名称。例如,如果我们想以特殊方式反繁饰函数 `foo`,我们将使用 `app.foo`。让我们来看一下实际操作:
4626-/
4727
4828import Lean
@@ -56,12 +36,10 @@ def delabFoo : Delab := do
5636 `(1 )
5737
5838#check foo -- 1 : Nat → Nat
59- #check foo 13 -- 1 : Nat, full applications are also pretty printed this way
39+ #check foo 13 -- 1 : Nat, 整个应用同样被这样打印出来了
6040
6141/-!
62- This is obviously not a good delaborator since reelaborating this `Syntax`
63- will not yield the same `Expr`. Like with many other metaprogramming
64- attributes we can also overload delaborators:
42+ 这显然不是一个好的反繁饰器,因为重新繁饰此 `Syntax` 不会产生相同的 `Expr`。像许多其他元编程属性一样,我们也可以重载反繁饰器:
6543-/
6644
6745@ [delab app.foo]
@@ -71,65 +49,48 @@ def delabfoo2 : Delab := do
7149#check foo -- 2 : Nat → Nat
7250
7351/-!
74- The mechanism for figuring out which one to use is the same as well. The
75- delaborators are tried in order, in reverse order of registering, until one
76- does not throw an error, indicating that it "feels unresponsible for the `Expr`".
77- In the case of delaborators, this is done using `failure`:
52+ 确定使用哪个反繁饰器的机制也是相同的。反繁饰器按照注册的相反顺序依次尝试,直到其中一个没有抛出错误,表明它「不对这个 `Expr` 负责」。在反繁饰器的情况下,这是通过使用 `failure` 来完成的:
7853-/
7954
8055@ [delab app.foo]
8156def delabfoo3 : Delab := do
8257 failure
8358 `(3 )
8459
85- #check foo -- 2 : Nat → Nat, still 2 since 3 failed
60+ #check foo -- 2 : Nat → Nat, 还是 2 因为 3 失败了
8661
8762/-!
88- In order to write a proper delaborator for `foo`, we will have to use some
89- slightly more advanced machinery though:
63+ 为了为 `foo` 编写一个合适的反繁饰器,我们将不得不使用一些稍微高级一点的机制:
9064-/
9165
9266@ [delab app.foo]
9367def delabfooFinal : Delab := do
9468 let e ← getExpr
95- guard $ e.isAppOfArity' `foo 1 -- only delab full applications this way
69+ guard $ e.isAppOfArity' `foo 1 -- 只能像这样反繁饰整个应用
9670 let fn := mkIdent `fooSpecial
9771 let arg ← withAppArg delab
9872 `($fn $arg)
9973
10074#check foo 42 -- fooSpecial 42 : Nat
101- #check foo -- 2 : Nat → Nat, still 2 since 3 failed
75+ #check foo -- 2 : Nat → Nat, 还是 2 因为 3 失败了
10276
10377/-!
104- Can you extend `delabFooFinal` to also account for non full applications?
105-
106- ## Unexpanders
107- While delaborators are obviously quite powerful it is quite often not necessary
108- to use them. If you look in the Lean compiler for `@[delab` or rather `@[builtin_delab`
109- (a special version of the `delab` attribute for compiler use, we don't care about it),
110- you will see there are quite few occurrences of it. This is because the majority
111- of pretty printing is in fact done by so called unexpanders. Unlike delaborators
112- they are of type `Lean.PrettyPrinter.Unexpander` which in turn is an alias for
113- `Syntax → Lean.PrettyPrinter.UnexpandM Syntax`. As you can see, they are
114- `Syntax` to `Syntax` translations, quite similar to macros, except that they
115- are supposed to be the inverse of macros. The `UnexpandM` monad is quite a lot
116- weaker than `DelabM` but it still has:
117-
118- - `MonadQuotation` for syntax quotations
119- - The ability to throw errors, although not very informative ones: `throw ()`
120- is the only valid one
121-
122- Unexpanders are always specific to applications of one constant. They are registered
123- using the `app_unexpander` attribute, followed by the name of said constant. The unexpander
124- is passed the entire application of the constant after the `Expr` has been delaborated,
125- without implicit arguments. Let's see this in action:
78+ 你能扩展 `delabFooFinal` 来处理非完整应用的情况吗?
79+
80+ ## 反扩展器
81+ 虽然反繁饰器非常强大,但很多情况下并不需要使用它们。如果你查看 Lean 编译器中的 `@[delab` 或 `@[builtin_delab`(编译器使用的 `delab` 属性的特殊版本,我们对此不关心),你会发现它们的出现次数很少。这是因为大多数美观打印实际上是由所谓的反扩展器完成的。与反繁饰器不同,反扩展器的类型是 `Lean.PrettyPrinter.Unexpander`,这实际上是 `Syntax → Lean.PrettyPrinter.UnexpandM Syntax` 的别名。正如你所看到的,它们是 `Syntax` 到 `Syntax` 的转换,类似于宏,区别在于它们应该是宏的逆向操作。`UnexpandM` monad 比 `DelabM` 弱得多,但它仍然具有:
82+
83+ - 支持语法引用的 `MonadQuotation`
84+ - 能够抛出错误,尽管错误信息并不十分详细:`throw ()` 是唯一有效的错误
85+
86+ 反扩展器始终针对一个常量的应用进行。它们通过 `app_unexpander` 属性注册,后面跟着该常量的名称。反扩展器在 `Expr` 经过反繁饰后被传递整个常量应用,而不包含隐式参数。让我们看看这个过程如何操作:
12687-/
12788
12889def myid {α : Type } (x : α) := x
12990
13091@ [app_unexpander myid]
13192def unexpMyId : Unexpander
132- -- hygiene disabled so we can actually return `id` without macro scopes etc.
93+ -- 禁用语法卫生,这样我们实际上可以返回 `id`,而不涉及宏范围等。
13394 | `(myid $arg) => set_option hygiene false in `(id $arg)
13495 | `(myid) => pure $ mkIdent `id
13596 | _ => throw ()
@@ -138,15 +99,10 @@ def unexpMyId : Unexpander
13899#check myid -- id : ?m.3870 → ?m.3870
139100
140101/-!
141- For a few nice examples of unexpanders you can take a look at
142- [ NotationExtra ] (https://github.com/leanprover/lean4/blob/master/src/Init/NotationExtra.lean)
143-
144- ### Mini project
145- As per usual, we will tackle a little mini project at the end of the chapter.
146- This time we build our own unexpander for a mini programming language.
147- Note that many ways to define syntax already have generation of the required
148- pretty printer code built-in, e.g. `infix`, and `notation` (however not `macro_rules`).
149- So, for easy syntax, you will never have to do this yourself.
102+ 关于一些反扩展器的不错示例,你可以查看 [ NotationExtra ] (https://github.com/leanprover/lean4/blob/master/src/Init/NotationExtra.lean)
103+
104+ ### 项目示例
105+ 像往常一样,我们将在本章末进行一个迷你项目。这次我们将为一个迷你编程语言构建自己的反扩展器。请注意,许多定义语法的方法已经内置了生成所需的漂亮打印代码,例如 `infix` 和 `notation`(但不包括 `macro_rules`)。因此,对于简单的语法,你不需要自己手动编写这些代码。
150106-/
151107
152108declare_syntax_cat lang
@@ -180,8 +136,7 @@ instance : Coe Ident (TSyntax `lang) where
180136]
181137
182138/-!
183- As you can see, the pretty printing output right now is rather ugly to look at.
184- We can do better with an unexpander:
139+ 正如你所见,目前的漂亮打印输出相当难看。我们可以通过使用反扩展器来改进它:
185140-/
186141
187142@ [app_unexpander LangExpr.numConst]
@@ -218,7 +173,5 @@ def unexpandLet : Unexpander
218173]
219174
220175/-!
221- That's much better! As always, we encourage you to extend the language yourself
222- with things like parenthesized expressions, more data values, quotations for
223- `term` or whatever else comes to your mind.
176+ 这样就好多了!一如既往,我们鼓励你自己扩展语言,比如添加带括号的表达式、更多的数据值、对 `term` 的引用,或其他你能想到的内容。
224177-/
0 commit comments