Lean 语言参考

23.4. 定义新语法🔗

Lean 的语法统一表示非常通用且灵活。 这意味着对 Lean 解析器的扩展不需要对解析语法的表示进行扩展。

23.4.1. 语法模型🔗

Lean 的解析器生成 Lean.Syntax 类型的具体语法树。 Lean.Syntax 是归纳类型,表示 Lean 的所有语法,包括命令、术语、策略和任何自定义扩展。 所有这些都由一些基本构建块表示:

原子

原子是语法的基本终端,包括文字(例如字符和数字)、括号、运算符和关键字。

标识符

标识符代表名称,例如 xNatNat.add。 标识符语法包括标识符可能引用的预解析名称列表。

节点

节点代表非终结符的解析。 节点包含 syntax kind,它标识生成节点的语法规则,以及子 Syntax 值的数组。

缺少语法

当解析器遇到错误时,它会返回部分结果,因此 Lean 可以提供有关部分编写的程序或包含错误的程序的一些反馈。 部分结果包含一个或多个缺失语法的实例。

原子和标识符统称为 tokens

🔗inductive type

Lean syntax trees.

Syntax trees are used pervasively throughout Lean: they are produced by the parser, transformed by the macro expander, and elaborated. They are also produced by the delaborator and presented to users.

Constructors

Lean.Syntax.missing : Lean.Syntax

A portion of the syntax tree that is missing because of a parse error.

The indexing operator on Syntax also returns Syntax.missing when the index is out of bounds.

Lean.Syntax.node (info : Lean.SourceInfo)
  (kind : Lean.SyntaxNodeKind) (args : Array Lean.Syntax) :
  Lean.Syntax

A node in the syntax tree that may have further syntax as child nodes. The node's kind determines its interpretation.

For nodes produced by the parser, the info field is typically Lean.SourceInfo.none, and source information is stored in the corresponding fields of identifiers and atoms. This field is used in two ways:

  1. The delaborator uses it to associate nodes with metadata that are used to implement interactive features.

  2. Nodes created by quotations use the field to mark the syntax as synthetic (storing the result of Lean.SourceInfo.fromRef) even when its leading or trailing tokens are not.

Lean.Syntax.atom (info : Lean.SourceInfo) (val : String) :
  Lean.Syntax

A non-identifier atomic component of syntax.

All of the following are atoms:

  • keywords, such as def, fun, and inductive

  • literals, such as numeric or string literals

  • punctuation and delimiters, such as (, ), and =>.

Identifiers are represented by the Lean.Syntax.ident constructor. Atoms also correspond to quoted strings inside syntax declarations.

Lean.Syntax.ident (info : Lean.SourceInfo)
  (rawVal : Substring.Raw) (val : Lean.Name)
  (preresolved : List Lean.Syntax.Preresolved) : Lean.Syntax

An identifier.

In addition to source information, identifiers have the following fields:

  • rawVal is the literal substring from the input file

  • val is the parsed Lean name, potentially including macro scopes.

  • preresolved is the list of possible declarations this could refer to, populated by quotations.

🔗inductive type

A possible binding of an identifier in the context in which it was quoted.

Identifiers in quotations may refer to either global declarations or to namespaces that are in scope at the site of the quotation. These are saved in the Syntax.ident constructor and are part of the implementation of hygienic macros.

Constructors

Lean.Syntax.Preresolved.namespace (ns : Lean.Name) :
  Lean.Syntax.Preresolved

A potential namespace reference

Lean.Syntax.Preresolved.decl (n : Lean.Name)
  (fields : List String) : Lean.Syntax.Preresolved

A potential global constant or section variable reference, with additional field accesses

23.4.2. 语法节点类型🔗

语法节点类型通常标识生成该节点的解析器。 这是为运算符或符号(或其自动生成的内部名称)指定的名称出现的地方。 虽然只有节点包含标识其类型的字段,但标识符按照约定具有 identKind 类型,而原子按照约定具有其内部字符串作为其类型。 Lean 的解析器将每个关键字原子 KW 包装在一个单例节点中,其类型为 `token.KW。 可以使用 Syntax.getKind 提取语法值的类型。

🔗def

Specifies the interpretation of a Syntax.node value. An abbreviation for Name.

Node kinds may be any name, and do not need to refer to declarations in the environment. Conventionally, however, a node's kind corresponds to the Parser or ParserDesc declaration that produces it. There are also a number of built-in node kinds that are used by the parsing infrastructure, such as nullKind and choiceKind; these do not correspond to parser declarations.

🔗def

Checks whether syntax has the given kind or pseudo-kind.

“Pseudo-kinds” are kinds that are assigned by convention to non-Syntax.node values: identKind for Syntax.ident, `missing for Syntax.missing, and the atom's string literal for atoms.

🔗def

Gets the kind of a Syntax.node value, or the pseudo-kind of any other Syntax value.

“Pseudo-kinds” are kinds that are assigned by convention to non-Syntax.node values: identKind for Syntax.ident, `missing for Syntax.missing, and the atom's string literal for atoms.

🔗def

Changes the kind at the root of a Syntax.node to k.

Returns all other Syntax values unchanged.

23.4.3. 令牌和文字类型🔗

许多命名类型与解析器生成的基本标记相关联。 通常,单令牌语法产生式由包含单个 atomnode 组成;节点中保存的种类允许识别该值。 解析器不会解释文字的原子:字符串原子包括其前导和尾随双引号字符以及其中包含的任何转义序列,并且十六进制数字保存为以 "0x" 开头的字符串。 提供 Helpers(例如 Lean.TSyntax.getString)来按需执行此解码。

🔗def

The pseudo-kind assigned to identifiers: `ident.

The name `ident is not actually used as a kind for Syntax.node values. It is used by convention as the kind of Syntax.ident values.

🔗def

`str is the node kind of string literals like "foo".

🔗def

`interpolatedStrKind is the node kind of an interpolated string literal like "value = {x}" in s!"value = {x}".

🔗def

`interpolatedStrLitKind is the node kind of interpolated string literal fragments like "value = { and }" in s!"value = {x}".

🔗def

`char is the node kind of character literals like 'A'.

🔗def

`num is the node kind of number literals like 42 and 0xa1

🔗def

`scientific is the node kind of floating point literals like 1.23e-3.

🔗def

`name is the node kind of name literals like `foo.

🔗def

`fieldIdx is the node kind of projection indices like the 2 in x.2.

23.4.4. 内部种类🔗

🔗def

The `group kind is used for nodes that result from Lean.Parser.group. This avoids confusion with the null kind when used inside optional.

🔗def

`null is the “fallback” kind, used when no other kind applies. Null nodes result from repetition operators, and empty null nodes represent the failure of an optional parse.

