myuan 2 years ago
parent
commit
2c7eeda694

+ 112 - 12
4. 抽象语法树编译到 llvm/readme.md

@@ -6,7 +6,7 @@
 
 ## 前言
 
-本节的目标是把上一节的「语言」编译到 llvm, 要想简单快捷地生成 llvm IR, 官方的方案是 C++, 虽然有 C 绑定, 但是我测试编译出来的 dll 易语言总是报找不到到导出函数, 而在 dllexport viewer 中却可以看到. 编译 llvm dll 然后让易语言调用花了大部分的时, 以至于这篇文章拖到了现在. 
+本节的目标是把上一节的「语言」编译到 llvm, 要想简单快捷地生成 llvm IR, 官方的方案是 C++, 虽然有 C 绑定, 但是我测试编译出来的 dll 易语言总是莫名其妙错或者找不到到导出函数, 而在 dllexport viewer 中却可以看到. 编译 llvm dll 然后让易语言调用花了大部分的时, 以至于这篇文章拖到了现在. 
 
 另外还有一个选项是 binaryen, 如果是 binaryen IR 的话, 倒还可以用易语言手写生成, 但是 binaryen 最终是运行在 wasm 上的, 不直接原生运行的话, 就代表着无法简单使用操作系统 API, 以及与已有的易语言生态相容, 那么这次的经验就对未来无用了. 
 
@@ -14,11 +14,11 @@
 
 ## 未来
 
