{ compile }

  • 如何编译函数闭包

    |

    // 知乎不仅不给我开专栏还说我这篇文章zz敏感,破网站吃枣药丸

    闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

    想要实现一个同时支持词法作用域与 first-class function 的编程语言,一个重要的转换就是所谓的 lambda-lifting:把嵌套函数定义(包括匿名函数)转换为独立的全局函数定义;把自由变量的引用转换为参数传递。

  • Windows LLVM 环境的二三事

    |

    讲道理,考研党被这种配环境的事情打扰了咸鱼的生活是很不开心的。

    起因是 Haskell 的 LLVM binding —— llvm-general 在 Hackage 上只支持到 3.5 版本。当然 repo 里是有后续版本的,但是作者在 issues 里说后续的版本并没有弄好。

    既然如此那就装 3.5 吧。然而下载了 LLVM 3.5.2 的源码后,Cmake 很顺利,用 VS 2017 打开也很顺利,然而编译的时候报错

    这里假装有编译 log (好吧忘记存了)

    总之就是某个类的方法后有 const 限定符,然后调用的上下文却又不是返回 const 于是不给过。

    奇怪的是报错的位置是 VS 提供的标准库文件。这就很尴尬了。我要么改 VS 的标准库要么研究一波 LLVM 源码。

    在 WSL 里试了一下,无痛编译。看来是 VS 有什么蛋疼的限制。网上稍微搜了一圈也没发现有类似情况(毕竟 3.5 的年代他们还没官方支持 VS 编译吧)。于是决定上 MinGW 来构建。

    使用 CMake 生成 MinGW 的 makefile 并没有问题,然而在 make 的时候提示命令语法错误

    命令语法不正确。
    mingw32-make.exe[2]: *** [tools\lto\CMakeFiles\LTO_exports.dir\build.make:61: tools/lto/LTO.def] Error 1
    mingw32-make.exe[2]: *** Deleting file 'tools/lto/LTO.def'
    mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:9977: tools/lto/CMakeFiles/LTO_exports.dir/all] Error 2
    mingw32-make.exe: *** [Makefile:151: all] Error 2

    找到了这个 build.make 发现这一行的命令很奇怪

    cd /d C:\Users\hcyue\code\llvm-3.5.2.build\tools\lto && "C:\Program Files\CMake\bin\cmake.exe" -E echo EXPORTS > LTO.def
    cd /d C:\Users\hcyue\code\llvm-3.5.2.build\tools\lto && type C:/Users/hcyue/code/llvm-3.5.2.src/tools/lto/lto.exports >> LTO.def

    纵横 Windows 这么多年没见过 cd 还可以带个 /d 参数的 似乎 powershell 的 cd 并不支持 /d 参数。那把它删了吧。

    之后继续报错,link 的时候找不到 symbol。发现是刚刚改的那两行生成的目标文件 LTO.def 有问题,不知道为什么没有写入成正常的文本文件,而是二进制文件(VS code 打开提示文件过大或为二进制文件)。直接 type 出来是一个奇怪的、字距拉得很开的、看起来像是文本文件的格式。很气。

    然而既然 .def 反正是文本文件,我大不了手动构建一下 def 。于是手动运行命令把 lto.exports 里的东西 type 出来

    type C:/Users/hcyue/code/llvm-3.5.2.src/tools/lto/lto.exports

    得到一大堆符号名

    lto_get_error_message
    lto_get_version
    lto_initialize_disassembler
    lto_module_create
    lto_module_create_from_fd
    lto_module_create_from_fd_at_offset
    lto_module_create_from_memory
    (……一大堆 lto 符号名)
    LLVMCreateDisasm
    LLVMCreateDisasmCPU
    LLVMDisasmDispose
    LLVMDisasmInstruction
    LLVMSetDisasmOptions

    新建个 LTO.def 把原来的覆盖掉,贴进去。

    重新运行 cmake --build . ,终于过了。

  • 使用 Parsec 处理左递归

    |

    在给之前写的 Lisp 解释器之前套上表达式语法时,遇到这样几条文法

    ExprFactor...ExprsExpr,ExprsFactorIntegerApplyIdentify...(Expr)Integer......ApplyFactor(Exprs)\begin{aligned} Expr & \rightarrow Factor ... \\\\ Exprs & \rightarrow Expr , Exprs\\\\ Factor & \rightarrow Integer|Apply|Identify|...|{(} {Expr} {)} \\\\ Integer & \rightarrow... \\\\ ...\\\\ Apply & \rightarrow Factor ( Exprs ) \end{aligned}

    显然,non-terminal ApplyApply 的派生最左端会进入 FactorFactor ,之后又会回到 ApplyApply 。教科书式的左递归。