The null kind is used for raw list parsers like many.

🔗def

The `choice kind is used to represent ambiguous parse results.

The parser prioritizes longer matches over shorter ones, but there is not always a unique longest match. All the parse results are saved, and the determination of which to use is deferred until typing information is available.

🔗def

`hygieneInfo is the node kind of the Lean.Parser.hygieneInfo parser, which produces an “invisible token” that captures the hygiene information at the current point without parsing anything.

They can be used to generate identifiers (with Lean.HygieneInfo.mkIdent) as if they were introduced in a macro's input, rather than by its implementation.

23.4.5. 来源职位🔗

原子、标识符和节点可选地包含 源信息,用于跟踪它们与原始文件的对应关系。 解析器保存所有标记的源信息,但不保存节点的源信息;已解析节点的位置信息是根据其第一个和最后一个标记重建的。 并非所有 Syntax 数据都来自解析器:它可能是 宏展开 的结果,在这种情况下,它通常包含生成和解析的语法的混合,或者它可能是 delaborating 内部术语的结果以将其显示给用户。 在这些用例中,节点本身可能包含源信息。

源信息有两种:

原件

原始源信息来自解析器。 除了原始源位置之外,它还包含解析器跳过的前导和尾随空格,这允许重建原始字符串。 该空白被保存为原始源代码的字符串表示形式的偏移量(即,Substring),以避免分配子字符串的副本。

合成

综合源信息来自元程序(包括宏)或来自 Lean 的内部。 因为没有要重建的原始字符串,所以它不保存前导和尾随空格。 即使术语已自动转换,合成源位置也可用于提供准确的反馈,并跟踪详细表达式与其在 Lean 输出中的表示之间的对应关系。 合成位置可能被标记为 canonical,在这种情况下,一些通常会忽略合成位置的操作会将其视为不存在。

🔗inductive type

Source information that relates syntax to the context that it came from.

The primary purpose of SourceInfo is to relate the output of the parser and the macro expander to the original source file. When produced by the parser, Syntax.node does not carry source info; the parser associates it only with atoms and identifiers. If a Syntax.node is introduced by a quotation, then it has synthetic source info that both associates it with an original reference position and indicates that the original atoms in it may not originate from the Lean file under elaboration.

Source info is also used to relate Lean's output to the internal data that it represents; this is the basis for many interactive features. When used this way, it can occur on Syntax.node as well.

Constructors

Lean.SourceInfo.original (leading : Substring.Raw)
  (pos : String.Pos.Raw) (trailing : Substring.Raw)
  (endPos : String.Pos.Raw) : Lean.SourceInfo

A token produced by the parser from original input that includes both leading and trailing whitespace as well as position information.

The leading whitespace is inferred after parsing by Syntax.updateLeading. This is because the “preceding token” is not well-defined during parsing, especially in the presence of backtracking.

Lean.SourceInfo.synthetic (pos endPos : String.Pos.Raw)
  (canonical : Bool := false) : Lean.SourceInfo

Synthetic syntax is syntax that was produced by a metaprogram or by Lean itself (e.g. by a quotation). Synthetic syntax is annotated with a source span from the original syntax, which relates it to the source file.

The delaborator uses this constructor to store an encoded indicator of which core language expression gave rise to the syntax.

The canonical flag on synthetic syntax is enabled for syntax that is not literally part of the original input syntax but should be treated “as if” the user really wrote it for the purpose of hovers and error messages. This is usually used on identifiers in order to connect the binding site to the user's original syntax even if the name of the identifier changes during expansion, as well as on tokens that should receive targeted messages.

Generally speaking, a macro expansion should only use a given piece of input syntax in a single canonical token. An exception to this rule is when the same identifier is used to declare two binders, as in the macro expansion for dependent if:

`(if $h : $cond then $t else $e) ~>
`(dite $cond (fun $h => $t) (fun $h => $t))

In these cases, if the user hovers over h they will see information about both binding sites.

Lean.SourceInfo.none : Lean.SourceInfo

A synthesized token without position information.

23.4.6. 检查语法🔗

检查 Syntax 值的主要方法有以下三种:

Repr 实例

Repr Syntax 实例根据 Syntax 类型的构造函数生成非常详细的语法表示。

ToString 实例

ToString Syntax 实例生成一个紧凑的视图,表示具有特定约定的某些语法类型,可以使其更易于一目了然。 此实例抑制源位置信息。

漂亮的打印机

Lean 的漂亮打印机尝试呈现语法,就像在源文件中一样,但如果语法的嵌套结构与预期形状不匹配,则会失败。

Representing Syntax as Constructors

Repr 实例的语法表示可以通过在 Lean.Parser.Command.eval : command`#eval e` evaluates the expression `e` by compiling and evaluating it. * The command attempts to use `ToExpr`, `Repr`, or `ToString` instances to print the result. * If `e` is a monadic value of type `m ty`, then the command tries to adapt the monad `m` to one of the monads that `#eval` supports, which include `IO`, `CoreM`, `MetaM`, `TermElabM`, and `CommandElabM`. Users can define `MonadEval` instances to extend the list of supported monads. The `#eval` command gracefully degrades in capability depending on what is imported. Importing the `Lean.Elab.Command` module provides full capabilities. Due to unsoundness, `#eval` refuses to evaluate expressions that depend on `sorry`, even indirectly, since the presence of `sorry` can lead to runtime instability and crashes. This check can be overridden with the `#eval! e` command. Options: * If `eval.pp` is true (default: true) then tries to use `ToExpr` instances to make use of the usual pretty printer. Otherwise, only tries using `Repr` and `ToString` instances. * If `eval.type` is true (default: false) then pretty prints the type of the evaluated value. * If `eval.derive.repr` is true (default: true) then attempts to auto-derive a `Repr` instance when there is no other way to print the result. See also: `#reduce e` for evaluation by term reduction. #eval 上下文中引用它来检查,它可以在命令精化monad CommandElabM 中运行操作。 为了减小示例输出的大小,使用帮助器 removeSourceInfo 在显示之前删除源信息。

