Browse Source

允许因子是非数字

myuan 2 years ago
parent
commit
c697887103
17 changed files with 1292 additions and 0 deletions
  1. 301 0
      3. 简单的数值语言及其虚拟机/readme.md
  2. BIN
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.e
  3. 10 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/DLL.e.txt
  4. BIN
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码.e
  5. 293 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/抽象语法树节点.class.e.txt
  6. 5 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/排序.list.txt
  7. 32 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/来自精易模块.static.e.txt
  8. 46 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/程序集1.static.e.txt
  9. 90 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/词法分析程序集.static.e.txt
  10. 373 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/语法分析程序集.static.e.txt
  11. 28 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/常量.e.txt
  12. 6 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/自定义类型.e.txt
  13. 10 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/资源/命令行帮助.txt
  14. 50 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/配置/支持库.config.json
  15. 23 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/配置/用户.config.json
  16. 13 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/配置/系统.config.json
  17. 12 0
      3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/项目.etprj

+ 301 - 0
3. 简单的数值语言及其虚拟机/readme.md

@@ -0,0 +1,301 @@
+# 简单的数值语言及其虚拟机
+
+> 上一节: https://bbs.125.la/forum.php?mod=viewthread&tid=14705938
+> 源代码 git: http://gogs.mkyr.fun:99/myuan/elang
+
+## 目标
+
+本节的目标是给上一节的「语言」加上一个函数定义, 然后运行起来. 不过, 我打算在这里面玩一点花哨的, 不提供可变量, 让我们看看能走到哪一步吧. 
+
+最初函数空间中只给出 个函数:
+- output(x, y, z...) 输出括号里的东西
+- dir() 展示当前有哪些函数定义
+
+允许输入的例子如下:
+```
+# 先热一下身
+
+f(x) = x * x
+output(f(15))
+
+# 都是编程初学者的内容啦
+
+fib(1) = 1
+fib(2) = 1
+fib(x) = fib(x - 1) + fib(x - 2)
+
+output(fib(20))
+
+factorial(0) = 1
+factorial(x) = x * factorial(x-1)
+
+output(factorial(15))
+
+exp(x, 0) = 1
+exp(x, y) = exp(x, y - 1) * x
+square(x) = exp(x, 2)
+
+output(exp(2, square(2, 2)))
+
+
+sqrt(x) = sqrt(x, x / 2, 1, 10)
+sqrt(x, s, y, 0) = s
+sqrt(x, s, y, n) = sqrt(x, (s + y) / 2, x / ((s + y) / 2), n - 1)
+
+output(sqrt(2), square(sqrt(2)))
+```
+
+## 新增的词法分析部分
+
+本节引入了不可变变量和函数声明, 那个新的词法定义如下:
+
+```
+语句       ::= 函数定义 | 表达式 # 新增了统筹的语句
+函数定义   ::= 函数 "=" 表达式   # 新增了函数定义
+表达式     ::= 加后表达式 (("+" | "-") 加后表达式)*
+加后表达式  ::= 因子 (("*" | "/") 因子)*
+因子       ::= 左括号 表达式 右括号 | 函数 | 数字 | 标识符  # 之前的语法中最简因子只能是数字, 现在可以是标识符了
+函数       ::= 标识符 左括号 (表达式 (逗号 表达式)*){0,1} 右括号
+```
+
+没什么新东西, 学会了上一节的东西之后, 就是体力活了. 
+
+## 词法分析
+
+此处的「词」是一个泛指, 意为某些同类字的集合, 如「12334.235」是「数字和小数点」的集合是词, 「sin」是「字母」的集合是词, 「# 注释文本」也是词. 
+
+这里定义了一些词类型
+```
+.版本 2
+
+.常量 词类型枚举
+.常量 词类_数字, "1"
+.常量 词类_整数部分, "2"
+.常量 词类_小数部分, "3"
+.常量 词类_标识符, "4"
+.常量 词类_算符, "5"
+.常量 词类_括号, "6"
+.常量 词类_注释, "7"
+.常量 词类_逗号, "8"
+.常量 词类_空格, "9"
+.常量 词类型枚举数量, "9"
+
+```
+词类_整数部分和词类_小数部分是为了迁就小数匹配, 词法分析的时候是跳过这两个的. 
+词法分析的时候逻辑很简单, 伪代码大约是:
+```
+对于每一个词类
+    如果 是整数部分 或 小数部分
+        到循环尾
+    end
+
+    如果 正则匹配对当前剩余文本匹配成功
+        记录匹配结果和类型
+        文本游标往右走匹配结果那么长
+        跳出循环
+    end
+end
+```
+
+至于匹配, 是用正则表达式做的, 这一部分当然也能手写, 但是太无聊了, 手写留到下部一份词法分析吧, 那地方更有价值一些. 正则如下: 
+
+```
+.版本 2
+.支持库 RegEx
+
+' 可能带°的数字 (\d+)(\.\d+)*[°]{0,1}
+' 标识符 [a-zA-ZΑ-Ωα-ω_][a-zA-ZΑ-Ωα-ω0-9_]*
+' 算符 [\+\-\*\/\^]
+' 括号 [\(\)]
+' 注释 \#.+
+' 逗号 ,
+
+' 易语言的正则表达式没办法用\u, 也没办法写希腊字母, 也没办法写「°」, 因此去掉希腊字母和「°」, 加上空白, 合起来就是这样的
+.如果真 (正则.是否为空 ())
+    正则.创建 (“((\d+)(\.\d+)*)|([a-zA-Z_][a-zA-Z0-9_]*)|([\+\-\*\/\^])|([\(\)])|(\#.+)|([,])|[ ]+$”, )
+
+' ________________m1______________ -> 数字
+' ____________ m2___m3____________ -> 数字的整数和小数部分
+' __________________________________________m4___________ -> 标识符 包含英文字母和希腊字母 只能以字母或下划线开头, 之后可以是数字或字母下划线
+' _______________________________________________________________m5______ -> 算符, +-*/^
+' __________________________________________________________________________m6___ -> 括号
+' _________________________________________________________________________________m7___ -> 注释
+' _______________________________________________________________________________________m8___ -> 逗号
+```
+
+这样一趟后就得到了这样的结果
+
+```
+> 词法分析 sin(x) + cos(pi * y) - ln(x) * 123.456 # 注释
+用时 0ms
+当前类型: 4, 内容: sin
+当前类型: 6, 内容: (
+当前类型: 4, 内容: x
+当前类型: 6, 内容: )
+当前类型: 5, 内容: +
+当前类型: 4, 内容: cos
+当前类型: 6, 内容: (
+当前类型: 4, 内容: pi
+当前类型: 5, 内容: *
+当前类型: 4, 内容: y
+当前类型: 6, 内容: )
+当前类型: 5, 内容: -
+当前类型: 4, 内容: ln
+当前类型: 6, 内容: (
+当前类型: 4, 内容: x
+当前类型: 6, 内容: )
+当前类型: 5, 内容: *
+当前类型: 1, 内容: 123.456
+当前类型: 7, 内容: # 注释
+----
+> 
+```
+
+
+## 语法树设计
+
+语法树通常没有定型, 需因地制宜设计. 数学表达式通常可以视为一个纯的无求值顺序问题的语言. 可以做如下规约:
+```
+0 ± x -> ±(0, x)
+x ± 0 -> ±(x, 0)
+a + b -> +(a, b)
+12345 -> +(12345, 0)
+```
+这样的话所有的计算表达式都可以写作 函(函甲, 函乙, 函丙, 函丁...)
+> 函: 指函数
+
+那么语法树的每一个节点应当包含如下信息
+
+- 原始文本, debug用
+- 函数名
+- 参数数组, 类型为节点
+- 父节点, 类型为节点
+
+但是很遗憾, 这样的代码会报递归定义错误, 我又没找到怎么把一个指针重新解释成某个自定义类型<sup>等我开始扩展易语言语法的时候首先要处理这个事情</sup>, 那只能自己申请内存管理数据了, 按C的写法数据结构定义如下: 
+
+```C
+{
+    char     函数名[40],  // 定长40, 再长不支持了
+    int32_t  参数量,      // 
+    int32_t  参数数组容量, // 参数数组指针后有多大容量, 按照每次*2扩充
+    uint64_t *参数数组指针, // 按长整数, 未来迁移到x64时兼容
+    uint64_t *父节点指针, // 该节点的父节点
+}
+// 此结构单个长度为 40+4+4+8+8 = 64 字节
+```
+内存结构如图: ![内存结构图](./static/memory-struct.png)
+
+> 这部分大概花了我三四个小时去写和 debug, 跟写汇编有一拼了. 终于可以开始写递归下降器了. 
+
+## 递归下降
+我不觉得我谈论递归下降能比网上的其他教程更好, 随便找了两个参考链接. 
+
+额外阅读:
+- 递归下降器 
+- - https://zhuanlan.zhihu.com/p/31271879
+- - https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92%E4%B8%8B%E9%99%8D%E8%A7%A3%E6%9E%90%E5%99%A8
+
+接下来将假设你有基础的相关知识了. 考虑合规的表达式要么是 `f(args)` `x + y`, 要么是二者在括号内外通过算符连接, 因此给出如下定义
+
+```
+# 尖括号内是词类型, 大括号内是语法
+# * 与正则表达式内星号含义相同, 指匹配0或任意次 ((女装)*)
+# + 与正则表达式内星号含义相同, 指匹配至少1次
+# | 表 或. 
+
+表达式     ::= 加后表达式 (("+" | "-") 加后表达式)*
+加后表达式  ::= 因子 (("*" | "/") 因子)*
+因子       ::= 左括号 表达式 右括号 | 函数 | 数字
+函数       ::= 标识符 左括号 (表达式 (逗号 表达式)*){0,1} 右括号
+
+```
+光写出来构造还是蛮简洁的, 匹配表达式时可以沿着`表达式->加后表达式->因子->函数->表达式`走, 这就是递归下降里的递归含义, 而下降则是指自顶向下慢慢解析. 如果你足够敏锐, 一定能意识到这个流程可能出现无限循环, 这就是左递归问题, 好在上面所提到的数学表达式是LL(1)文法(从左往右每次多看1个词就可以确定究竟是什么), 脑子不糊涂一般不会写出来无限循环. 对于更复杂的情况, 可以上LL(k)解析器, 尽量多看几个. 不过还有一些其他处理方法, 后面再提. 
+
+### 加减乘除分析
+
+将 git 历史切换到 `8259cc94337f4409d6a6fe6e4d17eedcd7685fc4 完成加减的词法分析`, 可以看到我当时的中间过程, 至此程序可以使用递归下降方法来区分加减乘除的优先级, 而不需要像第一节一样手动给出一个表来指定优先级
+
+```
+> 语法分析 1*2*3-4*5*6-7*8/9+10*11*12
+用时 0ms
+根 | 基地址: 7f5250 | 参数数量: 1 | 参数容量: 8 | 参数数组地址: 7f3af0
+  + | 基地址: 7f7e88 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f85c0
+    - | 基地址: 7f76f8 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f7ee0
+      - | 基地址: 7f6f68 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f7750
+        * | 基地址: 7f6ca8 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f6e60
+          * | 基地址: 7f6a98 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f6d00
+            1 | 基地址: 7f6938 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f6990
+            2 | 基地址: 7f6b48 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f6ba0
+          3 | 基地址: 7f6d58 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f6db0
+        * | 基地址: 7f7438 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f75f0
+          * | 基地址: 7f7228 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f7490
+            4 | 基地址: 7f70c8 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f7120
+            5 | 基地址: 7f72d8 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f7330
+          6 | 基地址: 7f74e8 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f7540
+      / | 基地址: 7f7bc8 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f7d80
+        * | 基地址: 7f79b8 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f7c20
+          7 | 基地址: 7f7858 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f78b0
+          8 | 基地址: 7f7a68 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f7ac0
+        9 | 基地址: 7f7c78 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f7cd0
+    * | 基地址: 7f8358 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f8510
+      * | 基地址: 7f8148 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 7f83b0
+        10 | 基地址: 7f7fe8 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f8040
+        11 | 基地址: 7f81f8 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f8250
+      12 | 基地址: 7f8408 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 7f8460
+> 
+```
+
+这个输出的阅读方式为: 找到两个相邻的叶结点, 其共同父节点即算符, 如此组成的新节点可以与其相邻节点加上父节点组合. 有没有一点正经语言的感觉了? 
+
+### 括号处理
+
+将 git 历史切换到 `55e0ea657e8b5f789574a1888415477901b32fdd 添加括号处理`, 可以发现只写了寥寥几新行, 就完成了括号的处理, 而且非常符合直觉. 
+
+### 递归下降器完结
+
+```
+> 语法分析 1 -( sin(2222, 4444, tan(5555) + arctan(6666)) - cos(3333) )* 4
+
+用时 1ms
+根 | 基地址: 62bf68 | 参数数量: 1 | 参数容量: 8 | 参数数组地址: 62c548
+  - | 基地址: 62d880 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 632e28
+    1 | 基地址: 62c480 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 62cf98
+    * | 基地址: 632bc0 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 632d78
+      括号 | 基地址: 62c3e8 | 参数数量: 1 | 参数容量: 8 | 参数数组地址: 632b68
+        - | 基地址: 6308d0 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 632ab8
+          sin | 基地址: 630ae0 | 参数数量: 3 | 参数容量: 8 | 参数数组地址: 630b38
+            2222 | 基地址: 630f90 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 630fe8
+            4444 | 基地址: 631320 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 631378
+            + | 基地址: 631610 | 参数数量: 2 | 参数容量: 8 | 参数数组地址: 631dd8
+              tan | 基地址: 631770 | 参数数量: 1 | 参数容量: 8 | 参数数组地址: 6317c8
+                5555 | 基地址: 631c20 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 631c78
+              arctan | 基地址: 631f30 | 参数数量: 1 | 参数容量: 8 | 参数数组地址: 631f88
+                6666 | 基地址: 6323e0 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 632438
+          cos | 基地址: 632540 | 参数数量: 1 | 参数容量: 8 | 参数数组地址: 632598
+            3333 | 基地址: 632900 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 632958
+      4 | 基地址: 632c70 | 参数数量: 0 | 参数容量: 8 | 参数数组地址: 632cc8
+
+```
+
+## 鸣谢
+
+感谢 e2txt 工具, 使得我可以按纯文本保存代码, 添入版本控制
+
+## 其他
+
+目前仍然没有合适的语法错误提示, 错了程序就挂掉, 以及保持了一个编译器界经典的操作, 内存只申请不释放, 等程序跑完一下子丢给操作系统处理, 很多编译原理书里都是这样做的! 
+
+### 额外阅读
+- 正则表达式 
+- - https://regex101.com/ 
+- - https://www.runoob.com/regexp/regexp-syntax.html
+
+
+## 下节预告
+
+- 使用易语言实现的计算器的虚拟机
+- 在JavaScript里实现的同样功能的解析器和虚拟机
+
+本节代码仍然使用纯的易语言做文本分析, 但是如果你详细读了这份代码, 会发现大量逻辑上的重复, 之后我将使用 JavaScript 的 [peggyjs](https://peggyjs.org/online) 库来重新做这件事, 同时, 如果你认真读了本节代码, 你也可以轻松读懂 peggyjs 的大部分东西. 为什么用 JavaScript 的库? 因为易语言没人做这种DSL. 另外就是 JavaScript 的话可以放到浏览器上去分析易语言语法啦, 也就有机会运行到浏览器里去了. 那么我现在写的命令行上的分析程序也能上浏览器了. 
+
+> *不过严格来说, 正则表达式也是 DSL, 因此用了正则表达式就不算是纯易语言了.*

BIN
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.e


+ 10 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/DLL.e.txt

@@ -0,0 +1,10 @@
+.版本 2
+
+.DLL命令 GetSystemTimeAsFileTime, 整数型, "kernel32.dll", "GetSystemTimeAsFileTime", , 获取当前系统时间
+    .参数 lpSystemTimeAsFileTime, 整数型, , 指向一个用于装载系统时间的 FILETIME 结构
+
+.DLL命令 lstrcpyn_长整数型, 整数型, "kernel32.dll", "lstrcpyn", 公开
+    .参数 目标, 长整数型, 传址
+    .参数 源, 长整数型, 传址
+    .参数 长度, 整数型
+

BIN
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码.e


+ 293 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/抽象语法树节点.class.e.txt

@@ -0,0 +1,293 @@
+.版本 2
+.支持库 spec
+.支持库 eAPI
+
+.程序集 抽象语法树节点, , 公开
+.程序集变量 当前节点指针, 长整数型, 数组
+.程序集变量 已初始化, 逻辑型, 数组
+.程序集变量 失败, 逻辑型, 数组
+
+.子程序 _初始化, , , 当基于本类的对象被创建后,此方法会被自动调用
+    已初始化 = 假
+    
+    ' {
+    ' char     函数名[16],   // 定长16, 再长不支持了
+    ' int32_t  参数量,
+    ' int32_t  参数数组容量, // 参数数组指针后有多大容量
+    ' uint64_t 参数数组指针, // 长整数, 未来迁移到x64时兼容
+    ' }
+    ' // 此结构单个长度为 16+8+8 = 32 字节
+    
+
+.子程序 _销毁, , , 当基于本类的对象被销毁前,此方法会被自动调用
+    .如果真 (已初始化)
+        ' 调试输出 (格式化文本 (“警告: %x : %s 未释放内存而销毁”, 到整数 (当前节点指针), 获取函数名 ()))
+    .如果真结束
+    
+
+.子程序 初始化内存, , 公开
+    .参数 函数名, 文本型, 可空
+    .参数 父节点地址, 长整数型, 可空
+
+    当前节点指针 = 到长整数 (申请内存 (#语法树节点_长度, 真))
+    .如果真 (是否为空 (函数名) = 假)
+        设置函数名 (函数名)
+    .如果真结束
+    .如果真 (是否为空 (父节点地址) = 假)
+        设置父节点指针地址 (父节点地址)
+    .如果真结束
+    ' 调试输出 (“设置参数指针地址”)
+    设置参数指针地址 (申请内存 (#语法树节点_参数容量初始值 × #指针大小, 真))
+    设置参数容量 (#语法树节点_参数容量初始值)
+    
+    已初始化 = 真
+    
+
+.子程序 初始化否, 逻辑型
+    返回 (已初始化)
+    
+
+.子程序 释放结构内存, , 公开
+    .如果真 (获取参数容量 () ≠ 0)
+        释放内存 (获取参数指针地址 ())
+    .如果真结束
+    释放内存 (当前节点指针)
+    已初始化 = 假
+    
+    
+
+.子程序 生成子节点, 抽象语法树节点, 公开
+    .参数 子节点函数名, 文本型, 可空
+    .局部变量 ast, 抽象语法树节点, 数组
+
+    .如果 (是否为空 (子节点函数名))
+        ast.设置函数名 (子节点函数名)
+    .否则
+        ast.设置函数名 (格式化文本 (“%s 的子节点”, 获取函数名 ()))
+    .如果结束
+    ast.设置父节点指针地址 (获取当前节点指针 ())
+    返回 (ast)
+    
+
+.子程序 生成匹配失败节点, 抽象语法树节点, 公开
+    .局部变量 ast, 抽象语法树节点, 数组
+
+    ast = 生成子节点 (格式化文本 (“%s 的失败子节点”, 获取函数名 ()))
+    ast._设置失败 ()
+    返回 (ast)
+    
+
+.子程序 失败, 逻辑型, 公开
+    返回 (失败)
+    
+
+.子程序 成功, 逻辑型, 公开
+    返回 (失败 = 假)
+    
+
+.子程序 _设置失败, , 公开
+    失败 = 真
+    已初始化 = 假
+    
+
+.子程序 从内存初始化, 抽象语法树节点, 公开
+    .参数 地址, 长整数型
+    .局部变量 res, 抽象语法树节点, 数组
+
+    res.设置当前节点指针 (地址)
+    返回 (res)
+    
+
+.子程序 获取当前节点指针, 长整数型, 公开
+    返回 (当前节点指针)
+    
+
+.子程序 设置当前节点指针, , 公开
+    .参数 地址, 长整数型
+
+    当前节点指针 = 地址
+    
+
+.子程序 设置函数名, , 公开
+    .参数 函数名, 文本型, , 最长实际上是 #语法树节点_函数名最大长度-1
+    .局部变量 字节数组, 字节型, 数组, "16"
+    .局部变量 i, , 数组
+    .局部变量 函数名长度, 整数型, 数组
+
+    重定义数组 (字节数组, 假, #语法树节点_函数名最大长度)
+    
+    函数名长度 = 取文本长度 (函数名)
+    
+    .计次循环首 (#语法树节点_函数名最大长度, i)
+        .判断开始 (i = #语法树节点_函数名最大长度 或 i > 函数名长度)
+            字节数组 [i] = 到字节 (0)
+        .默认
+            字节数组 [i] = 取代码 (函数名, i)
+        .判断结束
+        
+    .计次循环尾 ()
+    
+    写到内存 (字节数组, 当前节点指针 + #语法树节点_函数名偏移, #语法树节点_函数名最大长度)
+    
+
+.子程序 获取函数名, 文本型, 公开
+    返回 (指针到文本 (当前节点指针 + #语法树节点_函数名偏移))
+    
+
+.子程序 设置参数数量, , 公开
+    .参数 量, 长整数型
+
+    写到内存 (量, 当前节点指针 + #语法树节点_参数量偏移, 4)
+    
+
+.子程序 获取参数数量, 整数型, 公开
+    .局部变量 字节数组, 通用型, 数组, "0"
+
+    返回 (取字节集数据 (指针到字节集 (当前节点指针 + #语法树节点_参数量偏移, 4), #整数型, ))
+    
+
+.子程序 设置参数容量, , 公开
+    .参数 量, 整数型
+
+    写到内存 (量, 当前节点指针 + #语法树节点_参数容量偏移, 4)
+    
+
+.子程序 获取参数容量, 整数型, 公开
+    返回 (取字节集数据 (指针到字节集 (当前节点指针 + #语法树节点_参数容量偏移, 4), #整数型, ))
+    
+
+.子程序 设置参数指针地址, , 公开
+    .参数 地址, 长整数型
+
+    写到内存 (地址, 当前节点指针 + #语法树节点_参数指针偏移, #指针大小)
+    
+
+.子程序 获取参数指针地址, 长整数型, 公开, 已经不在本结构体了, 在远处了
+    返回 (取字节集数据 (指针到字节集 (当前节点指针 + #语法树节点_参数指针偏移, #指针大小), #长整数型, ))
+    
+
+.子程序 获取结构体字节集, 字节集, 公开
+    返回 (指针到字节集 (当前节点指针, #语法树节点_长度))
+    
+
+.子程序 获取参数数组字节集, 字节集, 公开
+    返回 (指针到字节集 (获取参数指针地址 (), 获取参数数量 () × #指针大小))
+    
+
+.子程序 扩容参数指针, 长整数型, 公开
+    .参数 新长度, 整数型, 可空
+    .局部变量 旧参数指针地址, 长整数型, 数组
+    .局部变量 旧参数数组打包, 字节集, 数组
+    .局部变量 新参数指针地址, 长整数型, 数组
+
+    .如果真 (是否为空 (新长度))
+        新长度 = 获取参数容量 () × #语法树节点_参数容量扩张系数
+    .如果真结束
+    
+    旧参数指针地址 = 获取参数指针地址 ()
+    旧参数数组打包 = 获取参数数组字节集 ()
+    新参数指针地址 = 申请内存 (新长度 × #指针大小, 真)
+    
+    
+    写到内存 (旧参数数组打包, 新参数指针地址, 获取参数数量 () × #指针大小)
+    释放内存 (旧参数指针地址)
+    
+    设置参数指针地址 (新参数指针地址)
+    
+    设置参数容量 (新长度)
+    返回 (新参数指针地址)
+    
+
+.子程序 添加参数节点, , 公开
+    .参数 节点, 抽象语法树节点
+    .局部变量 当前参数数量, 整数型, 数组
+
+    当前参数数量 = 获取参数数量 ()
+    
+    .如果真 (当前参数数量 ≥ 获取参数容量 ())
+        扩容参数指针 ()
+    .如果真结束
+    节点.设置父节点指针地址 (获取当前节点指针 ())
+    写到内存 (节点.获取当前节点指针 (), 获取参数指针地址 () + 当前参数数量 × #指针大小, )
+    设置参数数量 (当前参数数量 + 1)
+    
+
+.子程序 插入参数节点, , 公开
+    .参数 节点, 抽象语法树节点
+    .局部变量 当前参数数量, 整数型, 数组
+    .局部变量 新参数指针地址, 整数型, 数组
+    .局部变量 旧参数数组打包, 字节集, 数组
+    .局部变量 旧参数指针地址, 长整数型, 数组
+
+    当前参数数量 = 获取参数数量 ()
+    .如果真 (当前参数数量 ≥ 获取参数容量 ())
+        扩容参数指针 ()
+    .如果真结束
+    
+    旧参数指针地址 = 获取参数指针地址 ()
+    旧参数数组打包 = 获取参数数组字节集 ()
+    新参数指针地址 = 申请内存 (获取参数容量 () × #指针大小, 真)
+    
+    写到内存 (到字节集 (节点.获取当前节点指针 ()) + 旧参数数组打包, 新参数指针地址, (当前参数数量 + 1) × #指针大小)
+    释放内存 (旧参数指针地址)
+    
+    设置参数指针地址 (新参数指针地址)
+    设置参数数量 (当前参数数量 + 1)
+    
+
+.子程序 生成自词节点, 抽象语法树节点, 公开
+    .参数 词, 词
+    .局部变量 n, 抽象语法树节点, 数组
+
+    n.初始化内存 (词.内容)
+    返回 (n)
+    
+
+.子程序 添加词节点, , 公开
+    .参数 词, 词
+
+    添加文本节点 (词.内容)
+    
+
+.子程序 添加文本节点, , 公开
+    .参数 文本, 文本型
+    .局部变量 n, 抽象语法树节点, 数组
+
+    n.初始化内存 (文本)
+    添加参数节点 (n)
+    
+
+.子程序 获取参数节点, 抽象语法树节点, 公开
+    .参数 i, 整数型, 可空
+
+    .如果真 (是否为空 (i))
+        i = 获取参数数量 ()
+    .如果真结束
+    
+    返回 (从内存初始化 (指针到整数 (获取参数指针地址 () + (i - 1) × #指针大小)))
+    
+
+.子程序 生成展示文本, 文本型, 公开
+    返回 (格式化文本 (“%s | 基地址: %x | 参数数量: %d | 参数容量: %d | 参数数组地址: %x ”, 获取函数名 (), 到整数 (获取当前节点指针 ()), 到整数 (获取参数数量 ()), 到整数 (获取参数容量 ()), 到整数 (获取参数指针地址 ())))
+    
+
+.子程序 设置父节点, , 公开
+    .参数 父节点, 抽象语法树节点
+
+    设置父节点指针地址 (父节点.获取当前节点指针 ())
+    
+
+.子程序 设置父节点指针地址, , 公开
+    .参数 地址, 长整数型
+
+    写到内存 (地址, 当前节点指针 + #语法树节点_父节点指针偏移, #指针大小)
+    
+
+.子程序 获取父节点指针地址, 长整数型, 公开, 已经不在本结构体了, 在远处了
+    返回 (取字节集数据 (指针到字节集 (当前节点指针 + #语法树节点_父节点指针偏移, #指针大小), #长整数型, ))
+    
+
+.子程序 获取父节点, 抽象语法树节点, 公开
+    返回 (从内存初始化 (获取父节点指针地址 ()))
+    
+

+ 5 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/排序.list.txt

@@ -0,0 +1,5 @@
+程序集1
+词法分析程序集
+来自精易模块
+语法分析程序集
+抽象语法树节点

+ 32 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/来自精易模块.static.e.txt

@@ -0,0 +1,32 @@
+.版本 2
+
+.程序集 来自精易模块, , , 用于加速编译和导出文本
+.子程序 时间_取现行时间戳1, 长整数型, 公开, 生成一个10位或者13位的现行时间戳
+    .参数 精确到毫秒, 逻辑型, 可空, 默认为假 精确到秒10位数 精确到毫秒13位数
+    .局部变量 ret, 长整数型, 数组
+
+    ' https://bbs.125.la/forum.php?mod=viewthread&tid=14649396
+    GetSystemTimeAsFileTime (取指针地址_长整数型 (ret))
+    ret = FileTimeToUnixTime (ret)
+    .如果 (精确到毫秒)
+        ret = ret ÷ 10000
+    .否则
+        ret = ret ÷ 10000000
+    .如果结束
+    返回 (ret)
+    
+
+.子程序 FileTimeToUnixTime, 长整数型
+    .参数 FlieTime, 长整数型
+    .局部变量 ll, 长整数型, 数组
+
+    ll = FlieTime - 1.16444736e+017
+    返回 (ll)
+    
+
+.子程序 取指针地址_长整数型, 整数型, 公开
+    .参数 欲取地址的数据, 长整数型, 参考
+
+    返回 (lstrcpyn_长整数型 (欲取地址的数据, 欲取地址的数据, 0))
+    
+

+ 46 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/程序集1.static.e.txt

@@ -0,0 +1,46 @@
+.版本 2
+
+.程序集 程序集1
+.子程序 _启动子程序, 整数型, , 本子程序在程序启动后最先执行
+    语法分析并输出 (“sin(x, y, z)”)
+    
+    
+    .判断循环首 (真)
+        标准输出 (, “> ”)
+        解析命令 (标准输入 ())
+    .判断循环尾 ()
+    
+    返回 (0) ' 可以根据您的需要返回任意数值
+    
+
+.子程序 解析命令
+    .参数 命令, 文本型
+    .局部变量 t, 文本型, 数组, "0"
+
+    ' 支持命令
+    ' 「帮助」 「词法分析 some text」 「语法分析 some text」
+    t = 分割文本 (命令, “ ”, 1)
+    
+    
+    .判断开始 (t [1] = “帮助” 或 到半角 (t [1]) = “?”)
+        标准输出 (, #命令行帮助)
+    .判断 (t [1] = “词法分析”)
+        词法分析并输出 (取文本右边 (命令, 取文本长度 (命令) - 取文本长度 (“词法分析”)))
+    .判断 (t [1] = “语法分析”)
+        语法分析并输出 (取文本右边 (命令, 取文本长度 (命令) - 取文本长度 (“词法分析”)))
+    .默认
+        标准输出 (, “未知命令” + #换行符)
+    .判断结束
+    
+
+.子程序 连接字符串, 文本型
+    .参数 字符串组, 文本型, 数组
+    .局部变量 res, 文本型, 数组
+    .局部变量 i, 整数型, 数组
+
+    res = “”
+    .计次循环首 (取文本长度 (字符串组), i)
+        res = res + 字符串组 [i]
+    .计次循环尾 ()
+    
+

+ 90 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/词法分析程序集.static.e.txt

@@ -0,0 +1,90 @@
+.版本 2
+.支持库 RegEx
+
+.程序集 词法分析程序集
+.程序集变量 正则, 正则表达式, 数组
+
+.子程序 词法分析
+    .参数 表达式, 文本型
+    .参数 返回值, 词, 参考 数组
+    .局部变量 上次结果位置, 整数型, 数组
+    .局部变量 当前词, 词, 数组
+    .局部变量 当前匹配文本, 文本型, 数组
+    .局部变量 res, 搜索结果, 数组
+
+    .局部变量 i, 整数型, 数组
+
+    ' 可能带°的数字 (\d+)(\.\d+)*[°]{0,1}
+    ' 标识符 [a-zA-ZΑ-Ωα-ω_][a-zA-ZΑ-Ωα-ω0-9_]*
+    ' 算符 [\+\-\*\/\^]
+    ' 括号 [\(\)]
+    ' 注释 \#.+
+    ' 逗号 ,
+    
+    ' 易语言的正则表达式没办法用\u, 也没办法写希腊字母, 也没办法写「°」, 因此去掉希腊字母和「°」, 加上空白, 合起来就是这样的
+    .如果真 (正则.是否为空 ())
+        正则.创建 (“((\d+)(\.\d+)*)|([a-zA-Z_][a-zA-Z0-9_]*)|([\+\-\*\/\^])|([\(\)])|(\#.+)|([,])|[ ]+$”, )
+    .如果真结束
+    
+    ' ________________m1______________ -> 数字
+    ' ____________ m2___m3____________ -> 数字的整数和小数部分
+    ' __________________________________________m4___________ -> 标识符 包含英文字母和希腊字母 只能以字母或下划线开头, 之后可以是数字或字母下划线
+    ' _______________________________________________________________m5______ -> 算符, +-*/^
+    ' __________________________________________________________________________m6___ -> 括号
+    ' _________________________________________________________________________________m7___ -> 注释
+    ' _______________________________________________________________________________________m8___ -> 逗号
+    
+    ' 调试输出 (正则.取子表达式个数 ())
+    
+    清除数组 (返回值)
+    上次结果位置 = 1
+    
+    .判断循环首 (上次结果位置 ≤ 取文本长度 (表达式) 且 当前词.类型 ≠ -1)
+        当前词.内容 = “”
+        当前词.类型 = -1
+        res = 正则.搜索 (表达式, 上次结果位置, )
+        
+        .计次循环首 (#词类型枚举数量, i)
+            .如果真 (i = #词类_整数部分 或 i = #词类_小数部分)
+                到循环尾 ()
+            .如果真结束
+            
+            当前匹配文本 = res.取子匹配文本 (表达式, i, 上次结果位置)
+            
+            .如果真 (取文本长度 (当前匹配文本) > 0 且 i ≠ #词类_空格)
+                当前词.内容 = 当前匹配文本
+                当前词.类型 = i
+                上次结果位置 = 上次结果位置 + 取文本长度 (当前匹配文本) + 1
+                加入成员 (返回值, 当前词)
+                
+                跳出循环 ()
+            .如果真结束
+            
+        .计次循环尾 ()
+        
+        
+    .判断循环尾 ()
+    
+    
+
+.子程序 输出词法分析结果
+    .参数 词, 词, 数组
+    .局部变量 i, 整数型, 数组
+
+    .计次循环首 (取数组成员数 (词), i)
+        标准输出 (, “当前类型: ” + 到文本 (词 [i].类型) + “, 内容: ” + 词 [i].内容 + #换行符)
+    .计次循环尾 ()
+    标准输出 (, “----” + #换行符)
+    
+
+.子程序 词法分析并输出
+    .参数 表达式, 文本型
+    .局部变量 res, 词, 数组, "0"
+    .局部变量 t0, 长整数型, 数组
+
+    t0 = 时间_取现行时间戳1 (真)
+    词法分析 (表达式, res)
+    标准输出 (, “用时 ” + 到文本 (时间_取现行时间戳1 (真) - t0) + “ms” + #换行符)
+    输出词法分析结果 (res)
+    
+

+ 373 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/语法分析程序集.static.e.txt

@@ -0,0 +1,373 @@
+.版本 2
+.支持库 spec
+.支持库 eAPI
+
+.程序集 语法分析程序集
+.程序集变量 AST根, 抽象语法树节点, 数组
+.程序集变量 词, 词, 数组, "0"
+.程序集变量 当前词游标, 整数型, 数组
+
+.子程序 语法分析
+    .参数 _词, 词, 数组
+    .局部变量 当前节点, 抽象语法树节点, 数组
+    .局部变量 res, 抽象语法树节点, 数组
+
+    复制数组 (词, _词)
+    当前词游标 = 0
+    AST根.初始化内存 (“根”)
+    
+    .判断循环首 (当前词游标 ≤ 取数组成员数 (词))
+        res = 匹配表达式 (AST根)
+        调试输出 (“匹配表达式 结束”, res.生成展示文本 ())
+        
+        .如果 (res.失败 ())
+            跳出循环 ()
+        .否则
+            AST根.添加参数节点 (res)
+        .如果结束
+        跳出循环 ()
+    .判断循环尾 ()
+    
+
+.子程序 匹配表达式, 抽象语法树节点
+    .参数 父节点, 抽象语法树节点
+    .局部变量 l, 抽象语法树节点, 数组
+    .局部变量 r, 抽象语法树节点, 数组
+    .局部变量 当前节点, 抽象语法树节点, 数组
+    .局部变量 右侧无穷, 抽象语法树节点, 数组
+    .局部变量 天父, 抽象语法树节点, 数组
+
+    当前节点.初始化内存 (“匹配表达式 之起始”)
+    调试输出 (“当前父节点”, 父节点.获取函数名 ())
+    l = 匹配加后表达式 (当前节点)
+    
+    .如果真 (l.失败 ())
+        调试输出 (“未匹配到左加后表达式”)
+        返回 (父节点.生成匹配失败节点 ())
+    .如果真结束
+    当前节点.添加参数节点 (l)
+    调试输出 (“匹配到左加后表达式”, l.获取函数名 ())
+    
+    右侧无穷 = 匹配表达式右侧无穷 (父节点)
+    右侧无穷.插入参数节点 (l)
+    
+    天父 = 右侧无穷
+    .判断循环首 (天父.获取函数名 () ≠ “nop”)
+        调试输出 (天父.生成展示文本 ())
+        天父 = 天父.获取父节点 ()
+    .判断循环尾 ()
+    天父 = 天父.获取参数节点 (1)
+    返回 (天父)
+    
+
+.子程序 匹配表达式右侧无穷, 抽象语法树节点
+    .参数 父节点, 抽象语法树节点
+    .局部变量 当前节点, 抽象语法树节点, 数组
+    .局部变量 m, 抽象语法树节点, 数组
+    .局部变量 r, 抽象语法树节点, 数组
+    .局部变量 后续无穷, 抽象语法树节点, 数组
+
+    当前节点.初始化内存 (“nop”)
+    
+    .如果真 (下个词 ().内容 ≠ “+” 且 下个词 ().内容 ≠ “-”)
+        调试输出 (“未匹配到+-中算符”)
+        返回 (当前节点)
+    .如果真结束
+    m = 父节点.生成自词节点 (下个词 ())
+    词游标自增 ()
+    
+    r = 匹配加后表达式 (当前节点)
+    
+    .如果真 (r.失败 ())
+        返回 (父节点.生成匹配失败节点 ())
+    .如果真结束
+    
+    
+    .如果 (下个词 ().类型 ≠ #词类_结尾)
+        后续无穷 = 匹配表达式右侧无穷 (r)
+        .如果真 (后续无穷.失败 ())
+            返回 (后续无穷)
+        .如果真结束
+        
+    .否则
+        后续无穷.初始化内存 (“nop”, )
+    .如果结束
+    
+    调试输出 (下个词 ().类型)
+    m.设置父节点 (后续无穷)
+    m.添加参数节点 (r)
+    后续无穷.插入参数节点 (m)
+    返回 (m)
+    
+
+.子程序 匹配加后表达式, 抽象语法树节点
+    .参数 父节点, 抽象语法树节点
+    .局部变量 l, 抽象语法树节点, 数组
+    .局部变量 当前节点, 抽象语法树节点, 数组
+    .局部变量 右侧无穷, 抽象语法树节点, 数组
+    .局部变量 r, 抽象语法树节点, 数组
+
+    .局部变量 天父, 抽象语法树节点, 数组
+
+    当前节点.初始化内存 ()
+    调试输出 (“当前父节点”, 父节点.获取函数名 ())
+    l = 匹配因子 (父节点)
+    
+    .如果真 (l.失败 ())
+        调试输出 (“未匹配到左数字”)
+        返回 (父节点.生成匹配失败节点 ())
+    .如果真结束
+    调试输出 (“匹配到左数字”, l.获取函数名 ())
+    当前节点.添加参数节点 (l)
+    
+    右侧无穷 = 匹配加后表达式右侧无穷 (父节点)
+    .如果真 (右侧无穷.获取参数数量 () > 0)
+        调试输出 (“右侧无穷”, 右侧无穷.获取参数节点 (1).生成展示文本 ())
+    .如果真结束
+    右侧无穷.插入参数节点 (l)
+    调试输出 (“右侧无穷”, 右侧无穷.获取参数节点 (1).生成展示文本 ())
+    
+    天父 = 右侧无穷
+    .判断循环首 (天父.获取函数名 () ≠ “nop”)
+        调试输出 (天父.生成展示文本 ())
+        天父 = 天父.获取父节点 ()
+    .判断循环尾 ()
+    天父 = 天父.获取参数节点 (1)
+    返回 (天父)
+    
+
+.子程序 匹配加后表达式右侧无穷, 抽象语法树节点
+    .参数 父节点, 抽象语法树节点
+    .局部变量 当前节点, 抽象语法树节点, 数组
+    .局部变量 m, 抽象语法树节点, 数组
+    .局部变量 r, 抽象语法树节点, 数组
+    .局部变量 后续无穷, 抽象语法树节点, 数组
+
+    当前节点.初始化内存 (“nop”)
+    
+    .如果真 (下个词 ().内容 ≠ “*” 且 下个词 ().内容 ≠ “/”)
+        调试输出 (“未匹配到*/中算符”)
+        返回 (当前节点)
+    .如果真结束
+    m = 父节点.生成自词节点 (下个词 ())
+    词游标自增 ()
+    
+    r = 匹配因子 (父节点)
+    
+    .如果真 (r.失败 ())
+        返回 (父节点.生成匹配失败节点 ())
+    .如果真结束
+    
+    
+    .如果 (下个词 ().类型 ≠ #词类_结尾)
+        后续无穷 = 匹配加后表达式右侧无穷 (r)
+        .如果真 (后续无穷.失败 ())
+            返回 (后续无穷)
+        .如果真结束
+        
+    .否则
+        后续无穷.初始化内存 (“nop”, )
+    .如果结束
+    
+    调试输出 (下个词 ().类型)
+    m.设置父节点 (后续无穷)
+    m.添加参数节点 (r)
+    后续无穷.插入参数节点 (m)
+    返回 (m)
+    
+
+.子程序 匹配因子, 抽象语法树节点
+    .参数 父节点, 抽象语法树节点
+    .局部变量 当前节点, 抽象语法树节点, 数组
+    .局部变量 左括号, 抽象语法树节点, 数组
+    .局部变量 中表达式, 抽象语法树节点, 数组
+    .局部变量 右括号, 抽象语法树节点, 数组
+    .局部变量 数字, 抽象语法树节点, 数组
+    .局部变量 函数, 抽象语法树节点, 数组
+    .局部变量 标识符, 抽象语法树节点
+
+    .判断开始 (下个词 ().类型 = #词类_括号)
+        .如果真 (下个词 ().内容 ≠ “(”)
+            返回 (父节点.生成匹配失败节点 ())
+        .如果真结束
+        左括号 = 当前节点.生成自词节点 (下个词 ())
+        词游标自增 ()
+        中表达式 = 匹配表达式 (当前节点)
+        .如果真 (下个词 ().内容 ≠ “)”)
+            返回 (父节点.生成匹配失败节点 ())
+        .如果真结束
+        
+        右括号 = 当前节点.生成自词节点 (下个词 ())
+        词游标自增 ()
+        当前节点.初始化内存 (“括号”, 父节点.获取当前节点指针 ())
+        当前节点.添加参数节点 (中表达式)
+        返回 (当前节点)
+        
+    .判断 (下个词 ().类型 = #词类_数字)
+        数字 = 匹配数字 ()
+        返回 (数字)
+        
+    .默认
+        ' 判断看起来真浑 非常担心一行放错了地方 还是写成普通的如果真吧
+    .判断结束
+    
+    函数 = 匹配函数 (当前节点)
+    .如果真 (函数.成功 ())
+        返回 (函数)
+    .如果真结束
+    
+    ' 函数的开头是标识符, 所以先尝试匹配下函数再去匹配标识符, 否则可能把
+    ' sin(x) 当成 sin 后面没了
+    .如果真 (下个词 ().类型 = #词类_标识符)
+        标识符 = 匹配标识符 (当前节点)
+        返回 (标识符)
+    .如果真结束
+    
+    返回 (父节点.生成匹配失败节点 ())
+    
+
+.子程序 匹配函数, 抽象语法树节点
+    .参数 父节点, 抽象语法树节点
+    .局部变量 当前节点, 抽象语法树节点, 数组
+    .局部变量 标识符, 抽象语法树节点, 数组
+    .局部变量 参数, 抽象语法树节点, 数组
+    .局部变量 右侧无穷, 抽象语法树节点, 数组
+    .局部变量 括号内下一表达式, 抽象语法树节点, 数组
+
+    .如果真 (下个词 ().类型 ≠ #词类_标识符)
+        返回 (父节点.生成匹配失败节点 ())
+    .如果真结束
+    标识符 = 匹配标识符 (当前节点)
+    
+    .如果真 (下个词 ().内容 ≠ “(”)
+        返回 (标识符)
+    .如果真结束
+    词游标自增 ()
+    .如果真 (下个词 ().内容 = “)”)
+        词游标自增 ()
+        返回 (标识符)
+    .如果真结束
+    
+    参数 = 匹配表达式 (当前节点)
+    .如果真 (下个词 ().内容 = “)”)
+        标识符.添加参数节点 (参数)
+        词游标自增 ()
+        
+        返回 (标识符)
+    .如果真结束
+    标识符.添加参数节点 (参数)
+    
+    
+    .判断循环首 (真)
+        .如果真 (下个词 ().类型 ≠ #词类_逗号 或 假) ' 去掉「 或 假」后会触发e2txt的bug, 无法从.txt转回.e
+            返回 (父节点.生成匹配失败节点 ())
+        .如果真结束
+        词游标自增 ()
+        括号内下一表达式 = 匹配表达式 (当前节点)
+        .如果真 (括号内下一表达式.失败 ())
+            返回 (父节点.生成匹配失败节点 ())
+        .如果真结束
+        标识符.添加参数节点 (括号内下一表达式)
+        
+        .如果真 (下个词 ().内容 = “)”)
+            词游标自增 ()
+            跳出循环 ()
+        .如果真结束
+        
+    .判断循环尾 ()
+    返回 (标识符)
+    
+
+.子程序 匹配标识符, 抽象语法树节点
+    .参数 父节点, 抽象语法树节点
+    .局部变量 当前节点, 抽象语法树节点, 数组
+
+    当前节点.初始化内存 ()
+    
+    .如果 (下个词 ().类型 ≠ #词类_标识符)
+        当前节点.释放结构内存 ()
+        返回 (当前节点.生成匹配失败节点 ())
+    .否则
+        当前节点.设置函数名 (下个词 ().内容)
+        词游标自增 ()
+        返回 (当前节点)
+    .如果结束
+    
+
+.子程序 匹配数字, 抽象语法树节点
+    .局部变量 当前节点, 抽象语法树节点, 数组
+
+    当前节点.初始化内存 ()
+    
+    .如果 (下个词 ().类型 ≠ #词类_数字)
+        当前节点.释放结构内存 ()
+        返回 (当前节点.生成匹配失败节点 ())
+    .否则
+        当前节点.设置函数名 (下个词 ().内容)
+        词游标自增 ()
+        返回 (当前节点)
+    .如果结束
+    
+
+.子程序 下个词, 词
+    .局部变量 返回词, 词, 数组
+
+    .如果 (当前词游标 ≥ 取数组成员数 (词))
+        返回词.内容 = “”
+        返回词.类型 = #词类_结尾
+    .否则
+        返回词 = 词 [当前词游标 + 1]
+    .如果结束
+    
+    返回 (返回词)
+    
+
+.子程序 词游标自增
+    当前词游标 = 当前词游标 + 1
+    调试输出 (格式化文本 (“游标增至 %d 下一个:(%d,%s)”, 当前词游标, 下个词 ().类型, 下个词 ().内容))
+    
+
+.子程序 词游标自减
+    当前词游标 = 当前词游标 - 1
+    调试输出 (格式化文本 (“游标减至 %d 下一个:(%d,%s)”, 当前词游标, 下个词 ().类型, 下个词 ().内容))
+    
+
+.子程序 重复文本, 文本型
+    .参数 n, 整数型
+    .参数 s, 文本型
+    .局部变量 res, 文本型, 数组
+
+    res = “”
+    .计次循环首 (n, )
+        res = res + s
+    .计次循环尾 ()
+    返回 (res)
+    
+
+.子程序 输出语法分析结果
+    .参数 AST, 抽象语法树节点
+    .参数 当前层级, 整数型, 可空
+    .局部变量 i, 整数型, 数组
+
+    .如果真 (是否为空 (当前层级))
+        当前层级 = 0
+    .如果真结束
+    标准输出 (, 重复文本 (当前层级 × 2, “ ”))
+    标准输出 (, AST.生成展示文本 () + #换行符)
+    .计次循环首 (AST.获取参数数量 (), i)
+        输出语法分析结果 (AST.获取参数节点 (i), 当前层级 + 1)
+    .计次循环尾 ()
+    
+
+.子程序 语法分析并输出
+    .参数 表达式, 文本型
+    .局部变量 t0, 长整数型, 数组
+
+    t0 = 时间_取现行时间戳1 (真)
+    词法分析 (表达式, 词)
+    语法分析 (词)
+    标准输出 (, “用时 ” + 到文本 (时间_取现行时间戳1 (真) - t0) + “ms” + #换行符)
+    输出语法分析结果 (AST根)
+    
+    
+

+ 28 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/常量.e.txt

@@ -0,0 +1,28 @@
+.版本 2
+
+.常量 词类型枚举
+.常量 词类_数字, "1"
+.常量 词类_整数部分, "2"
+.常量 词类_小数部分, "3"
+.常量 词类_标识符, "4"
+.常量 词类_算符, "5"
+.常量 词类_括号, "6"
+.常量 词类_注释, "7"
+.常量 词类_逗号, "8"
+.常量 词类_空格, "9"
+.常量 词类型枚举数量, "9"
+.常量 词类_结尾, "99999"
+
+
+
+.常量 语法树节点_函数名最大长度, "24"
+.常量 语法树节点_函数名偏移, "0", , 长: 40
+.常量 语法树节点_参数量偏移, "40", , 长: 4
+.常量 语法树节点_参数容量偏移, "44", , 长: 4
+.常量 语法树节点_参数指针偏移, "48", , 长: 8
+.常量 语法树节点_父节点指针偏移, "56", , 长: 8
+.常量 语法树节点_长度, "64"
+.常量 语法树节点_参数容量初始值, "8"
+.常量 语法树节点_参数容量扩张系数, "2"
+.常量 语法树节点_匹配失败, "-1"
+.常量 指针大小, "8"

+ 6 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/自定义类型.e.txt

@@ -0,0 +1,6 @@
+.版本 2
+
+.数据类型 词, 公开
+    .成员 内容, 文本型, 数组
+    .成员 类型, 整数型, 数组
+

+ 10 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/资源/命令行帮助.txt

@@ -0,0 +1,10 @@
+--------
+> 帮助
+展示此帮助
+
+> 词法分析 [表达式]
+对[表达式]进行词法分析, 输出分析结果
+
+> 语法分析 [表达式]
+对[表达式]进行语法分析, 输出语法树
+--------

+ 50 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/配置/支持库.config.json

@@ -0,0 +1,50 @@
+[
+    {
+        "CmdCount": 664,
+        "Guid": "d09f2340818511d396f6aaf844c7e325",
+        "Key": "krnln",
+        "MaxRefConstPos": 68,
+        "MaxRefObjectPos": 0,
+        "Name": "系统核心支持库",
+        "Version": {
+            "Major": 5,
+            "Minor": 7
+        }
+    },
+    {
+        "CmdCount": 13,
+        "Guid": "684944CB04624eb7BD5412A519421D34",
+        "Key": "RegEx",
+        "MaxRefConstPos": 0,
+        "MaxRefObjectPos": 2,
+        "Name": "正则表达式支持库",
+        "Version": {
+            "Major": 2,
+            "Minor": 0
+        }
+    },
+    {
+        "CmdCount": 11,
+        "Guid": "A512548E76954B6E92C21055517615B0",
+        "Key": "spec",
+        "MaxRefConstPos": 0,
+        "MaxRefObjectPos": 0,
+        "Name": "特殊功能支持库",
+        "Version": {
+            "Major": 3,
+            "Minor": 1
+        }
+    },
+    {
+        "CmdCount": 85,
+        "Guid": "F7FC1AE45C5C4758AF03EF19F18A395D",
+        "Key": "eAPI",
+        "MaxRefConstPos": 0,
+        "MaxRefObjectPos": 0,
+        "Name": "应用接口支持库",
+        "Version": {
+            "Major": 3,
+            "Minor": 1
+        }
+    }
+]

+ 23 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/配置/用户.config.json

@@ -0,0 +1,23 @@
+{
+    "Address": "",
+    "Author": "",
+    "BuilderVersion": {
+        "Major": 0,
+        "Minor": 0
+    },
+    "CompilePlugins": "",
+    "Copyright": "",
+    "Description": "",
+    "Email": "",
+    "ExportPublicClassMethod": false,
+    "FaxNumber": "",
+    "Homepage": "",
+    "Name": "",
+    "ReleaseVersion": {
+        "Major": 1,
+        "Minor": 0
+    },
+    "TelephoneNumber": "",
+    "WriteVersion": true,
+    "ZipCode": ""
+}

+ 13 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/配置/系统.config.json

@@ -0,0 +1,13 @@
+{
+    "Lang": 1,
+    "ProjectType": 1,
+    "ProjectVersion": {
+        "Major": 1,
+        "Minor": 7
+    },
+    "Type": 1,
+    "Version": {
+        "Major": 5,
+        "Minor": 6
+    }
+}

+ 12 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/项目.etprj

@@ -0,0 +1,12 @@
+{
+    "AsyncFile": false,
+    "E2TXT-EFile": "",
+    "E2TXT-InSourceDir": true,
+    "E2TXT-IsCreateE": true,
+    "Encoding": "UTF-8",
+    "Level": 1,
+    "NameStyle": 2,
+    "Password": "",
+    "ResetNames": [],
+    "Source": "../简单的数值语言及其虚拟机.e"
+}