文言编程语言(wenyan-lang)入门与实战指南:语法、CLI 编译与古书 SVG 渲染
2026/9/20 8:52:46 网站建设 项目流程

文言编程语言(wenyan-lang)入门与实战指南:语法、CLI 编译与古书 SVG 渲染

【免费下载链接】wenyan文言文編程語言 A programming language for the ancient Chinese.项目地址: https://gitcode.com/gh_mirrors/we/wenyan

wenyan-lang(文言)是一门以古汉语语法为蓝本的自然语言编程语言,编译器可把文言代码转译为 JavaScript、Python 与 Ruby。本文以仓库 README.zh-Hans.md 为骨架,结合 src/cli.ts、src/parser.ts、src/render.ts 等源码,系统讲解语言语法、命令行工具、导入机制与古书样式渲染器,读完即可动手编写、编译并渲染属于自己的文言程序。

项目概览:用文言写代码是什么体验

wenyan-lang 试图让程序语言回归"文言"这一延续数千年的书写传统。项目序言中作者自述其志:"然以文言編程者,似所未有。此誠非文脈之所以傳,文心之所以保",意在补文言编程之空白,让代码亦可"文氣淋灕"。

从工程角度看,它有如下核心特征(引自 README.zh-Hans.md):

  • 符合古汉语语法的自然语言处理程序:代码读起来像一篇短文,而非符号堆砌;
  • 多目标编译:可编译为 JavaScript、Python 或 Ruby,三个转译器统一注册在 src/transpilers/index.ts;
  • 图灵完备:仓库内置了用文言编写的通用图灵机程序 examples/turing.wy 作为佐证;
  • 在线 IDE:仓库 site/ide.html 与 static/index.html 提供了浏览器内即时编辑体验;
  • 丰富的示例:埃拉托斯特尼筛法、快速排序、曼德博集合、汉诺塔等算法均有文言实现,集中存放于 examples/ 目录。

快速上手:Hello World 与"标点无关"特性

仓库中的 examples/helloworld.wy 内容如下:

吾有一言。曰「「問天地好在。」」。書之。

README 中给出了更完整的循环版本示例。文言代码:

吾有一數。曰三。名之曰「甲」。 為是「甲」遍。 吾有一言。曰「「問天地好在。」」。書之。 云云。

它等价于以下 JavaScript:

var n = 3; for (var i = 0; i < n; i++) { console.log("問天地好在。"); }

运行输出:

問天地好在。 問天地好在。 問天地好在。

标点与换行完全可选

正如古汉语中文字连绵不断,wenyan 的标点符号与换行都是可选的,上面的代码与下面这一行完全等价:

吾有一數曰三名之曰「甲」為是「甲」遍吾有一言曰「「問天地好在」」書之云云

这一设计在词法分析层面得到了体现:src/parser.ts 的wy2tokens。、\n\r\t视为可忽略符号(IGNORE_SYMBOLS),真正的语法单位由「」引号、数字关键字与文言关键字边界决定,而非依赖标点。

想继续深入,examples/ 目录提供了 40 余个可直接运行的程序,涵盖排序(quicksort.wy、mergesort.wy、selectionsort.wy)、数论(euclidean.wy、modinv.wy、crt.wy)、图形学(mandelbrot.wy、draw_heart.wy)以及经典算法(hanoi.wy、eightqueens.wy、turing.wy)等。

安装与环境准备

安装命令行编译器

通过 npm 全局安装编译器:

npm install -g @wenyan/cli

安装后即可直接运行仓库内置示例,例如:

wenyan examples/helloworld.wy -o helloworld.js

该命令读取文言源文件、编译为 JavaScript 并写入helloworld.js(详见下文 CLI 章节)。若直接执行wenyan examples/helloworld.wy而不带参数,则会编译并在终端中直接运行,输出問天地好在。

在线 IDE

不想安装任何东西时,可使用仓库内的浏览器版 IDE 页面 site/ide.html(生产构建对应 static/index.html),左侧编写文言代码、右侧即时查看编译结果与输出:

编辑器插件

社区为常用编辑器提供了语法支持:

  • 由 antfu 提供的适用于 VSCode 的插件;
  • 由 voldikss 提供的适用于 Vim 的插件;
  • 由 absop 提供的适用于 Sublime Text 的插件。

wenyan 命令行工具全参数详解

README 建议用wenyan -h获取帮助。结合 src/cli.ts 中commander的定义,当前 CLI 支持如下完整参数:

