2 个不稳定版本

0.21.0 2024 年 5 月 5 日
0.15.0 2024 年 2 月 26 日

文本编辑器 中排名第 445

Download history 331/week @ 2024-04-16 206/week @ 2024-04-23 248/week @ 2024-04-30 524/week @ 2024-05-07 405/week @ 2024-05-14 419/week @ 2024-05-21 444/week @ 2024-05-28 429/week @ 2024-06-04 446/week @ 2024-06-11 578/week @ 2024-06-18 668/week @ 2024-06-25 484/week @ 2024-07-02 462/week @ 2024-07-09 441/week @ 2024-07-16 470/week @ 2024-07-23 519/week @ 2024-07-30

每月下载量 1,943
4crate 中使用

MIT 许可协议

20MB
680K SLoC

C 680K SLoC // 0.0% comments Scheme 368 SLoC // 0.2% comments JavaScript 74 SLoC // 0.2% comments Rust 38 SLoC

tree-sitter-haskell

CI

Haskell 语法 for tree-sitter.

参考

支持的语言扩展

这些扩展是支持的 ✅,不受支持的 ❌ 或不适用,因为它们不涉及解析 ➖️

  • AllowAmbiguousTypes ➖️
  • ApplicativeDo ➖️
  • Arrows ❌
  • BangPatterns ✅
  • BinaryLiterals ✅
  • BlockArguments ✅
  • CApiFFI ✅
  • ConstrainedClassMethods ✅
  • ConstraintKinds ✅
  • CPP ✅
  • CUSKs ✅
  • DataKinds ✅
  • DatatypeContexts ✅
  • DefaultSignatures ✅
  • DeriveAnyClass ➖️
  • DeriveDataTypeable ➖️
  • DeriveFoldable ➖️
  • DeriveFunctor ➖️
  • DeriveGeneric ➖️
  • DeriveLift ➖️
  • DeriveTraversable ➖️
  • DerivingStrategies ✅
  • DerivingVia ✅
  • DisambiguateRecordFields ➖️
  • DuplicateRecordFields ➖️
  • EmptyCase ✅
  • EmptyDataDecls ✅
  • EmptyDataDeriving ✅
  • ExistentialQuantification ✅
  • ExplicitForAll ✅
  • ExplicitNamespaces ✅
  • ExtendedDefaultRules ➖️
  • FlexibleContexts ✅
  • FlexibleInstances ✅
  • ForeignFunctionInterface ✅
  • FunctionalDependencies ✅
  • GADTs ✅
  • GADTSyntax ✅
  • GeneralisedNewtypeDeriving ➖️
  • GHCForeignImportPrim ✅
  • Haskell2010 ➖️
  • Haskell98 ➖️
  • HexFloatLiterals ✅
  • ImplicitParams ✅
  • ImplicitPrelude ➖️
  • ImportQualifiedPost ✅
  • ImpredicativeTypes ➖️
  • IncoherentInstances ➖️
  • InstanceSigs ✅
  • InterruptibleFFI ✅
  • KindSignatures ✅
  • LambdaCase ✅
  • LexicalNegation ❌
  • LiberalTypeSynonyms ✅
  • LinearTypes ✅
  • ListTuplePuns ✅
  • MagicHash ✅
  • Modifiers ❌
  • MonadComprehensions ➖️
  • MonadFailDesugaring ➖️
  • MonoLocalBinds ➖️
  • MonomorphismRestriction ➖️
  • MultiParamTypeClasses ✅
  • MultiWayIf ✅
  • NamedFieldPuns ✅
  • NamedWildCards ✅
  • NegativeLiterals ➖️
  • NondecreasingIndentation ✅
  • NPlusKPatterns ➖️
  • NullaryTypeClasses ✅
  • NumDecimals ➖️
  • NumericUnderscores ✅
  • OverlappingInstances ➖️
  • OverloadedLabels ✅
  • OverloadedLists ➖️
  • OverloadedRecordDot ✅
  • OverloadedRecordUpdate ✅
  • OverloadedStrings ➖️
  • PackageImports ✅
  • ParallelListComp ✅
  • PartialTypeSignatures ✅
  • 模式守卫 ✅
  • 模式同义词 ✅
  • 多类型 ➖️
  • 后缀运算符 ➖️
  • 带资格的do ✅
  • 量化约束 ✅
  • 准引号 ✅
  • Rank2类型 ✅
  • RankN类型 ✅
  • 可重新绑定语法 ➖️
  • 记录通配符 ➖️
  • 递归do ✅
  • 必需类型参数 ✅
  • 角色注解 ✅
  • 安全 ➖️
  • 作用域类型变量 ✅
  • 独立推导 ✅
  • 独立类型签名 ✅
  • 星号是类型 ✅
  • 静态指针 ❌
  • 严格 ➖️
  • 严格数据 ✅
  • 模板Haskell ✅
  • 模板Haskell引号 ✅
  • 传统记录语法 ➖️
  • 转换列表推导 ✅
  • 可信 ➖️
  • 元组部分 ✅
  • 类型抽象 ✅
  • 类型应用 ✅
  • 类型数据 ✅
  • 类型族 ✅
  • 类型族依赖 ✅
  • 类型在类型中 ✅
  • 类型运算符 ✅
  • 类型同义词实例 ➖️
  • 无包装求和 ✅
  • 无包装元组 ✅
  • 不可判定的实例 ➖️
  • 不可判定的超类 ➖️
  • Unicode语法 ✅
  • 非提升FFI类型 ➖️
  • 非提升新类型 ✅
  • 不安全 ➖️
  • 视图模式 ✅

