1
0

2 Revīzijas 9b1c6b5d6a ... b13a2923d9

Autors SHA1 Ziņojums Datums
  myuan b13a2923d9 新增虚拟机解释 3 gadi atpakaļ
  myuan 4743ead56c 添加词类/语类, 修改展示效果 3 gadi atpakaļ

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

@@ -64,16 +64,50 @@ output(sqrt(2), square(sqrt(2)))
 
 第一个实现照例先做一个最朴素的, 就像第一节的四则运算器一样, 之后再用业界常用手段来做. 
 
-这样的一个虚拟机实现起来非常简单, 只需要记录当前已定义的函数, 就是全部的运行环境了, 函数调用时先看参数里有没有指定的函数, 然后找到就用, 找不到就再往上看, 都找不到就报错. 上面的语法里, 虽然有函数参数, 但是函数参数是不可变的, 也是无类型的, 因此这样的代码也并没有超出框架
+这样的一个虚拟机实现起来非常简单, 只需要记录当前已定义的函数. 由于没有可变量, 不必去记录状态, 一切都是纯的. 在常规命令式语言有点麻烦的函数作为参数也很容易做, 因为根本就没区分函数和量, 如下: 
 
 ```
 add_one(x) = x + 1
 g(f, x) = f(f(x))
 add_two(x) = g(add_one, x)
 
-output(add_two(1))
+output(add_two(1)) # 2
 ```
 
+在最大的环境里, 包含了前面说过的`output`和`dir`两个函数, 这两个函数是用易语言实现的, 默认的, 之后的其他函数都将在这门语言内部实现. 
+
+最复杂的部分大概是匹配函数调用, 调用时候要进行重载决议(从C++偷来的名词), 决议过程大致如下:
+
+```
+# 以如下举例
+
+f(0)    = 0               # 1
+f(x)    = x               # 2
+f(0, 0) = 0               # 3
+f(x, 0) = f(x)            # 4
+f(x, y) = f(x + 1, y - 1) # 5
+```
+
+首先按函数参数数量匹配, 比如 f(2, 1) 应当只匹配到函数345, 如果匹配失败直接报错.  
+之后尝试按尽量多常量相容匹配, 比如 f(2, 1) 会匹配到函数5, 然后调用 f(3, 0), 这个时候就应该去匹配到公式4, 而不应该继续使用公式5, 因为公式4有一个常量相容, 公式5没有. f(3, 0) 则将转到 f(3), 然后求值到3. 
+
+### 数据结构
+
+之前忘记在语法树节点上记录词类型了, 现在从函数名的40字节里分出来4个当做词类, 再分出来4字节用于语法结构类型, 当前语法类型为: 
+
+```
+.版本 2
+
+.常量 语类_未定义, "0"
+.常量 语类_定义, "1"
+.常量 语类_函数, "2"
+.常量 语类_常量, "3"
+.常量 语类_参数名, "4"
+```
+
+
+为了演示真正的在语法树上爬, 我没有额外定义什么数据结构来保存函数, 还是原来的那个抽象语法树上的函数节点. 至于函数环境, 应当用哈希表的, 但是手写一个哈希表在这个当下的易语言上有点傻, 就暴力数组查找了, 所以函数环境就是一个抽象语法树节点数组说, 没了.  
+
 ## 鸣谢
 
 感谢 e2txt 工具, 使得我可以按纯文本保存代码, 添入版本控制
@@ -89,8 +123,10 @@ output(add_two(1))
 
 - 基于栈或者基于寄存器的虚拟机
 - 虚拟栈或者虚拟寄存器的监视器
+- 将 AST 编译到 LLVM/CLI IR/WASM/binaryen IR
+
+以上三者, 我可能会根据大家的兴趣和实现难度挑一个做. 
 
 ### 可以从本节到达的其他方向
 