partial def removeSourceInfo : Syntax Syntax | .atom _ str => .atom .none str | .ident _ str x pre => .ident .none str x pre | .node _ k children => .node .none k (children.map removeSourceInfo) | .missing => .missing Lean.Syntax.node (Lean.SourceInfo.none) `«term_+_» #[Lean.Syntax.node (Lean.SourceInfo.none) `num #[Lean.Syntax.atom (Lean.SourceInfo.none) "2"], Lean.Syntax.atom (Lean.SourceInfo.none) "+", Lean.Syntax.missing]#eval do let stx `(2 + $(.missing)) logInfo (repr (removeSourceInfo stx.raw))
Lean.Syntax.node
  (Lean.SourceInfo.none)
  `«term_+_»
  #[Lean.Syntax.node (Lean.SourceInfo.none) `num #[Lean.Syntax.atom (Lean.SourceInfo.none) "2"],
    Lean.Syntax.atom (Lean.SourceInfo.none) "+", Lean.Syntax.missing]

在第二个示例中,通过引用插入的 宏范围 在调用 List.length 时可见。

Lean.Syntax.node (Lean.SourceInfo.none) `Lean.Parser.Term.app #[Lean.Syntax.ident (Lean.SourceInfo.none) "List.length".toRawSubstring (Lean.Name.mkNum (Lean.Name.mkStr (Lean.Name.mkStr (Lean.Name.mkNum `List.length.«_@».ManualZh.NotationsMacros.SyntaxDef 1704743902) "_hygCtx") "_hyg") 2) [Lean.Syntax.Preresolved.decl `List.length []], Lean.Syntax.node (Lean.SourceInfo.none) `null #[Lean.Syntax.node (Lean.SourceInfo.none) `«term[_]» #[Lean.Syntax.atom (Lean.SourceInfo.none) "[", Lean.Syntax.node (Lean.SourceInfo.none) `null #[Lean.Syntax.node (Lean.SourceInfo.none) `str #[Lean.Syntax.atom (Lean.SourceInfo.none) "\"Rose\""], Lean.Syntax.atom (Lean.SourceInfo.none) ",", Lean.Syntax.node (Lean.SourceInfo.none) `str #[Lean.Syntax.atom (Lean.SourceInfo.none) "\"Daffodil\""], Lean.Syntax.atom (Lean.SourceInfo.none) ",", Lean.Syntax.node (Lean.SourceInfo.none) `str #[Lean.Syntax.atom (Lean.SourceInfo.none) "\"Lily\""]], Lean.Syntax.atom (Lean.SourceInfo.none) "]"]]]#eval do let stx `(List.length ["Rose", "Daffodil", "Lily"]) logInfo (repr (removeSourceInfo stx.raw))

预解析标识符 List.length 的内容在此处可见:

Lean.Syntax.node
  (Lean.SourceInfo.none)
  `Lean.Parser.Term.app
  #[Lean.Syntax.ident
      (Lean.SourceInfo.none)
      "List.length".toRawSubstring
      (Lean.Name.mkNum (Lean.Name.mkStr (Lean.Name.mkStr (Lean.Name.mkNum `List.length.«_@».ManualZh.NotationsMacros.SyntaxDef 1704743902) "_hygCtx") "_hyg") 2)
      [Lean.Syntax.Preresolved.decl `List.length []],
    Lean.Syntax.node
      (Lean.SourceInfo.none)
      `null
      #[Lean.Syntax.node
          (Lean.SourceInfo.none)
          `«term[_]»
          #[Lean.Syntax.atom (Lean.SourceInfo.none) "[",
            Lean.Syntax.node
              (Lean.SourceInfo.none)
              `null
              #[Lean.Syntax.node (Lean.SourceInfo.none) `str #[Lean.Syntax.atom (Lean.SourceInfo.none) "\"Rose\""],
                Lean.Syntax.atom (Lean.SourceInfo.none) ",",
                Lean.Syntax.node (Lean.SourceInfo.none) `str #[Lean.Syntax.atom (Lean.SourceInfo.none) "\"Daffodil\""],
                Lean.Syntax.atom (Lean.SourceInfo.none) ",",
                Lean.Syntax.node (Lean.SourceInfo.none) `str #[Lean.Syntax.atom (Lean.SourceInfo.none) "\"Lily\""]],
            Lean.Syntax.atom (Lean.SourceInfo.none) "]"]]]

ToString 实例表示 Syntax 的构造函数,如下所示:

  • ident 构造函数表示为基础名称。未显示源信息和预先解析的名称。

  • atom 构造函数表示为字符串。

  • missing 构造函数由 <missing> 表示。

  • node 构造函数的表示取决于类型。 如果类型为 `null,则该节点由方括号中的子节点顺序表示。 否则,节点由其类型后跟其子节点表示,两者都用括号括起来。

Syntax as Strings

语法的字符串表示形式可以通过在 Lean.Parser.Command.eval : command`#eval e` evaluates the expression `e` by compiling and evaluating it. * The command attempts to use `ToExpr`, `Repr`, or `ToString` instances to print the result. * If `e` is a monadic value of type `m ty`, then the command tries to adapt the monad `m` to one of the monads that `#eval` supports, which include `IO`, `CoreM`, `MetaM`, `TermElabM`, and `CommandElabM`. Users can define `MonadEval` instances to extend the list of supported monads. The `#eval` command gracefully degrades in capability depending on what is imported. Importing the `Lean.Elab.Command` module provides full capabilities. Due to unsoundness, `#eval` refuses to evaluate expressions that depend on `sorry`, even indirectly, since the presence of `sorry` can lead to runtime instability and crashes. This check can be overridden with the `#eval! e` command. Options: * If `eval.pp` is true (default: true) then tries to use `ToExpr` instances to make use of the usual pretty printer. Otherwise, only tries using `Repr` and `ToString` instances. * If `eval.type` is true (default: false) then pretty prints the type of the evaluated value. * If `eval.derive.repr` is true (default: true) then attempts to auto-derive a `Repr` instance when there is no other way to print the result. See also: `#reduce e` for evaluation by term reduction. #eval 的上下文中引用它来检查,它可以在命令精化monad CommandElabM 中运行操作。

(«term_+_» (num "2") "+" <missing>)#eval do let stx `(2 + $(.missing)) logInfo (toString stx)
(«term_+_» (num "2") "+" <missing>)

在第二个示例中,通过引用插入的 宏范围 在调用 List.length 时可见。

