九章编程: 文言文编程的 Cirru 实现的一种试验

本文是对于 wenyan-lang 方向在的一些尝试, 利用 Cirru 的工具链, 作简化的方案.
代码实现看 九章编程, 以及对应的 Demo 页面.

基于九章编程的方案, 最终实现一个 Fibonacci 函数, 代码是这样的,html

术曰 菲氏数 (甲)
  若 (少于 甲 三) 一
    并 (菲氏数 (减 甲 一)) (菲氏数 (减 甲 二))

得 (菲氏数 一)

对比一下文言编程的例子, 会显得后者啰嗦不少.python

吾有一術。名之曰「斐波那契」。欲行是術。必先得一數。曰「甲」。乃行是術曰。
    若「甲」等於零者乃得零也
    若「甲」等於一者乃得一也
    減「甲」以一。減「甲」以二。名之曰「乙」。曰「丙」。
    施「斐波那契」於「乙」。名之曰「丁」。
    施「斐波那契」於「丙」。名之曰「戊」。
    加「丁」以「戊」。名之曰「己」。
    乃得「己」。
是謂「斐波那契」之術也。

施「斐波那契」於十二。書之。

固然了, 九章编程方案只是取巧地把 Lisp 的写法翻译成中文而已, 少了不少.
首先这东西挺好玩的. 再就是本身仔细看下来也有很多本身的想法.git

古文用法的一些想法

翻了代码里的问题, 文言编程几个做者古文词汇和句式都比我丰富挺多的.
不过由于如今的人都不是经常使用古文, 其实也挺不正宗的.
大体能感觉到例子里不少用法, 是混杂了不一样朝代的措辞, 因此有点怪.
并且有些没法解决的问题, 西方传入的数学和计算机术语, 免不了要用现代的词汇.github

固然, 按照个人偏好, 若是说有古文编程的话, 我首先想到用古代典籍做为模板.
好比说九章算术, 至少汉代的著做了, 这应该能充分表明古人对于数学的表达习惯.编程

〔三七〕今有環田,中周九十二步,外周一百二十二步,徑五步。問為田幾何?
荅曰:二畝五十五步。

〔三八〕又有環田,中周六十二步、四分步之三,外周一百一十三步、二分步之一,徑十二步、三分步之二。問為田幾何?
荅曰:四畝一百五十六步、四分步之一。
術曰:并中外周而半之,以徑乘之為積步。
密率術曰:置中外周步數,分母、子各居其下。母互乘子,通全步,內分子。以中周減外周,餘半之,以益中周。徑亦通分內子,以乘周為實。分母相乘為法,除之為積步,餘積步之分。以畝法除之,即畝數也。json

以及元代的四元玉鉴当中也有相似的表达习惯.(没有句读太难读了..)
我以为若是这些数学家当年发明编程语言的话, 怕是跟这差不了多少.
不过也还好如今的编程语言有各类标点符号, 否则真是有的受了.数组

另外比较明显的一个麻烦是, 代码当中必然会有较多的抽象, 或者说定义函数,
即使你们用的不是 Lisp 这样的前缀表达式语言, 也少不了会遇到这样的代码,安全

(f1 p1 p2)
(f2 q1 q2 q3 q4 (f3 a5 q6) (f4) q7 q8)

算了我仍是换个大家好接受一些的写法:ruby

f1(p1, p2)
f2(q1, q2, q3, q4, f3(a5, q6), f4(), q7, q8)

在文言编程当中, 能够看到 wenyan 用了 名之曰「丁」 来定义操做,
而实际上对应这种枯燥的抽象, 基本上很难也古文天然得表达出来.
或者说代码, 做为给机器执行的语言, 自己就有着特殊性.babel

固然, wenyan 能定义出这么一整套来, 仍是挺厉害的.

在九章编程当中, 我出于省事的考虑, 直接基于已有的 Lisp 风格直接作了.
也就是说, 九章编程基本上就是基于前缀表达式实现的. 不像天然语言.
可是具体的术语, 我基于九章算术的文本作了简单的统计, 选取了一些词汇,
总得来讲只是借了一层九章算术花样, 好比"对象", 九章算术里压根就没这东西.

中文数字和变量名的一些处理

我看 wenyan 当中用的中文数字表示, 在源码里有本身去解析和搜集.
翻了一下代码, 大概是本身进行了解析吧, 先转成阿拉伯数字表示, 就很快了.
九章编程里面直接找了个模块 nzh 进行转化的, 作 Demo 也够用.

另外一个是变量名的问题, 九章编程直接用中文字符串作的.
由于九章编程其实是 interpreter, 不是转义 js 的, 没这个限制.
wenyan 的实现当中我看到有转成拼音的操做, 不肯定具体状况.
按说中文, 都是 jia3, 虽然有字典, 但很容易会重名的.

JavaScript 方言的实现方案

wenyan 大体上提供了 js, py, ruby 的方案, 大体看了一下 js 的部分.
首先 tokenize, 再 parser 解析代码, 而后用 compiler 拼接 js.
拼接 js 的部分是直接用的字符串拼接, 相对来讲不那么完善, 可是够用.
另外手动拼接获得的代码, 通常格式都是乱的, 须要用 Prettier 或者 Babel 从新格式化.

另外比较省事并且可靠点的方案是用 babel/generator 去实现.
就是说用 Babel 来处理 JavaScript 代码生成的具体实现,
这样几方的工做只要作到能生成 AST 就行了, 这就安全不少.
比较熟悉是由于个人 CirruScript 用的就是这套方案, Babel 工具链真挺丰富的.