-- 将 AST 编译到 LLVM/CLI IR/WASM/binaryen IR, 不清楚未来会选哪个
-- 这一节我已经大量地在语法树上乱窜了, 并且在output时从树节点重新拼装了文本, 事实上这就是一个转换器了, 想想JavaScript混淆或者格式化, 都是基于这个原理完成的
+这一节我已经大量地在语法树上乱窜了, 并且在output时从树节点重新拼装了文本, 事实上这就是一个转换器了, 想想JavaScript混淆或者格式化, 都是基于这个原理完成的

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


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


+ 3 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/即时解释虚拟机.static.e.txt

@@ -0,0 +1,3 @@
+.版本 2
+
+.程序集 即时解释虚拟机

+ 11 - 0
3. 简单的数值语言及其虚拟机/简单的数值语言及其虚拟机.代码/代码/哈希表.class.e.txt

@@ -0,0 +1,11 @@
+.版本 2
+
+.程序集 哈希表
+.程序集变量 哈希内存, 变体型
+
+.子程序 _初始化, , , 当基于本类的对象被创建后,此方法会被自动调用
+    
+
+.子程序 _销毁, , , 当基于本类的对象被销毁前,此方法会被自动调用
+    
+

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

@@ -134,6 +134,55 @@
     返回 (指针到文本 (当前节点指针 + #语法树节点_函数名偏移))
     
 
+.子程序 设置语类, , 公开
+    .参数 类, 整数型
+    .局部变量 当前语类, 整数型
+    .局部变量 新类, 整数型
+
+    当前语类 = 获取语类 ()
+    新类 = 到整数 (当前语类 + 类)
+    
+    ' 调试输出 (当前语类, 类, 新类)
+    .如果 (位与 (当前语类, 类) ≠ 0)
+        ' 调试输出 (“警告: 重复设置语类”, 类)
+    .否则
+        写到内存 (新类, 当前节点指针 + #语法树节点_语类, 4)
+    .如果结束
+    
+    
+
+.子程序 清理语类, , 公开
+    .参数 类, 整数型
+    .局部变量 当前语类, 整数型
+    .局部变量 新类, 整数型
+
+    当前语类 = 获取语类 ()
+    新类 = 到整数 (当前语类 - 类)
+    
+    ' 调试输出 (当前语类, 类, 新类)
+    .如果 (位与 (当前语类, 类) = 0)
+        ' 调试输出 (“警告: 尝试清除不存在语类”, 类)
+    .否则
+        写到内存 (新类, 当前节点指针 + #语法树节点_语类, 4)
+    .如果结束
+    
+
+.子程序 获取语类, 整数型, 公开
+    .局部变量 字节数组, 通用型, 数组, "0"
+
+    返回 (取字节集数据 (指针到字节集 (当前节点指针 + #语法树节点_语类, 4), #整数型, ))
+
+.子程序 设置词类, , 公开
+    .参数 类, 整数型
+
+    写到内存 (类, 当前节点指针 + #语法树节点_词类, 4)
+    
+
+.子程序 获取词类, 整数型, 公开
+    .局部变量 字节数组, 通用型, 数组, "0"
+
+    返回 (取字节集数据 (指针到字节集 (当前节点指针 + #语法树节点_词类, 4), #整数型, ))
+
 .子程序 设置参数数量, , 公开
     .参数 量, 长整数型
 
@@ -239,7 +288,8 @@
     .参数 词, 词
     .局部变量 n, 抽象语法树节点, 数组
 
-    n.初始化内存 (词.内容)
+    n.初始化内存 (词.内容, )
+    n.设置词类 (词.类型)
     返回 (n)
     
 
@@ -268,8 +318,34 @@
     
 
 .子程序 生成展示文本, 文本型, 公开
-    返回 (格式化文本 (“%s | 基地址: %x | 参数数量: %d | 参数容量: %d | 参数数组地址: %x ”, 获取函数名 (), 到整数 (获取当前节点指针 ()), 到整数 (获取参数数量 ()), 到整数 (获取参数容量 ()), 到整数 (获取参数指针地址 ())))
+    .局部变量 词类展示名集合, 文本型, , "0"
+    .局部变量 语类展示名集合, 文本型, , "0"
+
+    .局部变量 语类, 整数型
+    .局部变量 语类展示名, 文本型
+    .局部变量 i, 整数型
+    .局部变量 当前掩码, 整数型
+
+    ' 返回 (格式化文本 (“%s | 基地址: %x | 参数数量: %d | 参数容量: %d | 参数数组地址: %x ”, 获取函数名 (), 到整数 (获取当前节点指针 ()), 到整数 (获取参数数量 ()), 到整数 (获取参数容量 ()), 到整数 (获取参数指针地址 ())))
+    语类展示名集合 = { “语类_未定义”, “语类_定义”, “语类_函数调用”, “语类_函数原型”, “语类_形式参数”, “语类_实际参数” }
+    语类 = 获取语类 ()
+    .如果 (语类 = 0)
+        语类展示名 = 语类展示名 + 语类展示名集合 [语类 + 1]
+    .否则
+        .计次循环首 (取数组成员数 (语类展示名集合), i)
+            当前掩码 = 左移 (1, i) ÷ 2
+            .如果真 (位与 (当前掩码, 语类) ≠ 0)
+                语类展示名 = 语类展示名 + 语类展示名集合 [i + 1] + “ ”
+            .如果真结束
+            
+        .计次循环尾 ()
+        
+    .如果结束
+    
+    
+    词类展示名集合 = { “词类_未定义”, “词类_数字”, “词类_整数部分”, “词类_小数部分”, “词类_标识符”, “词类_算符”, “词类_括号”, “词类_注释”, “词类_逗号”, “词类_双等号”, “词类_单等号”, “词类_空格”, “词类_结尾” }
     
+    返回 (格式化文本 (“%s | %s | %s| 参数数量: %d ”, 获取函数名 (), 词类展示名集合 [获取词类 () + 1], 语类展示名, 到整数 (获取参数数量 ())))
 
 .子程序 设置父节点, , 公开
     .参数 父节点, 抽象语法树节点

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

@@ -2,4 +2,6 @@
 词法分析程序集
 来自精易模块
 语法分析程序集
-抽象语法树节点
+抽象语法树节点
+即时解释虚拟机
+哈希表

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

@@ -3,7 +3,10 @@
 .程序集 程序集1
 .子程序 _启动子程序, 整数型, , 本子程序在程序启动后最先执行
     语法分析并输出 (“sin(x, y, f(g(h(z))))”)
-    语法分析并输出 (“f(x) = 2”)
+    语法分析并输出 (“f(g, x) = g(x)”)
+    语法分析并输出 (“h(1, i(2, t(3) + 4*(5+6)))”)
+    语法分析并输出 (“1 + 2”)
+    语法分析并输出 (“1 ”)
     
     
     .判断循环首 (真)

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

@@ -1,6 +1,5 @@
 .版本 2
 .支持库 spec
-.支持库 eAPI
 
 .程序集 语法分析程序集
 .程序集变量 AST根, 抽象语法树节点, 数组
@@ -18,7 +17,7 @@
     
     .判断循环首 (当前词游标 ≤ 取数组成员数 (词))
         res = 匹配语句 (AST根)
-        调试输出 (“匹配表达式 结束”, res.生成展示文本 ())
+        ' 调试输出 (“匹配表达式 结束”, res.生成展示文本 ())
         
         .如果 (res.失败 ())
             跳出循环 ()
@@ -37,7 +36,7 @@
 
     
     当前节点.初始化内存 (“匹配语句 之起始”, )
-    调试输出 (“当前父节点”, 父节点.获取函数名 ())
+    ' 调试输出 (“当前父节点”, 父节点.获取函数名 ())
     .如果 (词存在 (词, #词类_单等号))
         ' 这是个赋值语句
         函数定义 = 匹配函数定义 (当前节点)
@@ -60,6 +59,7 @@
     .局部变量 函数, 抽象语法树节点
     .局部变量 等号, 抽象语法树节点
     .局部变量 表达式, 抽象语法树节点
+    .局部变量 i, 整数型
 
     函数 = 匹配函数 (父节点)
     .如果真 (函数.失败 ())
@@ -76,8 +76,16 @@
         返回 (父节点.生成匹配失败节点 ())
     .如果真结束
     
+    函数.设置语类 (#语类_函数原型)
+    .计次循环首 (函数.获取参数数量 (), i)
+        函数.获取参数节点 (i).设置语类 (#语类_形式参数)
+        函数.获取参数节点 (i).清理语类 (#语类_实际参数)
+    .计次循环尾 ()
+    表达式.设置语类 (#语类_函数调用)
     等号.添加参数节点 (函数)
     等号.添加参数节点 (表达式)
+    等号.设置语类 (#语类_定义)
+    
     返回 (等号)
     
 
@@ -90,25 +98,27 @@
     .局部变量 天父, 抽象语法树节点, 数组
 
     当前节点.初始化内存 (“匹配表达式 之起始”)
-    调试输出 (“当前父节点”, 父节点.获取函数名 ())
+    ' 调试输出 (“当前父节点”, 父节点.获取函数名 ())
     l = 匹配加后表达式 (当前节点)
+    l.设置语类 (#语类_实际参数)
     
     .如果真 (l.失败 ())
-        调试输出 (“未匹配到左加后表达式”)
+        ' 调试输出 (“未匹配到左加后表达式”)
         返回 (父节点.生成匹配失败节点 ())
     .如果真结束
     当前节点.添加参数节点 (l)
-    调试输出 (“匹配到左加后表达式”, l.获取函数名 ())
+    ' 调试输出 (“匹配到左加后表达式”, l.获取函数名 ())
     
     右侧无穷 = 匹配表达式右侧无穷 (父节点)
     右侧无穷.插入参数节点 (l)
     
     天父 = 右侧无穷
     .判断循环首 (天父.获取函数名 () ≠ “nop”)
-        调试输出 (天父.生成展示文本 ())
+        ' 调试输出 (天父.生成展示文本 ())
         天父 = 天父.获取父节点 ()
     .判断循环尾 ()
     天父 = 天父.获取参数节点 (1)
+    天父.设置语类 (#语类_函数调用)
     返回 (天父)
     
 
@@ -120,16 +130,16 @@
     .局部变量 后续无穷, 抽象语法树节点, 数组
 
     当前节点.初始化内存 (“nop”)
-    
+    当前节点.设置语类 (#语类_nop)
     .如果真 (下个词 ().内容 ≠ “+” 且 下个词 ().内容 ≠ “-”)
-        调试输出 (“未匹配到+-中算符”)
+        ' 调试输出 (“未匹配到+-中算符”)
         返回 (当前节点)
     .如果真结束
     m = 父节点.生成自词节点 (下个词 ())
     词游标自增 ()
     
     r = 匹配加后表达式 (当前节点)
-    
+    r.设置语类 (#语类_实际参数)
     .如果真 (r.失败 ())
         返回 (父节点.生成匹配失败节点 ())
     .如果真结束
@@ -143,11 +153,13 @@
         
     .否则
         后续无穷.初始化内存 (“nop”, )
+        后续无穷.设置语类 (#语类_nop)
     .如果结束
     
-    调试输出 (下个词 ().类型)
+    ' 调试输出 (下个词 ().类型)
     m.设置父节点 (后续无穷)
     m.添加参数节点 (r)
+    m.设置语类 (#语类_函数调用)
     后续无穷.插入参数节点 (m)
     返回 (m)
     
@@ -162,29 +174,31 @@
     .局部变量 天父, 抽象语法树节点, 数组
 
     当前节点.初始化内存 ()
-    调试输出 (“当前父节点”, 父节点.获取函数名 ())
+    ' 调试输出 (“当前父节点”, 父节点.获取函数名 ())
     l = 匹配因子 (父节点)
     
     .如果真 (l.失败 ())
         调试输出 (“未匹配到左数字”)
         返回 (父节点.生成匹配失败节点 ())
     .如果真结束
-    调试输出 (“匹配到左数字”, l.获取函数名 ())
+    ' 调试输出 (“匹配到左数字”, l.获取函数名 ())
     当前节点.添加参数节点 (l)
     
     右侧无穷 = 匹配加后表达式右侧无穷 (父节点)
     .如果真 (右侧无穷.获取参数数量 () > 0)
-        调试输出 (“右侧无穷”, 右侧无穷.获取参数节点 (1).生成展示文本 ())
+        ' 调试输出 (“右侧无穷”, 右侧无穷.获取参数节点 (1).生成展示文本 ())
     .如果真结束
     右侧无穷.插入参数节点 (l)
-    调试输出 (“右侧无穷”, 右侧无穷.获取参数节点 (1).生成展示文本 ())
+    ' 调试输出 (“右侧无穷”, 右侧无穷.获取参数节点 (1).生成展示文本 ())
     
     天父 = 右侧无穷
     .判断循环首 (天父.获取函数名 () ≠ “nop”)
-        调试输出 (天父.生成展示文本 ())
+        ' 调试输出 (天父.生成展示文本 ())
         天父 = 天父.获取父节点 ()
     .判断循环尾 ()
     天父 = 天父.获取参数节点 (1)
+    天父.设置语类 (#语类_函数调用)
+    
     返回 (天父)
     
 
@@ -196,9 +210,9 @@
     .局部变量 后续无穷, 抽象语法树节点, 数组
 
     当前节点.初始化内存 (“nop”)
-    
+    当前节点.设置语类 (#语类_nop)
     .如果真 (下个词 ().内容 ≠ “*” 且 下个词 ().内容 ≠ “/”)
-        调试输出 (“未匹配到*/中算符”)
+        ' 调试输出 (“未匹配到*/中算符”)
         返回 (当前节点)
     .如果真结束
     m = 父节点.生成自词节点 (下个词 ())
@@ -219,12 +233,15 @@
         
     .否则
         后续无穷.初始化内存 (“nop”, )
+        后续无穷.设置语类 (#语类_nop)
     .如果结束
     
-    调试输出 (下个词 ().类型)
+    ' 调试输出 (下个词 ().类型)
     m.设置父节点 (后续无穷)
     m.添加参数节点 (r)
     后续无穷.插入参数节点 (m)
+    m.设置语类 (#语类_函数调用)
+    
     返回 (m)
     
 
@@ -256,7 +273,7 @@
         返回 (当前节点)
         
     .判断 (下个词 ().类型 = #词类_数字)
-        数字 = 匹配数字 ()
+        数字 = 通用词匹配 (#词类_数字)
         返回 (数字)
         
     .默认
@@ -271,7 +288,8 @@
     ' 函数的开头是标识符, 所以先尝试匹配下函数再去匹配标识符, 否则可能把
     ' sin(x) 当成 sin 后面没了
     .如果真 (下个词 ().类型 = #词类_标识符)
-        标识符 = 匹配标识符 (当前节点)
+        标识符 = 通用词匹配 (#词类_标识符)
+        标识符.设置语类 (#语类_实际参数)
         返回 (标识符)
     .如果真结束
     
@@ -289,11 +307,14 @@
     .如果真 (下个词 ().类型 ≠ #词类_标识符)
         返回 (父节点.生成匹配失败节点 ())
     .如果真结束
-    标识符 = 匹配标识符 (当前节点)
+    标识符 = 通用词匹配 (#词类_标识符)
     
+    ' 现在标识符要么是函数的一部分, 要么是参数
     .如果真 (下个词 ().内容 ≠ “(”)
+        标识符.设置语类 (#语类_实际参数)
         返回 (标识符)
     .如果真结束
+    标识符.设置语类 (#语类_函数调用)
     词游标自增 ()
     .如果真 (下个词 ().内容 = “)”)
         词游标自增 ()
@@ -328,38 +349,6 @@
         
     .判断循环尾 ()
     返回 (标识符)
-    
-
-.子程序 匹配标识符, 抽象语法树节点
-    .参数 父节点, 抽象语法树节点
-    .局部变量 当前节点, 抽象语法树节点, 数组
-
-    当前节点.初始化内存 ()
-    
-    .如果 (下个词 ().类型 ≠ #词类_标识符)
-        当前节点.释放结构内存 ()
-        返回 (当前节点.生成匹配失败节点 ())
-    .否则
-        当前节点.设置函数名 (下个词 ().内容)
-        词游标自增 ()
-        返回 (当前节点)
-    .如果结束
-    
-
-.子程序 匹配数字, 抽象语法树节点
-    .局部变量 当前节点, 抽象语法树节点, 数组
-
-    当前节点.初始化内存 ()
-    
-    .如果 (下个词 ().类型 ≠ #词类_数字)
-        当前节点.释放结构内存 ()
-        返回 (当前节点.生成匹配失败节点 ())
-    .否则
-        当前节点.设置函数名 (下个词 ().内容)
-        词游标自增 ()
-        返回 (当前节点)
-    .如果结束
-    
 
 .子程序 通用词匹配, 抽象语法树节点
     .参数 词类型, 整数型, , #词类枚举
@@ -372,6 +361,7 @@
         返回 (当前节点.生成匹配失败节点 ())
     .否则
         当前节点.设置函数名 (下个词 ().内容)
+        当前节点.设置词类 (词类型)
         词游标自增 ()
         返回 (当前节点)
     .如果结束
@@ -392,12 +382,12 @@
 
 .子程序 词游标自增
     当前词游标 = 当前词游标 + 1
-    调试输出 (格式化文本 (“游标增至 %d 下一个:(%d,%s)”, 当前词游标, 下个词 ().类型, 下个词 ().内容))
+    ' 调试输出 (格式化文本 (“游标增至 %d 下一个:(%d,%s)”, 当前词游标, 下个词 ().类型, 下个词 ().内容))
     
 
 .子程序 词游标自减
     当前词游标 = 当前词游标 - 1
-    调试输出 (格式化文本 (“游标减至 %d 下一个:(%d,%s)”, 当前词游标, 下个词 ().类型, 下个词 ().内容))
+    ' 调试输出 (格式化文本 (“游标减至 %d 下一个:(%d,%s)”, 当前词游标, 下个词 ().类型, 下个词 ().内容))
     
 
 .子程序 重复文本, 文本型

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

@@ -1,6 +1,7 @@
 .版本 2
 
 .常量 词类型枚举
+.常量 词类_未定义, "0"
 .常量 词类_数字, "1"
 .常量 词类_整数部分, "2"
 .常量 词类_小数部分, "3"
@@ -17,8 +18,11 @@
 
 
 
-.常量 语法树节点_函数名最大长度, "24"
-.常量 语法树节点_函数名偏移, "0", , 长: 40
+.常量 语法树节点_函数名最大长度, "32"
+.常量 语法树节点_函数名偏移, "0", , 长: 32
+.常量 语法树节点_语类, "32", , 长: 4
+.常量 语法树节点_词类, "36", , 长: 4
+
 .常量 语法树节点_参数量偏移, "40", , 长: 4
 .常量 语法树节点_参数容量偏移, "44", , 长: 4
 .常量 语法树节点_参数指针偏移, "48", , 长: 8
@@ -27,4 +31,14 @@
 .常量 语法树节点_参数容量初始值, "8"
 .常量 语法树节点_参数容量扩张系数, "2"
 .常量 语法树节点_匹配失败, "-1"
+
+.常量 语法类型枚举
+.常量 语类_未定义, "0"
+.常量 语类_定义, "1"
+.常量 语类_函数调用, "2"
+.常量 语类_函数原型, "4"
+.常量 语类_形式参数, "8"
+.常量 语类_实际参数, "16"
+.常量 语类_nop, "32"
+
 .常量 指针大小, "8"

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

@@ -4,7 +4,7 @@
         "Guid": "d09f2340818511d396f6aaf844c7e325",
         "Key": "krnln",
         "MaxRefConstPos": 68,
-        "MaxRefObjectPos": 0,
+        "MaxRefObjectPos": 49,
         "Name": "系统核心支持库",
         "Version": {
             "Major": 5,