参数含义默认值 / 说明
-v, --version输出版本号读取自 src/version.ts
-l, --lang <lang>目标语言js,可选jspyrb
-c, --compile只输出编译后代码,不执行需配合-o指定输出文件
-e, --eval <code>直接求值一段文言代码追加在源文件内容之后一并编译
-i, --interactive进入交互式 REPL仅支持目标语言js
-o, --output [file]输出到文件未给路径时会自动推导(见下)
-r, --render输出古书样式 SVG 渲染见"渲染器"章节
--roman [method]标识符罗马化可选pinyinbaxterunicode--roman裸用等价于--roman pinyin
--strict开启静态类型检查默认关闭
--allowHttp允许通过 HTTP 导入模块默认关闭(安全考虑)
--dir <path>追加导入搜索目录多个目录用逗号分隔
--no-outputHanzi关闭输出结果汉字化默认开启,数字/布尔输出会转为汉字
--log <file>将编译日志写入文件支持/dev/stdout/dev/stderr
--title <title>覆盖渲染标题默认取输出文件名或源文件名
-h, --help显示帮助无参数直接运行也会打印帮助与 ASCII Logo

输出文件的自动推导

由 src/cli.ts 的preprocess可见:当-o后未跟具体路径时,编译器会以源文件名(去掉扩展名)为基础自动生成:

  • --compile时推导为${base}.${lang}(如helloworld.js);
  • --render时推导为${base}.svg
  • 默认执行时推导为${base}.log

执行模式的限制

值得注意:直接执行(不带--compile)与交互式 REPL 仅支持目标语言js,src/execute.ts 的isLangSupportedForEval会显式抛错;Python / Ruby 目标必须配合--compile生成代码文件后另行运行。同时,直接执行时数字与布尔输出默认会被汉字化(outputHanziWrapper,见 src/execute.ts),例如打印5会输出"五",可用--no-outputHanzi关闭。

语法速查表:从变量到注释

README 提供了一份详尽的"文言 ↔ JavaScript"对照语法表,以下完整收录并补充说明。

变量

wenyanJavaScript
吾有一數。曰三。名之曰「甲」。var a = 3;
有數五十。名之曰「大衍」。var dayan = 50;
昔之「甲」者。今「大衍」是也。a = dayan;
吾有一言。曰「「噫吁戲」」。名之曰「乙」。var b = "alas!";
吾有一爻。曰陰。名之曰「丙」。var c = false;
吾有一列。名之曰「丁」。var d = [];
吾有三數。曰一。曰三。曰五。名之曰「甲」曰「乙」曰「丙」。var a=1,b=3,c=5;

数字由汉字书写(一、三、五…),其解析由 src/converts/hanzi2num.ts 负责:词法阶段先把汉字数字串转换为数值字符串(hanzi2numstr),再进入 AST 构建。

流程控制

wenyanJavaScript
若三大於二者。乃得「「想當然耳」」也。if (3>2){ return "of course"; }
若三不大於五者。乃得「「想當然耳」」。若非。乃得「「怪哉」」也。if(3<=5){return "of course"}else{return "no way"}
為是百遍。⋯⋯ 云云。for (var i = 0; i < 100; i++){ ... }
恆為是。⋯⋯ 云云。while (true) { ... }
凡「天地」中之「人」。⋯⋯ 云云。for (var human of world){ ... }
乃止。break;

运算

wenyanJavaScript
加一以二。1+2
加一於二。2+1
加一以二。乘其以三。(1+2)*3
除十以三。所餘幾何。10%3
減七百五十六以四百三十三。名之曰「甲」。var a = 756-433;
夫「甲」「乙」中有陽乎。a \|\| b
夫「甲」「乙」中無陰乎。a && b

注意「以」与「於」区分操作数顺序(加一以二1+2加一於二2+1),这正对应古汉语的语序习惯。

容器

数组下标从一开始,而非零。

wenyanJavaScript
吾有一列。名之曰「甲」。充「甲」以四。以二。var a = []; a.push(4, 2);
銜「甲」以「乙」。以「丙」a.concat(b).concat(c);
夫「甲」之一。a[0]
夫「甲」之其餘。a.slice(1);
夫「玫瑰」之「「名」」。rose["name"]
夫「寶劍」之長。sword.length;

对象

wenyanJavaScript
吾有一物。名之曰「甲」。var a = {};
吾有一物。名之曰「甲」。其物如是。物之「「乙」」者。數曰三。物之「「丙」」者。言曰「「丁」」。是謂「甲」之物也。var a = {b:3, c:"d"}

函数