错误

CPP

预处理器 #elif#else 指令无法正确处理,因为解析器状态必须手动重置为 #if 时的状态。作为 workaround,替代分支中的代码块被视为指令的一部分进行解析。

查询

语法包含几个 超类型,这些超类型将多个其他节点类型组合成一个单一的名称。

超类型名称不会在解析树中作为额外节点出现,但它们可以用特殊方式在查询中使用

  • 作为一个别名,匹配它们的任何子类型
  • 作为它们子类型的前缀,只有当它作为超类型的产生式出现时才匹配其符号

例如,查询 (expression) 匹配节点 infixrecordprojectionconstructor,以及此树中 cats <> Cat {mood = moods.sleepy} 的第二个和第三个 variable

(infix
  (variable)
  (operator)
  (record
    (constructor)
    (field_update
      (field_name (variable))
      (projection (variable) (field_name (variable)))))))))

variablefield_namemoodsleepy)中的两个出现不是表达式,而是复合 record 表达式的一部分的记录字段名称。

使用第二种特殊形式可以特别匹配 variable 节点,这些节点是表达式。查询 (expression/variable) 将仅匹配另外两个,catsmoods

语法的超类型包括以下集合

  • 表达式

    在任何表达式位置都有效的规则,不包括类型应用、显式类型和表达式签名。

  • 模式

    在任何模式位置都有效的规则,不包括类型绑定器、显式类型和模式签名。

  • 类型

    类型是原子类型(没有歧义的结合性,如括号构造、变量和类型构造函数)、应用类型或中缀类型。

  • 量化类型

    forall、上下文或函数参数为前缀的类型。

  • 约束

    几乎与 类型 的规则相同,但用于上下文时进行了镜像。

  • 约束

    quantified_type 的类似物,用于具有 forall 或上下文的约束。

  • type_param

    类型和类头中的原子节点,例如在 data A @k a (b :: k) 中的 A 之后的三个节点。

  • 声明

    所有顶层声明,如函数和数据类型。

  • decl

    在局部绑定(letwhere)以及类和实例体中有效的声明的缩写。它由 signaturefunctionbind 组成。

  • class_declinstance_decl

    在类和实例中有效的所有声明,包括关联的类型和数据族。

  • 语句

    do-表示语句的不同形式。

  • 限定符

    列表推导限定符的不同形式。

  • 守卫

    函数方程和情况替代中的守卫的不同形式。

开发

生成和测试此语法解析器的驱动程序主要是 tree-sitter CLI。项目的其他组件需要额外的工具,如下所述。

其中一些通过 npm 提供 – 例如,npx tree-sitter 运行 CLI。如果您没有其他方式获得 tree-sitter,请在以下章节中的所有命令前加上 npx

输出路径

CLI 将包含解析器的共享库写入由 $TREE_SITTER_LIBDIR 表示的目录。如果该变量未设置,则默认为 $HOME/.cache/tree-sitter/lib