-编译器前端要用 DSL 生成不能用易语言写, 后端又不能调用 llvm, 所以生成 IR 的逻辑也不能用易语言写, 可以说`使用易语言编译易语言`的最终目标完全落空. 我之前写过的那些目标本来都是为了这个最终目标努力的, 虽然某些目标仍然可以用易语言实现(比如易语言实现 lisp, 栈虚拟机, 语法扩展), 但是我现在的确非常意兴阑珊. 
+编译器前端要用 DSL 生成不能用易语言写, 后端又不能调用 llvm (最接近的是一个 clang 的 SDK, https://bbs.125.la/forum.php?mod=viewthread&tid=14543661), 所以生成 IR 的逻辑也不能用易语言写, 可以说`使用易语言编译易语言`的最终目标完全落空. 我之前写过的那些目标本来都是为了这个最终目标努力的, 虽然某些目标仍然可以用易语言实现(比如易语言实现 lisp, 栈虚拟机, 语法扩展), 但是我现在的确非常意兴阑珊. 
 
 加上易语言又跟易语言 IDE 强绑定, 之前考虑易语言语法扩展的时候, 我还觉得可以扩展 IDE 来兼容新语法, 但是后来我翻了一些逆向出来的东西, 发现以我的能力基本碰不了易语言 IDE 核心逻辑部分, 如果是在其他地方写然后转译成易语言, 那等于换了一门语言, 毕竟手写易语言的文本格式太扯了, 肯定是用另一种表达生成易语言的, 那能换一门语言的时候, 为什么不用 C# 呢?  
 
-综合来讲, 现在唯一能做的, 就是用易语言做一个易语言的虚拟机, 但是众所周知易语言语法已经很古老了, 我绝对没有勇气完全用易语言实现易语言的解释器, 现在版本的易语言写起来太无聊了. 第一篇第二篇实际上我曾经用 Python 实现过, 约 200 行就能完成, 但是在易语言里花了 844 行, 第三篇我没有用其他语言做过, 但是目测用 Python 300 行就够了, 而易语言用了 1359 行. 我曾见过有人用 Python 实现了 lc3 虚拟机, 大概花了 688 行, 按这个比例光虚拟机部分大约在易语言里需要两千八百行, 语法前端估计又要三四千行. 这样又回到了扩展语法, 而上面分析看来扩展语法是很麻烦的. 
+综合来讲, 现在唯一能做的, 就是用易语言做一个易语言的虚拟机, 但是众所周知易语言语法已经很古老了, 我绝对没有勇气完全用易语言实现易语言的解释器, 现在版本的易语言写起来太无聊了. 第一篇第二篇实际上我曾经用 Python 实现过, 约 200 行就能完成, 但是在易语言里花了 844 行, 单独虚拟机我在 Python 中总共花了 95 行(平均每行 30 字符), 相当于是 Python 300 行就够完成全部了, 而易语言用了 1359 行. 我曾见过有人用 Python 实现了 lc3 虚拟机, 大概花了 688 行, 按这个比例光 lc3 虚拟机部分大约在易语言里需要两千八百行, 易语言语法前端估计又要三四千行. 这样又回到了扩展语法, 而上面分析看来扩展语法是很麻烦的. 
 
 关于扩展语法与现有 IDE 兼容, 如果你有任何解决问题的想法, 请留言告知.  
 只要能扩展语法, 我可以先用旧易语言扩展语法, 扩展差不多了就在其他语言用 socket 接口把 llvm 的相关操作封装起来, 让扩展后的易语言通过 socket 调用, 最终勉强还算是`使用易语言编译易语言`. 
@@ -38,27 +38,27 @@ f(x, y) = f(x) + f(y)
 
 ```c
 // f(0)
-inline double f1() {
-    return 1
+inline double f1(double _) {
+    return 1;
 } 
 // f(x)
 inline double f2(double x) {
-    return x * f(x - 1)
+    return x * f(1, x - 1);
 }
 // f(x, y)
 inline double f3(double x, double y) {
-    return f(x) + f(y)
+    return f(1, x) + f(1, y)
 }
 
-double f(double args[], int args_count) {
+double f(int args_count, double arg0, double arg1) {
     switch (args_count) {
         case 1:
             if (x == 0) 
-                return f1()
+                return f1();
             else
-                return f2(x) 
+                return f2(1, x);
         case 2:
-            return f3(args[0], args[1])
+            return f3(args[0], args[1]);
     }
 }
 
@@ -77,7 +77,7 @@ double f(double args[], int args_count) {
 
 ## AST 导出
 
-没什么好说的, 手写了一个按 JSON 格式输出 AST 的函数, 这是最后的荣耀, 最后的完全使用易语言标准库完成东西
+没什么好说的, 手写了一个按 JSON 格式输出 AST 的函数, 这是最后的荣耀, 最后的完全使用易语言标准库完成东西
 
 ## LLVM IR 生成
 
@@ -85,4 +85,104 @@ double f(double args[], int args_count) {
 
 ```python
 
+```
+
+
+## 虚拟机 in Python
+
+```python
+from json import load
+import string
+from collections import defaultdict
+
+
+ast = load(open("ast.json", encoding="utf8"))
+funcs = defaultdict(list) # {name: [{args: body}]}
+
+op_funcs = {
+    "+": lambda x, y: x + y, "-": lambda x, y: x - y,
+    "*": lambda x, y: x * y, "/": lambda x, y: x / y,
+}
+builtin_func = {
+    'output': lambda *x: print(*x),
+    'dir': lambda *_: print(funcs)
+}
+
+is_number = lambda x: all([i in string.digits + "." for i in x])
+
+get_dict_key = lambda x: list(x.keys())
+get_dict_value = lambda x: list(x.values())
+
+
+def eval_function(name: str, params: list, env: dict):
+    current_funcs = funcs.get(name)
+    candidate_func_match = [0 for _ in current_funcs]
+
+    for func_index, (args, body) in enumerate(current_funcs):
+        if len(args) != len(params):
+            candidate_func_match[func_index] -= 99999
+            continue
+        for i, arg in enumerate(args):
+            if not all([a in string.digits for a in arg]):
+                continue
+            # only digits here
+            if params[i] != float(arg):
+                candidate_func_match[func_index] -= 99999
+                break
+            candidate_func_match[func_index] += 1
+
+    candidate_func = sorted(
+        zip(current_funcs, candidate_func_match),
+        key=lambda x: x[1]
+    )
+
+    if not candidate_func or candidate_func[-1][1] < 0:
+        print("error: cannot find a suitable overloaded function for", name)
+        return
+    
+    args, body = candidate_func[-1][0]
+    return eval_line(body, dict(zip(args, params)))
+
+
+def eval_line(line: dict, envs: dict):
+    if isinstance(line, dict):
+        name: str = get_dict_key(line)[0]
+        params: list = get_dict_value(line)[0]
+    else:
+        print('error line', line)
+        return
+    if name == 'root':
+        for arg in params:
+            eval_line(arg, envs)
+    elif name.startswith('#'):
+        pass
+    elif name == '括号':
+        return eval_line(params[0], envs)
+    elif name == '=':
+        assert len(params) == 2
+        func_name, args = list(params[0].items())[0]
+        args = [get_dict_key(i)[0] for i in args]
+        funcs[func_name].append([args, params[1]])
+    elif is_number(name):
+        assert not params
+        return float(name)
+    elif name in op_funcs:
+        assert len(params) == 2
+        return op_funcs[name](
+            eval_line(params[0], envs), 
+            eval_line(params[1], envs)
+        )
+    elif name in builtin_func:
+        return builtin_func[name](">", *[
+            eval_line(arg, envs) for arg in params
+        ])
+    elif name in funcs:
+        return eval_function(name, [
+            eval_line(arg, envs) for arg in params
+        ], envs)
+    elif name in envs:
+        return envs.get(name)
+    else:
+        print('cannot find', name)
+eval_line(ast, {})
 ```

BIN
4. 抽象语法树编译到 llvm/抽象语法树编译到 llvm.e