九章编程用的方案是 Interpreter, 解释执行, 没有生成 js 代码.
这也就意味着执行计算都是在 JavaScript 运行环境内部的,
单纯 JavaScript 执行, 能够有 V8 优化, 最终甚至可能以汇编的形式运行,
那样来讲性能就好不少了. 解释执行的问题就是性能会不好.
不过另外一方面, 解释执行不须要知足 js 语法, 也就没七七八八的限制了. 直接跑.

Cirru 提供的方案

虽然对于编译器来讲, 生成代码的优化是最难的部分, 但玩具项目的话...
要写个 Parser 把整个代码结构解析出来也是至关要命的工做量.
wenyan 光是 parser.js 就八百多行了, 还不算各类工具函数和关键字定义的,
没看明白 typechecker.js 具体逻辑, 校验结构么, 也快七百行了.
反而 compiler 生成 js 部分三百多行就搞定了...

我...毕竟是写着玩的, 若是 Parser 也要这么风风火火折腾一遍, 枯燥啊.
不过我有 Cirru 这边的工具链, 加上语法, 直接用 Lisp 风格套上去了.
Cirru 大体是是一套把缩进语法(或者数据)生成一个树结构的方案,
好比这样一段代码, 直接用 Cirru 的模块进行解析,

得 (菲氏数 一)

就能直接获得一个树形的结构:

[
  [
    "得",
    [
      "菲氏数",
      "一"
    ]
  ]
]

前面函数定义的部分, 代码复杂一些, 有缩进, 也对应解析出来:

术曰 菲氏数 (甲)
  若 (少于 甲 三) 一
    并 (菲氏数 (减 甲 一)) (菲氏数 (减 甲 二))

获得着要一个树形的结构:

[
  [
    "术曰"
    "菲氏数"
    ["甲"]
    [
      "若"
      ["少于" "甲" "三"]
      "一"
      [
        "并"
        [
          "菲氏数"
          ["减" "甲" "一"]
        ]
        [
          "菲氏数"
          ["减" "甲" "二"]
        ]
      ]
    ]
  ]
]

有这样一个结构, 后面的部分就相对容易了, 若是不校验的话, 直接就能算, 好比:

["减" "甲" "一"]

通过简单的变换就能获得对应的 JavaScript 代码:

(甲 - 1)

或者更加复杂一些的结构,

[
  "并"
  [
    "菲氏数"
    ["减" "甲" "一"]
  ]
  [
    "菲氏数"
    ["减" "甲" "二"]
  ]
]

其实就是判断一下, 对中间的数组进行递归计算, 也很容易完成求值.
固然, 具体到函数定义方面, 以及一些动态长度(或者复杂节够)的语句, 会麻烦一些.
原理上能够参考 http://norvig.com/lispy.html 提供的例子.

这套方案用了 Cirru 的 Parser, 同时也就继承了 Cirru 语法的约束,
好比用 ( ) $ , " 以及空格进行语法结构控制的事情.
放在九章编程里面, 主要是在古文编程当中插入了大量的英文符号...
或者说这些如何其实除了空格换成中文笔画符号.. 可能效果也是相似的, 总之有些奇怪的东西.
不过整体上讲, 直接省去了大量工做量.

其余

wenyan 高亮作得比较充分, 渲染图挺漂亮的. 九章这边没有专门作.
不过倒也不是一点高亮都没有, 能够看到文档里直接用 Cirru 进行了基础的高亮.
看源码那边, 好像 wenyan 用的是 SVG 渲染的图, 效果确实不错.

由于是个玩具项目, 九章编程试验到能求 Fibonacci 而后, 有点玩不动了.
我知道后面的工做量挺多的, 比较我以前 Cirru 项目当中就在尝试.
有兴趣过来 watch 一下 CirruScriptinterpreter.nim 这边的工做.
虽然暂时没有经历深刻开发, 可是断断续续会王中间追加功能的.

另外我在微博上也有提到, 中文的表达能力实际上是很是强的,
能够想象, 同个含义, 在古文当中能够获得多个表述, 排列组合下来, 不比英文少...
以往看到的中蟒算是作的比较完整的一个范例吧.
彻底有不少可能, 能够脑补一个少儿编程的场景, 写一段代码,

李雷的数 是 1
韩梅梅的数 是 2
(李雷的数 加上 韩梅梅的数) 是多少

这个代码用 Cirru 能解析出来一个简单的结构,

[
  [
    "李雷的数",
    "是",
    "1"
  ],
  [
    "韩梅梅的数",
    "是",
    "2"
  ],
  [
    [
      "李雷的数",
      "加上",
      "韩梅梅的数"
    ],
    "是多少"
  ]
]

作一下语法转换, 就获得一串很熟悉的前缀表达式了,

[
  [
    "是",
    "李雷的数",
    "1"
  ],
  [
    "是",
    "韩梅梅的数",
    "2"
  ],
  [
    "是多少",
    [
      "加上",
      "李雷的数",
      "韩梅梅的数"
    ]
  ]
]

而后求一下值, 拿去忽悠一零后有没有效果...

换成中文门槛也低一些吧, 应该有很多能够尝试的.
九章编程的 Demo 是能够执行的, 欢迎试玩 http://jiuzhang.cirru.org/

相关文章
相关标签/搜索