为了避免将开发版本覆盖此全局目录,您可以设置环境变量为本地路径

export TREE_SITTER_LIBDIR=$PWD/.lib

语法

JavaScript 文件 grammar.js 包含语法生产规则的入口点。请参阅 tree-sitter 文档 了解语法和语义的全面介绍。

解析从 rules 字段中的第一个条目开始。

{
  rules: {
    haskell: $ => seq(
      optional($.header),
      optional($._body),
    ),
  }
}

生成解析器

开发工作流程的第一步是将 JavaScript 规则定义转换为 src/parser.c 中的 C 代码。

$ tree-sitter generate

此过程的两个副产品被写入 src/grammar.jsonsrc/node-types.json

编译解析器

大多数提到的测试工具会自动编译 C 代码,但您可以指示 tree-sitter 一次性完成。

$ tree-sitter generate --build

如果您已设置 $TREE_SITTER_LIBDIR,如前所述,共享对象将写入 $PWD/.lib/haskell.so

除了生成的 src/parser.c 之外,tree-sitter 还会将 src/scanner.c 编译并链接到这个对象中。该文件包含 外部扫描器,这是内置词法分析器的自定义扩展,其目的是处理在 JavaScript 语法中无法(高效)表示的语言结构,例如 Haskell 布局。

WebAssembly

解析器也可以编译成 WebAssembly,这需要 emscripten

$ tree-sitter build --wasm

生成的二进制文件将写入到 $PWD/tree-sitter-haskell.wasm

测试解析器

tree-sitter 语法最基本测试基础设施是一组代码片段及其关联的参考 AST,存储在 ./test/corpus/*.txt 中。

$ tree-sitter test

可以通过指定(描述的子串)来运行单个测试 -f

$ tree-sitter test -f 'module: exports empty'

该项目还包含其他几种类型的测试

  • test/parse/run.bash [update] [测试名称 ...] 解析 test/parse/*.hs 中的文件,并将输出与 test/parse/*.target 进行比较。如果将 update 指定为第一个参数,则将更新第一个失败的测试的 .target 文件。

  • test/query/run.bash [update] [测试名称 ...] 解析 test/query/*.hs 中的文件,应用 test/query/*.query 中的查询,并将输出与 test/query/*.target 进行比较,类似于 test/parse

  • test/rust/parse-test.rs 包含一些测试,这些测试使用 tree-sitter 的 Rust API 以稍微方便的方式提取终端的测试范围。这需要安装 cargo,可以用 cargo test(它也会运行 bindings/rust 中的测试)来执行。

  • test/parse-libs [wasm] 将一组 Haskell 库克隆到 test/libs,并解析整个代码库。当以 test/parse-libs wasm 调用时,它将使用 WebAssembly 解析器。这需要安装 bc

  • test/parse-lib name [wasm] 仅解析该目录中的库 name(而不克隆存储库)。

调试

tree-sitter test 构建的共享库包含调试符号,所以如果扫描器崩溃,你可以运行 coredumpctl debug 来检查回溯和内存。

newline_lookahead () at src/scanner.c:2583
2583                ((Newline *) 0)->indent = 5;
(gdb) bt
#0  newline_lookahead () at src/scanner.c:2583
#1  0x00007ffff7a0740e in newline_start () at src/scanner.c:2604
#2  scan () at src/scanner.c:2646
#3  eval () at src/scanner.c:2684
#4  tree_sitter_haskell_external_scanner_scan (payload=<optimized out>, lexer=<optimized out>,
    valid_symbols=<optimized out>) at src/scanner.c:2724
#5  0x0000555555772488 in ts_parser.lex ()

为了获得更多控制,启动 gdb tree-sitter 并使用 run test -f 'some test' 开始进程,并使用 break tree_sitter_haskell_external_scanner_scan 设置断点。

要禁用优化,运行 tree-sitter test --debug-build

跟踪

testparse 命令提供了两种模式来获取关于解析过程的详细信息。

使用 tree-sitter test --debug,每个词法分析步骤和移位/归约操作都会打印到标准错误。

使用 tree-sitter test --debug-graph,CLI 将生成一个 HTML 文件,显示每一步的图形表示。这需要已安装 graphviz

依赖项

~2.8–4MB
~72K SLoC