(Term.app `List.length._@.ManualZh.NotationsMacros.SyntaxDef.3168789510._hygCtx._hyg.2 [(«term[_]» "[" [(str "\"Rose\"") "," (str "\"Daffodil\"") "," (str "\"Lily\"")] "]")])#eval do let stx `(List.length ["Rose", "Daffodil", "Lily"]) logInfo (toString stx)
(Term.app
 `List.length._@.ManualZh.NotationsMacros.SyntaxDef.3168789510._hygCtx._hyg.2
 [(«term[_]» "[" [(str "\"Rose\"") "," (str "\"Daffodil\"") "," (str "\"Lily\"")] "]")])

漂亮的打印语法通常在将其包含在给用户的消息中时最有用。 通常,Lean 在需要时会自动调用漂亮打印机。 但是,如果需要,可以显式调用 ppTerm

Pretty-Printed Syntax

语法的字符串表示形式可以通过在 Lean.Parser.Command.eval : command`#eval e` evaluates the expression `e` by compiling and evaluating it. * The command attempts to use `ToExpr`, `Repr`, or `ToString` instances to print the result. * If `e` is a monadic value of type `m ty`, then the command tries to adapt the monad `m` to one of the monads that `#eval` supports, which include `IO`, `CoreM`, `MetaM`, `TermElabM`, and `CommandElabM`. Users can define `MonadEval` instances to extend the list of supported monads. The `#eval` command gracefully degrades in capability depending on what is imported. Importing the `Lean.Elab.Command` module provides full capabilities. Due to unsoundness, `#eval` refuses to evaluate expressions that depend on `sorry`, even indirectly, since the presence of `sorry` can lead to runtime instability and crashes. This check can be overridden with the `#eval! e` command. Options: * If `eval.pp` is true (default: true) then tries to use `ToExpr` instances to make use of the usual pretty printer. Otherwise, only tries using `Repr` and `ToString` instances. * If `eval.type` is true (default: false) then pretty prints the type of the evaluated value. * If `eval.derive.repr` is true (default: true) then attempts to auto-derive a `Repr` instance when there is no other way to print the result. See also: `#reduce e` for evaluation by term reduction. #eval 的上下文中引用它来检查,它可以在命令精化monad CommandElabM 中运行操作。 因为新的语法声明还为漂亮打印机配备了显示它们的指令,所以漂亮打印机需要一个配置对象。 这个上下文可以用一个助手来构建:

def getPPContext : CommandElabM PPContext := do return { env := ( getEnv), opts := ( getOptions), currNamespace := ( getCurrNamespace), openDecls := ( getOpenDecls) } 2 + 5#eval show CommandElabM Unit from do let stx `(2 + 5) let fmt ppTerm ( getPPContext) stx logInfo fmt
2 + 5

在第二个示例中,通过引用插入到 List.length 上的 宏范围 导致它显示为带有匕首 ()。

List.length✝ ["Rose", "Daffodil", "Lily"]#eval do let stx `(List.length ["Rose", "Daffodil", "Lily"]) let fmt ppTerm ( getPPContext) stx logInfo fmt
List.length✝ ["Rose", "Daffodil", "Lily"]

漂亮的打印会自动换行并插入缩进。 强制 通常使用默认布局宽度将漂亮打印机的输出转换为 logInfo 所需的类型。 可以通过使用命名参数显式调用 pretty 来控制宽度。

List.length✝ ["Rose", "Daffodil", "Lily", "Rose", "Daffodil", "Lily", "Rose", "Daffodil", "Lily"]#eval do let flowers := #["Rose", "Daffodil", "Lily"] let manyFlowers := flowers ++ flowers ++ flowers let stx `(List.length [$(manyFlowers.map (quote (k := `term))),*]) let fmt ppTerm ( getPPContext) stx logInfo (fmt.pretty (width := 40))
List.length✝
  ["Rose", "Daffodil", "Lily", "Rose",
    "Daffodil", "Lily", "Rose",
    "Daffodil", "Lily"]

23.4.7. 类型化语法🔗

语法还可以用指定其属于哪个 语法类别 的类型进行注释。 TSyntax 结构包含语法类别的类型级列表以及语法树。 语法类别列表通常只包含一个元素,在这种情况下,不会显示列表结构本身。

🔗structure

Typed syntax, which tracks the potential kinds of the Syntax it contains.

While syntax quotations produce or expect TSyntax values of the correct kinds, this is not otherwise enforced; it can easily be circumvented by direct use of the constructor.

Constructor

Lean.TSyntax.mk

Fields

raw : Lean.Syntax

The underlying Syntax value.

🔗def

SyntaxNodeKinds is a set of SyntaxNodeKind, implemented as a list.

Singleton SyntaxNodeKinds are extremely common. They are written as name literals, rather than as lists; list syntax is required only for empty or non-singleton sets of kinds.

Quasiquotations 防止替换不来自正确语法类别的类型化语法。 对于许多 Lean 的内置语法类别,有一组 强制转换 适当地包装另一种类别的语法,例如从字符串文字语法到术语语法的强制转换。 此外,许多仅对某些语法类别有效的辅助函数仅针对适当的类型化语法定义。

TSyntax 的构造函数是公共的,没有什么可以阻止用户构造破坏内部不变量的值。 TSyntax 的使用应被视为减少常见错误的一种方法,而不是完全排除它们。

除了 TSyntax 之外,还有一些表示带或不带分隔符的语法数组的类型。 这些对应于语法声明或反引号中的 重复元素。 TSyntaxArray ksArray (TSyntax ks)缩写,而TSepArray ks sep是一个结构体;这意味着 广义字段表示法 可用于将数组函数应用于 TSyntaxArray,但不能应用于 TSepArrayTSepArray ksTSyntaxArray ks 之间存在 强制,以及显式转换函数。 此转换会从基础数组中插入或删除分隔符元素,所需时间与元素数量成线性关系。

🔗def

An array of syntaxes of kind ks.

🔗opaque

Converts a TSyntaxArray to an Array Syntax, without reallocation.

🔗structure

An array of syntax elements that alternate with the given separator. Each syntax element has a kind drawn from ks.

Separator arrays result from repetition operators such as ,*. Coercions to and from Array (TSyntax ks) insert or remove separators as required. The untyped equivalent is Lean.Syntax.SepArray.

Constructor

Lean.Syntax.TSepArray.mk

Fields

elemsAndSeps : Array Lean.Syntax

The array of elements and separators, ordered like #[el1, sep1, el2, sep2, el3].

🔗def

Extracts the non-separator elements of a separated array.

🔗def
Lean.Syntax.TSepArray.elemsAndSeps {ks : Lean.SyntaxNodeKinds} {sep : String} (self : Lean.Syntax.TSepArray ks sep) : Array Lean.Syntax
Lean.Syntax.TSepArray.elemsAndSeps {ks : Lean.SyntaxNodeKinds} {sep : String} (self : Lean.Syntax.TSepArray ks sep) : Array Lean.Syntax

The array of elements and separators, ordered like #[el1, sep1, el2, sep2, el3].

🔗def

Constructs a typed separated array from elements by adding suitable separators. The provided array should not include the separators.

Like Syntax.SepArray.ofElems but for typed syntax.

🔗def

Adds an element to the end of a separated array, adding a separator as needed.

23.4.8. 别名🔗

为常用的类型化语法变体提供了许多别名。 这些别名允许在更高的抽象级别编写代码。

🔗def

Syntax that represents a Lean term.

🔗def

Syntax that represents a command.

🔗def

Syntax that represents a universe level.

🔗def

Syntax that represents a tactic.

🔗def

Syntax that represents a precedence (e.g. for an operator).

🔗def

Syntax that represents a priority (e.g. for an instance declaration).

🔗def

Syntax that represents an identifier.

🔗def

Syntax that represents a string literal.

🔗def

Syntax that represents a character literal.

🔗def

Syntax that represents a quoted name literal that begins with a back-tick.

🔗def

Syntax that represents a numeric literal.

🔗def

Syntax that represents a scientific numeric literal that may have decimal and exponential parts.

🔗def

Syntax that represents macro hygiene info.

23.4.9. 构造语法的助手🔗

🔗def
Lean.mkIdent (val : Lean.Name) : Lean.Ident
Lean.mkIdent (val : Lean.Name) : Lean.Ident

Creates an identifier from a name. The resulting identifier has no source position.

🔗def
Lean.mkIdentFrom (src : Lean.Syntax) (val : Lean.Name) (canonical : Bool := false) : Lean.Ident
Lean.mkIdentFrom (src : Lean.Syntax) (val : Lean.Name) (canonical : Bool := false) : Lean.Ident

Creates an identifier with its position copied from src.

To refer to a specific constant without a risk of variable capture, use mkCIdentFrom instead.

🔗def
Lean.mkIdentFromRef {m : Type Type} [Monad m] [Lean.MonadRef m] (val : Lean.Name) (canonical : Bool := false) : m Lean.Ident
Lean.mkIdentFromRef {m : Type Type} [Monad m] [Lean.MonadRef m] (val : Lean.Name) (canonical : Bool := false) : m Lean.Ident

Creates an identifier with its position copied from the syntax returned by getRef.

To refer to a specific constant without a risk of variable capture, use mkCIdentFromRef instead.

🔗def
Lean.mkCIdent (c : Lean.Name) : Lean.Ident
Lean.mkCIdent (c : Lean.Name) : Lean.Ident

Creates an identifier that refers to a constant c. The identifier has no source position.

This variant of mkIdent makes sure that the identifier cannot accidentally be captured.

🔗def
Lean.mkCIdentFrom (src : Lean.Syntax) (c : Lean.Name) (canonical : Bool := false) : Lean.Ident
Lean.mkCIdentFrom (src : Lean.Syntax) (c : Lean.Name) (canonical : Bool := false) : Lean.Ident

Creates an identifier referring to a constant c. The identifier's position is copied from src.

This variant of mkIdentFrom makes sure that the identifier cannot accidentally be captured.

🔗def
Lean.mkCIdentFromRef {m : Type Type} [Monad m] [Lean.MonadRef m] (c : Lean.Name) (canonical : Bool := false) : m Lean.Syntax
Lean.mkCIdentFromRef {m : Type Type} [Monad m] [Lean.MonadRef m] (c : Lean.Name) (canonical : Bool := false) : m Lean.Syntax

Creates an identifier referring to a constant c. The identifier's position is copied from the syntax returned by getRef.

This variant of mkIdentFrom makes sure that the identifier cannot accidentally be captured.

🔗def

Creates syntax representing a Lean term application, but avoids degenerate empty applications.

🔗def
Lean.Syntax.mkCApp (fn : Lean.Name) (args : Lean.TSyntaxArray `term) : Lean.Term
Lean.Syntax.mkCApp (fn : Lean.Name) (args : Lean.TSyntaxArray `term) : Lean.Term

Creates syntax representing a Lean constant application, but avoids degenerate empty applications.

🔗def

Creates a literal of the given kind. It is the caller's responsibility to ensure that the provided literal is a valid atom for the provided kind.

If info is provided, then the literal's source information is copied from it.

🔗def

Creates literal syntax for the given character.

If info is provided, then the literal's source information is copied from it.

🔗def

Creates literal syntax for the given string.

If info is provided, then the literal's source information is copied from it.

🔗def

Creates literal syntax for a number, which is provided as a string. The caller must ensure that the string is a valid token for the num token parser.

If info is provided, then the literal's source information is copied from it.

🔗def

Creates literal syntax for a natural number.

If info is provided, then the literal's source information is copied from it.

🔗def

Creates literal syntax for a number in scientific notation. The caller must ensure that the provided string is a valid scientific notation literal.

If info is provided, then the literal's source information is copied from it.

🔗def

Creates literal syntax for a name. The caller must ensure that the provided string is a valid name literal.

If info is provided, then the literal's source information is copied from it.

🔗def

Creates an optional node.

Optional nodes consist of null nodes that contain either zero or one element.

🔗def

Creates a group node, as if it were parsed by Lean.Parser.group.

🔗def
Lean.mkHole (ref : Lean.Syntax) (canonical : Bool := false) : Lean.Term
Lean.mkHole (ref : Lean.Syntax) (canonical : Bool := false) : Lean.Term

Creates a hole (_). The hole's position is copied from ref.

23.4.9.1. 引用数据🔗

Quote 类允许将值转换为表示它们的类型化语法。 例如,quote 5 表示 .node .none `num #[.atom .none "5"]。 该类通过语法类型进行参数化;这允许相同的值以不同的种类适当地表示。 Quote 的实例解析考虑类型化语法 强制转换。 语法类型的默认值为 `term

无法保证 Quote.quote 的结果能够成功精化。 一般来说,生成的语法包含所有显式参数的带引号版本,并省略隐式参数。

🔗type class
Lean.Quote (α : Type) (k : Lean.SyntaxNodeKind := `term) : Type
Lean.Quote (α : Type) (k : Lean.SyntaxNodeKind := `term) : Type

Converts a runtime value into surface syntax that denotes it.

Instances do not need to guarantee that the resulting syntax will always re-elaborate into an equivalent value. For example, the syntax may omit implicit arguments that can usually be found automatically.

Instance Constructor

Lean.Quote.mk

Methods

quote : α  Lean.TSyntax k

Returns syntax for the given value.

定义 Quote 的实例时,请使用 mkCIdentmkCApp 以避免在生成的语法中捕获变量。

Defining Quote Instances

引用 Tree 类型的树,mkCIdentmkCApp 用于确保具有相似名称的本地绑定不会干扰。 使用双反引号可确保构造函数名称不包含拼写错误并得到正确解析。

inductive Tree (α : Type u) : Type u where | leaf | branch (left : Tree α) (val : α) (right : Tree α) instance [Quote α] : Quote (Tree α) where quote := quoteTree where quoteTree | .leaf => mkCIdent ``Tree.leaf | .branch l v r => mkCApp ``Tree.branch #[quoteTree l, quote v, quoteTree r]

23.4.10. 解码类型化语法🔗

对于文字,Lean 的解析器生成包含 atom 的单例节点。 内部原子包含一个带有源信息的字符串,而节点的种类指定如何解释该原子。 这可能涉及解码字符串转义序列或解释 16 进制数字文字。 本节中的帮助程序执行正确的解释。

🔗def
Lean.TSyntax.getId (s : Lean.Ident) : Lean.Name
Lean.TSyntax.getId (s : Lean.Ident) : Lean.Name

Extracts the parsed name from the syntax of an identifier.

Returns Name.anonymous if the syntax is malformed.

🔗def

Decodes a quoted name literal, returning the name.

Returns Lean.Name.anonymous if the syntax is malformed.

🔗def

Interprets a numeric literal as a natural number.

Returns 0 if the syntax is malformed.

🔗def

Extracts the components of a scientific numeric literal.

Returns a triple (n, sign, e) : Nat × Bool × Nat; the number's value is given by:

if sign then n * 10 ^ (-e) else n * 10 ^ e

Returns (0, false, 0) if the syntax is malformed.

🔗def

Decodes a string literal, removing quotation marks and unescaping escaped characters.

Returns "" if the syntax is malformed.

🔗def

Decodes a character literal.

Returns (default : Char) if the syntax is malformed.

🔗def

Decodes macro hygiene information.

23.4.11. 语法类别🔗

Lean 的解析器包含一个 syntaxcategories 表,它对应于上下文无关语法中的非终结符。 一些最重要的类别是术语、命令、宇宙层级、优先级、优先级以及表示标记(例如文字)的类别。 通常,每个 语法种类 对应于一个类别。 可以使用 Lean.Parser.Command.syntaxCat : commanddeclare_syntax_cat 声明新类别。

前导标识符行为是一项高级功能,通常不需要修改。 它控制解析器在遇到标识符时的行为,有时可能会导致标识符被视为非保留关键字。 这用于避免将每个 策略 的名称转换为保留关键字。

🔗inductive type

Specifies how the parsing table lookup function behaves for identifiers.

The function Lean.Parser.prattParser uses two tables: one each for leading and trailing parsers. These tables map tokens to parsers. Because keyword tokens are distinct from identifier tokens, keywords and identifiers cannot be confused, even when they are syntactically identical. Specifying an alternative leading identifier behavior allows greater flexibility and makes it possible to avoid reserved keywords in some situations.

When the leading token is syntactically an identifier, the current syntax category's LeadingIdentBehavior specifies how the parsing table lookup function behaves, and allows controlled “punning” between identifiers and keywords. This feature is used to avoid creating a reserved symbol for each built-in tactic (e.g., apply or assumption). As a result, tactic names can be used as identifiers.

Constructors

Lean.Parser.LeadingIdentBehavior.default :
  Lean.Parser.LeadingIdentBehavior

If the leading token is an identifier, then the parser just executes the parsers associated with the auxiliary token “ident”, which parses identifiers.

Lean.Parser.LeadingIdentBehavior.symbol :
  Lean.Parser.LeadingIdentBehavior

If the leading token is an identifier <foo>, and there are parsers P associated with the token <foo>, then the parser executes P. Otherwise, it executes only the parsers associated with the auxiliary token “ident”, which parses identifiers.

Lean.Parser.LeadingIdentBehavior.both :
  Lean.Parser.LeadingIdentBehavior

If the leading token is an identifier <foo>, then it executes the parsers associated with token <foo> and parsers associated with the auxiliary token “ident”, which parses identifiers.

23.4.12. 语法规则🔗

每个 语法类别 与一组 syntax Rules 相关联,这些规则对应于上下文无关语法中的产生式。 可以使用 Lean.Parser.Command.syntax : commandsyntax 命令定义语法规则。

与运算符和符号声明一样,文档注释的内容在用户与新语法交互时向用户显示。 可以添加属性以在结果定义上调用编译时元程序。

语法规则与 节范围 的交互方式与属性、运算符和符号相同。 默认情况下,任何模块中的解析器都可以使用语法规则,该模块可传递地导入在其中建立语法规则的语法规则,但可以将它们声明为 scopedlocal,以分别将其可用性限制为当前名称空间已打开的上下文或当前 节范围

当类别的多个语法规则可以匹配当前输入时,本地最长匹配规则用于选择其中之一。 与符号和运算符一样,如果最长匹配存在平局,则使用声明的优先级来确定应用哪个解析结果。 如果这仍然不能解决歧义,则保存所有并列的结果。 精化器预计将尝试所有这些方法,并在能够详细精化其中一个时成功。

语法规则的优先级紧跟在 Lean.Parser.Command.syntax : commandsyntax 关键字之后,限制解析器仅当优先级上下文至少为提供的值时才使用此新语法。 就像运算符和符号一样,语法规则可以手动提供名称;如果不是,则会生成一个未使用的名称。 无论是提供还是生成,此名称都用作生成的 node 中的语法类型。

语法声明的主体比符号的主体更加灵活。 字符串文字指定要匹配的原子。 子术语可以从任何语法类别中提取,而不仅仅是术语,并且它们可以是可选的或重复的,有或没有交错的逗号分隔符。 语法规则中的标识符指示语法类别,而不是像在符号中那样命名子术语。

最后,语法规则指定了它扩展的语法类别。 在不存在的类别中声明语法规则是错误的。

syntaxSyntax Specifiers

语法类别 stx 是可能出现在 Lean.Parser.Command.syntax : commandsyntax 命令主体中的说明符的语法。

字符串文字被解析为 atoms(包括 if#evalwhere 等关键字):

stx ::=
    Parses the literal symbol.

The symbol is automatically included in the set of reserved tokens ("keywords").
Keywords cannot be used as identifiers, unless the identifier is otherwise escaped.
For example, `"fun"` reserves `fun` as a keyword; to refer an identifier named `fun` one can write `«fun»`.
Adding a `&` prefix prevents it from being reserved, for example `&"true"`.

Whitespace before or after the atom is used as a pretty printing hint.
For example, `" + "` parses `+` and pretty prints it with whitespace on both sides.
The whitespace has no effect on parsing behavior.
str

字符串中的前导空格和尾随空格不会影响解析,但会导致 Lean 在显示 proof states 中的语法和错误消息时在相应位置插入空格。 通常,在语法规则中作为原子出现的有效标识符成为保留关键字。 在字符串文字前面加上 & 符号 (&) 可抑制此行为:

stx ::= ...
    | Parses a literal symbol. The `&` prefix prevents it from being included in the set of reserved tokens ("keywords").
This means that the symbol can still be recognized as an identifier by other parsers.

Some syntax categories, such as `tactic`, automatically apply `&` to the first symbol.

Whitespace before or after the atom is used as a pretty printing hint.
For example, `" + "` parses `+` and pretty prints it with whitespace on both sides.
The whitespace has no effect on parsing behavior.

(Not exposed by parser description syntax:
If the `includeIdent` argument is true, lets `ident` be reinterpreted as `atom` if it matches.)
&str

标识符指定给定位置预期的语法类别,并且可以选择提供优先级:

stx ::= ...
    | ident(:prec)?

* 修饰符是 Kleene 星号,匹配前面语法的零次或多次重复。 也可以使用 many 写入。

stx ::= ...
    | `p*` is shorthand for `many(p)`. It uses parser `p` 0 or more times, and produces a
`nullNode` containing the array of parsed results. This parser has arity 1.

If `p` has arity more than 1, it is auto-grouped in the items generated by the parser.
stx *

+ 修饰符匹配前述语法的一次或多次重复。 也可以使用 many1 写入。

stx ::= ...
    | `p+` is shorthand for `many1(p)`. It uses parser `p` 1 or more times, and produces a
`nullNode` containing the array of parsed results. This parser has arity 1.

If `p` has arity more than 1, it is auto-grouped in the items generated by the parser.
stx +

? 修饰符使子项成为可选,并匹配前面语法的零次或一次(但不能多次)重复。 也可写为optional

stx ::= ...
    | `(p)?` is shorthand for `optional(p)`. It uses parser `p` 0 or 1 times, and produces a
`nullNode` containing the array of parsed results. This parser has arity 1.

`p` is allowed to have arity n > 1 (in which case the node will have either 0 or n children),
but if it has arity 0 then the result will be ambiguous.

Because `?` is an identifier character, `ident?` will not work as intended.
You have to write either `ident ?` or `(ident)?` for it to parse as the `?` combinator
applied to the `ident` parser.
stx ?
stx ::= ...
    | optional(stx)

,* 修饰符与前面带有交错逗号的语法的零次或多次重复相匹配。 也可以使用 sepBy 写入。

stx ::= ...
    | `p,*` is shorthand for `sepBy(p, ",")`. It parses 0 or more occurrences of
`p` separated by `,`, that is: `empty | p | p,p | p,p,p | ...`.

It produces a `nullNode` containing a `SepArray` with the interleaved parser
results. It has arity 1, and auto-groups its component parser if needed.
stx ,*

,+ 修饰符与前面带有交错逗号的语法的一次或多次重复相匹配。 也可以使用 sepBy1 写入。

stx ::= ...
    | `p,+` is shorthand for `sepBy1(p, ",")`. It parses 1 or more occurrences of
`p` separated by `,`, that is: `p | p,p | p,p,p | ...`.

It produces a `nullNode` containing a `SepArray` with the interleaved parser
results. It has arity 1, and auto-groups its component parser if needed.
stx ,+

,*,? 修饰符将前面语法的零次或多次重复与交错逗号匹配,从而允许在最终重复之后使用可选的尾随逗号。 也可以使用 sepByallowTrailingSep 修饰符来编写。

stx ::= ...
    | `p,*,?` is shorthand for `sepBy(p, ",", allowTrailingSep)`.
It parses 0 or more occurrences of `p` separated by `,`, possibly including
a trailing `,`, that is: `empty | p | p, | p,p | p,p, | p,p,p | ...`.

It produces a `nullNode` containing a `SepArray` with the interleaved parser
results. It has arity 1, and auto-groups its component parser if needed.
stx ,*,?

,+,? 修饰符将前面语法的一次或多次重复与交错逗号相匹配,从而允许在最后一次重复之后使用可选的尾随逗号。 也可以使用 sepBy1allowTrailingSep 修饰符来编写。

stx ::= ...
    | `p,+,?` is shorthand for `sepBy1(p, ",", allowTrailingSep)`.
It parses 1 or more occurrences of `p` separated by `,`, possibly including
a trailing `,`, that is: `p | p, | p,p | p,p, | p,p,p | ...`.

It produces a `nullNode` containing a `SepArray` with the interleaved parser
results. It has arity 1, and auto-groups its component parser if needed.
stx ,+,?

<|> 运算符(可写为 orelse)与任一语法匹配。 然而,如果第一个分支消耗了任何令牌,那么它就会被提交,并且失败将不会被回溯:

stx ::= ...
    | `p1 <|> p2` is shorthand for `orelse(p1, p2)`, and parses either `p1` or `p2`.
It does not backtrack, meaning that if `p1` consumes at least one token then
`p2` will not be tried. Therefore, the parsers should all differ in their first
token. The `atomic(p)` parser combinator can be used to locally backtrack a parser.
(For full backtracking, consider using extensible syntax classes instead.)

On success, if the inner parser does not generate exactly one node, it will be
automatically wrapped in a `group` node, so the result will always be arity 1.

The `<|>` combinator does not generate a node of its own, and in particular
does not tag the inner parsers to distinguish them, which can present a problem
when reconstructing the parse. A well formed `<|>` parser should use disjoint
node kinds for `p1` and `p2`.
stx <|> stx
stx ::= ...
    | orelse(stx, stx)

! 运算符与其参数的补集匹配。 如果它的参数失败,那么它会成功,重置解析状态。

stx ::= ...
    | `!p` parses the negation of `p`. That is, it fails if `p` succeeds, and
otherwise parses nothing. It has arity 0.
! stx

语法说明符可以使用括号进行分组。

stx ::= ...
    | (stx)

可以使用 manymany1 定义重复。 后者需要至少一个重复语法实例。

stx ::= ...
    | many(stx)
stx ::= ...
    | many1(stx)

带有分隔符的重复可以使用 sepBysepBy1 来定义,它们分别匹配零个或多个出现以及一个或多个出现,由某种其他语法分隔。 它们分为三个品种:

  • 双参数版本使用字符串文字中提供的原子来解析分隔符,并且不允许尾随分隔符。

  • 三参数版本使用第三个参数来解析分隔符,使用原子进行漂亮的打印。

  • 四参数版本可选择允许分隔符在序列结束时出现额外的时间。 第四个参数必须始终是关键字 allowTrailingSep

stx ::= ...
    | sepBy(stx, str)
stx ::= ...
    | sepBy(stx, str, stx)
stx ::= ...
    | sepBy(stx, str, stx, allowTrailingSep)
stx ::= ...
    | sepBy1(stx, str)
stx ::= ...
    | sepBy1(stx, str, stx)
stx ::= ...
    | sepBy1(stx, str, stx, allowTrailingSep)
Parsing Matched Parentheses and Brackets

可以使用语法规则定义由匹配的圆括号和方括号组成的语言。 第一步是声明一个新的 语法类别

declare_syntax_cat balanced

接下来,可以为圆括号和方括号添加规则。 为了排除空字符串,基例由空对组成。

syntax "(" ")" : balanced syntax "[" "]" : balanced syntax "(" balanced ")" : balanced syntax "[" balanced "]" : balanced syntax balanced balanced : balanced

为了根据这些规则调用 Lean 的解析器,还必须将新语法类别嵌入到可能已解析的语法类别中:

syntax (name := termBalanced) "balanced " balanced : term

这些术语无法详细说明,但出现精化错误表明解析成功:

/-- error: elaboration function for `termBalanced` has not been implemented balanced () -/ #guard_msgs in example := balanced () /-- error: elaboration function for `termBalanced` has not been implemented balanced [] -/ #guard_msgs in example := balanced [] /-- error: elaboration function for `termBalanced` has not been implemented balanced [[]()([])] -/ #guard_msgs in example := balanced [[] () ([])]

同样,当它们不匹配时解析会失败:

example := balanced [() (unexpected token ']'; expected ')' or balanced]]
<example>:1:25-1:26: unexpected token ']'; expected ')' or balanced
Parsing Comma-Separated Repetitions

可以使用以下语法添加需要双方括号并允许尾随逗号的列表文字变体:

syntax "[[" term,*,? "]]" : term

添加 来描述如何将其转换为普通列表文字,使其可以在测试中使用。

macro_rules | `(term|[[$e:term,*]]) => `([$e,*]) ["Dandelion", "Thistle"]#eval [["Dandelion", "Thistle",]]
["Dandelion", "Thistle"]

23.4.13. 缩进🔗

在内部,解析器维护保存的源位置。 语法规则可能包括与这些保存的位置交互的指令,导致在不满足条件时解析失败。 缩进敏感构造(例如 Lean.Parser.Term.do : termdo)保存源位置,在考虑此保存位置的同时解析其组成部分,然后恢复原始位置。

特别是,缩进敏感度是通过组合 withPositionwithPositionAfterLinebreak(在解析某些其他语法开始时保存源位置)与 colGtcolGecolEq(将当前列与最近保存位置的列进行比较)来指定的。 lineEq 还可用于确保两个位置位于源文件中的同一行。

🔗parser alias
withPosition(p)

Arity is sum of arguments' arities

withPosition(p) runs p while setting the "saved position" to the current position. This has no effect on its own, but various other parsers access this position to achieve some composite effect:

  • colGt, colGe, colEq compare the column of the saved position to the current position, used to implement Python-style indentation sensitive blocks

  • lineEq ensures that the current position is still on the same line as the saved position, used to implement composite tokens

The saved position is only available in the read-only state, which is why this is a scoping parser: after the withPosition(..) block the saved position will be restored to its original value.

This parser has the same arity as p - it just forwards the results of p.

🔗parser alias
withoutPosition(p)

Arity is sum of arguments' arities

withoutPosition(p) runs p without the saved position, meaning that position-checking parsers like colGt will have no effect. This is usually used by bracketing constructs like (...) so that the user can locally override whitespace sensitivity.

This parser has the same arity as p - it just forwards the results of p.

🔗parser alias
withPositionAfterLinebreak
  • Arity: 1
  • Automatically wraps arguments in a null node unless there's exactly one
🔗parser alias
colGt
  • Arity: 0
  • Automatically wraps arguments in a null node unless there's exactly one

The colGt parser requires that the next token starts a strictly greater column than the saved position (see withPosition). This can be used for whitespace sensitive syntax for the arguments to a tactic, to ensure that the following tactic is not interpreted as an argument.

example (x : False) : False := by
  revert x
  exact id

Here, the revert tactic is followed by a list of colGt ident, because otherwise it would interpret exact as an identifier and try to revert a variable named exact.

This parser has arity 0 - it does not capture anything.

🔗parser alias
colGe
  • Arity: 0
  • Automatically wraps arguments in a null node unless there's exactly one

The colGe parser requires that the next token starts from at least the column of the saved position (see withPosition), but allows it to be more indented. This can be used for whitespace sensitive syntax to ensure that a block does not go outside a certain indentation scope. For example it is used in the lean grammar for else if, to ensure that the else is not less indented than the if it matches with.

This parser has arity 0 - it does not capture anything.

🔗parser alias
colEq
  • Arity: 0
  • Automatically wraps arguments in a null node unless there's exactly one

The colEq parser ensures that the next token starts at exactly the column of the saved position (see withPosition). This can be used to do whitespace sensitive syntax like a by block or do block, where all the lines have to line up.

This parser has arity 0 - it does not capture anything.

🔗parser alias
lineEq
  • Arity: 0
  • Automatically wraps arguments in a null node unless there's exactly one

The lineEq parser requires that the current token is on the same line as the saved position (see withPosition). This can be used to ensure that composite tokens are not "broken up" across different lines. For example, else if is parsed using lineEq to ensure that the two tokens are on the same line.

This parser has arity 0 - it does not capture anything.

Aligned Columns

这种保存注释的语法采用项目符号列表,每个项目必须在同一列对齐。

syntax "note " ppLine withPosition((colEq "◦ " str ppLine)+) : term

没有与此语法关联的精化器或宏,但解析器接受以下示例:

#check elaboration function for `«termNote__◦__»` has not been implemented note ◦ "One" ◦ "Two" note "One" "Two"
elaboration function for `«termNote__◦__»` has not been implemented
  note
    ◦ "One"
    ◦ "Two"
    

该语法不要求列表相对于起始标记缩进,这将需要额外的 withPositioncolGt

#check elaboration function for `«termNote__◦__»` has not been implemented note ◦ "One" ◦ "Two" note "One" "Two"
elaboration function for `«termNote__◦__»` has not been implemented
  note
    ◦ "One"
    ◦ "Two"
    

以下示例在语法上无效,因为项目符号点的列不匹配。

#check  note    ◦ "One"   expected end of input "Two"
<example>:4:3-4:4: expected end of input
#check  note   ◦ "One"     expected end of input "Two"
<example>:4:5-4:6: expected end of input