wenyanJavaScript
吾有一術。名之曰「吸星大法」。是術曰。⋯⋯是謂「吸星大法」之術也。function f(){...}
吾有一術。名之曰「六脈神劍」。欲行是術。必先得六數。曰「甲」。曰「乙」。曰「丙」。曰「丁」。曰「戊」。曰「己」乃行是術曰。⋯⋯是謂「六脈神劍」之術也。function f(a,b,c,d,e,f){...}
吾有一術。名之曰「翻倍」。欲行是術。必先得一數。曰「甲」。乃行是術曰。乘「甲」以二。名之曰「乙」。乃得「乙」。是謂「翻倍」之術也。function double(a){var b = a * 2; return b;}
施「翻倍」於「大衍」。double(dayan);
吾有一術。名之曰「甲」。欲行是術。必先得一數曰「乙」。二言。曰「丙」。曰「丁」function a(float b, string c, string d)
夫「甲」。夫「乙」。夫「丙」。取二以施「丁」。取二以施「戊」。名之曰「己」。var f = e(a,d(b,c))
夫「甲」。夫「乙」。夫「丙」。取二以施「丁」。取二以施「戊」。取一以施「己」。夫「庚」。夫「辛」。取三以施「壬」。名之曰「癸」。var j = i(f(e(a,d(b,c))),g,h)

函数实例如 examples/factorial.wy:定义递归的「階乘」術,先得一數「甲」,若等於一則直接返回,否则施「階乘」於「乙」递归调用,最后書之打印施「階乘」於五的结果。

导入

wenyanJavaScript
吾嘗觀「「算經」」之書。方悟「正弦」「餘弦」之義。var {sin,cos} = require("math");

导入机制的底层实现在 src/reader.ts(importReader),详见"导入机制与标准库"章节。

杂项

wenyanJavaScript
吾有一數。曰五。書之。console.log(5);

注释

wenyanJavaScript
批曰。「「文氣淋灕。字句切實」」。/*文氣淋灕。字句切實*/
注曰。「「文言備矣」」。/*文言備矣*/
疏曰。「「居第一之位故稱初。以其陽爻故稱九」」。/*居第一之位故稱初。以其陽爻故稱九*/

编译流水线:从文言到三种目标语言

从源码结构看,一次编译经历如下流水线:

  1. 词法分析(src/parser.tswy2tokens):处理「」字符串字面量、「「」」转义与汉字数字,生成 Token 流;关键字表定义在 src/keywords.ts,数字关键字(零一二三四五六七八九十…)由 src/converts/hanzi2num.ts 支持;
  2. 语法分析 / AST 构建:Token 序列被解析为 AST 节点(ASCNode),随后送入转译器;
  3. 多目标转译:src/transpilers/index.ts 以{ js, py, rb }映射注册三个转译器,它们继承自 src/transpilers/base.ts,将同一 AST 分别输出为 JavaScript、Python 与 Ruby 代码;
  4. 宏展开与导入打包compile过程中会先经 src/macro.ts 的extractMacros/expandMacros处理"或云…蓋謂…"宏定义,并经bundleImports内联导入模块;
  5. 可选的静态类型检查--strict会调用 src/typecheck.ts 的类型检查器,在编译期发现类型不匹配;
  6. 执行(仅 JS)evalCompiled(src/execute.ts)在受控作用域内eval编译产物,并把数字、布尔输出转换为汉字。

关键词NUMBER_KEYWORDS用于词法阶段把连续汉字数字累积为一个数值 Token,这正是"标点可选"能够成立的关键——数字与标识符的边界由关键字集合而非标点决定。

导入机制与标准库

README 语法表中的导入示例是吾嘗觀「「算經」」之書。方悟「正弦」「餘弦」之義。,即从模块"算經"中导入「正弦」「餘弦」两个符号。

从 src/reader.ts 的实现看,导入解析遵循以下规则:

  • 搜索路径:依次在 CLI 的--dir指定目录、向上查找到的藏書樓目录(MODULE_LIBRARY_NAME,见 src/cli.ts)、源文件所在目录、当前工作目录中查找模块名.wy模块名/序.wyINDEX_FILENAME为「序」,见 src/reader.ts);
  • HTTP 导入:支持https://形式的远端模块,但默认被安全策略拦截(isHostTrusted白名单机制),需显式传allowHttp才会放行;
  • 缓存:同一 URI 的导入结果会写入importCache,避免重复读取。

