5 个不稳定版本

0.3.0 2024年7月7日
0.2.0 2022年11月25日
0.1.2 2021年1月22日
0.1.1 2020年5月23日
0.1.0 2020年3月10日

#226 in 开发工具

每月 33 次下载

GPL-3.0-only

52KB
1K SLoC

verso|recto - 以不同方式实现的有益编程

CI Status Crates.io

有益编程是针对人类读者准备程序的艺术。

诺曼·拉姆齐在 noweb 主页 上说。

有益编程(LP)试图解决软件系统中的一个常见问题:通过阅读代码可以了解“如何”实现某个功能,但无法了解“为什么”要这样实现。每个程序都嵌入在其逻辑中的“操作理论”,但这通常被隐藏。代码中的注释和 API 文档很有帮助,但不足以完成这项任务。通常,编程语言提供的文档生成系统无法提供将他们描述的代码在其更大的系统中进行上下文化的方式。对于依赖于更高级数学背景的程序,嵌入方程或其他标记在文档中通常也很困难。

现有的 LP 工具,如 WEBnowebOrg Babel,试图通过以下方法解决此问题:

  1. 将程序源代码嵌入到解释作者思维的散文文档中。
  2. 提供机制将代码组织成抽象块,这些块可以在文档中按不同的顺序重新组合。
  3. 以两种方式处理组合文档:将源代码“tangle”从文档中提取到计算机友好的版本,或“weave”文档以供人类阅读。

总的来说,这比内联文档有所改进,可以为读者提供比主流方法更多的上下文。不幸的是,它也存在着一些严重的缺点。

  • 源代码嵌入在标记文件中,使得它无法被特定语言工具(如编辑器、编译器和静态分析工具)访问。为了使用这些工具,代码必须首先进行缠结。
  • 同样,为了构建你的文献程序,最终用户必须安装适当的工具,包括编译源代码所需的语言工具。
  • 大多数程序员多年来一直在直接使用机器友好的源代码表示形式,而不是文献风格,这为编写文献程序设置了进入门槛。将现有项目移植到文献表示形式并非易事。

verso|recto采用不同的方法。它将源代码文件视为文档引用的一等公民。而不是在文档中嵌入源代码,或在源代码中嵌入文档,使用轻量级注释来标记代码中感兴趣的部分,这些部分可以轻松地被散文文档引用。没有tangle步骤,源代码文件仍然是编译器、编辑器和其他工具的完全有效输入。无需在文献源和编译器所见之间转换行号或格式。

传统LP系统做得好的地方

许多事情!以下是传统系统开创的一些想法,以及verso|recto借鉴的

  • 支持LP文档和源文件之间的多对多关系。
    • noweb中,一个文档可以包含多个根块,每个块可以发送到不同的源文件,并且可以同时将多个LP文档输入到tangle工具(它们被连接起来)。
  • 更现代的工具支持多种编程和标记语言。
  • noweb提供了一种管道架构,这使得插入处理阶段以满足用户需求变得容易。
  • 它们在可读输出中生成索引和交叉引用。

使用verso|recto

verso|recto由源文件中的注释驱动,定义了其他文档可以引用的区域。这些区域称为“片段”。

注释文件

注释非常简单。要标记代码区域并创建一个片段,只需在该区域周围添加一对带有符号@<>@的注释,后跟一个唯一的ID。ID可以是任何由字母数字字符组成的字符串,以及字符/_-,尽管它应在你的项目中是唯一的,并且在你注释的源文件中是有效的。(点字符(.)是保留的,因为它用于插入有关片段的元数据。)将来可能还会向“安全”列表中添加其他字符。如果你想要使用的字符未在此列出,请在GitHub上提交问题(或者更好的是,发送PR)!

片段也可以嵌套。当你想要注释一个已包含在更大“外部”片段中的源文件区域时,这尤其有用。内部片段的注释将不会出现在输出中。这最好通过一个例子来说明;请参阅以下散文输出文件。

引用注释

为了在另一个文件中插入一个片段,请添加一行包含符号@@,后跟注释的ID(例如@@12345)。当使用recto命令(见下一节)将文件编织时,该行将替换为片段的内容。您可以在该行周围添加任何您喜欢的标记来提供格式化。

要插入一组片段,可以在@*符号之后使用正则表达式。所有ID与表达式匹配的片段都将按其ID的字典顺序插入符号的位置。

有时也希望能引用关于片段的元数据。目前,verso|recto支持以下元数据插入操作符

  1. 文件名。 @?id.file插入片段来源文件的名称。
  2. 行号。 @?id.line插入片段开始处的行号。
  3. 列号。 @?id.col插入片段开始处的列号。(目前此值始终为0,因为片段总是从行的开头开始。)
  4. 快速定位。 @?id.loc以格式file (line:col)插入片段的文件名、起始行号和列号。如果您只想快速引用元数据而不想修改格式,这很有用。

为人类消费编织文档

verso命令将读取命令行上指定的所有文件,提取它们的片段,并将结果输出到stdout。反过来,recto命令将读取stdin中的片段。这使得这两个程序可以通过管道轻松地一起使用。

verso main.rs lib.rs | recto build chap1.tex chap2.tex blog/home.md
      ^       ^              ^     ^         ^         ^
      +-------+              |     +---------+---------+
      |                      |                         |
      |                      |                         |
      +--- Source files      +--- Output directory     +--- Prose files

每个编织的文件都写入输出目录,该目录作为第一个参数提供,在命令行上给出的相对位置相同。例如,上面的文件blog/home.md在编织时将写入build/blog/home.md

请注意,尽管这两个程序看起来是并行运行的,但verso不会将输入发送到recto,直到它已成功从所有提供的源文件中提取片段,并且recto不会开始编织文件,直到它收到这些片段。因此,如果verso失败,则recto也会失败。

完整符号表

为了参考,以下是一个包含完整符号表的表格。请注意,在(希望是罕见的)您的语言中有与verso|recto使用的默认符号冲突的符号的情况下,您可以通过使用列出的环境变量来覆盖它们。

名称 符号 描述 覆盖变量
片段打开 @< 开始一个命名的片段。 VERSO_FRAGMENT_OPEN_SYMBOL
片段关闭 >@ 结束一个命名的片段。 VERSO_FRAGMENT_CLOSE_SYMBOL
停止 @!halt 停止片段提取。 VERSO_HALT_SYMBOL
插入片段 @@ 通过ID插入一个片段。 RECTO_INSERTION_SYMBOL
插入模式 @* 根据ID模式插入片段。 RECTO_PATTERN_SYMBOL
插入元数据 @? 插入关于片段的元数据。 RECTO_METADATA_SYMBOL

名称

正反两面分别指装订物品(如卷轴、书籍、报纸或小册子)的纸张的“右面”或“前面”和“背面”上所写的或打印的文字。

维基百科 - 正反面

(注意,方便起见,verso程序位于管道的左侧,而recto位于右侧。)

贡献者

未来工作

  • 在编织输出中添加对注释属性自定义格式化的支持。
  • 在Verso中并行化文件处理,并在Recto中从stdin读取和文件读取。
  • 添加--fragments-from选项以指定除stdin之外的片段源。

依赖项

~2.8–4.5MB
~88K SLoC