仓库自带的文言标准库位于 lib/ 目录,包括基础数学库 lib/算經.wy、易经相关 lib/易經.wy、历法库 lib/曆法.wy 与 lib/曆表.wy、线性代数库 lib/列經.wy、组合数学库 lib/籌經.wy、混沌/随机库 lib/渾沌經.wy;按目标语言分发的版本在 lib/js/(位經、天地經、格物、畫譜、西曆法)、lib/py/ 与 lib/rb/。导入测试覆盖见 test/import.test.ts 与嵌套导入夹具 test/fixture/nested-import/四庫全書/。

渲染器:把文言代码排版成古书样式 SVG

这是 wenyan 最具特色的功能之一。src/render.ts 能把.wy源文件渲染成仿历史印刷书籍版式的矢量图(SVG):竖排文字、朱红批注、栏线边框一应俱全(颜色常量RED/BLACK定义于 src/render.ts)。

README 给出的命令为:

wenyan examples/turing.wy --render 圖靈機 --output .

注意:当前版本 CLI 中--render为布尔开关,标题需通过--title指定(见 src/cli.ts 与 src/cli.ts),等价且与当前 CLI 完全一致的写法是:

wenyan examples/turing.wy --render --title 圖靈機 --output .

渲染行为细节(对应 src/cli.ts 的doRender):

  • 渲染结果始终写入文件;若只生成一页,输出为文件名.svg
  • 多页(长程序)时输出为文件名.001.svg文件名.002.svg… 依次编号;
  • 渲染前会剥离换行、制表符与空格,把『』归一化为嵌套引号「「」」,再进行词法分析(见 src/render.ts);
  • 排版按竖排栏位推进(页宽792、栏宽由CW等常量控制,见 src/render.ts),注释以朱色小字呈现;
  • 渲染器还实现了反向解析unrender可把生成的 SVG 解析回原始文言代码,因此wenyan命令行也支持直接输入.svg文件作为源(见 src/cli.ts)。

下图即是用文言编写的通用图灵机程序 examples/turing.wy 渲染而成的古书版式 SVG:

仓库 renders/ 目录保留了mandelbrot.svgturing.svgturing-wfont.svg等渲染成品,可供参考。

测试与验证

项目使用 Jest 作为测试框架(package.json 中npm test即运行jest --detectOpenHandles),覆盖范围包括:

  • test/examples.test.ts:逐一编译并运行 examples/ 下的示例,快照存放于 test/snapshots/examples.test.ts.snap;
  • test/import.test.ts:验证嵌套导入与模块解析;
  • test/numbers.test.ts 与 test/reader.test.ts:数字转换与词法/读取器行为;
  • test/stdlib.math.test.ts、test/stdlib.calendar.test.ts、test/stdlib.wonton.test.ts:标准库函数正确性。

此外 documentation/wenyan.g4 提供了上下文无关文法的 ANTLR 描述,documentation/ 目录还有编译器 API、运行时、宏、嵌套函数调用、Try-Catch 等专题文档,可作为深入阅读的入口。

路线图与已知问题

README 末尾列出了社区功能请求与已知问题,摘录如下(供参与贡献者参考):

功能请求:

名称优先级需要帮助状态
语言规范★★★★★正在进行中
类 / 对象文法★★★对象文法已经添加
导入语句★★★导入语句已经添加
标准库(Math 数学 / Bitwise ops 位运算 / Random 随机)★★★★★正在进行中
测试套件★★★★正在进行中
Switch 语句★★★
函数式程序设计★★★
更严格的编译器★★★★
其他语言的编译器★★
编辑器的插件★★适用于 VSCode、Vim、Sublime 的插件已添加
将 js / py / anything 转换回 wenyan(文言)
转义 / 生成特殊符号★★★
对「「」」的替换语法★★
对 。的替换语法★★
在线 IDE 的字体和垂直文本★★
将注释呈现为小型内联文本★★
更多示例★★

已知问题:

名称优先级需要帮助状态
汉字到数字的转换问题★★★★★
汉字到数字转换中多字符数字没有被加入支持★★★

如果你愿意帮助实现表中"需要帮助"一栏带 √ 的功能,或任何其他功能,都欢迎提交 Pull Request 参与协作。


综上,wenyan-lang 从语法表到 CLI、从导入机制到古书渲染器,构成了一条完整且可运行的文言编程工具链。下一步建议:先跑通wenyan examples/factorial.wy,再用--compile --lang py生成 Python 版本对照阅读,最后用--render把 examples/mandelbrot.wy 渲染成一册"线装书",亲自体会文心与代码的相遇。

【免费下载链接】wenyan文言文編程語言 A programming language for the ancient Chinese.项目地址: https://gitcode.com/gh_mirrors/we/wenyan

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询