<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[RSS Feed of Vincent's Bus Station]]></title><description><![CDATA[Vincent Chan's Bus Station. Blog of Vincent.]]></description><link>https://diverse.space</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 22 Dec 2025 15:15:37 GMT</lastBuildDate><item><title><![CDATA[在 MacOS 下编译 Skia 以及 Wasm 版本]]></title><link>https://diverse.space/2024/7/notes-about-skia-on-macos-and-wasm</link><guid isPermaLink="false">https://diverse.space/2024/7/notes-about-skia-on-macos-and-wasm</guid><pubDate>Sat, 06 Jul 2024 00:12:20 GMT</pubDate><content:encoded>&lt;h3&gt;MacOS&lt;/h3&gt;
&lt;p&gt;Skia 官网对于如何下载和编译 Skia 有详细的说明，但是对于 macOS 下的编译，有一些细节需要注意。
在开始编译之前，出来要先安装 depot_tools，还用同步 third_party 依赖库，可以通过下面的命令完成：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;python3 tools/git-sync-deps&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果不希望依赖系统库，可以通过下面的命令编译 Skia：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;bin/gn gen out/Static &lt;span class=&quot;token parameter variable&quot;&gt;--args&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;is_official_build=false target_cpu=&quot;arm64&quot; skia_use_system_libjpeg_turbo=false skia_use_system_harfbuzz=false&apos;&lt;/span&gt;`&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中 &lt;code class=&quot;language-text&quot;&gt;is_official_build=false&lt;/code&gt; 表示不是官方编译，&lt;code class=&quot;language-text&quot;&gt;target_cpu=&quot;arm64&quot;&lt;/code&gt; 表示编译为 arm64 架构，&lt;code class=&quot;language-text&quot;&gt;skia_use_system_libjpeg_turbo=false&lt;/code&gt; 表示不使用系统的 libjpeg-turbo 库，&lt;code class=&quot;language-text&quot;&gt;skia_use_system_harfbuzz=false&lt;/code&gt; 表示不使用系统的 harfbuzz 库。&lt;/p&gt;
&lt;h3&gt;Wasm 版本的 CanvasKit&lt;/h3&gt;
&lt;p&gt;Skia 提供了一个 Wasm 版本的 CanvasKit，可以在浏览器中使用。编译 Wasm 版本的 CanvasKit 需要使用 &lt;code class=&quot;language-text&quot;&gt;gn&lt;/code&gt; 工具，可以通过下面的命令编译：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; skia/modules/canvaskit
./compile.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[群晖如何在非官方 SSD 上创建储存池]]></title><link>https://diverse.space/2023/09/how-to-create-sdd-storage-pool-on-synology</link><guid isPermaLink="false">https://diverse.space/2023/09/how-to-create-sdd-storage-pool-on-synology</guid><pubDate>Sat, 02 Sep 2023 12:04:00 GMT</pubDate><content:encoded>&lt;p&gt;年初因为老的群晖 CPU 烧了，升级了新的带有 M.2 SSD 插槽的群晖。
看官方的介绍，说这个 SSD 插槽支持把 SSD 用做缓存或者可以用来做储存池。&lt;/p&gt;
&lt;p&gt;直到昨天我插上一条 SSD 才发现原来如果要创建 SSD 分区只能用群晖自己的 SSD。
而群晖自己的 SSD 买得比普通牌子贵 2-3 倍，简直是抢钱。&lt;/p&gt;
&lt;p&gt;去问了官方客服无解，甚至还说 SSD 没用，简直无语。幸好最后在 Reddit 找到一篇帖子，
介绍如何通过 SSD 登录 NAS 创建储存池：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/synology/comments/pwrch3/how_to_create_a_usable_poolvolume_to_use_as/&quot;&gt;https://www.reddit.com/r/synology/comments/pwrch3/how_to_create_a_usable_poolvolume_to_use_as/&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;.  &lt;span class=&quot;token function&quot;&gt;ls&lt;/span&gt; /dev/nvme*             &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Lists your NVMe drives&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;.  &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt;                   &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Type this, &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; your password &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; Super User&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;.  &lt;span class=&quot;token function&quot;&gt;fdisk&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; /dev/nvme0n1     &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Lists the partitions on NVMe1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;.  &lt;span class=&quot;token function&quot;&gt;fdisk&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; /dev/nvme1n1     &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Lists the partitions on NVMe2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;.  synopartition &lt;span class=&quot;token parameter variable&quot;&gt;--part&lt;/span&gt; /dev/nvme0n1 &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Creates the Syno partitions on NVMe1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;.  synopartition &lt;span class=&quot;token parameter variable&quot;&gt;--part&lt;/span&gt; /dev/nvme1n1 &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Creates the Syno partitions on NVMe2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;.  &lt;span class=&quot;token function&quot;&gt;fdisk&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; /dev/nvme0n1     &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Lists the partitions on NVMe1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;.  &lt;span class=&quot;token function&quot;&gt;fdisk&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; /dev/nvme1n1     &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Lists the partitions on NVMe2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;.  &lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /proc/mdstat          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Lists your RAID arrays/logical drives&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;. &lt;span class=&quot;token function&quot;&gt;mdadm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--create&lt;/span&gt; /dev/md4 &lt;span class=&quot;token parameter variable&quot;&gt;--level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; --raid-devices&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--force&lt;/span&gt; /dev/nvme0n1p3 /dev/nvme1n1p3      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Creates the RAID array RAID &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; RAID &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;. &lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /proc/mdstat          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Shows the progress of the RAID resync &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; md3 or md4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;. mkfs.btrfs &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /dev/md4    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Formats the array as btrfs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;. &lt;span class=&quot;token function&quot;&gt;reboot&lt;/span&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Reboots the DS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最后成功在群晖创建了 SSD 分区：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2a956fe8f119157b8dc719af99c2d830/16604/synology.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.27906976744186%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3aAH/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAEQESH/2gAIAQEAAT8hWNxZ/9oADAMBAAIAAwAAABDzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAAMBAQEAAAAAAAAAAAAAAAABIRFBcf/aAAgBAQABPxCAlODe5Dwf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;synology&quot;
        title=&quot;&quot;
        src=&quot;/static/2a956fe8f119157b8dc719af99c2d830/8d3e2/synology.jpg&quot;
        srcset=&quot;/static/2a956fe8f119157b8dc719af99c2d830/34fa0/synology.jpg 215w,
/static/2a956fe8f119157b8dc719af99c2d830/40c27/synology.jpg 430w,
/static/2a956fe8f119157b8dc719af99c2d830/8d3e2/synology.jpg 860w,
/static/2a956fe8f119157b8dc719af99c2d830/1af2f/synology.jpg 1290w,
/static/2a956fe8f119157b8dc719af99c2d830/16604/synology.jpg 1524w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[读懂 WebAssembly 如何表达 C++]]></title><link>https://diverse.space/2021/08/understanding-cpp-on-wasm</link><guid isPermaLink="false">https://diverse.space/2021/08/understanding-cpp-on-wasm</guid><pubDate>Tue, 10 Aug 2021 23:12:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文同步发布到&lt;a href=&quot;https://zhuanlan.zhihu.com/p/397664711&quot;&gt;知乎&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;最近因为项目原因，在项目中使用 Emscripten 将 C++ 代码编译到 WASM 以便在浏览器执行。借助这个过程我也学习了 WASM 上代码执行的方法。借助这个机会，用这篇文章介绍 C++ 是如何借助 WASM 字节码是执行的。&lt;/p&gt;
&lt;p&gt;名词解释&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WASM: WebAssembly，这个不多介绍了。&lt;/li&gt;
&lt;li&gt;WAT: 用来表示 WASM 字节码的一种文本格式。&lt;/li&gt;
&lt;li&gt;Emscripten: 用于把 C++ 代码编译成 WASM 的编译器，基于 LLVM。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;WebAssembly 字节码执行&lt;/h2&gt;
&lt;p&gt;这一段主要解释 WASM 字节码执行的过程。如果了解可以跳过。&lt;/p&gt;
&lt;p&gt;要想知道 WebAssembly 怎么执行。我们就要知道 WASM 字节码如何表示，有哪些类型和指令。编译完成可以执行的 WASM 程序是一个紧凑的二进制程序。
但是我们仍然可以用一种语言来表示 WASM 程序，我们称之为 &lt;a href=&quot;https://www.webassemblyman.com/wat_webassembly_text_format.html&quot;&gt;WAT - WebAssembly Text Format&lt;/a&gt;。
WAT 就像汇编之于机器码一样。我们虽然不能直接阅读 WASM 字节码，但是我们可以通过阅读 WAT 来了解程序的执行过程。&lt;/p&gt;
&lt;h3&gt;基本类型&lt;/h3&gt;
&lt;p&gt;和 Js 不同，WASM 是强类型的，WASM 程序里面运行的“值”有四个基本类型。其他的类型都由这四个基本类型组合而成。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;i32&lt;/li&gt;
&lt;li&gt;i64&lt;/li&gt;
&lt;li&gt;f32&lt;/li&gt;
&lt;li&gt;f64&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从字面意思我们可以知道分别代表 32 位和 64 位整数和浮点数。&lt;/p&gt;
&lt;h3&gt;指令&lt;/h3&gt;
&lt;p&gt;要了解 WASM 的指令。就要了解 WASM 的“机器模型”。和我们现在运行的物理机不同，WASM 的运行环境是虚拟机，而且是一个栈式的虚拟机。栈式的虚拟机是和寄存器机器区分开的一个概念。下图展示了一个栈式机器执行的过程。前两句分别表示往栈上 push 两个值，最后一句执行加法操作，取栈顶两个值相加，然后结果再推回栈上。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/289c89c9d97af054cff6055cbfc0d390/f4bfa/Water-7.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.51162790697674%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB35QB/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAFxABAAMAAAAAAAAAAAAAAAAAEAARIf/aAAgBAQABPyGaU//aAAwDAQACAAMAAAAQU8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEBAAMBAAAAAAAAAAAAAAABABExQSH/2gAIAQEAAT8QXfurPUdkMmSDF//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;栈式虚拟机上 32 位整数的加法操作&quot;
        title=&quot;&quot;
        src=&quot;/static/289c89c9d97af054cff6055cbfc0d390/8d3e2/Water-7.jpg&quot;
        srcset=&quot;/static/289c89c9d97af054cff6055cbfc0d390/34fa0/Water-7.jpg 215w,
/static/289c89c9d97af054cff6055cbfc0d390/40c27/Water-7.jpg 430w,
/static/289c89c9d97af054cff6055cbfc0d390/8d3e2/Water-7.jpg 860w,
/static/289c89c9d97af054cff6055cbfc0d390/1af2f/Water-7.jpg 1290w,
/static/289c89c9d97af054cff6055cbfc0d390/f4bfa/Water-7.jpg 1492w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;图为栈式虚拟机上 32 位整数的加法操作&lt;/p&gt;
&lt;h3&gt;本地变量&lt;/h3&gt;
&lt;p&gt;对于 WASM 程序里面的每个“函数”都有“本地变量”这个概念，在函数的头部定义。
每个本地变量都有一个“编号”，而通过相应的指令，我们可以读取，写入这个变量的值，这两个指令分别是 &lt;code class=&quot;language-text&quot;&gt;local_set&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;local_get&lt;/code&gt;。
再配合上类型，比如 &lt;code class=&quot;language-text&quot;&gt;i32.local_get 0&lt;/code&gt; 就把 0 号本地变量读取出来，然后 push 到栈上。&lt;/p&gt;
&lt;h3&gt;内存空间&lt;/h3&gt;
&lt;p&gt;WASM 的内存空间是线性，这个倒是跟我们的物理机很像。
Emscripten 编译出来的产物里面，对于内存空间，同样分了“栈”空间和“堆”空间。这里很容易混淆的一点是。
这里的“栈”空间并不是上面说的“栈”式虚拟机的那个栈，而是内存空间上的一个概念。
C 语言的函数里面的本地变量，其实也存在于这个内存栈上（如果没有被编译器优化的话）。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/44aca9044b20409940e5e45b4ea1ec34/c9d92/Water-8.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.27906976744185%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd5UihB//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAQABBQJjdyf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAACAwEAAAAAAAAAAAAAAAAAARARITH/2gAIAQEAAT8hwihrYatHUf/aAAwDAQACAAMAAAAQwM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAQUBAAAAAAAAAAAAAAABEQAQITFBcf/aAAgBAQABPxBCvQx7QYAGzKHSK0IxE2//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;栈式虚拟机上 32 位整数的加法操作&quot;
        title=&quot;&quot;
        src=&quot;/static/44aca9044b20409940e5e45b4ea1ec34/8d3e2/Water-8.jpg&quot;
        srcset=&quot;/static/44aca9044b20409940e5e45b4ea1ec34/34fa0/Water-8.jpg 215w,
/static/44aca9044b20409940e5e45b4ea1ec34/40c27/Water-8.jpg 430w,
/static/44aca9044b20409940e5e45b4ea1ec34/8d3e2/Water-8.jpg 860w,
/static/44aca9044b20409940e5e45b4ea1ec34/1af2f/Water-8.jpg 1290w,
/static/44aca9044b20409940e5e45b4ea1ec34/c9d92/Water-8.jpg 1413w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;上图展现了一个 Emscripten 编译出来的 C 语言程序的内存空间。Emscripten 默认分配的栈空间是 5M。而栈空间是从内存的高位往低位增长的，这里可以抛出一个问题，这样设计会带来什么好处呢？。而“堆“空间就很容易理解了，这里不展开。&lt;/p&gt;
&lt;h2&gt;反编译&lt;/h2&gt;
&lt;p&gt;懂了大概这些概念之后，我们就可以动手了。如果还不了解的话，实际用到的时候再回来看看，会更容易理解。&lt;/p&gt;
&lt;h3&gt;实验代码&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;工具&lt;/h3&gt;
&lt;p&gt;反编译的工具这里用的是 WABT: &lt;a href=&quot;https://github.com/WebAssembly/wabt&quot;&gt;WABT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;命令&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;wasm2wat test_wasm.wasm &gt; test.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;得到的这个 test.txt 就是反编译后 WAT 格式的代码了。然后你应该能看到类似这样的代码：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;(module
  (type (;0;) (func (result i32)))
  (type (;1;) (func (param i32)))
  (type (;2;) (func (param i32) (result i32)))
  (type (;3;) (func))
  (type (;4;) (func (param i32 i32) (result i32)))
  (type (;5;) (func (param i32 i32 i32) (result i32)))
  (type (;6;) (func (param i32 i64 i32) (result i64)))
  (func $__wasm_call_ctors (type 3)
    call $emscripten_stack_init)
  (func $add_int__int_ (type 4) (param i32 i32) (result i32)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里你可能看不懂，但是也能大概猜出来里面的含义。以 type 开头的意思为分别定义了这些类型。以 func 开头则是代表定义了这些函数。下面我很很自然就看到 add_int 这个函数，这里摘取一部分代码：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;(func $add_int__int_ (type 4) (param i32 i32) (result i32)
    (local i32 i32 i32 i32 i32 i32)
    global.get $__stack_pointer
    local.set 2
    i32.const 16
    local.set 3
    local.get 2
    local.get 3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;看到这段代码我们要给这些变量编个号码，从 param 开始从 0 开始数，result 不算，遇到一个变量就加一，直到分配完所有变量。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca74028b1c028426f98c45c7529e4ef7/34898/Water-9.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.86046511627907%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd6AsD//xAAWEAADAAAAAAAAAAAAAAAAAAAAECH/2gAIAQEAAQUCKv/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABkQAAEFAAAAAAAAAAAAAAAAAAEAEBFBUf/aAAgBAQABPyEzSA03/9oADAMBAAIAAwAAABDzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAAIBBQAAAAAAAAAAAAAAAAERABAhUXGB/9oACAEBAAE/EM8dlwwGw6f/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Water 9&quot;
        title=&quot;&quot;
        src=&quot;/static/ca74028b1c028426f98c45c7529e4ef7/8d3e2/Water-9.jpg&quot;
        srcset=&quot;/static/ca74028b1c028426f98c45c7529e4ef7/34fa0/Water-9.jpg 215w,
/static/ca74028b1c028426f98c45c7529e4ef7/40c27/Water-9.jpg 430w,
/static/ca74028b1c028426f98c45c7529e4ef7/8d3e2/Water-9.jpg 860w,
/static/ca74028b1c028426f98c45c7529e4ef7/34898/Water-9.jpg 1270w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;而聪明的你一定就会发现，下面代码里面所谓的 &lt;code class=&quot;language-text&quot;&gt;local.get&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;local.set&lt;/code&gt; 后面的数字所代表的变量就是这几个变量。
接下来看看 debug 模式下如何读取和存储 a 变量。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;global.get $__stack_pointer
local.set 2  // 2 &amp;lt;- stack_pointer ; 把栈指针放到 2 号槽
i32.const 16
local.set 3  // 3 &amp;lt;- 16 ; 把数字 16 放到 3 号槽
local.get 2
local.get 3
i32.sub     // 2 号和 3 号相减，推到栈上
local.set 4 // 4 &amp;lt;- stack_pointer - 16 ; 把 stack - 16 放到 4 号槽，这个代表这个函数的基地址，函数内变量都通过基地址 + 偏移来进行访问。这里表示这个函数占用 16 bytes 的栈大小&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/47075fc88f7e0087644efcafa4a9137f/de520/Water-10.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.348837209302324%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3lBR/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAFxABAQEBAAAAAAAAAAAAAAAAEQEQAP/aAAgBAQABPyGvFdJn/9oADAMBAAIAAwAAABAjD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAAICAwEAAAAAAAAAAAAAAAERACEQMUFR/9oACAEBAAE/EHUKPC4CMSfKM5NyxoPH/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Water 10&quot;
        title=&quot;&quot;
        src=&quot;/static/47075fc88f7e0087644efcafa4a9137f/8d3e2/Water-10.jpg&quot;
        srcset=&quot;/static/47075fc88f7e0087644efcafa4a9137f/34fa0/Water-10.jpg 215w,
/static/47075fc88f7e0087644efcafa4a9137f/40c27/Water-10.jpg 430w,
/static/47075fc88f7e0087644efcafa4a9137f/8d3e2/Water-10.jpg 860w,
/static/47075fc88f7e0087644efcafa4a9137f/1af2f/Water-10.jpg 1290w,
/static/47075fc88f7e0087644efcafa4a9137f/de520/Water-10.jpg 1345w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;图为 add 函数的内存布局&lt;/p&gt;
&lt;h3&gt;Release 模式的优化&lt;/h3&gt;
&lt;p&gt;Debug 模式下，生成的代码会比较啰嗦，先把参数 a, b 放到内存的这两个位置。然后再重新读出来，然后进行相加。然后再返回。这个时候或许我们会想，其实这里是不是没有必要写到内存，直接相加就可以了。就像我们 native 程序，有些变量直接在寄存器就能相加。所以 Release 模式下是没有写到内存的。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;(func (;2;) (type 0) (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;以上就是 Release 下生成的代码，不涉及内存操作。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;以上讲述了 C++ 如何在借助 WASM 在浏览器里执行。相比机器码，WASM 读起来要易懂得多，我们也可可以借此机会多了解编译器如何理解和编译我们的代码，达到举一反三的效果。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[QuickJS 源码解读（二）：基础设施和标准库]]></title><link>https://diverse.space/2020/12/understanding-source-code-of-quickjs-2</link><guid isPermaLink="false">https://diverse.space/2020/12/understanding-source-code-of-quickjs-2</guid><pubDate>Fri, 25 Dec 2020 23:01:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/2019/08/understanding-source-code-of-quickjs-1&quot;&gt;上一篇文章&lt;/a&gt; 里面主要解释了
了 QuickJS 虚拟机的运作。第二篇文章打算介绍一下 QuickJS 里面 JavaScript
基础设施的实现。&lt;/p&gt;
&lt;h2&gt;基础设施&lt;/h2&gt;
&lt;p&gt;注意，使用 QuickJS 新建 JSContext 的时候，默认是不带基础设施的
(比如说 JSON 解析、Object、等等)。
这时候，可以调用以下命令进行添加，这些内建对象的支持都是内置的：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicBaseObjects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicEval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicStringNormalize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicRegExpCompiler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicRegExp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicMapSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicTypedArrays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicBigInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicBigFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_AddIntrinsicBigDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;至于这些内置方法，比如 Object 是怎么实现，我们可以直接点开 &lt;code class=&quot;language-text&quot;&gt;JS_AddIntrinsicBaseObjects&lt;/code&gt; 代码&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Object */&lt;/span&gt;
obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_NewGlobalCConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Object&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_constructor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                ctx&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;class_proto&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;JS_CLASS_OBJECT&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;JS_SetPropertyFunctionList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_funcs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;countof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;js_object_funcs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;JS_SetPropertyFunctionList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;class_proto&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;JS_CLASS_OBJECT&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            js_object_proto_funcs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;countof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;js_object_proto_funcs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这两行分别给 Object 和 Object.prototype 设置了相应的函数，
函数的定义在 &lt;code class=&quot;language-text&quot;&gt;js_object_proto_funcs&lt;/code&gt; 这个静态变量里面。&lt;/p&gt;
&lt;p&gt;我们可以跟踪看看 &lt;code class=&quot;language-text&quot;&gt;js_object_proto_funcs&lt;/code&gt; 分别定义了什么函数：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; JSCFunctionListEntry js_object_proto_funcs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;toString&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_toString &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;toLocaleString&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_toLocaleString &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;valueOf&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_valueOf &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hasOwnProperty&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_hasOwnProperty &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;isPrototypeOf&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_isPrototypeOf &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;propertyIsEnumerable&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_propertyIsEnumerable &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CGETSET_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;__proto__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_get___proto__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object_set___proto__ &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_MAGIC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;__defineGetter__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object___defineGetter__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_MAGIC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;__defineSetter__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object___defineGetter__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_MAGIC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;__lookupGetter__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object___lookupGetter__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;JS_CFUNC_MAGIC_DEF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;__lookupSetter__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_object___lookupGetter__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以看到这些内置方法都是 C 实现的方法。对应的函数都是在 &lt;code class=&quot;language-text&quot;&gt;quickjs.c&lt;/code&gt; 里面定义。&lt;/p&gt;
&lt;p&gt;知道了这些，我们就可以动手修改 QuickJS 代码，比如说我们想改变 Object 的 &lt;code class=&quot;language-text&quot;&gt;toString&lt;/code&gt; 的表现。让它输出更详细的信息，那我们更改 &lt;code class=&quot;language-text&quot;&gt;js_object_toString&lt;/code&gt; 的实现就可以了。&lt;/p&gt;
&lt;h2&gt;标准库&lt;/h2&gt;
&lt;p&gt;标准库的实现，不是语言的一部分。这一部分的实现内容放在了 &lt;code class=&quot;language-text&quot;&gt;quickjs-libc.c&lt;/code&gt; 这个文件。
这里稍微提一句，Bellard 实现标准库用了不少 POSIX 方法，这样实现起来代码会比较简单，但是
同时导致了代码无法在 Windows 上编译（除非用 MingW)，所以想要在 Windows 上独立编译通过的话，需要做不少改动。&lt;/p&gt;
&lt;p&gt;我自己也做了一份 Fork，修复了 Windows 的编译问题，同时支持 CMake：
&lt;a href=&quot;https://github.com/vincentdchan/quickjs&quot;&gt;https://github.com/vincentdchan/quickjs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;需要的可以自取。&lt;/p&gt;
&lt;h3&gt;console.log&lt;/h3&gt;
&lt;p&gt;写过 JS 的人估计都用过 console.log 吧。那么在 QuickJS 里面，console.log 怎么实现呢。
答案就在 &lt;code class=&quot;language-text&quot;&gt;quickjs-libc.c&lt;/code&gt; 这个文件的 &lt;code class=&quot;language-text&quot;&gt;js_std_add_helpers&lt;/code&gt; 这个函数里面：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;JSValue global_obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

global_obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_GetGlobalObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_NewObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;JS_SetPropertyStr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token function&quot;&gt;JS_NewCFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; js_print&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;JS_SetPropertyStr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; global_obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;console&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以看到实现方法就是 &lt;code class=&quot;language-text&quot;&gt;js_print&lt;/code&gt; 这个函数：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; JSValue &lt;span class=&quot;token function&quot;&gt;js_print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; JSValueConst this_val&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                              &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; argc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; JSValueConst &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;argv&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; argc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;putchar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        str &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_ToCString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; argv&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; JS_EXCEPTION&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;fputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;JS_FreeCString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; str&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;putchar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;\n&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; JS_UNDEFINED&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以看到底层调用的是 C 语言的 &lt;code class=&quot;language-text&quot;&gt;fputs&lt;/code&gt; 方法输出到 &lt;code class=&quot;language-text&quot;&gt;stdout&lt;/code&gt;。
如果你想让 console.log 输出到自己的文件，或者数据库，那么你就可以更改 &lt;code class=&quot;language-text&quot;&gt;js_print&lt;/code&gt;
这个方法了。&lt;/p&gt;
&lt;p&gt;我们还可以看到，Bellard 只实现了 console.log，但是没有输出 console.error。
那么我们就可以把 console.error 给实现上，输出到 stderr，也是轻而易举了。&lt;/p&gt;
&lt;h3&gt;setTimout&lt;/h3&gt;
&lt;p&gt;setTimeout 的实现会稍微复杂一点。要了解 setTimeout 的实现，就要了解 QuickJS 的
事件循环。要了解 QuickJS 的事件循环，其实只要看懂一个函数：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* main loop which calls the user JS callbacks */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;js_std_loop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    JSContext &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ctx1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;/* execute the pending jobs */&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JS_ExecutePendingJob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;JS_GetRuntime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;ctx1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;js_std_dump_error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;os_poll_func &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;os_poll_func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;QuickJS 里面维护着一个队列。而主循环就是一直从这个队列里面捞任务出来进行处理。
&lt;code class=&quot;language-text&quot;&gt;JS_ExecutePendingJob&lt;/code&gt; 就是执行一个 JS 任务。这个 JS 任务可能添加了一个系统任务。
比如如果代码调用了 setTimeout 那么 QuickJS 会往系统添加一个定时器任务。&lt;/p&gt;
&lt;p&gt;随后这个循环调用 &lt;code class=&quot;language-text&quot;&gt;os_poll_func&lt;/code&gt; 这个方法，会一直阻塞，等到有任务完成，
这个函数会往 QuickJS 队列添加一个回调，然后返回。&lt;/p&gt;
&lt;p&gt;返回后进入下一次循环，就会执行 setTimeout 的回调，这样就完成了 setTimeout 的调用。&lt;/p&gt;
&lt;p&gt;这里和我们熟知的 v8 不一样，v8 使用的是 libuv 作为事件循环的库。
而 QuickJS 为了轻量化，简单的封装了一下系统的信号，有兴趣的同学可以深入了解 &lt;code class=&quot;language-text&quot;&gt;os_poll_func&lt;/code&gt;
函数的实现。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[写一个飞快的 JavaScript 打包压缩工具]]></title><link>https://diverse.space/2020/04/implement-a-extremely-fast-js-bundler</link><guid isPermaLink="false">https://diverse.space/2020/04/implement-a-extremely-fast-js-bundler</guid><pubDate>Sat, 25 Apr 2020 17:35:00 GMT</pubDate><content:encoded>&lt;h1&gt;背景&lt;/h1&gt;
&lt;p&gt;平时大家在开发 Js 项目的时候，可能已经离不开 webpack 等打包工具了。而 webpack 打包速度大概就是“能用“的水平。大概去年开始，我就开始在构想，如果能写一个极速的打包工具，功能未必需要很强，可能对小项目非常有用。去年我用 C++ 写完 parser 之后，便没什么动力写下去了。但是最近发现有这个想法的不止我一个，Figma 的 CTO 业余之际写了一个打包器 &lt;a href=&quot;https://github.com/evanw/esbuild&quot;&gt;https://github.com/evanw/esbuild&lt;/a&gt;，可以说完完全全实现了我想象中的需求，不过他是用 Go 语言实现的。我看到这个项目时心里一想，这不是我去年就想做的事吗，这 push 我赶紧把打包压缩部分完成。&lt;/p&gt;
&lt;h1&gt;代码&lt;/h1&gt;
&lt;p&gt;Github 地址：&lt;a href=&quot;https://github.com/vincentdchan/jetpack.js&quot;&gt;https://github.com/vincentdchan/jetpack.js&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;优化思路&lt;/h1&gt;
&lt;h2&gt;并行 Parsing&lt;/h2&gt;
&lt;p&gt;毫无疑问，每一个 js 文件的 parsing 可以在不同线程完成，这就需要支持并行的语言。由于 parsing 的结果是 AST，所以需要可以共享内存的语言（排除通过 messeage parsing 实现多线程的语言）。满足以上两个要求的语言不多。 Evan 选择了 Go，我选择了 C++。&lt;/p&gt;
&lt;h2&gt;减少遍历次数&lt;/h2&gt;
&lt;p&gt;要想速度快，就要减少 AST 的遍历次数。最好就是只遍历一次来生成代码，在 Parsing 构建 AST 的时候就收集足够的信息。但是这也意味着只能做比较浅层次的优化，不能做深层次的压缩（死代码消除，tree shaking 都做不了）。&lt;/p&gt;
&lt;h2&gt;架构&lt;/h2&gt;
&lt;p&gt;由上述思路我总结出了以下打包的架构：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;并行 parse 文件&lt;/li&gt;
&lt;li&gt;作用域提升、生成框架代码、重命名变量&lt;/li&gt;
&lt;li&gt;并行生成代码&lt;/li&gt;
&lt;li&gt;合并输出文件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;流程图如下：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/daa0952c9a8d60b61578df2874246f63/da952/Rocket-Bundle-Arch.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 99.06976744186048%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC0UlEQVR42qVUX0haURg/lrWVbW2jrVqGLzLcnTPllra9CGP4FAwGvpT0MJh7qDH21FMIE6J2aexhwXwYaFAbF3qRPbgHkQi6SSgKbkJ/zD/3dm+WVwiJiOUZn3iZyWaufS+He77vnvP7/b7fdxCqEVartZHjuHFRFKlcLvcml8u9SyaTDyGHMW5A9QbGWAar2WyWp9PpLzzPx3d3dxlBELY2NjZGIEfTdGOtM2S19qenp9udTqcK/W/EYrHm8jqWSqV+zM3N9SKEGmui0+l0CoPBoDYajVdJkmwaHBxsQQg1VCKkKEpB0/SNshSyKkYylUp1GeQpfZUP0vT39/eSJNlhNBqVUBQKhVSBQOADwzBP4XtoaKgV6hmG6VxZWRmNxWKE1Dj4lyCItr81pEQnkUi8Ojk5waurq06EkNxms92C/VAoZMtmsz9Zln0vdZsgiOY/iX+GDhQ6HI5roBlIAbRgnyTJ1pmZmSuVtpFydcX8/LyCoijt5OTkA7fb3WO325uqa5RKZUsNp5ylvre39/Lo6AgLgoBFUeSDwWBXpVch9Hr9TWBR09gY45IuBwcH48ViER8eHuJCocAtLy93SxdKh2q12k61Wn2pLsqzs7M9Lpfr/tTU1B2Px3PXbre3Vtec0RBE1mg03QDbbDZ3IITa1tbWHmUymc/hcHgYapLJ5AjLsh/T6XSP1+vVbG5ufovH418XFxdvQ95kMv1GCN7r6+u7B+a2WCwwCfJoNDrM8/zWzs7Oa6jhOO4tzDHLsvpIJGLI5/PfOY77NDEx0Q75gYGBLhiQehjL4WYQvIxAVq9WleMEDWmQfObz+Xr9fv/o0tLSWDgcfuxwOJqrm3KuZSptk81mn2GM8enpKT4+Pk4tLCxcr7bNP72J29vbndFo9Mn6+vpoIpEwuVyupgs/XxJKQRCei6KYomm6/ULoqlFmMhnd/v7+i0AgcO7M/gIH1Wg6+e2x1AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Rocket Bundle Arch&quot;
        title=&quot;&quot;
        src=&quot;/static/daa0952c9a8d60b61578df2874246f63/914ae/Rocket-Bundle-Arch.png&quot;
        srcset=&quot;/static/daa0952c9a8d60b61578df2874246f63/2eb24/Rocket-Bundle-Arch.png 215w,
/static/daa0952c9a8d60b61578df2874246f63/05ed2/Rocket-Bundle-Arch.png 430w,
/static/daa0952c9a8d60b61578df2874246f63/914ae/Rocket-Bundle-Arch.png 860w,
/static/daa0952c9a8d60b61578df2874246f63/46115/Rocket-Bundle-Arch.png 1290w,
/static/daa0952c9a8d60b61578df2874246f63/6c86f/Rocket-Bundle-Arch.png 1720w,
/static/daa0952c9a8d60b61578df2874246f63/da952/Rocket-Bundle-Arch.png 1872w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;打包压缩原理&lt;/h1&gt;
&lt;p&gt;本章节主要讲如何“最简单“地压缩 Js 代码。本章节假设读者对编译原理有一定了解，知道什么是 AST。如果不懂请直接跳到下文「性能」章节。&lt;/p&gt;
&lt;h2&gt;字面量替换&lt;/h2&gt;
&lt;p&gt;字面替换最简单。规则有一下几个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;undefined 替换为 &lt;code class=&quot;language-text&quot;&gt;void 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;true 替换为 &lt;code class=&quot;language-text&quot;&gt;!0&lt;/code&gt;, false 替换为 &lt;code class=&quot;language-text&quot;&gt;!1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚠️ 注意：在 ES 中，undefined 是标识符（Identifier)，而不是关键字，也就是说你可以定义一个叫 undefined 的变量，所以这个时候不能简单地替换为 &lt;code class=&quot;language-text&quot;&gt;void 0&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;常量折叠&lt;/h2&gt;
&lt;p&gt;计算简单的运算：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; two &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foobar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;foo&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;转换成&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; two &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foobar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;foobar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;⚠️ 注意：这里要注意实现的平台和 js 的差异，比如在 C++ 里面大整数相加可能会溢出，而在 Js 会自动转换成 bigint. 加法问题就如此，其他运算符问题更多。如果要完整实现常量折叠，可能要部分实现 js 引擎。&lt;/p&gt;
&lt;h2&gt;变量别名&lt;/h2&gt;
&lt;p&gt;别名就是要给变量重新赋予比较短的变量名。从字母一直排上去，abcd，一个字母用完了用两个字母。实现起来也很简单，用一个计数器，一直加上去就可。最后每个变量分配一个数字，把这个数字映射到相应的英文字母上，有点像 36 进制转换成字母的面试题。不过这里有一点值得注意的是，变量名第一个字母不能是数字，第二个字母开始可以是数字，要考虑到这一点，才能尽可能“压榨”变量名。&lt;/p&gt;
&lt;p&gt;为了尽可能地“压榨”变量名，同一级的作用域里面的变量名是可以使用相同的变量名。到下一级的时候，对子作用域进行合并。&lt;/p&gt;
&lt;p&gt;举个例子：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Mother&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; e &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;capture&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// d 不能使用跟子作用域同样的变量名，不然子作用域无法捕获这个变量&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	
	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// B 跟 A 函数同级，分配同样的变量名&lt;/span&gt;
	  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;上述例子中，A 和 B 都没有子作用域了，变量名从 0 开始分配。到给 Mother 下 e 分配变量名时，找到子作用域最大的计数器。分配最多的子作用域 A 分配了 4 个，所以 B 计数器从 5 开始分配，所以给 e 分配了5，所以 e 就得到了这个名字。&lt;/p&gt;
&lt;p&gt;所以变量别名就是从 AST 的叶子开始向上构造，一直分配到根结点把所有作用域都分配完为止。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;小技巧&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里 esbuild 采用了比较聪明的技巧。它统计了所有变量的引用次数，然后进行排序，引用次数最多的变量分配到的名字就是尽量短的，这样也可以减少编译出来 js 的体积。我在写 jetpack 打包的时候，也借鉴了这种做法。&lt;/p&gt;
&lt;h2&gt;模块合并&lt;/h2&gt;
&lt;p&gt;模块合并的办法有很多。webpack 采用的是用 function 把每个函数包起来，放到了一个长长的数组里面，然后实现了自己的 require，esbuild 也采用了类似的方法。&lt;/p&gt;
&lt;p&gt;Rollup.js 实现的方法则是作用域提升（Scope hoisting），把模块都放到根作用域。这里我采用的方法也是作用域提升。&lt;/p&gt;
&lt;p&gt;假设有 a.js 文件：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后有 main.js 文件：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ExternalA &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;local A&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ExternalA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;使用 jetpack 打包完的结果：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// a.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// main.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A_0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;local A&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;A_0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; main &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;难点在于作用域合并。实际上在 ES modules 里面不同 modules 之间引用是一个图结构。&lt;/p&gt;
&lt;h1&gt;C++ 的优化&lt;/h1&gt;
&lt;p&gt;除了策略上的优化，C++ 还提供了诸多基础数据结构/内存方面的优化。&lt;/p&gt;
&lt;h2&gt;shared_ptr&lt;/h2&gt;
&lt;p&gt;AST 的结点全部使用 shared_ptr，有人可能认为这是一个很大的开销。但是早期的时候我实现过一个裸指针版本（不释放内存），并没有测出有明显差距。&lt;/p&gt;
&lt;p&gt;使用 shared_ptr 很重要一个原因是，一个子树可能被其他类拥有（打包模块，Scope，ES Module 管理器）。这个时候如果用 unique_ptr 的话就会 gg。只能说 GC 大法好。&lt;/p&gt;
&lt;p&gt;对于 C++ 这种没有 GC 的语言有一个毛病就是：析构 AST 非常耗时。AST 够大的话能耗上十几 ms（这个时间跟 gc 比有何优势？），所以因此我也能想出了一个办法：&lt;strong&gt;不释放内存&lt;/strong&gt;……。&lt;/p&gt;
&lt;p&gt;最后说一句：&lt;strong&gt;GC 大法好&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;robin hood hashing&lt;/h2&gt;
&lt;p&gt;由于打包器中大量使用哈希表，所以提高哈希表速度尤其重要，这里我使用了 robin hood hashing&lt;/p&gt;
&lt;p&gt;参见：&lt;a href=&quot;https://martin.ankerl.com/2019/04/01/hashmap-benchmarks-01-overview/&quot;&gt;https://martin.ankerl.com/2019/04/01/hashmap-benchmarks-01-overview/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在 hash 方面我有一个设想，就是像 Lua 一样，对于短字符，在字符串创建的时候把 hash 记下来，这样在多次使用哈希表的时候可以节省 hash 的时间（但是要求字符串是 immutable 的）。为此我专门写了个 String 类，最后的结果是总体速度慢了 2-3x，测出来是 immutable 字符串拼接耗时太多，最后放弃了这个方案。&lt;/p&gt;
&lt;h2&gt;jemalloc&lt;/h2&gt;
&lt;p&gt;Parsing 过程中需要大量分配 node，大家都知道很明显 C++ 的 new 并不够快。经过测试在 macOS 下使用 jemalloc 会让 parsing 速度提升 1 倍。使&lt;strong&gt;用系统 malloc 会导致 parsing 速度比 Go 慢 1x 左右，慢在 new&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;当然了，内存池我也试过的，测出来速度基本和 jemalloc 一样，所以就直接用 jemalloc 了。&lt;/p&gt;
&lt;h1&gt;性能&lt;/h1&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 600px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4a0bbc222354481a55461d78be203165/0a47e/chart.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.86046511627906%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABg0lEQVR42pVSy04CMRSd78G1Ihr9AhNXxmD8DdyB6NK4EQlqXGj8Bk00MPADEONziYkudIPDTOfV6eOYzoMZDYI2Oem5t+3t7TnVEA/GGKSUIU/mZGTjaWtaEli2j4HFYbqAYQsMHTlCNlbccCSYCEtACDEqprjGoxiXXQcrewTFuov1mjMRawcOHl55eI7zHx2alg3beMdFZ4hcyURhm2CuPB75MsFsmWB5l6D38ktBLqLEVc/G6j7BZsPFRt2ZiOKhg/u4Q8ok1CuVlN80pJRm9BChPvG9cYw/rElocYO47Xuo3Xg47QQ41n2ctOkI2TjhRzpFo0XRaPqoNyneBtFFGlMaMIIzfYCZLQsLVYJ8ZTrmKwSFmC/tEHT7LFOQE5zrn8iVrImmjDNJzYvVTMHkX/Y/PFzfUbSfOfQn9i+0HhkGJH5yaoqfmiIYIHmiNKQIUuEVT7qQPNobG6KMTQsGDAGToX+244WQYZ7DGFpQ5qnvobjKRftcENsNOecCpmniCy73Ulj34pQTAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;chart&quot;
        title=&quot;&quot;
        src=&quot;/static/4a0bbc222354481a55461d78be203165/0a47e/chart.png&quot;
        srcset=&quot;/static/4a0bbc222354481a55461d78be203165/2eb24/chart.png 215w,
/static/4a0bbc222354481a55461d78be203165/05ed2/chart.png 430w,
/static/4a0bbc222354481a55461d78be203165/0a47e/chart.png 600w&quot;
        sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;总结&lt;/h1&gt;
&lt;p&gt;写编译器需要快速大量产生 node 结点，大量树和图的结构，这一方面的运算 C++ 并没有什么优势可言。&lt;/p&gt;
&lt;p&gt;不得不承认，使用 C++ 你要思考很多东西，做很多很多额外的工作，才能获得比 Go 还快的速度（什么都不想做出来只会比 Go 还慢）。另一方面使用 C++ 会让你额外考虑很多和业务无关的东西，大大降低开发速度，而对于打包器这个场景 C++ 在这一块本身不能提供很大优势。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[使用 JS 操作 mac 下文件拓展属性]]></title><link>https://diverse.space/2020/02/using-js-to-manipulate-xattr-on-macos</link><guid isPermaLink="false">https://diverse.space/2020/02/using-js-to-manipulate-xattr-on-macos</guid><pubDate>Sat, 22 Feb 2020 21:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;任何可以使用 JavaScript 来实现的应用都最终都会使用 JavaScript. --Atwood&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通过浏览器和 Node.js，JavaScript 可以说上天入地无所不能。现在许多客户端应用程序也使用 Electron 进行开发。但是 Node.js 本身在一些 native 操作上稍有欠缺，比如说 mac 下文件拓展属性。&lt;/p&gt;
&lt;h1&gt;什么是拓展属性&lt;/h1&gt;
&lt;p&gt;拓展属性是 mac 下文件系统提供的一种机制。除了基本的文件属性（修改时间、创建时间等），mac 还允许用户和程序自定义属性。这个属性是一个以 key-value 形式储存的键值对。key 是 UTF-8 字符串，value 则是任意形式的数据。&lt;/p&gt;
&lt;p&gt;Mac 下一些系统程序也依赖这个拓展属性。比如说我们去 Node.js 官网下载一个 pkg 安装包，然后打开，mac 会弹出这个框。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9861b50f81a6a48053283ea22289be12/dd104/quarantine.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.930232558139544%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAACKklEQVR42rWTTY/SQBjHG40e3MtGL8ph+QwcCScSThxNuAifwMNe+BC7elv1JGAXNcaQ3YR4AUmAgzHZBcVteVtJaekLBWbKa2iBpu1j2uwimL1oYpNf5j8znV+eznQI4n88kUhkh6KoF7Va7RNN0yc0TZ/W6/U1zWZzq3+N/a69hqKoo2g0urMWhkKhhxhjFgBgsVya8/kcJpOJw3A4hMFgABhjJyuK4mBnVVVNy7LsuXY4HH60Fvp8vj1ZltvfqCqcfi7oktw3MpmMkU6njVwu5+RsNmsUi0WjUCg4Y/l83iiVSnq73QZRFBm/3+9eCz0ejxvjQSf54QTiRxFTkc7gx0UDSudnwLIs9Pt96PV6IMsySJIE3W7XaXmeN8fjMQiCwG0JvV6vWxCEznQ6AyQzpoIkaDQvgaIoqFarUKlUnFwul6HVagHHccAwDHS7sqlqGvCCwAX/FPI831HVOQxHUxPjIXAcC1ef41RmV2W39n4ihGEyQjBWeuZKm4Io3lwhN5vN7A02EEJWo9GwaJq2BEGwFEWxMMYO9hzGyPreUqzzy5Fx0dGAYUX2cXBDGAgE9hBCHdM0nZPTNA1WqxXoug6LxQLs/ib6UgPyqwUvC2C++gLwk1e4p0+Cv4Uul+sBSZL7yWTyWTweP0gkEoexWMzBzjfxLhk7PD5+ffDx/Zvnb0ly33Zs/tt3CIK4RxDELkEQ9/+S3au1d7duCwDcSqVSt/+Va9EvKsYe4w4Y9jIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;quarantine&quot;
        title=&quot;&quot;
        src=&quot;/static/9861b50f81a6a48053283ea22289be12/914ae/quarantine.png&quot;
        srcset=&quot;/static/9861b50f81a6a48053283ea22289be12/2eb24/quarantine.png 215w,
/static/9861b50f81a6a48053283ea22289be12/05ed2/quarantine.png 430w,
/static/9861b50f81a6a48053283ea22289be12/914ae/quarantine.png 860w,
/static/9861b50f81a6a48053283ea22289be12/dd104/quarantine.png 1064w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;你可能会好奇，这个对话框上信息很全呀，系统怎么知道我是用 Chrome 从 nodejs.org 下载的呢？实际上是因为 Chrome 在你下载的这个 pkg 文件上设置了一个叫 com.apple.quarantine 的属性。怎么查看这个属性呢，其实可以用命令行看到：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;xattr -l node-v13.9.0.pkg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c19598bcd7f9ff4b895192278e3913a9/864a6/screenshot-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.418604651162795%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABlElEQVR42qWQ3Y7TMBBGv7hNbMdp082PkzZttjTbwAVXqBeFN0HLsyIReCpobDSjdGklBEhcHM3EiU++GRzwkNpV+Eko8QzgDqXUszGGufZUhRB3TN9/BJAhDcN39a72D03tVunKJ0ni4zj2YRj6KIq8UoqhnpjP5x7ALW6qPwCcUdf16VXX+dJWY5ZlzhjjtNYujmOnlOKezpIkcVJKF4bhHVLKMQiCq/ADC/ePj95a66y1Pssyn+e5r6qKa1mW3hjjtdYMJaeUs9mMK0kn4YWFu93udDweSeC2261fr9csI/FyuWRo5Ktkuvy7kS+/Eu73lIYTUipisVjwLklGe03TlJP+VUgJ+77nkSkdjUhQTz8oisLXde3btuXnfxJ2XUcXL1VVjdbaMc/zkfqyLLkvimJsmobfBUEwArjlMkm/A3iPw+Fwet33vMPNZsMpCOopGfW0RxqZ1iCE+HPCKIreZE3zrWzbwRgzSCmHOI4HrfULdBZFEVchxADgli8AvgL4DOAtAAg8Pcnt+awB/A+KXD8BfIzBTPcxWsIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;screenshot 1&quot;
        title=&quot;&quot;
        src=&quot;/static/c19598bcd7f9ff4b895192278e3913a9/914ae/screenshot-1.png&quot;
        srcset=&quot;/static/c19598bcd7f9ff4b895192278e3913a9/2eb24/screenshot-1.png 215w,
/static/c19598bcd7f9ff4b895192278e3913a9/05ed2/screenshot-1.png 430w,
/static/c19598bcd7f9ff4b895192278e3913a9/914ae/screenshot-1.png 860w,
/static/c19598bcd7f9ff4b895192278e3913a9/46115/screenshot-1.png 1290w,
/static/c19598bcd7f9ff4b895192278e3913a9/6c86f/screenshot-1.png 1720w,
/static/c19598bcd7f9ff4b895192278e3913a9/864a6/screenshot-1.png 2276w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;如果你使用命令 xattr 把属性去掉，系统则不会再弹框了：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;xattr -d com.apple.quarantine your_file&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;像 &lt;code class=&quot;language-text&quot;&gt;com.apple.metadata:kMDItemWhereFroms&lt;/code&gt; 这个属性就告诉了我们这个文件是从哪里来的。像上图中的文件是我从浏览器下载的，就储存了这个文件下载的 URL。&lt;/p&gt;
&lt;p&gt;比如你使用 AirDrop 传输文件，这个属性就会储存你的来源设备：&lt;/p&gt;
&lt;p&gt;像上图，我用 iPhone 传了一张图片，属性里面储存了我的 iPhone 的设备名字。属性显示这个文件是从 sharingd 来的，而 sharingd 就是 AirDrop 的后台进程。所以对于一些文件来说，读取这个拓展属性可以知道这个文件的来源。在一些操作中，这个信息还是挺有用的。&lt;/p&gt;
&lt;p&gt;而这个 kMDItemWhereFroms 属性的格式则是 mac 下比较常见的 plist 格式，下文会讲解如何读取这个格式。&lt;/p&gt;
&lt;p&gt;拓展属性它还可以为文件设置自定义图标，比如说你可以为普通文件和文件夹设置你喜欢的图标，这个图标的内容就储存在拓展属性里面：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/75381309d74dd791282115402ea4fe74/6f464/custom-icon-screenshot.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.37209302325581%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAACBElEQVR42qWRP2/aQByGr0ViIB+jHaoqquQRBhbUj9CtM3Mllnby57A6dKpUdciQIVXdpX8kIkhKiAIYZBufsQ0cNv5LgML5fpUdBRZUUvFKj3539t2jVzqEEEKfXz3Pvn754kgQTnMAkBMEIYXn72ZZOM2Vy+Ucz/Mpd//4DZVK5Yjn+Sy6j1r9+aEn17xG6wr/+v7DkOWeoaqqgS3L6Gt9o97uGR1JMjRNS78rirJZ67qOTdN0JUl6vxG2uz1xvlxDH+v0vHYB1mgM7nQKWrMJE0LAcRwIggDCMATf98HzPIiiCFzXTfYUAABjfIYQyqTCq5YkqgMLdIus3WjOojUwrVZnbzJZdn1ywgCAxZSmcwfrROg4zlZ43e6K2BqDrA3oxAvBn/+BqWHA2dt3MO5IyXlgcQy7whhLG9q2/WUrlHqiPiQgKRq1Ji644S3MFwuwfR8WyyX8KzuFl80bUcYmdOQ+NckUvNkc4jhOWzHG/l/YbEmiZo5A0U1qEgdsL9wr2ivsG0O46cpU0c3Dha2u/NUY2zAYTVZOMIsT1pTGD8wqERJCtq8sydq3YLEC4gZ0Gt5CtFgd1rB60fhUrf+enV82Rl1VI9gcEtfziO/7e/E8bxRFUYQx/ngvzHAc9/TZ8XGe47h8sVjMFwqFdD6UUqmU3HmyaYgQeoQQenwgiQP9BXpQEwMyzkdEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;custom icon screenshot&quot;
        title=&quot;&quot;
        src=&quot;/static/75381309d74dd791282115402ea4fe74/914ae/custom-icon-screenshot.png&quot;
        srcset=&quot;/static/75381309d74dd791282115402ea4fe74/2eb24/custom-icon-screenshot.png 215w,
/static/75381309d74dd791282115402ea4fe74/05ed2/custom-icon-screenshot.png 430w,
/static/75381309d74dd791282115402ea4fe74/914ae/custom-icon-screenshot.png 860w,
/static/75381309d74dd791282115402ea4fe74/46115/custom-icon-screenshot.png 1290w,
/static/75381309d74dd791282115402ea4fe74/6f464/custom-icon-screenshot.png 1464w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;更多关于拓展属性的用法大家可以自行去搜索资料了解，下文讲讲如何在 JS 里面操作拓展属性。&lt;/p&gt;
&lt;h1&gt;使用 JS 操作&lt;/h1&gt;
&lt;p&gt;由于 Node.js 环境下缺乏比较好用的操作 xattr 的库，所以这里推销一下我封装的 &lt;a href=&quot;https://github.com/vincentdchan/node-xattr&quot;&gt;node-xattr&lt;/a&gt;，功能很全很强大。使用 N-API，支持同步异步操作、设置自定义 icon，解析 plist 等实用的功能。同时支持 TypeScript，提高开发效率。&lt;/p&gt;
&lt;h1&gt;安装&lt;/h1&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yarn add node-xattr&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;获取 xattr&lt;/h1&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 同步版本&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getXattrSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./test.txt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//获取 Buffer&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getXattrSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./test.txt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;utf8&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 获取 string&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 异步版本&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;getXattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./test.txt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;buffer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;getXattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./test.txt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;utf8&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;设置 xattr&lt;/h1&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;setXattrSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./test.txt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;value&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setXattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./test.txt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;value&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;设置自定义图标&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/node-xattr&quot;&gt;node-xattr&lt;/a&gt; 提供了简单的 API 可以直接设置自定义图标，不需要去了解系统的储存格式。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; macUtils &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;node-xattr&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

macUtils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCustomIconSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TestFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; iconPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
macUtils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCustomIcon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TestFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; iconPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;解析 plist&lt;/h1&gt;
&lt;p&gt;目前来说 &lt;a href=&quot;https://github.com/vincentdchan/node-xattr&quot;&gt;node-xattr&lt;/a&gt; 提供了简单的两个函数，可以用于解析 kMDItemWhereFroms 这个字段。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getXattrSync&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; macUtils &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;node-xattr&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getXattrSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./test.txt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;com.apple.metadata:kMDItemWhereFroms&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//获取 Buffer&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; macUtils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deserializeArrayOfString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;总结&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/node-xattr&quot;&gt;node-xattr&lt;/a&gt; 这个库提供了操作 xattr 的能力，可以让你完成很多 Node.js 本身做不到的事情。有了这个库，你可以发挥你的想象力，在你的 Node 程序、 Electron 程序里面直接操作拓展属性，实现你的 macOS 原生化功能。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[多线程 SQLite with C++ 踩坑汇总]]></title><link>https://diverse.space/2020/01/using-sqlite-with-multi-threads-cpp</link><guid isPermaLink="false">https://diverse.space/2020/01/using-sqlite-with-multi-threads-cpp</guid><pubDate>Mon, 13 Jan 2020 23:55:00 GMT</pubDate><content:encoded>&lt;p&gt;因为工作的原因使用 SQLite 作为本地数据库，然而 SQLite 和 C++ 配合使用有许许多多的坑，此文用于记录工作过程中使用 SQLite 的坑。&lt;/p&gt;
&lt;h2&gt;Last Insert rowId&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/SQLite-with-C-db4c079d709c452dbee7fc41844766d4#24c415960ef841babe1639f7b826fe10&quot;&gt;Last Insert Rowid&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SQLite 中有一个函数可以获得最最近 insert 的一行的 id。也就是说，当你 insert 新的自增的一行时，你可以用 &lt;code class=&quot;language-text&quot;&gt;sqlite3_last_insert_rowid()&lt;/code&gt; 这个函数拿到这一行的 ID 这是一个取巧的办法，实际上这个函数是记录在 sqlite 实例里面的一个变量。这个变量的读写当然是没问题的，但是它需要和上一次 step 串行执行并上锁。&lt;/p&gt;
&lt;p&gt;假设你有如下代码：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sqlite3_step&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int64_t&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqlite3_last_insert_rowid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;你必须为这两句话上锁，因为如果这两句语句中间有其他线程插入了数据，你从 &lt;code class=&quot;language-text&quot;&gt;sqlite3_last_insert_rowid&lt;/code&gt; 拿到的是其他线程的数据。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sqlite3_step&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 其他线程也执行了 sqlite_step() 导致下面语句 rowid 不对&lt;/span&gt;
std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int64_t&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqlite3_last_insert_rowid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;正确写法&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;lock_guard&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;mutex&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;guard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sqlite3_step&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stmt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int64_t&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqlite3_last_insert_rowid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;附上 SQLite 获取 last_rowid 的源码：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*
** Return the ROWID of the most recent insert
*/&lt;/span&gt;
sqlite_int64 &lt;span class=&quot;token function&quot;&gt;sqlite3_last_insert_rowid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sqlite3 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;db&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; db&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;lastRowid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;事务&lt;/h2&gt;
&lt;p&gt;事务可以说是 SQLite 里面多线程使用最坑的东西了。SQLite 不像其他 C/S 数据库一样一条链接保持一个事务。它就是纯函数调用。&lt;/p&gt;
&lt;h3&gt;乱序提交&lt;/h3&gt;
&lt;p&gt;SQLite 本身可以保证 step 是线程安全的，也就是多个线程同时 step 是没问题的。但是会有乱序提交的问题。比如一个线程开启事务进行提交，另一个线程也同时进行提交，这个线程回滚导致了其他线程的提交也回滚了。&lt;/p&gt;
&lt;p&gt;所以事务提交要上锁，主动上锁的话，一来性能下去了不说，二来忘了上锁就 gg，这个地方很坑。&lt;/p&gt;
&lt;h3&gt;多实例&lt;/h3&gt;
&lt;p&gt;既然 SQLite 是一个实例一个事务，那么每一个事务我开一个 SQLite 实例是不是就可以了呢。答案是依然有坑。&lt;/p&gt;
&lt;p&gt;一个 DEFERRED 的事务（SQLite 事务默认是 DEFFERED）如果执行了一句写的语句，就会锁住整个数据库，直到 COMMIT，在写事务开始到结束之前，其他实例执行任何语句都会返回 SQLITE_BUSY（数据库繁忙）。不止 step 哦，是 prepare 也会哦，是不是很惊喜。如果你这个事务很长，其他实例也用不了数据库了（跟上锁也没啥区别）。&lt;/p&gt;
&lt;p&gt;峰回路转，我终于在 SQLite 的 API 里面找到了一个 &lt;code class=&quot;language-text&quot;&gt;busy_handler&lt;/code&gt;，可以在 busy 的时候执行一个回调函数，让你去做一些操作，比如重试。刚好官网也实现了一个实现了一个重试的 busy handler，你只要设置好 &lt;code class=&quot;language-text&quot;&gt;busy_timeout&lt;/code&gt; 这个参数就可以了，这个默认的 handler 就会贴心地帮你重试。而它的做法就是过一会儿再帮你提交一遍，直到 timeout 为止，显而易见这种做法非常挫，这挫做法导致它比用户自己加锁更慢。&lt;/p&gt;
&lt;p&gt;下面看一下 sqlite 的默认 busy handler 的源码：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*
** This routine implements a busy callback that sleeps and tries
** again until a timeout value is reached.  The timeout value is
** an integer number of milliseconds passed in as the first
** argument.
*/&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqliteDefaultBusyCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;               &lt;span class=&quot;token comment&quot;&gt;/* Database connection */&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; count                &lt;span class=&quot;token comment&quot;&gt;/* Number of times table has been busy */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;SQLITE_OS_WIN &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;HAVE_USLEEP&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; HAVE_USLEEP&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; u8 delays&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; u8 totals&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;78&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;103&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;178&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;228&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;NDELAY&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;delays&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;delays&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
  sqlite3 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sqlite3 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; timeout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; db&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;busyTimeout&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; delay&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prior&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; count&lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; NDELAY &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    delay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; delays&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    prior &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; totals&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    delay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; delays&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;NDELAY&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    prior &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; totals&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;NDELAY&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; delay&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;NDELAY&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; prior &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; delay &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; timeout &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    delay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; timeout &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; prior&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; delay&lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sqlite3OsSleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;db&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;pVfs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; delay&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
  sqlite3 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sqlite3 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; timeout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sqlite3 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;busyTimeout&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; timeout &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sqlite3OsSleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;db&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;pVfs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这是一段退避重试的代码。可知重试时间是毫秒级的。这个延时速度真的还不如自己上锁呢……&lt;/p&gt;
&lt;h3&gt;正确写法&lt;/h3&gt;
&lt;p&gt;所以这里建议在 C++ 里面使用事务，还是使用单实例 + 使用的时候上锁来解决。另外要保证只有一个线程使用了事务，而且这个线程使用事务的时候，其他线程不能提交。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;SQLite 是一个很古老的数据库 lib。在业界也被广为使用。但是它的一些 api 设计真的有点坑。鉴于 SQLite 古老的历史，也许这些 api 不易修改得对多线程友好。所以在使用的时候还是要多多注意。写到这里，我想到了 sqlite 官方 FAQ 的一句话：&lt;a href=&quot;https://www.sqlite.org/faq.html&quot;&gt;https://www.sqlite.org/faq.html&lt;/a&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Threads are evil. Avoid them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同时还贴出了一篇论文：&lt;a href=&quot;https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf&quot;&gt;https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;或许 SQLite 的作者真的不喜欢多线程，才设计成这个样子吧。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[使用 N-API 和 CMake 为 Node.js 添加 C++ 拓展]]></title><link>https://diverse.space/2019/10/using-napi-and-cmake-to-write-a-nodejs-extension</link><guid isPermaLink="false">https://diverse.space/2019/10/using-napi-and-cmake-to-write-a-nodejs-extension</guid><pubDate>Sun, 13 Oct 2019 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;以前为 Node.js 编写拓展的时候，使用的是 Node.js 的 C++ addon API，直接使用
v8 提供的 api 和 Node.js 打交道。使用 C++ Addon 的 API 有个缺点就是
ABI 层面不兼容，升级 Node.js 之后 extension 要重新编译。&lt;/p&gt;
&lt;p&gt;现在 Node.js 提供了 N-API。这个 API 是 C 层面的支持（也有 C++ 绑定）。同时，使用 N-API
写出来的拓展是 ABI 兼容的。&lt;/p&gt;
&lt;p&gt;而使用 N-API 编写 Node.js 扩展不一定用 node-gyp 进行编译，也可以使用 cmake，
这对一些使用 CMake 的 C++ 项目可以说非常友好了。&lt;/p&gt;
&lt;h1&gt;使用方法&lt;/h1&gt;
&lt;p&gt;按照&lt;a href=&quot;https://github.com/nodejs/node-addon-api/blob/master/doc/cmake-js.md&quot;&gt;官方教程&lt;/a&gt;：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ yarn add node-addon-api cmake-js bindings&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后在 &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; 里面加上一句：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;install&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cmake-js compile&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后愉快的 &lt;code class=&quot;language-text&quot;&gt;yarn install&lt;/code&gt; 就可以使用了。&lt;/p&gt;
&lt;p&gt;贴一下我的 CMakeList.txt 配置：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;project (zparser)
cmake_minimum_required(VERSION 3.15)

set(CMAKE_CXX_STANDARD 17)

include_directories(${CMAKE_JS_INC})
file(GLOB SOURCE_FILES &quot;src/*.cc&quot; &quot;src/*.h&quot;)
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX &quot;&quot; SUFFIX &quot;.node&quot;)

set(JSON_BuildTests OFF CACHE INTERNAL &quot;&quot;)

# Include N-API wrappers
execute_process(COMMAND node -p &quot;require(&apos;node-addon-api&apos;).include&quot;
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE NODE_ADDON_API_DIR
        )
string(REPLACE &quot;\n&quot; &quot;&quot; NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
string(REPLACE &quot;\&quot;&quot; &quot;&quot; NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以说非常简单易用了。&lt;/p&gt;
&lt;h1&gt;Api 使用&lt;/h1&gt;
&lt;p&gt;C++ &lt;a href=&quot;https://github.com/nodejs/node-addon-api/blob/master/doc/value.md&quot;&gt;官方文档&lt;/a&gt; 的介绍还是比较简陋的，如果不了解 C-API 用起来有些地方会比较困惑。
但是如果用过之前用过 v8 的 addon-api 的话，还是比较容易理解的。&lt;/p&gt;
&lt;p&gt;大部分的 Object 和 Array 等操作都有，你甚至可以用 &lt;a href=&quot;https://github.com/nodejs/node-addon-api/blob/master/doc/object_wrap.md&quot;&gt;ObjectWrap&lt;/a&gt; 去定义自己
的 Class:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Example&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token base-clause&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ObjectWrap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Example&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Object &lt;span class=&quot;token function&quot;&gt;Init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Env env&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Object exports&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;CallbackInfo &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;FunctionReference constructor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; _value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Value &lt;span class=&quot;token function&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;CallbackInfo &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Value &lt;span class=&quot;token function&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Napi&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;CallbackInfo &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;我目前没有发现的就是如何定义一个 Undefined 的 Value。所以用到 Undefined
的时候，我就不得不用 C-API 去定义一个 &lt;code class=&quot;language-text&quot;&gt;napi_value&lt;/code&gt; 来用 undefined。好在
C++ 的 API 可以和 C 混用：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;napi_value&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;napi_get_undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; napi_ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在 js 里面的使用也很简单，不过要配合 bindings 库使用：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const example = require(&apos;bindings&apos;)(&apos;example&apos;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[QuickJS 源码解读（一）：虚拟机的实现]]></title><link>https://diverse.space/2019/08/understanding-source-code-of-quickjs-1</link><guid isPermaLink="false">https://diverse.space/2019/08/understanding-source-code-of-quickjs-1</guid><pubDate>Mon, 26 Aug 2019 23:55:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;Quick JS 是 Fabrice Bellard 今年发布的一款 JavaScript 引擎，具有以下特性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;轻量而且易于嵌入：只需几个C文件，没有外部依赖，一个x86下的简单的“hello world”程序只要180 KiB。&lt;/li&gt;
&lt;li&gt;具有极低启动时间的快速解释器： 在一台单核的台式PC上，大约在100秒内运行ECMAScript 测试套件1 56000次。运行时实例的完整生命周期在不到300微秒的时间内完成。&lt;/li&gt;
&lt;li&gt;几乎完整实现ES2019支持，包括： 模块，异步生成器和和完整Annex B支持 (传统的Web兼容性)。&lt;/li&gt;
&lt;li&gt;通过100％的ECMAScript Test Suite测试。&lt;/li&gt;
&lt;li&gt;可以将Javascript源编译为没有外部依赖的可执行文件。&lt;/li&gt;
&lt;li&gt;使用引用计数（以减少内存使用并具有确定性行为）的垃圾收集与循环删除。&lt;/li&gt;
&lt;li&gt;数学扩展：BigInt, BigFloat, 运算符重载, bigint模式, math模式.&lt;/li&gt;
&lt;li&gt;在Javascript中实现的具有上下文着色和完成的命令行解释器。&lt;/li&gt;
&lt;li&gt;采用C包装库构建的内置标准库。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文主要记录我阅读 QuickJS 源码时记录的一些心得，主要用于学习用途。&lt;/p&gt;
&lt;h2&gt;编译&lt;/h2&gt;
&lt;p&gt;源码下载地址：&lt;a href=&quot;https://bellard.org/quickjs/&quot;&gt;https://bellard.org/quickjs/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;编译 qjs 引擎本身&lt;/h3&gt;
&lt;p&gt;qjs 本身用于直接执行 JavaScript 代码：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ make qjs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;编译 qjsc 编译器&lt;/h3&gt;
&lt;p&gt;qjsc 编译器可以把 JavaScript 代码编译成 QuickJS 虚拟机的字节码（可直接通过 QuickJs 虚拟机执行）。 也可以把 JavaScript 代码编译成一个 C 语言的 .c 文件，这个文件包含了字节码：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e55cbce2ab1591c58a3346e75b42ca95/c1328/qjs-bytecodes.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAADK0lEQVR42qWTTYsjRRjHI+JZWFghnlwlmU66uqs61d313i9JZ94y2VlnGBUExctevAwse8llLnPV+7AMePawAYV8AL+MZnA+wYb+S3eyOr7cbPjTTz1P1a/+9RTVueHP3/v8RE3CTM4pTWceSWeEqhmlfEYIn3FVtFKuakW4mnk0nVG+neNRPvOjeM5iWV1cXLzbefHi6oPq4OQ3Kgt4VNWpmYBxjY+jDJ+MMhAag4wUvIDjowFDj8QY0hRP+gRPAoFhJOum1htGv3/36tWjzmJx3c2q0zuWjrHHdJ2aKUaihBdK9EMJL0jgBSn6JEbP59gju7HP0d/G9R6JMaDi/vb29nELtOXJmqUlPJLUJJ4gEAfwmUAQWfiRAYksyMgi4BYkMvCZacdNTEa29plGYg7uV6vVFmiK2ZomebN7PQwlOCsRsRwkkQh2Go4E+n7cqnH5QHWT43pyv1r98rhz+fzLbnny9TqZfoP+MKp9qjFiBUaxBhcKUaQwYhKcKjAqwWO5PfZOb4GRKO9/bBxeLhbdYv9szdW06VM9CARIYBBGAuFIgAYSLJRgkUTjfo+k6O2c9rZu62YD/2EP3Xi+jkTZTK4DpjA2BUrnUDqLXFqMzTZ22sFIAyU0ZKpglWlAdd9/ALy8XHTHh+drribwAlETJqGlbBdpqaHk9m+UhlMaRv6Vz/R/AB9eyiCUtU8FRKpaYCORbhcLodq4he3yWuh/A6+vr7syO1qHcYYBVXUQSVTOYmwtCmuQG4OJs61Ka1FlFpPMYuwsDguHMJJ17+/A77uJ2V8H3P3pUAqFJFFIEwUtdm6bowrVumrcNXWrGofpP3q4aHp49mvc9DCUbwgVm1zrTa7NJjNmU1q7KcxWTutNac0mN2bjtGljwsSb3jCGH8m7Fvjty5cf5tNnd+0tB6IOuUVRTGFtCaVzWJPDmBzOljCmgHMlTFsr4GwBluS137woprcOr66uHo0P5rfV0bOfiurk9fz8i+XTi6+Wk8NPl8JVS5lNW1XH58ty+nRZTOfLopovXXm4zKrZcv/47PXx6Wc/V0enP9zc3LzfAfBOo87//N5y/gDh8OTBLnkxPwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;QuickJS 把 JavaScript 编译成字节码&quot;
        title=&quot;&quot;
        src=&quot;/static/e55cbce2ab1591c58a3346e75b42ca95/914ae/qjs-bytecodes.png&quot;
        srcset=&quot;/static/e55cbce2ab1591c58a3346e75b42ca95/2eb24/qjs-bytecodes.png 215w,
/static/e55cbce2ab1591c58a3346e75b42ca95/05ed2/qjs-bytecodes.png 430w,
/static/e55cbce2ab1591c58a3346e75b42ca95/914ae/qjs-bytecodes.png 860w,
/static/e55cbce2ab1591c58a3346e75b42ca95/c1328/qjs-bytecodes.png 866w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;但是编译 qjsc 编译器首先需要编译 libquickjs 库：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ make libquickjs.a
$ make qjsc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;使用 qjsc 编译器把 &lt;code class=&quot;language-text&quot;&gt;my_test.js&lt;/code&gt; 文件编译成 &lt;code class=&quot;language-text&quot;&gt;my_test.c&lt;/code&gt; 文件：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ./qjsc -e -o my_test.c my_test.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;使用 XCode 进行 Debug&lt;/h3&gt;
&lt;p&gt;如果你想很清楚地了解整个虚拟机的执行过程，可能你需要 XCode 来进行单步调试：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d5d570bd15780631e08356203da540ad/c1bea/xcode-debug.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.3953488372093%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACP0lEQVR42oWSy07bQBiF8zo8SfseZFECz4a8qcSqi+5YIIECsRNsTEJ8m5tv8W08MweNgagtVftLR57xjD6df/4ze3Rvv6zX91cPD+7ler123PXGcd17Z7NZO77vO0EYOLvdzgnDcFIQBM5qtXKWy6Xjuq5zfX3t3N4tLx88/+r23vs6C3bJ+UEa0GpQou5hYKDUgGHoYQym/b+q73uUVa3tuiiKi9km3J7FtMA+FUOcMaX1qKqyVoOUCsB/1TSNopTKpmmQZdli9uNuv/i5Yvh+k6obXwDQaA4Non0EzgWatkU/DJMTKSXw7vjD+eFwACFEd11ngeezXZwtgu0eqzBRmahhjEbbtoheIlR1/daXMTDv+rM+AfdxtmB5BcJzRfMCh65FlCTIqxLSaAxavUmNv6wVOjWAdwKs4BakrYkJGFOxaAaNlOWKFxXUqJCmGZI0xSAltPn7WKxbZRTqukaSJNo6PQIPvULGC5VQhoRm4JSBUgbOcwjOkYscJMvAGENVVaCUoipLyEGiLMvfW34DjiC8VC8shfvsoRAF5CiPscjzHJ7nwfd9Gw0EQTDBbVmHn4B1Jy1w3JLEuKFn0jg1fd/bTk3Xdebp6ckkSWIYY6ZtW7Pdbo0QYjqr68oQQtTxDakozkcbyrpXaSnwnD5PsfmY6DiOEEJM07Sy0SGEwObOVtf31rXWWtt7F7Mwjk8ySs8Swk69IJoTks7zPJ9UVdWcEDIpiqI55/z4334J5/PH3XR2GsfxtziOT14BUrtxgPYkJ+4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;使用 XCode 进行 Debug&quot;
        title=&quot;&quot;
        src=&quot;/static/d5d570bd15780631e08356203da540ad/914ae/xcode-debug.png&quot;
        srcset=&quot;/static/d5d570bd15780631e08356203da540ad/2eb24/xcode-debug.png 215w,
/static/d5d570bd15780631e08356203da540ad/05ed2/xcode-debug.png 430w,
/static/d5d570bd15780631e08356203da540ad/914ae/xcode-debug.png 860w,
/static/d5d570bd15780631e08356203da540ad/46115/xcode-debug.png 1290w,
/static/d5d570bd15780631e08356203da540ad/c1bea/xcode-debug.png 1388w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;为了能在 XCode 下进行调试，你可能需要先在 Makefile 里面把优化给去掉。
打开 Makefile， 在里面找到这一句：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;CFLAGS_OPT=$(CFLAGS) -O2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;改成：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;CFLAGS_OPT=$(CFLAGS) -O0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;接下来就好办了，我们照着 &lt;a href=&quot;https://blog.csdn.net/u011577874/article/details/73000207&quot;&gt;这篇教程&lt;/a&gt; 走就可以在
XCode 里面 debug 了。&lt;/p&gt;
&lt;h2&gt;OPCode&lt;/h2&gt;
&lt;p&gt;QuickJs 的虚拟机使用栈式虚拟机。对于什么式栈虚拟机推荐阅读 &lt;a href=&quot;https://www.iteye.com/blog/rednaxelafx-492667&quot;&gt;这篇文章&lt;/a&gt; 了解。&lt;/p&gt;
&lt;p&gt;QuickJS 的 OPCode 十分简洁和紧凑。所有 OP 码的定义都放在 &lt;code class=&quot;language-text&quot;&gt;quickjs-opcode.h&lt;/code&gt; 文件里面：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/75f622fd7f08e5fa7bc3eaa3a6a2913f/e996b/quickjs-opcode.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.53488372093024%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAADE0lEQVR42qVTu3LbRhRlHv4BVw7VZCYSiddiASywABYPgg+QlEgLQ48aNynVuFFhNmzQsFHB2pPJ5A+smeSrEib4gwQ4mV1KtuOkS3Hm3r0Xe/becy96P9zePnu9yhY0jza6zqoBOUF3eOUwUfFsWjFeKCuyaUUcXumOOFnCK8MNry0abSgT89vb22e9t2/rF9Ny9YcZFnDDoouyGbgYQ3cTfOdlIG4I04kwsDycmz4MJ8S5ZuNbN4Nmc+jEb3XiY0jYb3d3d9/0tvV9P5tdN6aXg/h5x8UUYVKCeAnOzQAD01e4MBjOdQ9DK8CF4eHC4o851g5MBp2Gx91uf9ar6/t+nC8bSaDZURe6JRw2ge7E0KwAAytQJP+Gj+HpwVaeLTc57vaHs972zZt+fvV9Q+MVdMI7h2Yw3QxDEqoL/0n0z1grK3WC4rg/vDvrbbfb/mh+01A+gW5Hne+M4QYldC+VD0CnEXQ7/ADt8azJ3CnWSktY9khY1/18dt3YLFOEKVvC8aaK0HJjmK6Q7ShNDSeBwXIQT6iczVS8lTnLS4673e6kYV5Wje3nsppOd1NQlsJ0Qph2ANsTMKSeHytSrT4NS2ooremK426vhlL3i/mmof5IVSgvW6oqAZOGMGwJrlZkSLgi/lxDNRTvaSjbuj8qq4bKCh3RmfFCfRjI9cnmcFkM3xfg/gwmjTEkgSL9BJ9NeVv3s8nLk4aO6IhYwgkyRPkCYnwFz48RBAJhWIK4ySetfxiWIjSfCOv7+74vykYKr1PRWWIBg0Zw/Aw8nsD1BQIuECQTFR9aH6t79FvpWyw/7vbvTkMR4/XvxEuh0fgvM5q3GgnaMJm2gZi0LhMt50nLxbQ1adRqRK2JgqbA/5SE1M9/3T+1PLu8aWhQwHDTjhUVHF4gSEok4xVYmINHI6WnjFteKncOlpsq3/KS1qAC1EuPh9Ovd3g+u6x+mpTrnxerzfv15vXDcn3zMJqulM2K5UM6mitbXm4eJvNrhaJcK4zLl+/nV69+ma9e/VgfDs97AL4A8CWAr/8nvpJcfwPAHgn+8jDYUwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;QuickJS 的 OP 码&quot;
        title=&quot;&quot;
        src=&quot;/static/75f622fd7f08e5fa7bc3eaa3a6a2913f/914ae/quickjs-opcode.png&quot;
        srcset=&quot;/static/75f622fd7f08e5fa7bc3eaa3a6a2913f/2eb24/quickjs-opcode.png 215w,
/static/75f622fd7f08e5fa7bc3eaa3a6a2913f/05ed2/quickjs-opcode.png 430w,
/static/75f622fd7f08e5fa7bc3eaa3a6a2913f/914ae/quickjs-opcode.png 860w,
/static/75f622fd7f08e5fa7bc3eaa3a6a2913f/e996b/quickjs-opcode.png 1050w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;从上图可以知道 OPCode 定义分为这几个部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;id: OPCode 的名字&lt;/li&gt;
&lt;li&gt;size: OPCode 的字节大小。比如说 &lt;code class=&quot;language-text&quot;&gt;push_i32&lt;/code&gt; 指令的 size 是 5. 那么第一个字节用来存 OPCode 本身，&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;后面四个字节用来存 32 为的整型，也就是四个字节。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;n_pop: 从栈上面弹出的元素的数量。和下面的 n_push 一起用户统计函数需要分配的栈的大小。&lt;/li&gt;
&lt;li&gt;n_push: 从栈上面插入元素的数量。&lt;/li&gt;
&lt;li&gt;f: 字节码的格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Runtime&lt;/h2&gt;
&lt;p&gt;在虚拟机内部，运算的最基本单位是 JSValue，一个 JSValue 可以用以下 Tag 来标示：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/* all tags with a reference count are negative */&lt;/span&gt;
    JS_TAG_FIRST       &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* first negative tag */&lt;/span&gt;
    JS_TAG_BIG_INT     &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_BIG_FLOAT   &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_SYMBOL      &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_STRING      &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_SHAPE       &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* used internally during GC */&lt;/span&gt;
    JS_TAG_ASYNC_FUNCTION &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* used internally during GC */&lt;/span&gt;
    JS_TAG_VAR_REF     &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* used internally during GC */&lt;/span&gt;
    JS_TAG_MODULE      &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* used internally */&lt;/span&gt;
    JS_TAG_FUNCTION_BYTECODE &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* used internally */&lt;/span&gt;
    JS_TAG_OBJECT      &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    JS_TAG_INT         &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_BOOL        &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_NULL        &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_UNDEFINED   &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_UNINITIALIZED &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_CATCH_OFFSET &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_EXCEPTION   &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    JS_TAG_FLOAT64     &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/* any larger tag is FLOAT64 if JS_NAN_BOXING */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;那么一个 JSValue 的表示是这样的：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;union&lt;/span&gt; JSValueUnion &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;int32_t&lt;/span&gt; int32&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; float64&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; JSValueUnion&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    JSValueUnion u&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;int64_t&lt;/span&gt; tag&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; JSValue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;从这个结构体可以看出一个 JSValue 占用 16 字节的内存。tag 用来表示这个 Value 的类型。而 &lt;code class=&quot;language-text&quot;&gt;JSValueUnion&lt;/code&gt; 则是
用来储存真正的值。这个值可以是 int/float 或者一个指针指向一个真正的对象。&lt;/p&gt;
&lt;p&gt;真正的对象在 QuickJS 里面则是使用引用计数来进行内存管理，所以都有一个引用计数器：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;ypedef &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSRefCountHeader&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; ref_count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; JSRefCountHeader&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;命令执行&lt;/h2&gt;
&lt;p&gt;知道了上述背景之后，我们就可以看看 QuickJS 虚拟机具体的执行过程了。需要知道的是，一个栈虚拟机，需要两个很重要的
指针，分别是 pc 和 sp。&lt;/p&gt;
&lt;p&gt;pc 指的是程序计数器。指向正在读取的 OPCode，读取之后 pc 会 ++，指向下一个字节。&lt;/p&gt;
&lt;p&gt;sp 是栈指针。指向栈机器栈顶的下一个元素，用来进行栈相关的操作。&lt;/p&gt;
&lt;h3&gt;入栈&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5a01f65ed89f2ad910820f02f6125e98/e996b/qjs-add.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.53488372093024%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAADZ0lEQVR42qWSzW7bRhSF1Z+8QFatvGoTieLPcMghZ0gOf0RREi3HthgZqYugAQq0gBbNxgGijTfceOOFH6AoCvQBQqBF3qHv0Cdo1eoNWuoUQ7spgmbXxcVgBhff3HvO6X27Xt87P04PaRauCBHVkHgVcURFPFnpjqgckVYinVZpmleeyCudiEpXPZ6siONVBvGXI8pX1JPler2+13v5zcuPpvPjP5wwh+7IfZTOQf0EDwyGocVhuSEGOsUnfAbDjWBSDoMGeKDZ+NSWMN2w1QnH0GS/XVxcfNzbbOp+OlvuHFFAc+Q+SGagXoyBApoeNJNhYLh4aAlo3d2DZnkY6C4emr7qaVWfQfj26vLqoLep636cH+8ckUOn0d6kEiM7hGYHGLEMWljeAg0Xww5wW5rl333gt+pzJsvt6593/wIpz8HldE+9DCM7AskfI15+jXn5BFn+OQTLYNGgk0F7u9qh4YElR9vXv+Cgt3n+VT9/9GzHZ1+CuNHeYimGugNTFMgWT3GSzDHPTuG4MUb/hXVAzfRh+dn26uaHg95ms+mPy9WOhXOYTrw3HdmtYvgTiMkK82iGlKcwaIih+W6gendFvr26ujm4M+V054octj/eWyzB0GDQgxmSoy9QxSXm0RS2K++0e8eEFofphNvLzpRN3S8WZzsvmsJiyd5041sgLyDLcyziGfIgB1H6GZ0JnY7/1MDi7cDkMN4A6+u+HD/aUX8My03erKx7Y/D0GLNgAhlMYCqzHAmdhjCJgEUCGESA2EFr2QEsV24vLy8PenV93Q/Txc72M1CedyurKVRsnGgBmZ7CZhlMMYXu5TBZAoeG8BSEBOA0bH1XgrB4+6IDXl/3/bjcKZDtZ93KasKRm8CPj5AHMwR8AiqmGLExTFdipHpYBqrksXirAv6WhpPD1e8sLGC68V8Gle3A8FS2WmKHLfOLVndlO6Cy1ahsDRq2Oo1a3Ylb4oTtiPA/lVmWE/3aAZ93sak6U9SEKuDd+qIAy04Qjpfw+Ri+yEG8FMTLulM5q+IyIkGrdLdYsn2hgPXNzf18fvJ9US5/LA6Xr5ZnT5vVZ8+a6aJqZDpvkqRo0rxsFidPmqJcNkV50kwXyyYtFs24OGpmi8evjqrzn8rjs+/qur7fA/AegPcBfPg/6wPF+hvSew0YyqdHUgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;qjs add&quot;
        title=&quot;&quot;
        src=&quot;/static/5a01f65ed89f2ad910820f02f6125e98/914ae/qjs-add.png&quot;
        srcset=&quot;/static/5a01f65ed89f2ad910820f02f6125e98/2eb24/qjs-add.png 215w,
/static/5a01f65ed89f2ad910820f02f6125e98/05ed2/qjs-add.png 430w,
/static/5a01f65ed89f2ad910820f02f6125e98/914ae/qjs-add.png 860w,
/static/5a01f65ed89f2ad910820f02f6125e98/e996b/qjs-add.png 1050w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;入栈操作就是把读入的值变成一个 JSInt32，然后赋值到 sp 指针指向的内存，然后 &lt;code class=&quot;language-text&quot;&gt;sp++&lt;/code&gt;。就完成了一个入栈操作。
我们可以看到 QuickJS 的字节码实际是十分冗余的，具体为什么这么设计我不太清楚。估计是 0-7 之间的数可以用一个
字节搞定，节省空间，但是我个人感觉也太省了，对实际运行应该帮助不大。&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;OP_push_i8&lt;/code&gt; 是一个两字节的 OP 码，第二个字节就是一个 8 位的整数，同理 &lt;code class=&quot;language-text&quot;&gt;OP_push_i16&lt;/code&gt; 就是一个三个字节
的指令，入栈一个 16 位的整数。&lt;/p&gt;
&lt;h3&gt;加法&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/26fc04630a6d21d4f24798f129c58c7c/68638/qjs-add-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.7906976744186%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACsElEQVR42l1STW/bRhDlqX+g/6C1SXGXy11yP0guSVGiVDtuIvfgFjDQa4CefQjghgkIJIAOhS8F+g+bawQ45S2SXsGlGsQ9PMzM25m3b4f0lqvlq5nQb0Oavg5Y0nE97xJddQGTXch1R5OsY1nbcWm7kMku4HriTzGg6WsS67chU6+0Xn3rLS5+GiJZgavqGDANrhtwVcNnBoRnCCINXzaIUgsSG5xxi1liEYn8dC6PdMxFNlxebqiXletHruYQav7ZJ3JP1WJvTLsnzOy/C9P9OZF7n0zxnKp94Go51WNO1eezMAWTzeNvv/9JvKK9GbhZIUrsMYgUiLBIVQWtCiQiRxQb+JFyTp1bF80Jjjv6RIKZ5fDyzV/Us+ufT4LlcRpQiFmGMm9hihZhUiNIKoRpjSjOHGZMI6DKifuRdnNEFMPm5pZ6trkamKy/CM7YdDuXFXS5BM9aUPMDSH6BNK0RJyVCtUQgG8RpDcrM0acKNLHDze1L6lXti4Gl9XjDV4IaUVwgMWtIewEh56A8R8AMfOfs9OSp182FPBs2mxvqVcsXQ6yaJw5nLEMuGySyQVg8Ay2vwPI1WFKCCYtQFAjiAuQrQcLzYbO5pV65eD6w0YGwTxwKPv4aJUK5cE+kpkVsnyG2V6Dlj0hNCz4K/19QFutP0bgLYfdBpA8jCM8PUjaHGTOH8zA9+CQ9BFQe/EgffKpcz4wqVweRHufGlfxzff0r8ezoUM1BRHk8J8rtZnSZqgW4XoNlK3BZg7Bs2t8X6P/gPkrIsslh1T7/O80Xj1l9+VEVy10s7S4x8x0X+Y4nUz7yQtcuF6p2eZzaHZfVGD8KXT+acvXh+vqXwLu7uzvr+97fPjz42+2Df9/3fr/dunh/f+/Qn7gnGLmxp+/9N+/eBe/f//F93/ff/AvU5IuXMajZcAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;qjs add 2&quot;
        title=&quot;&quot;
        src=&quot;/static/26fc04630a6d21d4f24798f129c58c7c/914ae/qjs-add-2.png&quot;
        srcset=&quot;/static/26fc04630a6d21d4f24798f129c58c7c/2eb24/qjs-add-2.png 215w,
/static/26fc04630a6d21d4f24798f129c58c7c/05ed2/qjs-add-2.png 430w,
/static/26fc04630a6d21d4f24798f129c58c7c/914ae/qjs-add-2.png 860w,
/static/26fc04630a6d21d4f24798f129c58c7c/46115/qjs-add-2.png 1290w,
/static/26fc04630a6d21d4f24798f129c58c7c/68638/qjs-add-2.png 1510w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;栈虚拟机上的加法就是从栈弹出两个元素，相加，然后入栈。这里首先从栈顶取出 op1 和 op2。然后判断是不是 int。
这里的 int 类型是 32 位的。所以它先把他们切到 64 位进行相加。加完再切回 32 位的 int，看看数据是不是一样。
若不一样，则说明加法产生了溢出，这个时候就丢到 add_slow 进行处理。&lt;/p&gt;
&lt;p&gt;若两个 op 都是 float64 则进行浮点数的加法。&lt;/p&gt;
&lt;h3&gt;If 分支跳转&lt;/h3&gt;
&lt;p&gt;If 分支跳转是程序里面很重要的一步，实现了分支跳转，for 和 if 语句都可以实现了。
QuickJs 有 goto 指令，比较简单，这里就不说了，这里介绍一下 &lt;code class=&quot;language-text&quot;&gt;OP_if_true&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/83e893320956c5ea56965e06303824f9/68638/qjs-if-true.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.7906976744186%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACoUlEQVR42n1Ry2rcMBT1qj/QT2jGHkvWy5JsjR8z40nStEwmXUwLgUJXga6zCKRxiiGBWZRsCv3DdhtDWu8ynlukDCGF0MXh6OpIV+foetVudeZT/TUg6iJkug6wrInIaqGLOiCyJjKrmSzqUI5rq2Mi6wGRdSCyGjNdhzy5QFR/DYk603r3pTd5/a4bihyYKjZYGNjBEkgyBWkq8KMEEDcwpAkM4jFgbgDzFHYiDYEsIXK12dg9xNPu4GCBvbTYv4viHKgs7ynN14jn60hVax+ptY/VeoDk2neI3drV2PJjfb8TxkDk5O7zl+/IG82WnTB7wFS5GUYJkGQGxuxCEucQkgQC6+b/2PjIpqq6k8sf2Mv23ndEzwDzbONHGqyIaAKJyoGy0cMlrCCIFDh9i8GThpYRH3WL5SfsZdO3XSRyu+GEgCRAqAEZ58DjEQxpCgG1/2gAkRSQ01OHbYKNjxVgkXXL4xPsFbPDjsjSObQNh0RDSA0ksgSRTgEXc0D5HKTZA85HgEkKkhlQfATIPrZ1GLK0WyyW2Cuqw47KMWCRPzpE1ABnOQx5AYGcQChyIMw4R4Nt3MH2Cx4jM9MtFsfYy6vDLhLFk8gaMDUgeAYRTQAR7SLbZkOb4Jmh/NNQmr3fUVxah+sg0v2Q6D4kuqe87Jke9ywZ96GqehKXPRWjHtGkt+eewN4DzMyfo6OPyMum8y6SY0As2wyQcpFDXgARE6C6gkhPncOA5RAyAz7WYIfwwA5uKCFJHxwWs/lPkU7u0nL/VpmqZapo5fhNi5huQ6JaIkwr9Ljlqmi5LluuSsc0zlomC8u3XJd3Sb776+joQ+Cdnp7uNE3jr25u/NXqxj9vGr85P3fs1s3Kb1bPwGkPZy6vroLr62+vmqZ58Rduyon3TzffHwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;qjs if true&quot;
        title=&quot;&quot;
        src=&quot;/static/83e893320956c5ea56965e06303824f9/914ae/qjs-if-true.png&quot;
        srcset=&quot;/static/83e893320956c5ea56965e06303824f9/2eb24/qjs-if-true.png 215w,
/static/83e893320956c5ea56965e06303824f9/05ed2/qjs-if-true.png 430w,
/static/83e893320956c5ea56965e06303824f9/914ae/qjs-if-true.png 860w,
/static/83e893320956c5ea56965e06303824f9/46115/qjs-if-true.png 1290w,
/static/83e893320956c5ea56965e06303824f9/68638/qjs-if-true.png 1510w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If 分支跳转主要是看栈顶元素是不是为 true。若为 true，则把 pc 加等于相应的 offset。
这个 offset 在 QuickJS 里面是 32 位的，这意味着 &lt;code class=&quot;language-text&quot;&gt;OP_if_true&lt;/code&gt; 是一个 5 个字节的
OPCode。这个 offset 在编译的过程中就可以被算出来。&lt;/p&gt;
&lt;p&gt;图中有一个 tag 是否小于等于 &lt;code class=&quot;language-text&quot;&gt;JS_TAG_UNDEFINED&lt;/code&gt; 的判断。这个判断根据上面的 tag 的定义。
我们知道只有这几个类型是不符合这个判断的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JS_TAG_UNINITIALIZED = 4&lt;/li&gt;
&lt;li&gt;JS_TAG_CATCH_OFFSET = 5&lt;/li&gt;
&lt;li&gt;JS_TAG_EXCEPTION   = 6&lt;/li&gt;
&lt;li&gt;JS_TAG_FLOAT64     = 7&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上几个类型需要用 &lt;code class=&quot;language-text&quot;&gt;JS_ToBoolFree&lt;/code&gt;，进行判断，其他类型则可以从 JSValue 读出真正的 bool 值。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;QuickJS 源码实现非常简单易懂，是一个非常适合学习的 JS 引擎。有一点谈不上好坏的就是
像 Bellard 这样的 hacker 出来的代码非常的骚气。比如说通篇的 goto，不过幸好不是很难懂，
有些逻辑确实 goto 写起来比较爽这一点，可以理解。另一方面不太好的就是大部分代码都写尽了一个 .c 文件
里面了，读函数调用跳来跳去比较麻烦。&lt;/p&gt;
&lt;p&gt;总的来说是比较适合阅读的源码了。&lt;/p&gt;
&lt;h2&gt;相关阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2020/12/understanding-source-code-of-quickjs-2&quot;&gt;QuickJS 源码解读（二）：基础设施和标准库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;QuickJS 官方文档：&lt;a href=&quot;https://bellard.org/quickjs/quickjs.pdf&quot;&gt;https://bellard.org/quickjs/quickjs.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;栈式虚拟机：&lt;a href=&quot;https://www.iteye.com/blog/rednaxelafx-492667&quot;&gt;https://www.iteye.com/blog/rednaxelafx-492667&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;X86 架构：&lt;a href=&quot;https://blog.csdn.net/ajigegege/article/details/17055779&quot;&gt;https://blog.csdn.net/ajigegege/article/details/17055779&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;使用 Xcode 进行 Debug：&lt;a href=&quot;https://blog.csdn.net/u011577874/article/details/73000207&quot;&gt;https://blog.csdn.net/u011577874/article/details/73000207&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[转换网易 ncm 格式到 mp3]]></title><link>https://diverse.space/2019/05/convert-ncm-to-mp3-on-mac</link><guid isPermaLink="false">https://diverse.space/2019/05/convert-ncm-to-mp3-on-mac</guid><pubDate>Tue, 07 May 2019 17:23:00 GMT</pubDate><content:encoded>&lt;p&gt;不知道什么时候开始，网易下载的歌曲都不用以前的 mp3 格式，而采用了自家的 ncm，说真的，比较恶心，平时自己想要剪个视频用个配乐都很麻烦，搜了很久很多工具，最后搜到了这个：&lt;a href=&quot;https://github.com/anonymous5l/ncmdump&quot;&gt;ncmdump&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;首页提供了 Windows 和 Linux 的使用方法，mac 下面使用也很简单，先用 brew 安装 taglib，然后 make 即可：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/anonymous5l/ncmdump
$ brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; taglib
$ &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;实测可以转换成功：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/39d4dbc9482ac806db796cf198e30f78/769f8/screenshot.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.30232558139535%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAACuklEQVR42nVT21KbUBTlD0zHF9OcIwkCMUCAyDWQCzHaaKed/lRnHPtQc7PGT7El4RaIab+iRv+js0lwYmsf1pzNcPZirbU3xPXXz6uryeBpMrl9nExun77d3DwNBoPH8Xi8Go3Gq8svXx4vLi5XF5eXaT0aX6+Gw9FqOB6v+v3BA+Cq338YjkarwWD4myjzom9ZdqybZqwZ9fmRosV4v+SXKMYrUfSsWKJnNMN6FY73oc4XsJvL7X4H7AB23twBcrndu53c7h1RU9QIyGy7mWiaMbesRmzZjVjV9DlAkmuhKMkhRTMekBUK2MW4OCXJ0hROTK6BoMbFKdFyOove+fufHz5++nVy2ltmOO2dLU9758vO8dl9p9Nd6EY9JVdULeL4qs+yFR+eeUEMOF4IJGldpwo73e49EJtg3TDmoDCD3Wgntt2Km20ngTutlrPQdWMOzSxb8SianVE06zHMoUfT7IxotpwFqOmevLuH0+l2lzVFTRVBBJq+hqrpURaDXFMiEMKLUigIUsDzYgCxCKIUECC70W4mLcfZKKzPeV4MZVmJ5JoSiqIcplYVLTrk+ACUIFScvkXkFCEyzQ1tgaipeqQZ1lyQ1bBEs97zJUSmTYACIl2UntgF4M0AskFsg1CMRqxb7ZiX1JDjxRDj4gxIMIIG8sXXXyP4h9C0ncRyjhdAatYbiW5YMUyywlV9CL5c4XyaZr1yZb2HGJOvk5EbQt12kiOjFQMEsTYXqlJE0cwMIdKFXdsvUjNMlp73DnYxv2U7w16+4MI7AnasXOb8vb1CmlM+j1wggAExbMVLw9+ya5pWDBPNss7gmJ1EU8yIMO1mIshKKMpqyDBlHzIE+WCXTif6shGi4DghwNmfsoEMM+DEgOAlNWAqVf+gXPWpA9bfL5ZmqbU8cmG6f1tLLRfQfyyjH38Awhx0Wf3gdGoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;screenshot&quot;
        title=&quot;&quot;
        src=&quot;/static/39d4dbc9482ac806db796cf198e30f78/914ae/screenshot.png&quot;
        srcset=&quot;/static/39d4dbc9482ac806db796cf198e30f78/2eb24/screenshot.png 215w,
/static/39d4dbc9482ac806db796cf198e30f78/05ed2/screenshot.png 430w,
/static/39d4dbc9482ac806db796cf198e30f78/914ae/screenshot.png 860w,
/static/39d4dbc9482ac806db796cf198e30f78/46115/screenshot.png 1290w,
/static/39d4dbc9482ac806db796cf198e30f78/6c86f/screenshot.png 1720w,
/static/39d4dbc9482ac806db796cf198e30f78/769f8/screenshot.png 1924w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[使用 OCaml 解析 JavaScript]]></title><link>https://diverse.space/2019/02/parse-javascript-with-ocaml</link><guid isPermaLink="false">https://diverse.space/2019/02/parse-javascript-with-ocaml</guid><pubDate>Wed, 20 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;起因&lt;/h1&gt;
&lt;p&gt;最近在写一个关于 JavaScript 的静态分析器，作为我的毕业设计。我写静态分析主要是为了 ES6 层面的语法做更好的 DCE(Dead Code Elimination)。我选择 OCaml 来实现这个静态分析工具，用 flow 来 parse JavaScript。&lt;/p&gt;
&lt;h1&gt;为什么选择 OCaml&lt;/h1&gt;
&lt;p&gt;在和导师讨论题目的时候，我跟导师说这个项目用 Java 来写，主要是 Java 比较容易说服老师。实际上开始写的时候就塞了很多私货，我的第一个版本其实用的是 Scala，原因是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scala 里面的很多语言特性非常有 FP 风格，也很适合用来写编译器和静态分析工具。&lt;/li&gt;
&lt;li&gt;运行在 JVM 上面更容易做多线程，可以使静态分析过程有较大的提升。&lt;/li&gt;
&lt;li&gt;如果有需要，Scala 可以编译成 JS。JVM 上面也有很多可以和 JS 联动的方案，方便和其他 JS library 联动。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但是开始使用 Scala 的时候却发现了许多问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我使用的 Parser 和 AST 结构都是 Java 写的（使用了 Graal.js 的 Parser），因此无法使用 Scala 的 case class 风格，这意味着我要在 Scala 里面写 Java Style 的代码，我表示很拒绝。我当然可以用 implicit 语法写一层转换层，但是我觉得这样很蛋疼，所以放弃。&lt;/li&gt;
&lt;li&gt;我要进行的静态分析是基于图（Graph）的，似乎没办法进行并行计算，即使可以，也会有超级多 overhead，还不如使用单线程。&lt;/li&gt;
&lt;li&gt;巨慢的编译速度和启动时间。编译速度不说了。运行速度 Scala 应该是很快的，但是启动（预热）却很慢。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;种种蛋疼的情况让我重新审视用 Scala 是不是特别好的方案，我想要一门执行速度相对较快，语法对写编译器相对友好的语言。Scala 已经很接近了，但是还不够。这让我想起之前用过 OCaml 来写过编译器，感觉代数形式的数据结构天生对编译器和静态分析非常友好。而且有两个项目非常启发到我：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://flow.org/&quot;&gt;Flow&lt;/a&gt;&lt;/strong&gt;：静态分析工具，但是已经演化到类似 TypeScript 那样，变成一门语言了。我个人用过 flow，但是感觉非常慢，比 TypeScript 慢很多，但是我觉得不是 OCaml 慢，而是 flow 本身设计的问题。设计 TypeScript 的 Anders Hejlsberg 毕竟是编译器领域的大牛，很多坑都踩过，像 TypeScript 和 VSCode 联动体验这么好肯定是经过优良设计的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://bucklescript.github.io&quot;&gt;BuckleScript&lt;/a&gt;&lt;/strong&gt;：把 OCaml 编译到 JS 的编译器（其实也算一门语言？）。作者号称编译速度最快的语言，编译速度比 TypeScript 还快，并附上了 benchmark。这点也可以理解。毕竟 OCaml 这门语言比 TypeScript 简单太多了。这让我看到了一线生机，OCaml 写出来的编译器也可以很快的啊。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;优点：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ADT&lt;/li&gt;
&lt;li&gt;冷启动速度却可以秒杀 Scala。想象一下编译几个文件的项目，Scala 还要等 JVM 预热才能达到峰值，其实很蛋疼。&lt;/li&gt;
&lt;li&gt;编译速度很快（相比 Scala）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;缺点：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;现在 OCaml 依然没有办法实现真正意义上的多线程：有 GIL。而我觉得这不是痛点，而且 multi-core OCaml 准备推出了（2020 年？）。&lt;/li&gt;
&lt;li&gt;如果需要和 JS 联动，需要编译成 JS&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;使用 Flow Parser&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt; &lt;a href=&quot;https://github.com/facebook/flow&quot;&gt;https://github.com/facebook/flow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Flow 是利器。&lt;/p&gt;
&lt;p&gt;自己从头开始写 ES 的 Parser 是不太现实的。很幸运 flow 就提供了这么一个工具。Flow 的代码结构还是蛮清晰，我一看项目目录基本了解各个模块的作用，而 parser 是作为独立的一部分：&lt;a href=&quot;https://github.com/facebook/flow/tree/master/src/parser&quot;&gt;Flow Parser&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AST 定义：&lt;a href=&quot;https://github.com/facebook/flow/blob/master/src/parser/flow_ast.ml&quot;&gt;https://github.com/facebook/flow/blob/master/src/parser/flow_ast.ml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Flow 的 AST 都写在一个文件里面，我们看 program 的定义：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ocaml&quot;&gt;&lt;pre class=&quot;language-ocaml&quot;&gt;&lt;code class=&quot;language-ocaml&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; program &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Statement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t list &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt; Comment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t list &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;@@&lt;/span&gt;deriving show&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;一个 program 就是 &lt;code class=&quot;language-text&quot;&gt;list of Statment.t&lt;/code&gt;。有两个泛型参数 &lt;code class=&quot;language-text&quot;&gt;&apos;M&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;&apos;T&lt;/code&gt; 充斥着整个 AST，虽然它的作用很巧妙，但是却并不是那么直观。实际上 parser 输出的结果中，&lt;code class=&quot;language-text&quot;&gt;&apos;M&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;&apos;T&lt;/code&gt; 都是 &lt;code class=&quot;language-text&quot;&gt;Loc.t&lt;/code&gt;，就是 AST 的位置信息，那么为什么需要两个范型呢？我们只要看 &lt;code class=&quot;language-text&quot;&gt;Statement&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;Expression&lt;/code&gt; 两个地方的定义就明白了：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ocaml&quot;&gt;&lt;pre class=&quot;language-ocaml&quot;&gt;&lt;code class=&quot;language-ocaml&quot;&gt;  &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t&apos;
  &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t&apos; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Block &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Block&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Break &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt; Break&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ClassDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Class&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Continue &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt; Continue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Debugger
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareClass &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; DeclareClass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareExportDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; DeclareExportDeclaration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareFunction &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; DeclareFunction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareInterface &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Interface&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareModule &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; DeclareModule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareModuleExports &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;annotation
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareTypeAlias &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareOpaqueType &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; OpaqueType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DeclareVariable &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; DeclareVariable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; DoWhile &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; DoWhile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Empty
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ExportDefaultDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ExportDefaultDeclaration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ExportNamedDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ExportNamedDeclaration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Expression &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Expression&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; For &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; For&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ForIn &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ForIn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ForOf &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ForOf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; FunctionDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Function&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; If &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; If&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ImportDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ImportDeclaration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; InterfaceDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Interface&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Labeled &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Labeled&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Return &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Return&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Switch &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Switch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Throw &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Throw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Try &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Try&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; TypeAlias &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; OpaqueType &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; OpaqueType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; VariableDeclaration &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; VariableDeclaration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; While &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; While&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; With &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; With&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Expression&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ocaml&quot;&gt;&lt;pre class=&quot;language-ocaml&quot;&gt;&lt;code class=&quot;language-ocaml&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t&apos;
  &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t&apos; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Array &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ArrowFunction &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Function&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Assignment &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Assignment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Binary &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Binary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Call &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Call&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Class &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Class&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Comprehension &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Comprehension&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Conditional &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Conditional&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Function &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Function&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Generator &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Generator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Identifier &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt; Identifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Import &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; JSXElement &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; JSX&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;element
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; JSXFragment &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; JSX&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fragment
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Literal &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Literal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Logical &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Logical&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Member &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Member&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; MetaProperty &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt; MetaProperty&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; New &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; New&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Object &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; OptionalCall &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; OptionalCall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; OptionalMember &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; OptionalMember&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Sequence &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Sequence&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Super
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; TaggedTemplate &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; TaggedTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; TemplateLiteral &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; TemplateLiteral&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; This
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; TypeCast &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; TypeCast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Unary &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Unary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Update &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Update&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Yield &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-variable function&quot;&gt;&apos;M&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token type-variable function&quot;&gt;&apos;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Yield&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;t&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;上述代码就是在说，所有的 Expression 结点都会附上一个 &lt;code class=&quot;language-text&quot;&gt;&apos;T&lt;/code&gt; 信息，所有的 Statement 都会附上一个 &lt;code class=&quot;language-text&quot;&gt;&apos;M&lt;/code&gt; 信息，这就很容易理解了。所以这两个范型是用来区分 Statement 和 Expression 的。在 flow 里面进行 type infer 和 type check 的时候，实际上 &lt;code class=&quot;language-text&quot;&gt;&apos;T&lt;/code&gt; 会是 &lt;code class=&quot;language-text&quot;&gt;(Loc.t * Type.t)&lt;/code&gt;。这个是 type check 阶段附上的信息，而 Statement 不需要这个信息，所以它仍然是 &lt;code class=&quot;language-text&quot;&gt;Loc.t&lt;/code&gt;。举个类型推导例子 🌰 来说明 &apos;T 附在表达式上的作用：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var a = 1 + 1;

var a = ((1: &apos;T) + (1: &apos;T)): &apos;T
var a = ((1: number) + (1: number)): &apos;T
var a = ((1: number) + (1: number)): number
var a: number = ((1: number) + (1: number)): number&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;从上述过程可以推出 a 的类型是 number，这个类型信息挂在所有 expression 的 AST 的上面，而 var 语句的 node 不需要这个信息。&lt;/p&gt;
&lt;h1&gt;安装 Flow Parser&lt;/h1&gt;
&lt;p&gt;原本 &lt;code class=&quot;language-text&quot;&gt;flow_parser&lt;/code&gt; 可以从 opam 安装，但是我更新了 opam 之后似乎因为一些安全策略禁止包里面的 bash 脚本执行（当时社区发生了一件事，一个包的 bash 脚本把用户的整个 ~ 目录删掉，似乎是手滑写的脚本就发布了，后来 opam 对 bash 脚本做了些限制）。所以后来我只能 clone 了 flow 的代码本地进行安装&lt;/p&gt;
&lt;p&gt;进入 &lt;code class=&quot;language-text&quot;&gt;src/parser&lt;/code&gt; 目录执行：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; ocamlfind-install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;即可安装成功。如果使用 ocamlbuild 编译，进入 &lt;code class=&quot;language-text&quot;&gt;_tag&lt;/code&gt; 文件，加上 flow_parser。我的 &lt;code class=&quot;language-text&quot;&gt;_tag&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;true: package(ppx_deriving.show), package(flow_parser), package(core_kernel)
&amp;lt;dist&gt;: -traverse&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;使用方法&lt;/h1&gt;
&lt;p&gt;使用 &lt;code class=&quot;language-text&quot;&gt;Flow_ast&lt;/code&gt; 自带的 &lt;code class=&quot;language-text&quot;&gt;pp&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;show&lt;/code&gt; 方法可以把 AST 打印出来，前提是你要传入 &lt;code class=&quot;language-text&quot;&gt;&apos;T&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;&apos;M&lt;/code&gt; 的 pretty print 方法， 这里我把 &lt;code class=&quot;language-text&quot;&gt;Loc.pp&lt;/code&gt; 传进去即可：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ocaml&quot;&gt;&lt;pre class=&quot;language-ocaml&quot;&gt;&lt;code class=&quot;language-ocaml&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ast&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Parser_flow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;program code &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
Flow_ast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;show_program Loc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pp Loc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pp ast &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; print_endline&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;大功告成！&lt;/p&gt;
&lt;h1&gt;相关阅读&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;QuickJS 源码解读：&lt;a href=&quot;https://diverse.space/2019/08/understanding-source-code-of-quickjs-1&quot;&gt;https://diverse.space/2019/08/understanding-source-code-of-quickjs-1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[2018 不停留]]></title><link>https://diverse.space/2019/01/2018-never-stops</link><guid isPermaLink="false">https://diverse.space/2019/01/2018-never-stops</guid><pubDate>Tue, 01 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;人生中一些很重要的时间段，往往会像细水那样慢慢流走，回过头来才发现，涓涓细水汇成了江河大海。对于我来说 2018 年就是这样的一年，回头一看觉得很平淡，但是不乏丰富的细节。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键词：&lt;/strong&gt; GSoC，大四，实习&lt;/p&gt;
&lt;p&gt;（暂不开放)&lt;/p&gt;
&lt;!-- 今年年底的时候我时常会回忆起 2017 年的 12 月，那时候刚来深圳，可能因为同是 12 月，渐渐变冷的天气让人有相近的感觉，便容易想起往事。17 年的很多心结在现在的我看来都已经解开，即时现在的我也有新的纠结点也无妨，因为我已经在这一年里面得到了足够的成长。

一年很长，足以发生很多事情，在这一年里面，我最耀眼的事情就是参加了 GSoC 2018。现在回过头来看，参加 GSoC 的收益远远超过了我的预期。我本以为，GSoC 只是一个为开源项目做贡献的事情，只算是一种情怀，因此我也没有特别去跟别人说这件事。可是它给我带来的好处却远超我的预期，原因之一无非是碰巧今年 Sean 来广州参加前端大会，而且 Sean 也不遗余力地在 Twitter 和 Medium 上推广我的 GSoC 成果，突然也让我小有了名气。

当然，一次的成功是远远不够的，如果我对此就满意了，那么我以后也不会有什么进步。一次的小爆发说明不了什么，只有持续地输出才能证明自己的能力。因此，2018 年的这些幻光我可以通通忘掉，它虽然很耀眼，但它终究只是我奋斗途中的一个小小的发光点，我相信以后会有更耀眼的成就。

这一年来，我持续地在深圳实习，比很多同龄人都要早。现在回想起来，想起 17 年年底一个乳臭未干的小伙子一个人跑来深圳实习都有点不可思议。这一年来，作为组里面年级最小的员工的记录从来没有打破。但是这对于我来说也不是特别值得炫耀的事情，因为我一直觉得「闻道有先后，术业有专攻」，有些人比较早工作，有些人大器晚成，这些都不重要，重要的是可持续地输出。

我觉得实习给我最大的好处就是：自由。在学校里面，总能感觉到各种束缚，这种束缚的感觉已经伴随我好多年了。当然出来工作并不是完全自由，只不过是从一个小监狱转移到了一个更大的监狱。虽然都是坐牢，待遇却好上不少。实习的时候自己住，没有人理会我回家的时间，也没有人阻碍我做任何事。除了这些肤浅的自由，更大的精神层面的自由就是我可以做自己喜欢的事，这种状态在学校里面可以说是求之不得的。对于厌倦校园生活很久的我来说，2018 年不得不说是一种解脱。

（未完待续） --&gt;</content:encoded></item><item><title><![CDATA[在 Mojave 下编译 SpiderMonkey]]></title><link>https://diverse.space/2018/10/在-Mojave-下编译-SpiderMonkey</link><guid isPermaLink="false">https://diverse.space/2018/10/在-Mojave-下编译-SpiderMonkey</guid><pubDate>Mon, 29 Oct 2018 00:16:14 GMT</pubDate><content:encoded>&lt;p&gt;自从 macOS 升级到 Mojave(10.14) 之后，编译 SpiderMonkey 变得有点不同。&lt;/p&gt;
&lt;p&gt;有一点比较重要的变化是 libstdc++ 被替换成 libc++，这个修改是从 OS X 10.9 就开始有的修改，所以直接调用 ./configure 有这样的 warning 和报错：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;checking whether the C++ compiler &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/usr/bin/clang++  -fno-common -fno-rtti  -lobjc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; actually is a C++ compiler&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. no
configure: error: /usr/bin/clang++  -fno-common -fno-rtti  &lt;span class=&quot;token parameter variable&quot;&gt;-lobjc&lt;/span&gt; failed to compile and &lt;span class=&quot;token function&quot;&gt;link&lt;/span&gt; a simple C++ source.
------ config.log ------
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
configure:7837: checking &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;64&lt;/span&gt;-bit OS
configure:7846: /usr/bin/clang &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt;  &lt;span class=&quot;token parameter variable&quot;&gt;-std&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;gnu99 -Qunused-arguments  conftest.c &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;1&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;5&lt;/span&gt;
configure:8119: checking &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-framework&lt;/span&gt; ExceptionHandling
configure:8129: /usr/bin/clang &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; conftest  &lt;span class=&quot;token parameter variable&quot;&gt;-std&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;gnu99 -fno-common -Qunused-arguments   &lt;span class=&quot;token parameter variable&quot;&gt;-lobjc&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-framework&lt;/span&gt; ExceptionHandling conftest.c  &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;1&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;5&lt;/span&gt;
configure:8151: checking &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-dead_strip&lt;/span&gt; option to ld
configure:8162: /usr/bin/clang &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; conftest  &lt;span class=&quot;token parameter variable&quot;&gt;-std&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;gnu99 -fno-common -Qunused-arguments   &lt;span class=&quot;token parameter variable&quot;&gt;-lobjc&lt;/span&gt; -Wl,-dead_strip conftest.c  &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;1&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;5&lt;/span&gt;
configure:9034: checking &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; valid debug flags
configure:9045: /usr/bin/clang &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt;  &lt;span class=&quot;token parameter variable&quot;&gt;-std&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;gnu99 -fno-common &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; -Qunused-arguments  conftest.c &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;1&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;5&lt;/span&gt;
configure:9134: checking whether the C++ compiler &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/usr/bin/clang++  -fno-common -fno-rtti  -lobjc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; actually is a C++ compiler
configure:9153: /usr/bin/clang++ &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; conftest  -fno-common -fno-rtti -Qunused-arguments   &lt;span class=&quot;token parameter variable&quot;&gt;-lobjc&lt;/span&gt; conftest.C  &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;1&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;5&lt;/span&gt;
clang: warning: libstdc++ is deprecated&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; move to libc++ with a minimum deployment target of OS X &lt;span class=&quot;token number&quot;&gt;10.9&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;-Wdeprecated&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
warning: include path &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; stdlibc++ headers not found&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; pass &lt;span class=&quot;token string&quot;&gt;&apos;-std=libc++&apos;&lt;/span&gt; on the &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; line to use the libc++ standard library instead &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;-Wstdlibcxx-not-found&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
configure:9147:10: fatal error: &lt;span class=&quot;token string&quot;&gt;&apos;new&apos;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; not found
&lt;span class=&quot;token comment&quot;&gt;#include &amp;lt;new&gt;&lt;/span&gt;
         ^~~~~
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; warning and &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; error generated.
configure: failed program was:
&lt;span class=&quot;token comment&quot;&gt;#line 9146 &quot;configure&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#include &quot;confdefs.h&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#include &amp;lt;new&gt;&lt;/span&gt;
int &lt;span class=&quot;token function-name function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
int *foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; new int&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
configure: error: /usr/bin/clang++  -fno-common -fno-rtti  &lt;span class=&quot;token parameter variable&quot;&gt;-lobjc&lt;/span&gt; failed to compile and &lt;span class=&quot;token function&quot;&gt;link&lt;/span&gt; a simple C++ source.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;很明显，报错原因是因为标准库 new 找不到，原因也在于 libstdc++ 被替换了，但是天杀的报错信息写错了。应该加入的 flag 是 &lt;code class=&quot;language-text&quot;&gt;-stdlib=libc++&lt;/code&gt; 而不是 &lt;code class=&quot;language-text&quot;&gt;-std=libc++&lt;/code&gt;，这一点很坑爹。&lt;/p&gt;
&lt;p&gt;但是加上了这个 flag 仍然不能编译，因为又有报错：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;clang: error: invalid deployment target &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-stdlib&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;libc++ &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requires OS X &lt;span class=&quot;token number&quot;&gt;10.7&lt;/span&gt; or later&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;想了半天，我的系统不是 10.14 吗，后来才知道是因为要设置 deployment target，很坑爹，所以最后让我编译成功的命令是：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ &lt;span class=&quot;token assign-left variable&quot;&gt;CXXFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-stdlib=libc++ -mmacosx-version-min=10.7&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;写篇 blog 庆祝一下。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[理解 Scala 中的 for 表达式]]></title><link>https://diverse.space/2018/06/understanding-for-comprehansion-in-scala</link><guid isPermaLink="false">https://diverse.space/2018/06/understanding-for-comprehansion-in-scala</guid><pubDate>Sun, 10 Jun 2018 02:58:33 GMT</pubDate><content:encoded>&lt;p&gt;Scala 中的 for 表达式非常强大，用起来很简单顺手，但是理解起来可能需要一些背景知识。这个 for 从名字上看让我们觉得它很像命令式语言中的那种 for statement，但是其实 Scala 的 for 是非常函数式的，它跟命令式语言中的 for 语句根本不是一个东西，因为它根本不支持 break 和 continue 这些语句。&lt;/p&gt;
&lt;p&gt;官方文档提供的例子：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; twentySomethings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; userBase &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name  &lt;span class=&quot;token comment&quot;&gt;// i.e. add this to a list&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Scala 的 for 表达式倒是很像 Python 和 Haskell 里面的 list comprehension，其实这个 for 表达式基本上可以等同于 Haskell 的 list comprehension 加上 do annotation 了。然而 Haskell 里面 list comprehension 和 do 也是一个东西，所以基本等同于 for comprehension。&lt;/p&gt;
&lt;p&gt;在理解 for 之前，需要先了解 &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt;，这些概念在 Scala 和 Haskell 中很相似：&lt;/p&gt;
&lt;h2&gt;Map&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; 大多数人会用在 List 上面，但是更广义的概念上，map 可以用在任何带有 context 的类型上面，不仅限于 List：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// map 通过一个函数，可以把 A 元素的列表转换成列表 B 的
map(f: A =&gt; B): List[A] =&gt; List[B]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;同理，map 不仅可以用在 &lt;code class=&quot;language-text&quot;&gt;List&lt;/code&gt; 上面，也可以用在类似 Option 和 Future 这样的类型上面。从直观意义上理解，map 同样可以把 &lt;code class=&quot;language-text&quot;&gt;Option[A]&lt;/code&gt; 转换成 &lt;code class=&quot;language-text&quot;&gt;Option[B]&lt;/code&gt;，把 &lt;code class=&quot;language-text&quot;&gt;Future[A]&lt;/code&gt; 转换成 &lt;code class=&quot;language-text&quot;&gt;Future[B]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在 Hasekll 里面，这种支持 map 操作的类型，叫做 Functor，Scala 里面似乎没有一个名字。&lt;/p&gt;
&lt;h2&gt;FlatMap&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt; 理解起来则稍微复杂一些，看一下 List 里面的 &lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; flatMap&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;U&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; T &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; List&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;U&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;U&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里面的 &lt;code class=&quot;language-text&quot;&gt;f&lt;/code&gt; 不再像 &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; 里面返回一个新的子类型，而是，返回一个带有 context 类型本身： &lt;code class=&quot;language-text&quot;&gt;List[U]&lt;/code&gt;。也就是说 f 函数返回的新类型是包含在 context 里面的。&lt;strong&gt;需要注意的是&lt;/strong&gt;在这里，返回的结果并不是 List[List[U]]，而是仅仅是 &lt;code class=&quot;language-text&quot;&gt;List[U]&lt;/code&gt; 。通过 &lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt; 这个名字中的 &lt;code class=&quot;language-text&quot;&gt;flat&lt;/code&gt; 我们也知道，最后的结果会是把每个 list 都拼接在一起，也就是所谓的拍平了。&lt;/p&gt;
&lt;p&gt;上面提到的是 List 的 flatMap，同样地，&lt;code class=&quot;language-text&quot;&gt;Future&lt;/code&gt; 类型同样也是支持 &lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt; 的，它的的参数 &lt;code class=&quot;language-text&quot;&gt;f&lt;/code&gt; 类型我们应该也猜到了：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; flatMap&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;U&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; T &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; Future&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;U&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Future&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;U&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;通过 flatMap 我们就可以做很多事情了。这个时候假设我们有函数 &lt;code class=&quot;language-text&quot;&gt;fetch1&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;fetch2&lt;/code&gt; ，都是返回 &lt;code class=&quot;language-text&quot;&gt;Future[Response]&lt;/code&gt;，如果我们要先 fetch1 然后根据 1 的 response 来 fetch2 ，然后把 2 的结果打印出来，我们要怎么做呢：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 写法一&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; resp2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fetch1
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flatMap &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; resp1 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;
    fetch2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 通过 1 的结果来返回新的 Future&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; resp2 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
    printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后回到开头，我们可以用 for comprehension 来完成这个事情：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 写法二&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; resp2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    resp1 &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; fetch1&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    resp2 &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; fetch2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其实实际上，Scala 会把上述代码翻译成与下面等价的样子：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 写法三&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; resp2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; resp1
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flatMap &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; resp1 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
    fetch2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    	resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;只要你仔细观察，你就会发现，在这个例子里面，这三种写法的结果都是一样的，但是第一种写法有什么缺点呢，缺点就是在第二个 &lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt; 里面，&lt;code class=&quot;language-text&quot;&gt;resp1&lt;/code&gt; 已经不在作用域里面了，这个时候如果你还想调用 resp1 已经不可能了，但是写法三是可以的：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 写法三&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; resp2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; resp1
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flatMap &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; resp1 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
    fetch2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 在这里仍然可以调用 resp1，因为它还在作用域里面&lt;/span&gt;
        printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    	resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样看起来就跟 Haskell 的 Monad 很接近了。&lt;/p&gt;
&lt;h2&gt;For 表达式的几种翻译方式&lt;/h2&gt;
&lt;p&gt;其实明白了 map 和 flatMap 之后，for 表达式很容易理解了。但是实现起来还要参考 &lt;a href=&quot;https://docs.scala-lang.org/tutorials/FAQ/yield.html&quot;&gt;for 表达式的翻译方式&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;If&lt;/h3&gt;
&lt;p&gt;值得注意的是 if 语法：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; cond&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;会被翻译成：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;withFilter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; cond&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;当然会有 fallback （如果不支持 withFilter）：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; cond&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这时候再看文章开头的例子：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; twentySomethings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; userBase &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name  &lt;span class=&quot;token comment&quot;&gt;// i.e. add this to a list&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;会被翻译成：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; twentySomethings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userBase
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; user &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;就很容易理解了&lt;/p&gt;
&lt;h3&gt;赋值语句&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; 表达式中同样支持赋值语句（这里说的是 &lt;code class=&quot;language-text&quot;&gt;=&lt;/code&gt; 不是 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;-&lt;/code&gt;），这在处理异步请求的时候非常常见：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; resp2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    resp1 &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; fetch1&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; doSomething&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    resp2 &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; fetch2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;注意，在 for comprehension 中写 &lt;code class=&quot;language-text&quot;&gt;=&lt;/code&gt; 赋值语句并不需要写 &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; 或者 &lt;code class=&quot;language-text&quot;&gt;val&lt;/code&gt; ，至于为什么，只要看它会被翻译成什么样子就能明白：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; resp2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    resp1 &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; fetch1&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; doSomething&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    resp2 &lt;span class=&quot;token keyword&quot;&gt;&amp;lt;-&lt;/span&gt; fetch2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; resp2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fetch1&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; resp1 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doSomething&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flatMap &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
    	fetch2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; resp2 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt;
        printf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        resp2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;所以这个时候再写 &lt;code class=&quot;language-text&quot;&gt;val&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; 已经没什么意义了&lt;/p&gt;
&lt;h2&gt;CPS 变换&lt;/h2&gt;
&lt;p&gt;其实 Scala 做的这个变换是一种 CPS(Continuation Passing Style) 变换，把一系列看上去想是赋值 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;-&lt;/code&gt; 的操作转换成 CPS 的形式，这有什么好处呢，通过这种变换，map 和 flatMap 的参数 f 就可以是一个纯（Pure）的函数，也就是说通过一系列纯函数的组合，可以实现像命令式编程里面的有状态的赋值操作，而这些操作是含有上下文（context）的。&lt;/p&gt;
&lt;p&gt;另一个好处是，像下面这种调用：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scala&quot;&gt;&lt;pre class=&quot;language-scala&quot;&gt;&lt;code class=&quot;language-scala&quot;&gt;c1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p1 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; p1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p2 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; p2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p3 &lt;span class=&quot;token keyword&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其实是尾递归调用，编译器可以为此做很多优化，提高运行速度。&lt;/p&gt;
&lt;h1&gt;总结&lt;/h1&gt;
&lt;p&gt;for comprehension 其实是一个很甜很好用的语法糖，可以帮我们省下很多代码。我们当然可以选择继续用 &lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt;，但是 for 就能用很简单的语言写出来。当然，简单的背后是有代价的，用 for 的过程中就需要使用者明白表达式最终会被怎样转换成相应的 &lt;code class=&quot;language-text&quot;&gt;flatMap&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; 代码，这样才能让我们写出简洁的 for comprehension。&lt;/p&gt;
&lt;p&gt;这个时候，我真的很佩服 Scala 的设计，让整个语言各个部分的设计都非常容易和优雅地组合，设计得相当地精妙，同时我在其中也看到了一些 Haskell 的味道。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[webpack 如何通过作用域分析消除无用代码]]></title><link>https://diverse.space/2018/05/better-tree-shaking-with-scope-analysis</link><guid isPermaLink="false">https://diverse.space/2018/05/better-tree-shaking-with-scope-analysis</guid><pubDate>Fri, 25 May 2018 14:24:18 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;GSoC 2018 中，我的项目就在于给 webpack 实现深作用域分析（Deep Scope Analysis），主要还是为了改进 webpack 的 tree-shaking 工作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;JS 的 tree-shaking 一直是前端开发中的痛点，大家都在想尽办法减少打包的代码体积。Tree shaking 是一个帮助在不同模块之间消除无用代码的 feature。在编译原理中，我们把这项技术叫做 DCE(dead code elimination)。但是 DCE 和 tree shaking 有些许不同，按照 Tobias 的说法，tree shaking 主要应用于于模块（module）之间，用于帮助进行 DCE（webpack 的 DEC 通过 uglify 完成），rollup 的作者也曾经提到， tree shaking 是打包的过程中抽取有用的部分，别的部分像树叶一样落下，所以叫 tree shaking。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/webpack-deep-scope-analysis-plugin&quot;&gt;项目地址&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;从前&lt;/h2&gt;
&lt;p&gt;webpack 本身的 tree shaking 比较简单，主要是找一个 import 进来的变量是否在这个模块内出现过，非常简单粗暴。但是这种方式往往作用不大，因为一般人不会去 import 一个没有用到的变量。比较多的情况是可能曾经引用过，但是忘了删掉。现在的编辑器和 lint 工具都会提示你去删掉无用的变量，所以 webpack 本身的 tree shaking 功能是不够强大的。&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/vincentdchan/9ccec2d1b603ef119e473cf285c7fa6b.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;在上面的例子中，变量 &lt;em&gt;isNumber&lt;/em&gt; 并没有被引用到，所以会被消去。&lt;/p&gt;
&lt;h2&gt;开端&lt;/h2&gt;
&lt;p&gt;在今年年初，webpack 项目下面有一个 &lt;a href=&quot;https://github.com/webpack/webpack/issues/6264&quot;&gt;issue&lt;/a&gt; 提到了 webpack 打包了多余的代码和模块。但是这也为优化 tree-shaking 提供了一个思路，就是找到作用域之间的关系，来进行优化。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/3199950/34681428-28df7576-f49c-11e7-942d-12caa6e905b8.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;在上面的例子中，其实 &lt;em&gt;function2&lt;/em&gt; 和整个 &lt;em&gt;external2&lt;/em&gt; 都可以被消去，因为 &lt;em&gt;function2&lt;/em&gt; 并没有被 &lt;em&gt;entry&lt;/em&gt; 引用到。但是目前 webpack 的机制不能做到这一点。借助于 webpack 强大的插件极致，我的插件就可以帮助 webpack 做到。&lt;/p&gt;
&lt;h2&gt;我的插件做了什么&lt;/h2&gt;
&lt;p&gt;插件包括了一个作用域分析器，可以分析一个模块里面的作用域，从此我们可以得到不同作用域之间变量的引用关系。当我们知道一个作用域是否会被使用，就可以因此而推断出这个作用域做引用的其他作用域是否也会被使用。这就是作用域分析器帮助消除无用代码的原理。&lt;/p&gt;
&lt;h3&gt;什么是作用域&lt;/h3&gt;
&lt;p&gt;下面的代码列举了 JS 中会&lt;strong&gt;新建&lt;/strong&gt;一个作用域的代码：&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/vincentdchan/a67ee79011c9c1fbb699a137538dcbe7.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;对于 ES6 模块来说，module scope 是最底层的作用域。而对于一个模块来说，&lt;strong&gt;只有 class 和 function 的作用域是可以导出到其他模块的&lt;/strong&gt;。所以在这张需要遍历的图里面，并不是所有的作用域都可以被当作一个独立的遍历结点，像 if-else 作用域其实是归属于父作用域的。&lt;/p&gt;
&lt;h2&gt;插件的工作原理&lt;/h2&gt;
&lt;p&gt;在我们去分析作用域之间的引用关系之前，我们先需要去分析代码的作用域。代码的作用域分析建立在 AST(Abstract Syntax Tree) 之上。在这里，我借助了一个叫 &lt;a href=&quot;https://github.com/estools/escope&quot;&gt;escope&lt;/a&gt; 的工具。&lt;/p&gt;
&lt;p&gt;解析完之后，其实就是图的深度遍历，找到那些作用域是会被使用到了，哪些是可以消去的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/tree-shaking.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;因为这个插件可以从导出的作用域之间分析出这些导出的作用域和导入变量之间的关系，也就是说。只要知道哪些导出作用域被使用的到，那么就知道哪些导入变量被引用，那些没有被引用。&lt;/p&gt;
&lt;p&gt;另一方面，webpack 本身是可以分析出模块之间的变量引用关系的，从 webpack 我可以得知一个模块哪些导出变量是被用到的，这是 webpack 4 的新 feature。所以我的插件 tap 上了 webpack 相应的 hook，获取这个模块中会被其他模块引用的导出变量，返回给 webpack 哪些引入的变量被用到，这样 webpack 就可以根据我的插件的信息进行更完善的 tree-shaking。&lt;/p&gt;
&lt;h2&gt;Edge cases&lt;/h2&gt;
&lt;p&gt;实际上，JavaScript 的分析有很多 Edge cases 会导致代码不会被消去，这里列举一些比较常见的：&lt;/p&gt;
&lt;p&gt;同时提供一个 &lt;a href=&quot;https://vincentdchan.github.io/webpack-deep-scope-demo/&quot;&gt;Demo&lt;/a&gt; 来给大家尝试。&lt;/p&gt;
&lt;h3&gt;根作用域的引用&lt;/h3&gt;
&lt;script src=&quot;https://gist.github.com/vincentdchan/c8cbb1198bfef580c216343e1ef4eedf.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;在根作用域引用到的作用域不会被消除。&lt;/p&gt;
&lt;h3&gt;给变量重新赋值 👎&lt;/h3&gt;
&lt;script src=&quot;https://gist.github.com/vincentdchan/f7f94c8e28bf8cc29e0e691371012783.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;因为缺少&lt;a href=&quot;https://en.wikipedia.org/wiki/Data-flow_analysis&quot;&gt;数据流分析&lt;/a&gt;，对变量重新赋值的作用域不会被消去。在上面的例子中，因为对 &lt;em&gt;fun&lt;/em&gt; 变量进行了重新赋值，所以 &lt;em&gt;isNull&lt;/em&gt; 无论如何都会被引入。&lt;/p&gt;
&lt;h3&gt;纯函数调用 👍&lt;/h3&gt;
&lt;script src=&quot;https://gist.github.com/vincentdchan/473b219b3cbbc64c903e9a1bf100e791.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;如果一个匿名函数被包在一个函数调用中，那么其实这个插件是无法分析的，像上面的例子。但是如果加上了 PURE 注释的话，这个插件会把这个函数调用当作一个独立的域，所以在上述的例子中，tree-shaking 是会生效的。&lt;/p&gt;
&lt;h2&gt;实际使用的过程中应该注意什么&lt;/h2&gt;
&lt;p&gt;深作用域分析原理很简单，实现起来也不复杂，但是真的要使用再实际项目的过程中，却有很多要注意的地方：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一、必须使用 ES6 的 import/export 模块机制。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其实整个深作用域分析都是基于 ES6 模块完成的，也就是说深作用域分析无法分析 CommonJS 和 AMD 等等模块规范。这个时候，就要求项目中引用的模块都遵循 ES6 的规范，比如使用 lodash-es 代替 lodash。另外就是要注意 babel-loader 和 TypeScript 的设置，是否会把代码转换到 ES5 语法，导致深作用域分析失效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二、学会使用 PURE 注释。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;由于 JS 语法的复杂程度，webpack 没有打算给 JS 实现数据流分析，所以插件是无法知道一个函数调用是否具有副作用的。所以对于一些导出模块，如果是纯的函数调用，则需要加上 &lt;code class=&quot;language-text&quot;&gt;/*@__PURE__*/&lt;/code&gt; 注释来表明这个函数是 pure 的，这是 &lt;a href=&quot;https://github.com/mishoo/UglifyJS2&quot;&gt;Uglify&lt;/a&gt; 使用的方法。当然也可以使用相关的 babel 插件进行批量添加。&lt;/p&gt;
&lt;h1&gt;总结&lt;/h1&gt;
&lt;p&gt;其实我这插件的实现是归根于 ES6 中良好的 import/export 语法的设计的。相信很多前端大佬都提到，就是模块的设计一定要合理。tree shaking 再强大它也只是一个编译器的工具，如果模块设计不合理，它一样会在打包的时候引入很多无用的代码。一个合理设计的模块一定能借助 tree shaking 机制只引入它需要的代码。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[GSoC 之旅]]></title><link>https://diverse.space/2018/04/beginning_gsoc_2018</link><guid isPermaLink="false">https://diverse.space/2018/04/beginning_gsoc_2018</guid><pubDate>Sun, 29 Apr 2018 14:16:00 GMT</pubDate><content:encoded>&lt;p&gt;有幸被 &lt;a href=&quot;https://webpack.github.io&quot;&gt;webpack&lt;/a&gt; 选中参加今年的 GSoC，这次的 GSoC 我主要负责实现 Deep Scope Analysis，这个最后会被用到最新版本的 webpack 里面用于 Tree Shaking&lt;/p&gt;
&lt;p&gt;Github 项目地址：&lt;a href=&quot;https://github.com/vincentdchan/webpack-deep-scope-analysis-plugin&quot;&gt;webpack-deep-scope-analysis-plugin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;根据 webpack 的要求，我会把 GSoC 期间发现的事情记录在 Medium&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/webpack/better-tree-shaking-with-deep-scope-analysis-a0b788c0ce77&quot;&gt;&lt;strong&gt;Better tree shaking with deep scope analysis&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Here’s my project in GSoC 2018: Improve tree-shaking for webpack, a widely used JS code bundler.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/webpack/my-story-of-gsoc-2018-beginning-4c98d8966bfe&quot;&gt;&lt;strong&gt;My story with GSoC 2018: Beginning&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the first part of my stories with GSoC 2018. I will tell my personal experiences and all the things happened with GSoC 2018 in the series.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://vincentdchan.github.io/webpack-deep-scope-demo/&quot;&gt;&lt;strong&gt;Deep Scope Analysis Demo&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个简易的用于测试分析结果的 demo&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[初窥 Racket：解析一个数学表达式]]></title><link>https://diverse.space/2018/01/parsing-math-expression-by-racket</link><guid isPermaLink="false">https://diverse.space/2018/01/parsing-math-expression-by-racket</guid><pubDate>Sun, 28 Jan 2018 19:09:00 GMT</pubDate><content:encoded>&lt;p&gt;当 Haskell 这些静态类型的函数式语言玩久了，就想尝试一下动态类型的函数式语言，比如 Lisp，最古老的编程语言之一。不过现在写 Lisp 是不现实，因为原始的 Lisp 是 dynamic scope 的，写起来会异常痛苦。所以我打算从 Lisp 的一些比较现代方言下手，比如说 Racket 这种广受赞誉的语言。&lt;/p&gt;
&lt;p&gt;看了一下官方文档之后感觉还是应该写点什么实际的东西感受一下，个人比较喜欢写数学表达式解析器了。之前写过一篇「&lt;a href=&quot;/2017/10/parsing-math-expression-by-haskell/&quot;&gt;初窥Haskell：解析一个数学表达式&lt;/a&gt;」。所以这次打算用 Racket 写解析一个数学表达式程序。&lt;/p&gt;
&lt;p&gt;Racket 不是一个“纯”的语言，所以在 Racket 里面写有副作用的代码是没有问题的，说不定还会有更高的效率。但是这次我没有这么做，因为受到 Haskell 的影响，所以我还是用“纯”函数式的方法去实现。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scheme&quot;&gt;&lt;pre class=&quot;language-scheme&quot;&gt;&lt;code class=&quot;language-scheme&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;char-&gt;op&lt;/span&gt; ch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;cond&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;char=?&lt;/span&gt; ch &lt;span class=&quot;token char&quot;&gt;#\+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;&apos;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;char=?&lt;/span&gt; ch &lt;span class=&quot;token char&quot;&gt;#\-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;&apos;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;char=?&lt;/span&gt; ch &lt;span class=&quot;token char&quot;&gt;#\*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;&apos;mul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;char=?&lt;/span&gt; ch &lt;span class=&quot;token char&quot;&gt;#\/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;&apos;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;struct&lt;/span&gt; token &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  #:prefab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tokenizer&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;letrec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;char-list&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string-&gt;list&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-tokenizer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token lambda-parameter&quot;&gt;reading-buffer number-buffer token-buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;null?&lt;/span&gt; reading-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;null?&lt;/span&gt; number-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          token-buffer &lt;span class=&quot;token comment&quot;&gt;;;; 所有字符都已经解析完毕&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;;;; 还有 number-buffer 的字符串&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;&apos;number&lt;/span&gt; 
              &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string-&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list-&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;reverse&lt;/span&gt; number-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
            token-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;first-char&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; reading-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buffer-tail&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; reading-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;char-numeric?&lt;/span&gt; first-char&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;;;; 当前字符是一个数字&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-tokenizer&lt;/span&gt; buffer-tail &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; first-char number-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; token-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;null?&lt;/span&gt; number-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;current-token&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;&apos;operator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;char-&gt;op&lt;/span&gt; first-char&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-tokenizer&lt;/span&gt; buffer-tail &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; current-token token-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;number-token&lt;/span&gt; 
                    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;&apos;number&lt;/span&gt; 
                      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string-&gt;number&lt;/span&gt; 
                        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list-&gt;string&lt;/span&gt; 
                          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;reverse&lt;/span&gt; number-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-tokenizer&lt;/span&gt; 
                    reading-buffer 
                    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
                    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; number-token token-buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-tokenizer&lt;/span&gt; char-list &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;以上代码实现了一个 tokenizer，是用尾递归的方法来实现的。&lt;/p&gt;
&lt;p&gt;Lisp 的括号语法一直饱受争议，但是这样做不是没有好处的，就是整个 Lisp 写起来其实没有太多复杂的语法，而且语法都显得很一致，很容易组合，而且 Racket 有很强的宏编程能力，关于这点我还没有了解。有人把 Lisp 理解成手写 AST(Abstract Syntax Tree)，这种说法我觉得也没有问题。&lt;/p&gt;
&lt;p&gt;一般的类 C 语言的变量名喜欢用下划线或者驼峰标记法来区分，而在 Racket 里面，则可以用 &lt;em&gt;this-is-a-variable&lt;/em&gt; 这种小短杠来表示，个人觉得看上去还是很舒服的。另一方面，很多标点符号也可以用来作为变量名，比如 &lt;em&gt;string?&lt;/em&gt; 是一个用来判断变量是否为 &lt;em&gt;string&lt;/em&gt; 的函数。比如 &lt;em&gt;string-&gt;numer&lt;/em&gt; 用来表示字符串转数字的函数，个人感觉非常直观。&lt;/p&gt;
&lt;p&gt;和 Haskell 相比起来会麻烦一点的地方就是要自己去判断变量的类型，但是这也意味着给语言提供更大的灵活性。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scheme&quot;&gt;&lt;pre class=&quot;language-scheme&quot;&gt;&lt;code class=&quot;language-scheme&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get-precedence&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt; x
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;add&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;sub&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;mul&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;div&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;eval&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;letrec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get-handler&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token lambda-parameter&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt; op
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;add&lt;/span&gt; +&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;sub&lt;/span&gt; -&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;mul&lt;/span&gt; *&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;&apos;div&lt;/span&gt; /&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token lambda-parameter&quot;&gt;tokens num-stack op-stack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;null?&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt; op-stack
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; top-op op-stack-tail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let*&lt;/span&gt; 
                        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;num1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;num-stack-tail&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get-handler&lt;/span&gt; top-op&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt; num1 num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; result num-stack-tail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; op-stack-tail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next-op&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token-value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next-number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token-value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tokens-tail&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt; op-stack
                      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;null&lt;/span&gt; 
                        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-eval&lt;/span&gt; 
                          tokens-tail 
                          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; next-number num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
                          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; next-op op-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; top-op op-stack-tail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;op-stack-tail&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; op-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;top-op&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; op-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;top-op-precedence&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get-precedence&lt;/span&gt; top-op&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next-op-precedence&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get-precedence&lt;/span&gt; next-op&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; next-op-precedence top-op-precedence&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                              &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-eval&lt;/span&gt; 
                                tokens-tail 
                                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; next-number num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
                                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; next-op op-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                              &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;num1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;num2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;num-stack-tail&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; num-stack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get-handler&lt;/span&gt; top-op&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                                &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt; num1 num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                              &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-eval&lt;/span&gt; tokens &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cons&lt;/span&gt; result num-stack-tail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; op-stack-tail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token-value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;car&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token-tail&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;cdr&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;my-eval&lt;/span&gt; token-tail &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt; num&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;上述代码定义了一个 eval 函数，用优先级解析的方法去解析上面写的函数生成的 tokens&lt;/p&gt;
&lt;p&gt;代码写起来会比 Haskell 版本的稍长一点，但是个人感觉非常清晰，语法也很一致和漂亮。个人感觉是 Racket 确实是一门非常漂亮的语言，也没有像 Haskell 玩各种概念这么烧脑，还是动态类型的，平时娱乐一下开拓一下眼界感觉非常好。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[关于函数式]]></title><link>https://diverse.space/2017/11/some-opinions-about-fp</link><guid isPermaLink="false">https://diverse.space/2017/11/some-opinions-about-fp</guid><pubDate>Mon, 13 Nov 2017 12:54:18 GMT</pubDate><content:encoded>&lt;p&gt;此篇博文整理了我和 &lt;a href=&quot;http://walkerlala.github.com/&quot;&gt;walkerlala&lt;/a&gt; 关于函数式相关问题的讨论&lt;/p&gt;
&lt;p&gt;Vincent Chan&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我觉得函数式很重要的一点是 Immutable，使得每一次更改都要重新生成一次整个数据结构（当然我们可以通过编译器优化来减少某些生成），但是immutable 也使得整个程序的状态可以被记录和更改。我之前写前端的时候就尝试了 Redux + React 这种模式。这种模式很有意思，我刚写起来的时候觉得超级别扭，写了很多无用的代码。但是当我开始着手用它来写一个前端项目的时候，我开始体会到了这里面的思想（虽然我仍然觉得很恶心和别扭，因为它尝试用 js 来写函数式)。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;我们都知道 html 界面其实就是一颗很庞大的树，有很多 html 节点，这些html 节点里面有很多子节点，每个子节点又有自己的属性（attributes）。当我们以前写的 jQuery 代码都在干什么？就是绑定一个节点的事件，然后更改另一个节点。想想看，我相信你写过 jQuery，我们先在这个庞大的 document 树里面 query 到节点，比如一个 button，然后我们监听它的click 事件，当这个事件发生时，我们 query 另外一个节点，比如一个label，然后修改里面的内容。这就是我们以前用 jQuery 做的事情。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;而 Redux+React 的事情是这样的，React 维护着一个 js 的假的 document tree，这棵树和真正的 document tree 比起来是很轻量级的，也很快，然后通过这颗虚拟的树去生成真的 document 树。每当一个状态改变了，然后，重新生成一颗虚拟树，然后和老的虚拟树做 diff，然后更新真的 DOM。这种做法看起来很折腾，但是却有它的用处：什么状态就对应怎样的 DOM，也就是你状态没错，你界面肯定就没错。想想之前 jQuery 的办法，逻辑多起来，你怎么保证状态一定是你脑中的那样。那我们怎么管理这个状态呢？这里就用 Redux 了：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;每当我们要改变一个状态时，我们就是要 sending a message，这个message 是一个数据，告诉 redux 我要更改什么，删除什么，添加什么……然后我们会定义一个 reducer，reducer 的作用就是通过一颗旧的状态树，接收消息之后，通过消息来生成一颗新的状态树，然后把新的状态树交给React 生成新的虚拟 document 树，然后再做 diff，然后更新视图。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;刚才 jQuery 做的事情，我现在用 Redux+React 的方法重新做一遍：把某个按钮绑定某个 meesage，当用户按下某个按钮，就发送一个 message，告诉 Redux 要更新某个 label 的值，Redux 根据 message 重新生成新的状态树，包含了新的 label 值，然后生成新的虚拟 DOM 树，然后和旧的比较，做 diff，然后更新真正的 DOM。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;当你开始用 Redux+React 重新写上面的流程的时候，你会沉浸在很多细节里面，因为要解决很多问题，但是你简化这个模型，发现这就是 FP 的思想，而这种思想来源于 Elm，这是一个 Haskell 写成的语言，语法是仿Haskell 的，可以编译成 js 代码，而这门语言所实现的，就是刚才所说的Redux+React 架构，当我们用 Elm 写上面这一切的时候，你会发现，这种做法用FP语言去做是多么自然，React+Redux 几百行代码才做好的事情，用Elm可以用不到一百行写出来，建议你去官网看看，代码真的非常漂亮。我觉得写 Redux+React 简直就是在人工编译 Elm 代码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;至于为什么 Elm 没有火起来，而 Redux+React 火了，我相信你也明白，Redux+React 是用 js 写的，可以和现有的代码很好的结合，而 Elm 要重写代码，Haskell 语法一般人也很难接受，加上 React 有 Facebook 的爹（摊手&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yubin Ruan&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hmm... 对我来说，FP 的 Immutable 的好处有两种：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;因为没有 side effect 所有比较好 reasoning；&lt;/li&gt;
&lt;li&gt;因为看起来像看数学公式一样所以逻辑清晰（同时也方便形式化验证）。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;但是你上面所说的状态，真的也是 FP 的好处吗？即使是一个状态对应一个虚拟树，但是如果逻辑多起来(e.g., 状态A =&gt; 状态B =&gt; 状态C)，怎么保证状态一定是脑海中的那样呢？JQuery 的确有这个问题(状态比较乱），但是 Elm 貌似也治标不治本...?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;当然，如果 Elm 是内嵌 Redux+React 的模式的话，肯定是比 jQuery 要好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;我其实写过很多 JS 代码，虽然没有正式看过他的语法（至今还分不清 === 和 == 的区别）。给我的感觉的确很操蛋...大括号换行还能改变程序的意思；闭包是假闭包，而且你还不知道这个闭包是真的还是假的（C++ 的闭包虽然可以非 Immutable，但是起码都会在闭包声明的时候看得到）...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;我觉得终究会有一门语言来操翻 js 的 (摊手&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;诶，如果C语言里面有 hygien macro 我觉得就完美了。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[初窥 Haskell：解析一个数学表达式]]></title><link>https://diverse.space/2017/10/parsing-math-expression-by-haskell</link><guid isPermaLink="false">https://diverse.space/2017/10/parsing-math-expression-by-haskell</guid><pubDate>Mon, 30 Oct 2017 20:26:27 GMT</pubDate><content:encoded>&lt;p&gt;最近在学习 Haskell，不得不说，这真的是一门令我着迷的语言，lazy 和纯函数式等特性都非常吸引我，不过短时间内还无法掌握得很好，最重要是思维的转变非常苦难。&lt;/p&gt;
&lt;p&gt;学习一门语言最好的办法就是多实践，我还记得我写过一片文章&lt;a href=&quot;https://vincentdchan.github.io/2016/07/parse-math-expression/&quot;&gt;编译原理学习笔记1：解析数学表达式&lt;/a&gt;
来讲述怎样去解析数学表达式，但是我没有讲如何去实现，现在刚好用 Haskell 实现一个。&lt;/p&gt;
&lt;p&gt;本篇文章所有源码&lt;a href=&quot;https://gist.github.com/vincentdchan/78435adcbb007df77e0c674201202925&quot;&gt;https://gist.github.com/vincentdchan/78435adcbb007df77e0c674201202925&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;gists 里包含了我曾经在我的作业里面用 C# 实现了一个过程式的数学表达式运算器，总代码 161 行，而 Haskell 版只用了54行。FP 的 pattern matching 在写状态复杂的程序的时候真的如虎添翼。&lt;/p&gt;
&lt;h2&gt;写一个词法分析器 Tokenizer&lt;/h2&gt;
&lt;p&gt;我们要把字符串解析成一个个 token，也就是我们平时所说的词法分析器里面的”词“，想想一个数学表达式里面有什么类型的 token？大概是只有数学符号和是数字？&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;haskell&quot;&gt;&lt;pre class=&quot;language-haskell&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Num&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt; 
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Plus&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Sub&lt;/span&gt; 
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Mul&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Div&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;deriving&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;注意我在上一篇博文里面提到的优先级问题，我们需要有一个函数来返回操作符的优先级，我们可以用 pattern matching&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;haskell&quot;&gt;&lt;pre class=&quot;language-haskell&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;&lt;span class=&quot;token hvariable&quot;&gt;getPrecedence&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;getPrecedence&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Plus&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;getPrecedence&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Sub&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;getPrecedence&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Mul&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;getPrecedence&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Div&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这种表达可以说是非常优雅了，然后开始写一个词法分析器……的定义&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;haskell&quot;&gt;&lt;pre class=&quot;language-haskell&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;&lt;span class=&quot;token hvariable&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Haskell 的类型定义，一个词法分析器输入一个字符串，然后输出一串 Token&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;接下来看我放大招&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;tokenizer&lt;/code&gt; 函数将调用下面这个稍微复杂一点的 &lt;code class=&quot;language-text&quot;&gt;_tokenizer&lt;/code&gt; 来完成词法分析操作，下面我来解释一下这个函数的参数有什么作用&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一个参数就是待解析的字符串&lt;/li&gt;
&lt;li&gt;第二个参数是暂存的数据缓冲区，如果我们要读入数字1234，那么要先把123先缓存起来，读入4后再一起解析&lt;/li&gt;
&lt;li&gt;第三个参数是&lt;strong&gt;之前已经读入的token&lt;/strong&gt;, 我们把这次解析出来的token连接进去，然后通过递归传给下一次运算，最后直接返回下一次的递归调用返回的值，这种写法叫做尾递归，可以减少内存的使用。关于尾递归这里不详细说。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;haskell&quot;&gt;&lt;pre class=&quot;language-haskell&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;&lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 尾递归写法&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 所有字符都解析完毕&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Num&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;reverse&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 字符都解析完毕，但是缓冲区还有数据&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Char&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;isDigit&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 当前字符是一个数字&lt;/span&gt;
        &lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 把数字放入缓冲区&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 当前字符不是数字&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;-- 检查缓冲区是否为空&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;-- 缓冲区为空&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 解析当前字符&lt;/span&gt;
                    &lt;span class=&quot;token char string&quot;&gt;&apos;+&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T_Plus&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token char string&quot;&gt;&apos;-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T_Sub&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token char string&quot;&gt;&apos;*&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T_Mul&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token char string&quot;&gt;&apos;/&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T_Div&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token hvariable&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Num&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;reverse&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- 缓冲区不为空，读取缓冲区字符&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：缓冲区保存的数字缓冲是倒序的，当我们用 &lt;code class=&quot;language-text&quot;&gt;read&lt;/code&gt; 函数读取数值的时候，要先用 &lt;code class=&quot;language-text&quot;&gt;reverse&lt;/code&gt; 函数反转&lt;/p&gt;
&lt;p&gt;然后 &lt;code class=&quot;language-text&quot;&gt;tokenizer&lt;/code&gt; 的定义就很简单了，我们词法分析出来的列表是倒序的，我们要用reverse函数来反转。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;haskell&quot;&gt;&lt;pre class=&quot;language-haskell&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;&lt;span class=&quot;token hvariable&quot;&gt;tokenizer&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;reverse&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;_tokenizer&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;表达式求值&lt;/h2&gt;
&lt;p&gt;到此为止，词法分析器就完成了，当我们对一串字符串调用 tokenizer 就可以得到一串 token 了，接下来就是如何对表达式进行求值，原理可以回顾&lt;a href=&quot;https://vincentdchan.github.io/2016/07/parse-math-expression/&quot;&gt;编译原理学习笔记1：解析数学表达式&lt;/a&gt;，这里讲讲实现&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;haskell&quot;&gt;&lt;pre class=&quot;language-haskell&quot;&gt;&lt;code class=&quot;language-haskell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- 定义操作符运算&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;evalOp&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;evalOp&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Plus&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;evalOp&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Sub&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;evalOp&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Mul&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;evalOp&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T_Div&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;`div`&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;b&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 搞一个好看点的包装函数&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Num&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;tokens&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 真正运作的函数在这里&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Int&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- 操作数栈里面只有一个数了，就是我们要求的值了&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;value&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- 操作符栈数值为空，进行SHIFT操作&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Num&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;numStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;tokens&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;numStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- 操作符栈不为空&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Num&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;numStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;topOp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;opStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;getPrecedence&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;getPrecedence&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;topOp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;tokens&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;numStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;topOp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;opStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- SHIFT&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;-- REDUCE&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;numStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;num1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
                &lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Num&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;evalOp&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;topOp&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;num1&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;opStack&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- 栈里还有一些残留的值，继续运算&lt;/span&gt;
&lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;num1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;numStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;topOp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;opStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;token hvariable&quot;&gt;_eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;evalOp&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;topOp&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;num1&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;num2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hvariable&quot;&gt;numStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token hvariable&quot;&gt;opStack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里 &lt;code class=&quot;language-text&quot;&gt;_eval&lt;/code&gt; 的参数&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;要解析的 token 集合&lt;/li&gt;
&lt;li&gt;操作数的栈&lt;/li&gt;
&lt;li&gt;操作符的栈&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;总的来说，在 pattern matching 的帮助下，写一个表达式求值的程序可以说非常简洁明了。当你用过程式语言去写 tokenizer 的时候，你需要控制一个指针在字符串上移来移去，非常容易出错，如果用 pattern matching，只需要定义好相应的字符串就可以了，非常优雅。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[用 Python 生成饼状旋转动画]]></title><link>https://diverse.space/2017/10/generating-spin-animation-using-python</link><guid isPermaLink="false">https://diverse.space/2017/10/generating-spin-animation-using-python</guid><pubDate>Sun, 29 Oct 2017 01:53:48 GMT</pubDate><content:encoded>&lt;p&gt;在写一个小游戏过程中，打算做一个饼状旋转的动画，于是打算用 Python 来生成这个动画，假设我们有这样一张图片&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 128px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38af76d5aae29e7a83399b6046f7f4d3/750d2/red-circle.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACV0lEQVR42q2Vy08TURTGb8GFgntNHzMk8jAkBmMgsBBbmA6PhL5oQQpCC2GpZUMkIAlbAol/gwb1LzFsURMTEtYs2CMYOr0/c+4UpcYoVG5yMif3nvPNd+ec841S1QUEiEZvVP1bvJzK6bnH73Su6zP53lNm+r7r8Qd7FGMfKGUyHB42mdgJ1Si56uICFZAD468VEzztPiDZDiOWxo3AYNA38WUv0QpTPQesF9ImRwmo+gWKUgGgkRep16TvgxvWOGGNa5VxLc2QjTHX0jpuecQjFRMjsZIjuaoKiFIN5lnKbJFqFyZlXLuCa/F3sys4wTKpNiilti5iKVbncvI2PRD0GLL1v8F+gmo9GPRIdiAY58W4Sb7nK/EQl2P2B6bxMEx1fwGaFaszOVMAJ1wHmG/aCXsk2mHlWVZRePKGYQvzsesFlNzRFij2v1Xkuj7hhuWgfoau5TEcgVTnrtLTvSemx/y2qM+kkLE7MB89Vsz0nV4LYFQAY9+Unni4Z6r031e2qldeHNhh5DqKYsNC7L1ieTLLWCs4kboZmlzBWJ7MSmM3Md1rGlu7tnf169oeTgjyPdLYt/1peVWcMIPuhGSO9RWK4c+zjN76Yq5WHJbS20YcnODZZZiamMHgGekOeJ7crhUHX74ClNKbZgzjIU08ItJVNhUcatHGfDkrmzOJSRqwTZOr1G8ie850bTbF5KN9Em0wamvTDrG7mMYVX/bG7onA7rNSyNYwq1VtAmxsNFT9ZpbzWT3bv0Oyc5f52LFeGDhhrOOj+QUsjac5OjIFkJyL7H4AQaJWMx1LAwEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;red circle&quot;
        title=&quot;&quot;
        src=&quot;/static/38af76d5aae29e7a83399b6046f7f4d3/750d2/red-circle.png&quot;
        srcset=&quot;/static/38af76d5aae29e7a83399b6046f7f4d3/750d2/red-circle.png 128w&quot;
        sizes=&quot;(max-width: 128px) 100vw, 128px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;最后生成的动画是这个样子的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/red-ani2.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;当然，中间会生成很多帧的动画&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/41eb5fcf99a8e3f6b23642b5889142e3/712f7/red-circle2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.30232558139535%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAABYlAAAWJQFJUiTwAAACU0lEQVR42iWOP0wTURyA366DRBfjgDujfxIxDGiig5Nx0MTEAU1YSAiMJkqiCwoKSkEQamjLv7YgxbS0ioBAJYAgpoWCjW2hVI7ru7v2jvZ699679zON25cv3/AhntzhT+pYZJWvzfOXzSyzx0Nuy9nBhAMYemMtBVlql7s6WGKHry9y3yCVjnhoDFbnVFFA3NML5xDpbuGtjVCJqM8Bj65bVyvo7CRUn2J11+iUA6oQfdFk2Z5CFSLeft54G2pOS9+/olJOxsPd6n5S3U/ioe5jTVPXw1nvhyKhSsCtzPuLJSPrtOW3NwuigAfaj0Uhv/VTcnVlhUPEYhtmyKMXilpoUlxZwAVKP7lK6QTJyaa336CUxrdM/7ABQMOfjZU5E4D4HCSdkIo64qM2qEIle5vRUg+XTmh+D39Ya927SOemoOaM1drIPtrh8knqH7LamuFGJY2u8bpaaLiVy+whXc1jl02Jx9LJgz897XvpIyU8Izi7CqYpB8fl4Liml+SR3tzGsiZlsf1VPp1Ud6N4sFPFImKRNTPkMQBIaDwTno3sZ5Ji9pAwJmfJaI8BYMajxOcoby8EzOWZ8vaEnaR+Fy1A3NUJtWdpIgaPH9D7V5R0Sn/eQByvaWoXqiusrR8sOAY3z1PLsp7VQ9MdCsDvXgBvnw6ACpqKh3uU7U1V+Cs532oSVpdn8cg7zSTyl0llPqCVDOwekHciGhaxuz+fy6mxX+K0N1/UEYusksCICUCnR+nGYhm8781Mih6rzNVJOCfxKJkYKPtvfrIU/B/QRKxArX/dvNjPc6yqwQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;red circle2&quot;
        title=&quot;&quot;
        src=&quot;/static/41eb5fcf99a8e3f6b23642b5889142e3/914ae/red-circle2.png&quot;
        srcset=&quot;/static/41eb5fcf99a8e3f6b23642b5889142e3/2eb24/red-circle2.png 215w,
/static/41eb5fcf99a8e3f6b23642b5889142e3/05ed2/red-circle2.png 430w,
/static/41eb5fcf99a8e3f6b23642b5889142e3/914ae/red-circle2.png 860w,
/static/41eb5fcf99a8e3f6b23642b5889142e3/712f7/red-circle2.png 1181w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;怎么去生成每一帧动画呢？其实关键就在于挖去一个饼状图形里面的像素&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 128px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a236b82cf5500f6f0b887cbfd78a486d/750d2/red-ani3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACv0lEQVR42p2UTU8TURSGb8GFClstTjt3iAoYNhClARfYj/koJO20OgUpCBRkp5YNEUGTbglN/A0q0YWJ/8KwMtGYmJAYY6IbiFvEhLb3NffMtLSVGnSSm7m5555n3jPngzHvAeBDOHzK25/Bw6kM5kZfwBnYEdnQV9xN+snGmM97t9F7grVLX1b/AMwnDbRfzyVxe+gz7F6IOC/DUoGosovr/vNkL1RBIwE8mrGrcMk4AjLmA9COB6mnSF8BzKCAHhQw1UPENcDkuzAvusAwc6Mw1SKcfiBvF8nXU34kPX9zE6leIKaUYGoVmFyCKi5QrQFdmDYoLO0AsYCA3QPkU5v1LIa1uYxUJqJKGZYmPFhroMXfYLxb2ksippRh90Eyqsk4jWzoE4wA6pT9CYzzCx4sQWcG9z6sVWAEgamhjwA6GNZmMjIB0IPNsBpQGOoexrrO0c+3+DuMEbB2X+jBMpK9wOodh2H+xjPEOYTBy8cCLUrKD5j+Dpj8XjXU+nvkK89zo88ZMgMfYAZd55YK+TdY/Bos/t3LumgAmryMuAqk+reZmB4+QEyBp6R5CQ/wBRZ/3RxqbclERvzAQnifYWbk11+A7jJ4hVS0sktgWAIjP5mYGHxPWTo+5HqoaGVzQ+ZeyEvRLYy1TMqJlpsUDViMvGRYmXSQuAzoauV/geQrGSuTjizss5gepsIWpvbPKslHDwDZkCzsTrdbHucmaCjoAdnH4sRAS6tAV0rUek+WMo3DYTldpOGgK4cnUUp3Ysoh0n3AfbvYOBzc8eVDPr1BbWgEBAy1RM0vM2h1C1puh5TIJu/YBNsgX8aahmxV6fpsCpNXd5DsAcY1QeUQ6QIVrtzLs8QlYCq0g9V5p0FZ49SGD4VCm7fvwErWEbOjW7D7t7EQ2ReL0QMk+t4iF3mF5Vtp7O11uhO80Fav7jfYnii3Y2lpVgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;red ani3&quot;
        title=&quot;&quot;
        src=&quot;/static/a236b82cf5500f6f0b887cbfd78a486d/750d2/red-ani3.png&quot;
        srcset=&quot;/static/a236b82cf5500f6f0b887cbfd78a486d/750d2/red-ani3.png 128w&quot;
        sizes=&quot;(max-width: 128px) 100vw, 128px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;那我们只要遍历这张图片的所有元素，然后挖去在这个饼状范围内的像素即可，从这个思路出发，我们可以很容易想到使用极坐标。假设我们有两条极坐标空间里面的直线 &lt;code class=&quot;language-text&quot;&gt;p = Theta1&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;p = Theta2&lt;/code&gt; 那我们只要把这两条直线中间的像素挖掉即可。当我们遍历所有元素的时候，我们需要判断这个像素点在极坐标空间中的位置。当然，我们坐标的圆心是整个图片的中心，所以对于每个像素点，我们要减去整张图片中点坐标，求出的相对坐标再作 atan2 计算。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://wikimedia.org/api/rest_v1/media/math/render/svg/86ad33236e019c6a82a8dd674f266fb70f51ac57&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;用 math.atan2 求出 theta 之后若在 theta1 和 theta2 之内，则删去即可，所以说核心代码其实很简单：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SIZE&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; y &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SIZE&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            relative_point &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; CENTER_POINT&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; CENTER_POINT&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            theta &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;atan2&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;relative_point&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relative_point&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            theta &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; theta &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;180&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pi &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;180&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; theta &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; theta1 &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; theta &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; theta2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                draw &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ImageDraw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Draw&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;img&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;RGBA&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                draw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;point&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;# write null pixel&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最后附上源码 gist 地址:
&lt;a href=&quot;https://gist.github.com/vincentdchan/e120f3cacf88efbba3e51fa1b0084086&quot;&gt;https://gist.github.com/vincentdchan/e120f3cacf88efbba3e51fa1b0084086&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[HTML Canvas实现《黑客帝国》数字雨]]></title><link>https://diverse.space/2017/06/matrix-rain-by-html-canvas</link><guid isPermaLink="false">https://diverse.space/2017/06/matrix-rain-by-html-canvas</guid><pubDate>Mon, 19 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;《黑客帝国》是我最喜欢的电影之一，我感觉《黑客帝国》里面的数字雨简直是一种艺术，非常好看，看上去非常高大上，而且和“雨”这种东西结合，非常有意思，于是想自己实现一下。&lt;/p&gt;
&lt;p&gt;效果图:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d0dab848e704fc95b1559329d05867f9/21b4d/matrix-ani.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.906976744186046%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA60lEQVR42l2R2w7EIAhEB7xV2802+///yqY6tKQPRgN4BgYgwVBg0sTkJ4YBExWDwmYuw9Bg6Lz9DeZl1cgQw/eKFxgqP1YeYXEEbjyVtwMpLJuY7GKYBW11eR+q3iIO3mFpT3OKG+i54UJxDE/qC+ixDSZFDB8Cc2jicGAnvfNzBHt3hUAwdoYpHJiYu8aYylkenxw2Ho/uBaS1wBmvhLUIPEInyoIczI9+OryFWGGdTzX9yK+CzNEHgSX4Kq/tu/+F9RPYWZDCGJfqZ8V06CME5mvw/ozAPSxCYVp1bbI/QlpewGhJ5YbZ9R9EesvNMaIOVwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;matrix ani&quot;
        title=&quot;&quot;
        src=&quot;/static/d0dab848e704fc95b1559329d05867f9/914ae/matrix-ani.png&quot;
        srcset=&quot;/static/d0dab848e704fc95b1559329d05867f9/2eb24/matrix-ani.png 215w,
/static/d0dab848e704fc95b1559329d05867f9/05ed2/matrix-ani.png 430w,
/static/d0dab848e704fc95b1559329d05867f9/914ae/matrix-ani.png 860w,
/static/d0dab848e704fc95b1559329d05867f9/21b4d/matrix-ani.png 1280w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;在Canvas里面实现动画的思路其实很简单，在很短的时间里面刷新一次就行了。每次刷新的时候，字符都会下降，然而数字雨里面并不是整串“雨”往下移动一个单位，而是这个雨的最下端，显示了一个新的字符。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/matrix.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;所以我们不妨换个思路，就是其实每个字符的位置其实是固定，每次刷新的不是每个字符的位置，而是刷新他们的颜色，让一个字符的颜色从最绿慢慢变黑，然后又变最绿，然后慢慢变黑……周而复始。最后把每个字符用Canvas在对应的坐标画出来就行了。&lt;/p&gt;
&lt;p&gt;当然这当中有先后顺序，一列里面，第n个字符的颜色应该刚好比第n-1个字符少一个梯度，也就是说&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;第i秒的时候：
    第N个字符的颜色为 A
		第N-1字符的颜色为 A -1
	
第i+1秒的时候
		第N个字符的颜色为A-1
		第N-1个字符的颜色为A-2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;当然了，为了要让每一条雨条下落的先后顺序不一样，必须给它们的颜色加一个随机的偏移量，然后取模，这样就会有先后落下的效果。&lt;/p&gt;
&lt;p&gt;最后附上源码，需要用Babel编译一下&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/vincentdchan/aa426fc9767b2508d79fbefc87c659fd&quot;&gt;Github gist 源代码&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[写一个光线追踪渲染器]]></title><link>https://diverse.space/2017/05/writing-a-ray-tracing-renderer</link><guid isPermaLink="false">https://diverse.space/2017/05/writing-a-ray-tracing-renderer</guid><pubDate>Thu, 18 May 2017 00:01:00 GMT</pubDate><content:encoded>&lt;p&gt;最近都在做一件事情，做一个光线追踪渲染器，一直很想揭开光线追踪渲染器的大门，于是跟着 Milo Yip 的步伐开撸了&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.cnblogs.com/miloyip/archive/2010/03/29/1698953.html&quot;&gt;用 JavaScript 玩转计算机图形学(一)光线追踪入门&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;渲染结果图：
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6f3405112aa6623034871f512c1a8cd9/2bef9/renderer-img.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD/0lEQVR42qVT209bdRz/nUvPpefSnnN6OT09LYXSQltKO25by5zQXbg4IFAoGtTAKBNIWTKCiFxaaAmUlJU1xcEcmZLgpnOyhS1bYqISMxMflpioBGPETB981H/ArIbuZYlZfPCT79P3c8nn4fsF4P8Dys8zwC+U/Fv7goj/4p8TSQjSSpIRlh3juB6WNaLo8xUgRImxZZTUrC3pNTjeJFX2/DbPW1D0PE3PqdVrJtOXPt+vzc2fu921OP7MD6MUpq6gpFfk8sGW3q2Z1Sfnpr4TCk4dOnEIaiCIPoq6yHHrLtdvkUhue/tpNrvr9Vrhw2wE4xWsg9bXlh8fml5+9M1+bufbXGPvfQjTAjUMn8DxDpIcYNmlkpIfBwdzDx/m7t37pTN0nqYFHBeVLMBFpeAprXwtPLq5dvNg4epe3as3SG0tUELQEYJoZtk3BGHMYvmgvv6HSOTnCxfuvnTiXbXar9PZOQ5SMIxQItsCRwODjaGFE63zlupJQvABGIbtPH9SFDsMhn6z+aLVmqyoSHk800ZjH01bFApRSfKChteYDQVVcnGgsCxocffI7kEFXQQwDFMxTIlG48bxl1l2wOnsNZvPGY0jhYXHaJoiSQJFBZ6naAbAlFxYbXM3aSwNorWJJCnQ1tbm8XoFni82GFwajZdla2i6CsOO6/X+ysqqmhq9TmezWGRZFg0SJ5gUStlY5K85VmctKgTBzs5IJNLS0lLqcLjLyvQcRyNIqcVy5vTpxqYmv99vt9tlWXY6nXpRD8OIaDDZ7KWiqHe7y0E4HI7FYpOTkyMjI6FQSKfTAQBUHMcwjCAIw8PDExMTMzMzqVRqaGjI5XIZDCJB4BAESZIEnE6nw+EIBoPxeLw/HO5obw8Fg51tba93dbWePVtVXe3z+U4FAvV1dYFAwOMppyjG463oygNIktTd3T07N5dOp1czmWwqtba0tLmycmdt7XI0SjMMAIBQKHAMQ1Hk8OYQQsWbnC7vQLgfJBKJVB6L8fhKPL6VydxIpz9ZXr67vPz++LisUmEwTFE0juMwDKs4rVqQCdoIowIEQWB6ejoWjV5OJq8uLW2mUp9lMl9sbDy6fv3rK1c+npqya7U0iuIEThCEklapeInXWdUau5JzIBgDYtFocnb2vUTiw2Ty00uXvrp27cmDB3/u7v5++/bNqSmzRsMetoVQhZJWiQaTo9jhL7D5WX0VQujBfCyWjcc34vGt+fn76fRPt279vbeXOzj4a/vO6vg7JE3nnw+HFQyllosd/rqGnpPN/TZPO0YXgIXZ2Y1E4qP5+e3Fxd1s9o+dndz+/tPHj79fX397dJQ6NCOIgkVwDcNbXUfOBHvG+oYXjgbewpTSP5wjAweOG5HAAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;renderer img&quot;
        title=&quot;&quot;
        src=&quot;/static/6f3405112aa6623034871f512c1a8cd9/914ae/renderer-img.png&quot;
        srcset=&quot;/static/6f3405112aa6623034871f512c1a8cd9/2eb24/renderer-img.png 215w,
/static/6f3405112aa6623034871f512c1a8cd9/05ed2/renderer-img.png 430w,
/static/6f3405112aa6623034871f512c1a8cd9/914ae/renderer-img.png 860w,
/static/6f3405112aa6623034871f512c1a8cd9/2bef9/renderer-img.png 1024w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;其实制作的过程是非常坎坷的，我首先用 C# 按照博文的思路重写了一边，渲染出结果（1024x1024），总共耗时6秒，结果令人很不满意，觉得有点慢，于是打算用 C++ 重新实现一遍，使用 MSVC 编译器，第一次做出来的结果是2.1s，比 C# 快一点，但是优势还不是很明显。但是我做了如下改动之后，性能得到了极大的提升：&lt;/p&gt;
&lt;p&gt;本来的写法：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;shared_ptr&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Result&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;make_share&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Result&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;改为：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Result&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; xxx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;经过这一改动之后，渲染实践从原来的 2.1 秒瞬间缩短到了 0.6s，有点令人不可思议，个人猜测是 shared_ptr 进行频繁的分配和释放空间导致的速度大幅度变慢。0.6s 的渲染时间让我觉得很满意，本来想继续做下一步实验，但是不知道怎么的，我总是觉得程序还能优化。于是我看了看 Profile，没发现什么问题，我觉得该改的都改了，再改可能也得不到大幅度的提升，后来我往另一个方面想：多线程&lt;/p&gt;
&lt;p&gt;光线追踪算法每个像素的计算之间没有任何关系，这意味着，可以同时进行计算，然后最后把结果合并，pbrt 也讲了这种思想，于是我也试试。一开始我使用了 Windows 的 API，可是我没有什么 Windows 的编程经验，Win32 的 API 都是现查现用，于是出现了一个我解决不了的问题，就是我用WaitForMultipleObjects 函数去等待子线程结束，调度线程才结束，可是每次调度线程自己先结束了，WaitForMultipleObjects 函数没起到作用，我检查了结果，在正确范围内，也没发现问题，但是就是不行，没办法解决。&lt;/p&gt;
&lt;p&gt;后来采用 boost 的 thread 框架，成功地使用了 join，调度程序等所有渲染都完成了才结束，由于我使用的电脑的 CPU 是 i5-5200U，双核四线程，所以我开了四线程，进行渲染，总共耗时 0.3s，速度可以说是加快了许多，我尝试开八线程进行渲染，也是 0.3s，速度没有得到提升，说明四线程在我的电脑上应该是最合适的。&lt;/p&gt;
&lt;p&gt;多线程的问题解决了，然后就可以进行下一步的实验了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[从头打造一个 Markdown 编辑器（一）：数据结构和展现]]></title><link>https://diverse.space/2017/01/markdown-editor-1</link><guid isPermaLink="false">https://diverse.space/2017/01/markdown-editor-1</guid><pubDate>Tue, 03 Jan 2017 10:26:00 GMT</pubDate><content:encoded>&lt;p&gt;刚开始写这个编辑器的时候，我是毫无思路的，就是完全不知道如何下手，后来去翻了一下 CodeMirror, ACEditor, VSCode 这些优秀编辑器的代码，但是我没有全看，因为我要我的编辑器大部分都是我自己想出来的，只有我想不到的时候才去看。
首先我们需要一个数据结构来储存我们的文本，为什么要用数据结构而不直接用 string，是因为编辑器需要大量的增删查改操作，而当一个文本很大的时候，string 是不够快的，因为 string 的增删查改操作的时间复杂度基本上都是 O(n)，还不够快。&lt;/p&gt;
&lt;h1&gt;数据结构 Data structure&lt;/h1&gt;
&lt;p&gt;一开始我打算用的是 Rope，是因为看上这个数据结构足够快，后来弃用了，是因为它和我的编辑器的 View 部分的构想不太一样，很难融合在一起，另外就是它本身自己也比较难实现，所以我就用了很简单的 chains of lines 来实现了，就是用一个数组，里面存的是每一行的内容。废话不多说先上代码&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/MDE/blob/master/src/model/textModel.ts&quot;&gt;/src/model/textModel.ts&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; _lines &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LineModel&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_string&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ctor&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// other methods&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/MDE/blob/master/src/model/lineModel.ts&quot;&gt;/src/model/lineModel.ts&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LineModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; _number &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; _text &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_num &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _t &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ctor&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// other methods&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;数据结构就是这么简单，就是用数组把每一行的内容都存起来存起来。在这里我用了一个 LineModel 的类来储存，是因为我还要实现一些别的方法，比如最典型的增删操作。当然我们实际上要实现的数据结构的操作不止那么多，至少要把insert, delete, replace这几个操作都实现了才行。这样我们文中所有的字符都可以用一个对象 Position 来表示，就是行+位移&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    line &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    offset &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;而一段文字，也就是我们说的 Range，或者说选区（Selection）则可以用两个Position 来表示：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Range&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    begin&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Position&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    end&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Position&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;想想我们要做编辑器要做的操作&lt;/p&gt;
&lt;p&gt;1.插入（Insert）: 在文本内的一个位置（Position）插入一段文字（string）&lt;/p&gt;
&lt;p&gt;2.删除（Delete）: 删除掉一段范围（Range）内的文字&lt;/p&gt;
&lt;p&gt;3.替换（Replace）: 把一段范围（Range）内的文字替换为一段新的文字（string）&lt;/p&gt;
&lt;p&gt;&lt;samll&gt;其实替换操作很容易理解，我们先删除，再插入即可，所以我们主要实现 Insert 和 Delete。具体的实现方法可以参考我的源码&lt;/small&gt;&lt;/p&gt;
&lt;h1&gt;展现 Presentation&lt;/h1&gt;
&lt;p&gt;下面讲讲如何展现（presentation)，就是如何通过 HTML DOM 操作把数据结构里面的数据展现出来，我想大家都已经想到了，一个 LineModel 对应一行，一个父 DOM 包含着每一行的 DOM&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fbbaf443f03da85035ccb2a80b3965f1/c02c7/screen-present.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.930232558139537%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAwElEQVR42j3Oy0oEMRBA0f7/73FABlRwKeJjIy4EBRlxpqtSSSX1SDrd8YG4vXDgTg+3T+e7q/3u8u351aLxKUlyr6PWAchZDU9yeE+EArNS8Hk2AJtnM+vTzf3L2cXd/vrx+BkXH6bdbHMfrQ4Azuo5OWOk1DiZcI6xlrJwbqp9yrm2tvY+at1EVtXVffzjKF7Y8JhCUIACWAAkxhp5UesTkeJPyiEIYiHSX7z9bYsRyMeBUxAKFqkhegiO6N/bX/Tg4NX7kqgnAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;screen present&quot;
        title=&quot;&quot;
        src=&quot;/static/fbbaf443f03da85035ccb2a80b3965f1/914ae/screen-present.png&quot;
        srcset=&quot;/static/fbbaf443f03da85035ccb2a80b3965f1/2eb24/screen-present.png 215w,
/static/fbbaf443f03da85035ccb2a80b3965f1/05ed2/screen-present.png 430w,
/static/fbbaf443f03da85035ccb2a80b3965f1/914ae/screen-present.png 860w,
/static/fbbaf443f03da85035ccb2a80b3965f1/46115/screen-present.png 1290w,
/static/fbbaf443f03da85035ccb2a80b3965f1/6c86f/screen-present.png 1720w,
/static/fbbaf443f03da85035ccb2a80b3965f1/c02c7/screen-present.png 2472w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;看图可知，MDE 里面每一行其实就是一个 &amp;#x3C;p&gt;，所以其实没什么神秘的东西，不过就是加上了行号，还有 Syntax Highlighting 而已（这个我们后面会说）。怎么把上面提到的 TextModel 编程 HTML DOM 元素了，自己写个遍历器遍历一遍就好了，在这里不推荐自己拼接 HTML 字符然后用 innerHTML 更新，这样一来效率低下，二来需要手动过滤字符，三来不方便我们后续的更新。&lt;/p&gt;
&lt;p&gt;遍历一遍 TextModel，然后用 document.createElement 好了，这里你可能需要一个方便的工具类&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/MDE/blob/master/src/util/dom.ts&quot;&gt;/src/util/dom.ts&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;elemName &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; className&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; _elm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;elemName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;className&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _elm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; className&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; props &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            _elm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; _elm&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;具体的实现大家可以参考下列几个文件，不过因为已经实现了 Syntax Highlighting，可能现在的版本已经很复杂了，初学的话看可能有点压力&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/MDE/blob/master/src/view/viewLine.ts&quot;&gt;/src/view/viewLine.ts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/MDE/blob/master/src/view/viewDocument.ts&quot;&gt;/src/view/viewDocument.ts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;到现在为止，你可能已经知道怎么把 TextModel 里面的内容展现出来了，但是做一个编辑器，仅仅这样还是不够的，下一张讲讲如何更新视图。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[从头打造一个 Markdown 编辑器（序章）]]></title><link>https://diverse.space/2017/01/markdown-editor-2</link><guid isPermaLink="false">https://diverse.space/2017/01/markdown-editor-2</guid><pubDate>Sun, 01 Jan 2017 09:13:00 GMT</pubDate><content:encoded>&lt;p&gt;开始写文章之前，先打个小广告，就是小弟新鲜出炉的作品 MDE，一个完全免费的，开源的，基于 Electron 的 Markdown 编辑器&lt;a href=&quot;https://github.com/vincentdchan/MDE&quot;&gt;MDE Editor，点击链接&lt;/a&gt;进入GitHub&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2c8b1f22315d52bfaada2c27347674af/c2d13/screen-mde.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.48837209302325%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABhElEQVR42m2Q226rMBRE+f+P6nlIH5oUAQGpJFzVggPGYAo2VxvvCnKaNm2X5mFL1sgzoz08/DscDqZpGvfoup4kCQAIIeZ7pg2Msfb4qFvWi+OcbNu9yXFOhuEYhqnrOkIIAJRS8MmyLABgWZbWV0yOQq3Pd5JyOZ+93W7HGPvTbNu2xoqma4ZhhGFQ39V10nXPcRzDL77MbcXGQU4TTBPMM9yOYVC+H+Z5ppSSG8uGlFIIAQDH41G7XKqmHTmf23biXDA2cy62WyKURVFAKS2KAmNcVSWl72VZN02jlFo7I0TStEhSkqTkklWUcko5xnVd9xgXCGUIXbquY4xxzuqaj+Ma/P9gSYJRRmla0bQq0rIs2WfshRCSpqyuux+dr+N5nreaCWmHTg6dHNedxHWwvl+yLC+Kqm3ZteqtuZQSAPI81xDCGNeEvDdtfz+porRESGw5//iZEKI9PRnPz0fXDV03PJ2im9yXwDQtz/OCIAjDVUHgv72+RlHk+34cx/v9/gNBQWY2hiZYOQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;screen mde&quot;
        title=&quot;&quot;
        src=&quot;/static/2c8b1f22315d52bfaada2c27347674af/914ae/screen-mde.png&quot;
        srcset=&quot;/static/2c8b1f22315d52bfaada2c27347674af/2eb24/screen-mde.png 215w,
/static/2c8b1f22315d52bfaada2c27347674af/05ed2/screen-mde.png 430w,
/static/2c8b1f22315d52bfaada2c27347674af/914ae/screen-mde.png 860w,
/static/2c8b1f22315d52bfaada2c27347674af/46115/screen-mde.png 1290w,
/static/2c8b1f22315d52bfaada2c27347674af/6c86f/screen-mde.png 1720w,
/static/2c8b1f22315d52bfaada2c27347674af/c2d13/screen-mde.png 2560w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;截图为在 Windows 10 上运行的效果&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9ef094f703837e7c78add9c69ca7b7a2/c2d13/screen-mde-night.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.48837209302325%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABeElEQVR42m3Qy46bMBiGYRMgAXzABxgwYAwKgZRhVKRcbGdT0UWnSsIqd9jiBimK8sjbV59+g88fn19ff+Z5Pp/Pl8vluprn+Xq9/jKmafpp/F5N03S73YBUrGpjtRc0pJTSIAh8IwiCzCjLsqqqvu/HcbRtGwBgWRYAoK5rsN/XzaHOC5lly6OUYgMhFIkoLwtd6crQWj/Gvu8DLoSIYoxDMxYgBPGK0LDiMoYUWJa9WYAH2+0WyFiUKq10plWaZalMU/K/xCTAqEKJ8Al4Zbfbge8fw8fQv79/aw8NNe6zhEAIkzQ9nU5RFD3N3uOm2WutCQkhRAuMHmMpZdd1SqkwDF/Eh6bp+/7tLSYEPyGEcM6Xj1k/6UXctoe2bTnny/AKISSEGMcxSZLNZuO6ruM4tuE4zj3WWjNGnzYRxj6EQoiu6+q6llIqpbIsk1LmeV4UBefc8zyAEYJ35mS8nM1CnHDMGDsej8MwMMYAAK7rep63Wf1b/gta+Eqh9lljBgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;screen mde night&quot;
        title=&quot;&quot;
        src=&quot;/static/9ef094f703837e7c78add9c69ca7b7a2/914ae/screen-mde-night.png&quot;
        srcset=&quot;/static/9ef094f703837e7c78add9c69ca7b7a2/2eb24/screen-mde-night.png 215w,
/static/9ef094f703837e7c78add9c69ca7b7a2/05ed2/screen-mde-night.png 430w,
/static/9ef094f703837e7c78add9c69ca7b7a2/914ae/screen-mde-night.png 860w,
/static/9ef094f703837e7c78add9c69ca7b7a2/46115/screen-mde-night.png 1290w,
/static/9ef094f703837e7c78add9c69ca7b7a2/6c86f/screen-mde-night.png 1720w,
/static/9ef094f703837e7c78add9c69ca7b7a2/c2d13/screen-mde-night.png 2560w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;支持的功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;即时渲染&lt;/li&gt;
&lt;li&gt;Markdown 语法高亮（Syntax Highlighting）&lt;/li&gt;
&lt;li&gt;白天模式和夜间模式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第一个已经版本在2017年元旦发布了，现在我只打包了 Windows win32 64 位版本，有兴趣的朋友可以下载来跑一下。迟些我会打包 Linux 版本，至于 Mac 的话，我身边没有设备还真不好搞。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vincentdchan/MDE/releases&quot;&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;其实现在这个时代，好用，能用的 Markdown 编辑器已经很多了，比如我很喜欢的 Typora，CMD Markdown，还有各种编辑器加上 Markdown 插件也很好用，加上 Markdown 语法比较小众，所以说自己撸一个其实意义不大，但是为什么我还要写这样一个编辑器呢？&lt;/p&gt;
&lt;p&gt;一来其实是我一直很好奇我们平时用的这么多编辑器是怎么实现的，这方面的资料也不多，于是打算自己从头撸一个，看看能不能掌握一些知识。&lt;/p&gt;
&lt;p&gt;二来我是广州某高校的 CS 学生，网上很多人说现在学校出来的学生什么都做不了，所以我也写个能用的东西来证明一下自己对吧&lt;/p&gt;
&lt;p&gt;三来，这个程序是开源的，免费无广告的，希望有志同道合的朋友可以一起学习，交流，所以我会把我开发过程的一些想法和心得都写出来，我会在知乎专栏发表一系列关于这个编辑器的文章，这个只是序章，希望有朋友可以一起交流&lt;/p&gt;
&lt;p&gt;在这个序章，我简单介绍一下我做这个编辑器需要的一些东西，我的编辑器是基于 Electron，主要是看上了跨平台的特性，以及我对 Javascipt 比较熟悉，对 Typescript 比较感兴趣的原因，我的编辑器是用 Typescript + Electron 实现的，我的文章主要也是讲这个，如果有要动手的朋友可以选择自己喜欢的框架和语言，打造一个 Markdown 编辑器的旅程就要开始了，你准备好了吗？&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Syntax Highlighting之后的视图更新]]></title><link>https://diverse.space/2016/12/views-update-after-syntax-highlighting</link><guid isPermaLink="false">https://diverse.space/2016/12/views-update-after-syntax-highlighting</guid><pubDate>Fri, 16 Dec 2016 09:56:00 GMT</pubDate><content:encoded>&lt;p&gt;前一篇blog讲述了如何给编辑器做Syntax Highlighting，可是做完之后要怎么样给视图更新才会做到更高效呢&lt;/p&gt;
&lt;p&gt;毕竟我们采用这种Syntax Highlighting的方法， 就是要保证高效，如果不能好好利用，那么我们的这种方法就没有意义了。&lt;/p&gt;
&lt;p&gt;我们知道，当一行改变了之后，这行下面的状态可能全部都会改变，所以这一行包括下面所有行都要重新进行Tokenize，毫无疑问，如果文件很大，如果用UI线程进行Tokenize，可能会造成卡顿。不过就算是一个几千行的文件，每输入一次，进行全文Tokenize，全文扫一遍，也是瞬间完成的事情，我们大可不必为此操心，但是谁知道会不会有其它情况，比如突然发生了GC，或者输入者手速太快了。当然，这层我们是不知道的，但是我想到了更好的办法。&lt;/p&gt;
&lt;p&gt;一行发生了改变，那么这一行要马上更新，这时肯定的，也几乎不会消耗什么时间，就算一行有好几百个字，也几乎可以忽略不计，所以我的编辑器在输入这一行的时候，会马上进行Tokenize，马上进行上色。但是这一行下面的行却不要求马上进行更新，因为如果正在输入这一行，只是输入了一些正常的字符，而没有触发什么加粗什么的，那么下面的行其实无需更新，就算触发了，我们也不要求马上进行更新，对吧。很明显下面行数的更新就要异步进行。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 623px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/05ee6c32981bb80cd2f4c5820305f335/6114d/RenderLine.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.06976744186047%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABU0lEQVR42mNgoCf4//8/IxYxpv///zM7OzsLx8bGCjMwMHBevHgx5fr160lkWyQjI8MpLi4uBuUyP3/+nLulpUVSS0uLDe6SVatWMYNsDw0NZQYp2r9/v8jVq1dVQYrq6+u5jI2NWdPS0kRevHhR/+zZs50PHjzY9/bt24nr16+XBZnh4uLCDzcQGZiamoK8wbpt2zbla9euWSJJcYSFhRm9fPly76tXr/4+f/789+vXr2+uXr3akIGBgd3Dw4OPgYGBkWHbtm3sd+/eDbl9+3bVvXv38ufOnWv+6NEjod27d/OfO3dO//bt28X3798vPnr0qLekpCQXyOT6+nomEMYaHsuWLZN68eLFuZcvX/598+bNj1OnThWcP3/eccWKFY6XL1+e9fTp078vXrz4e+fOnY2gCIBqY0SLOEZkMTCwt7fngIYfCkhLS2NVUVFhJzbGAPItm35lSP1xAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;RenderLine&quot;
        title=&quot;&quot;
        src=&quot;/static/05ee6c32981bb80cd2f4c5820305f335/6114d/RenderLine.png&quot;
        srcset=&quot;/static/05ee6c32981bb80cd2f4c5820305f335/2eb24/RenderLine.png 215w,
/static/05ee6c32981bb80cd2f4c5820305f335/05ed2/RenderLine.png 430w,
/static/05ee6c32981bb80cd2f4c5820305f335/6114d/RenderLine.png 623w&quot;
        sizes=&quot;(max-width: 623px) 100vw, 623px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;我们看这张图，就是一行的&lt;strong&gt;渲染状态&lt;/strong&gt;图了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Null -&gt; 没有进行渲染&lt;/p&gt;
&lt;p&gt;Plain Text -&gt; 渲染了，没有上色，是纯色文字&lt;/p&gt;
&lt;p&gt;Colored -&gt; 已经完成了上色&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们渲染有两种模式，分别是立即（Imd）和延迟（Lazy）更新，这样我们可以有下图的表格&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Null&lt;/th&gt;
&lt;th&gt;Plain Text&lt;/th&gt;
&lt;th&gt;Colored&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Imd&lt;/td&gt;
&lt;td&gt;马上在UI层进行渲染，跳到Colored状态&lt;/td&gt;
&lt;td&gt;马上进行上色，跳到Colored状态&lt;/td&gt;
&lt;td&gt;立即进行重新渲染，更新&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lazy&lt;/td&gt;
&lt;td&gt;先渲染成纯色的字符，不进行上色，跳到PlainText状态&lt;/td&gt;
&lt;td&gt;调用后台线程进行上色，上色完成后调用Callback，然后更新UI，然后跳到Colored状态&lt;/td&gt;
&lt;td&gt;调用后台线程进行重新上色和渲染，完成后调用Callback更新UI层&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;所以如果是更新&lt;strong&gt;正在编辑&lt;/strong&gt;的行的话，就调用Imd模式进行渲染和更新，如果是&lt;strong&gt;正在编辑下面的行&lt;/strong&gt;的话，就调用Lazy渲染和更新。&lt;/p&gt;
&lt;p&gt;当然，由于我是用NodeJS进行实现，所以就没有办法调用后台线程（Thread），只能通过IPC调用后台进程（Process），不过原理是一样的。&lt;/p&gt;
&lt;p&gt;但是关于后台更新有一个问题，就是后台Tokenize完成以后，用户已经又更新了，比如说我第7行完成了Tokenize，但是这时可能用户又重新输入了第3行，这时第3行之后的行数都会在后台进行Tokenize操作，可是这时原来已经Tokenize的第7行的结果已经没有意义了，所以不能让它更新UI层。这时我们可能需要维护一个优先队列（Priority Queue），来得到目前等待Tokenize的最小的行号。&lt;/p&gt;
&lt;p&gt;仍然用回刚才的例子，当我们第7行已经Tokenize完成的时候，准备更新UI层，这时第3行触发了输入，第三行本身立刻完成了渲染，这时第3行之后的行数（4..）全部加入Priority Queue，这时第7行的callback调用Priority Queue的队列头，得到当前最小的是4，也就是第4行正在后台进行Tokenize，所以第7行Tokenize的结果不会更新到视图。当第4行的Tokenize完成之后，调用更新视图的Callback，这时检查到当前Priority Queue 的队列头正是4，然后更新视图，把4在队列里面Pop出来。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[如何实现编辑器文本语法高亮着色]]></title><link>https://diverse.space/2016/11/how-to-hightlight-syntax-for-editor</link><guid isPermaLink="false">https://diverse.space/2016/11/how-to-hightlight-syntax-for-editor</guid><pubDate>Sun, 06 Nov 2016 13:06:00 GMT</pubDate><content:encoded>&lt;p&gt;最近正在尝试造一个Markdown编辑器的轮子 &lt;a href=&quot;https://github.com/vincentdchan/MDE&quot;&gt;MDE&lt;/a&gt; 现在算是实现了简单的数据结构，用 chains of lines实现了，支持插入，删除，替换操作。&lt;/p&gt;
&lt;p&gt;至于 Model 至视图（View）层面的更新就简单了，只要判断出插入、删除的那几行，去更新 DOM 里面那几行就可以了，也可以说是非常简单。&lt;/p&gt;
&lt;p&gt;目前比较棘手的一个问题是给 &lt;strong&gt;Syntax Highlighting&lt;/strong&gt;。Syntax Highlighting 是编辑器很重要的一部分。因为它需要速度非常快，必须在输入瞬间就完成，这样不会给用户发现有卡顿。而且它必须在输入瞬间完成。我想过一种做法就是，先显示没有着色的文本，然后在后台进行着色，然后再更新视图(View)。但是我觉得这样的做法就体验非常差了，用户输入的时候，文本没有被着色，而是等一段时间才有，会让人觉得非常不流畅。所以 Syntax Highlighting 必须再用户输入的瞬间完成。另外，我用很多基于Web的编辑器(Atom, Typora)都觉得不如 Native 的（Sublime Text）之类的来得快，这样就造成了体验不好，所以我写的 Syntax Highlighting 必须要快。&lt;/p&gt;
&lt;p&gt;我的编辑器只支持 Markdown 语法，按道理来说 Markdown 语法（包含HTML）是属于**有限自动机（Finite-State）**语法，也就是说，只要写Lexer(Tokenizer)就好了。&lt;/p&gt;
&lt;p&gt;如果这样想的话，就简单了，有限自动机语法直接用&lt;strong&gt;正则表达式（Regular Expression）&lt;strong&gt;做就好了，如果一行有更新，就对这一行进行&lt;/strong&gt;重新 Tokenize&lt;/strong&gt;，然后再更新视图（View），一行来说一不会超过 50 个字，如果是 DFA 的话，速度会很快，基本不用担心速度问题，即使是正则表达式也不会慢多少，但是这就有一个问题，就是换行的问题。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;因为 Markdown 语法是兼容 HTML 的，假设，我们有一句 html，它的 tag 在第 n 行，它的 attribute在第 n+1，如果我们只对一行做正则表达式，那么第 n+1 行的 attribute 就无法感知上一行的改变了。另一方面，在上面得例子第二行上面加上&lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&gt;&lt;/code&gt;这样一行，下面的语法高亮都会有所不同。那是否意味着，我们每一次改变，都要对整个文本进行 tokenize 呢？当然这样肯定是不切实际的，上面说过，语法高亮必须是实时的，这样才能保证好的用户体验，但是 Syntax Highting 依然与上下文有关。这里我们可以采用 CodeMirror 的做法了： 每一行保存一个 state&lt;/p&gt;
&lt;p&gt;我们刚才说到，Syntax Highting 需要用到上下文的信息，那么我们可以为每一行保存一个 state。当我们对当前行进行 high lighting 的时候，就可以使用前一行的 state 的。仍然用回上面的例子，第一行里面我们 tokenize 了，进入了一个state 这个 state 告诉我们这个 tag 还没有定义完，例如 &lt;code class=&quot;language-text&quot;&gt;state.finishTag = false&lt;/code&gt;  当我们第二行修改的时候，就可以利用上一行的 state，得知我们仍在一个tag里面，这样，第二行的 attribute 就可以正确着色了。第二行完成了 tag 之后 &lt;code class=&quot;language-text&quot;&gt;state.finishTag = true&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;仍然用回上面的例子，我们知道，如果某一行的 state 改变了，下面所有的内容都必须进行重新 tokenize，但是我们可以考虑下一下，第 n 行被修改了，那么下文的修改其实不需要实时进行修改，我们可以把这个工作交给后台，用别的 process 或者 thread 进行，完成以后再更新视图，我们只需要保证当前行的着色是实时的就可以了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[（翻译）用 Javascript 在浏览器里面虚拟一个可编辑的控件]]></title><link>https://diverse.space/2016/10/（翻译）用Javascript在浏览器里面虚拟一个可编辑的控件</link><guid isPermaLink="false">https://diverse.space/2016/10/（翻译）用Javascript在浏览器里面虚拟一个可编辑的控件</guid><pubDate>Fri, 21 Oct 2016 21:44:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;此文翻译自 &lt;a href=&quot;http://marijnhaverbeke.nl/&quot;&gt;Marijn Haverbeke&lt;/a&gt; 的 &lt;a href=&quot;http://marijnhaverbeke.nl/blog/browser-input-reading.html&quot;&gt;Faking an editable control in browser JavaScript&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你当前的问题是：你正在写一个和文本输入控件（Text intput Filed）相似的Javascript 控件——它必须是 focusable 的，而且要支持复制和粘贴，接受任意类型的输入，但是它又不是一个真正的控件，因为你想去自己画，而且可以完全控制它的内容。&lt;/p&gt;
&lt;p&gt;在这篇文章里面，我不想讲任何关于画一个指针（Cursor），维护自己的 selection 之类的东西。当然，对于实现一个可靠的文本编辑器来说，这些东西都是必需的。但是他们实现起来都相对简单。&lt;/p&gt;
&lt;h2&gt;隐藏的 Textarea&lt;/h2&gt;
&lt;p&gt;我的解决方案的结症在于一开始我从 ACE editor 获得的灵感，让我围着一个隐藏的 textarea 团团转。这就是当 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 看起来是 focused 的时候，浏览器就认为它是 focused 的。它就像一个常规的 focusable 的东西，你可以赋值一个 &lt;code class=&quot;language-text&quot;&gt;tabindex&lt;/code&gt; 给它，让它得到或者失去 focus 的时候接收 &lt;code class=&quot;language-text&quot;&gt;focus&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;blur&lt;/code&gt; 事件，这允许我们去更新编辑器的样式（显示/隐藏的指针，彩色/黑白的选区）来反映出它是否是 focused 的。&lt;/p&gt;
&lt;p&gt;然而这个 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 一定不能是可见的。因为如果当你输入的时候，这个 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 一直跟着你，而且还带上它那个自带的一闪一闪的光标的时候，这个编辑控件就会显得毫无真实感。&lt;/p&gt;
&lt;p&gt;不过，如果你给你的编辑器设置 &lt;code class=&quot;language-text&quot;&gt;display:none&lt;/code&gt; 或者 &lt;code class=&quot;language-text&quot;&gt;visibility: hidden&lt;/code&gt; 属性的时候，浏览器会觉得它不是这个页面真正的部分从而不会去 focus 它。CodeMirror 让 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; （变小）放在一个 &lt;code class=&quot;language-text&quot;&gt;overflow: hidden; height : 0&lt;/code&gt; 的元素（div）里面，这就让它即不可见，也可以 focus，从而解决了这个问题。&lt;/p&gt;
&lt;p&gt;（另外，你需要一个 &lt;code class=&quot;language-text&quot;&gt;outline: none&lt;/code&gt; 样式，去防止一些浏览器在它 focused 的时候显示一圈光环，但却某些原因没有被 &lt;code class=&quot;language-text&quot;&gt;overflow: hidden&lt;/code&gt; 禁止掉）&lt;/p&gt;
&lt;p&gt;另外一个不幸的（或者说是幸运的，取决与你怎么看）在编辑器上效果就是浏览器每次都会滚动来提醒我们编辑器有事件发生，这就意味着，如果 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 简单地放在编辑器地顶部，然后你就会不停地滚动到顶部因为你正在编辑编辑器地步地内容，每当你输入一个字符，窗口都会滚动。&lt;/p&gt;
&lt;p&gt;CodeMirror 给 &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; 元素设置了绝对路径来隐藏 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; ，每当指针移动的时候， &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 也会跟着移动。这样的话，之就会帮助滚动到真正的视图。&lt;/p&gt;
&lt;h2&gt;保持选择区域&lt;/h2&gt;
&lt;p&gt;当用户选择了一些文字，然后复制或者剪切，正确的文字会被放在粘贴板里面。&lt;/p&gt;
&lt;p&gt;这就意味着选区里面的文字要被正确地放在 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 里面，和选择它们。达到这个目的有两种方法。第一种，被ACE采用了，就是去监听 &lt;code class=&quot;language-text&quot;&gt;copy&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;cut&lt;/code&gt; 事件（在真正复制和剪切之前就会被触发），仅当这样地事件触发的时候，把当前选择地文字插入到 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 里面然后选择它。&lt;/p&gt;
&lt;p&gt;CodeMirror 的实现方法并没有那么聪明，不过更实用。它就是简单地在每一次选择的时候把当前选择的文字复制到 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 里面。这种做法的一个缺点就是当你从从 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; 里面获得（get）和设置（set）许多文字的时候，速度会很慢。如果你在 CodeMirror 里面放一个很大的文档，然后按 &lt;code class=&quot;language-text&quot;&gt;Ctrl-A&lt;/code&gt; 或者 &lt;code class=&quot;language-text&quot;&gt;cmd-A&lt;/code&gt; （全选），这将会有一个可以察觉的停顿。（在一些老的浏览器上，取决于文档的大小，这看上去可能更像整个浏览器卡住，而不是一个短暂的停顿）&lt;/p&gt;
&lt;p&gt;但这个做法的优势就是它适用于 Opera 浏览器，Opera 浏览器不会触发 &lt;code class=&quot;language-text&quot;&gt;copy&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;cut&lt;/code&gt; 事件，在 Linux 上某些浏览器，这种做法可以和 X Windows 的选择粘贴板很好的兼容。CodeMirror 会花更大精力去缩小 &lt;code class=&quot;language-text&quot;&gt;textarea.value&lt;/code&gt; 的消耗，例如当一个选区拖动的时候不去更新，在拖动完成后才去更新。&lt;/p&gt;
&lt;h2&gt;感知输入&lt;/h2&gt;
&lt;p&gt;因此隐藏的 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 包含着当前选区，它的内容被选取了。这意味着当用户输入一些东西，或者粘贴文字，&lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 当前内容会被插入文字（如果之前有选择文字的话，就会覆盖它们），然后文字会被插入到真正的文档上面指针的位置当中。&lt;/p&gt;
&lt;p&gt;不过谁告诉我们输入发生了？一开始，我们可以监听 &lt;code class=&quot;language-text&quot;&gt;keypress&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;paste&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt;这样的事件。这些事件会告诉我们有些事情发生了，这样，我们设置一个延迟，然后在几毫秒之后检查 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 的内容。&lt;/p&gt;
&lt;p&gt;不过这并不完美的。Opera 不会触发 &lt;code class=&quot;language-text&quot;&gt;paste&lt;/code&gt; 事件——当你从菜单粘贴时，也不会有任何鼠标事件被触发。另外，在一些浏览器上面，输入法输入的时候 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 不会触发任何事件。&lt;/p&gt;
&lt;p&gt;所以我们必须检测。然而如果我们检测次数过多从而每次从 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 读取数据（可能会很大），那么检测的代价可能就很昂贵了。幸运的是，如果 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 有一个很大的值（选择的文字），那些文字会被选择（selected），而输入文字就会覆盖它们。因此，如果 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 有一个选区（检查的代价很低），它的值不必被读出。这就会让检测的代价降低，从而允许 CodeMirror 去当编辑器focused 的时候密集地检测也不会消耗太多 CPU（当编辑器失去聚焦，停止检测）。&lt;/p&gt;
&lt;h2&gt;关于输入法&lt;/h2&gt;
&lt;p&gt;我在上文提到过输入法。我不是这方面的专家，因为我不说任何需要用到输入法的语言。不过，这允许人们在写很长的脚本的时候，用一系列快捷键去输入字符。这就需要实现在编辑器展示当前的输入法正在候选的字符（composition），然后在输入法输入完成的时候，用真正的结果去替代它。&lt;/p&gt;
&lt;p&gt;如果 CodeMirror 在每次读入输入的时候都去清空 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt;，这就会留下还没完成的输入法的输入。那它真正的做法是，当没有选区存在的时候，把当前输入留在 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt;，然后把它的值（value）存在别的地方。然后下一次检测的时候，它就会用新的值和和旧的进行比较（切除共同的前缀字符），这意味着之前的值被新的值替换，新的值应该替代掉文档里面那些旧的值。&lt;/p&gt;
&lt;h2&gt;Drag and drop&lt;/h2&gt;
&lt;p&gt;现代的浏览器都提供了 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/DragDrop/Drag_and_Drop&quot;&gt;drap and drop API&lt;/a&gt;。这就会让我们的编辑器更加方便地支持在编辑器里面放置(drop)内容，拉取内容离开编辑器。这里面有一点细微之处，这是 CodeMirror 的 &lt;code class=&quot;language-text&quot;&gt;dragstart&lt;/code&gt; 的handler:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dragstart&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Set the dragged data to the currently selected text&lt;/span&gt;
  e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataTransfer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Text&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; editor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSelection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Use dummy image instead of default browsers image.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataTransfer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;setDragImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataTransfer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDragImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;img&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;当 &lt;code class=&quot;language-text&quot;&gt;setDrapImage&lt;/code&gt; 方法被调用的时候，抑制了默认的拉动的映像，阻止了一些浏览器显示整个编辑器都被拉动了，因为外部元素被设置成 &lt;code class=&quot;language-text&quot;&gt;draggable=true&lt;/code&gt; 的。&lt;/p&gt;
&lt;p&gt;在 CodeMirror 的 &lt;code class=&quot;language-text&quot;&gt;mousedown&lt;/code&gt; handler里面，我也在不在选区（selection）里面的点击调用了 &lt;code class=&quot;language-text&quot;&gt;preventDefault()&lt;/code&gt;，这样拉动产生的选区就不会触发下一次拉动（dragging）。在 Webkit 引擎里面，这是必须的，因此，你不仅在处理 &lt;code class=&quot;language-text&quot;&gt;mousedown&lt;/code&gt; 事件的时候把 &lt;code class=&quot;language-text&quot;&gt;draggable&lt;/code&gt; 属性设置成真，还要在之后把它设置回来。&lt;/p&gt;
&lt;p&gt;编辑器里面 &lt;code class=&quot;language-text&quot;&gt;drop&lt;/code&gt; 事件的 handler 支持从 &lt;code class=&quot;language-text&quot;&gt;FileReader&lt;/code&gt; 读取文件，可以控制被拉进编辑器的文件。&lt;/p&gt;
&lt;h2&gt;全局菜单&lt;/h2&gt;
&lt;p&gt;全局菜单就像蛋糕上的糖霜一样，一个好的编辑器在被右键的时候应该表现正常才行。全局菜单应该包含可以工作的复制，剪切和粘贴按钮。&lt;/p&gt;
&lt;p&gt;不幸的是， 浏览器没有提供 API 去操作全局菜单。你可以捕捉点击事件和展示你自己的菜单，不过这样并不好，而且更坏的是，你没有权限去使用粘贴板，即使是正确地实现复制、粘贴你也做不到。&lt;/p&gt;
&lt;p&gt;就像浏览器领域一贯地风格一样，这里有一个极差地实现方法去弥补API的不足。这次，我们可以通过短暂地不隐藏 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt;（给它一个很低地透明度，以及没有边界来不让别人发现它）来响应鼠标点击事件和 &lt;code class=&quot;language-text&quot;&gt;contextmenu&lt;/code&gt; 事件，然后把它放在鼠标指针下面。&lt;/p&gt;
&lt;p&gt;因为 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 已经包含了当前地选区地内容，以及，如果它有一个选区，它地左上角（我们把它放在指针下面），就是选区地位置。现在浏览器就会相信我们点击在 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 的选区上面，然后就会提供我们想要的菜单。即使这个结点在几毫秒之后隐藏了，这个点击也会和它联系起来，随后的粘贴也会应用到我们的 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 当中。&lt;/p&gt;
&lt;p&gt;有一件事要注意的是，Firefox 在打开全局菜单&lt;strong&gt;之后&lt;/strong&gt;会触发 &lt;code class=&quot;language-text&quot;&gt;contextmenu&lt;/code&gt; 事件，不过这还是让我们太晚知道 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 被点击了。因此在那个浏览器上面，我们就蹩脚地使用 &lt;code class=&quot;language-text&quot;&gt;mousedown&lt;/code&gt; 来代替它（右键按下后就会被触发）。&lt;/p&gt;
&lt;p&gt;关于全局菜单我们要实现地第四项就是&lt;strong&gt;全选&lt;/strong&gt;。为了做到这个，我们在内容地开头加了一个假的、没有被选择的空间。然后定期进行检查看看这个控件是否被选择。如果它被选择了（剩下地内容完好无损），我们就选择编辑器里面所有内容。如果 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 别的东西改变了，或者时间过得太长，我们就放弃。&lt;/p&gt;
&lt;h2&gt;弯路&lt;/h2&gt;
&lt;p&gt;对于没有输入的键盘事件，例如指针移动按键，CodeMirror 简单地处理原始的事件然后内部展现合适的选区效果。&lt;/p&gt;
&lt;p&gt;一开始 CodeMirror 版本 2 采用了一个不同的实现方法，这个方法非常讨人喜欢，但是最后并没有成功。它不但把选区放进 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt;，也把附近的几行放进去，让本地的指针也可以自由地移动。这将会不仅从 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 得到输入，也可以得到选区（selection）信息。&lt;/p&gt;
&lt;p&gt;这有一个有点就是使用了浏览器“本地”的按键绑定。这对于一些特定的按键绑定来说是可行的，也把一些复杂的选区操作交给了浏览器。&lt;/p&gt;
&lt;p&gt;我们最终抛弃这个做法是因为它需要太多的hack做法来正常工作，比如说，当你在 &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; 选择一段文字的时候，你不能设置外链。&lt;/p&gt;
&lt;p&gt;当你按下 &lt;em&gt;shift-left&lt;/em&gt; 或者其他 &lt;em&gt;shift&lt;/em&gt; 移动的时候，外链不会移动。浏览器假设它永远在选区的左边当我们通过 &lt;code class=&quot;language-text&quot;&gt;selectionStart&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;selectionEnd&lt;/code&gt; 设置选区的时候。为了让选区可以正常工作，我们必须用很多痛苦和蹩脚的做法。&lt;/p&gt;
&lt;p&gt;另外，似乎很少用户会在他们的浏览器里面真正地重新配置按键绑定（keyboard binding），有趣的是，用户更喜欢 CodeMirror 提供自定义按键绑定而不是在他们的浏览器里面重新定义。&lt;/p&gt;
&lt;p&gt;最后，控制我们自定义键盘事件的额外的复杂度似乎没有上面所述的这种做法的复杂度大。因为 CodeMirror 使用 textarea 只包含选区这种做法，而不必去解决 &lt;code class=&quot;language-text&quot;&gt;cursor-motion&lt;/code&gt; 按钮事件。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[编译原理学习笔记 3：实现一个虚拟机]]></title><link>https://diverse.space/2016/08/implement-a-vm</link><guid isPermaLink="false">https://diverse.space/2016/08/implement-a-vm</guid><pubDate>Sat, 13 Aug 2016 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;至于 Lexer 和 Parser 部分，教程和用法实在太多，实现起来也比较简单，所以也没什么好说的，这里说说如何实现一个虚拟机（Virtual Machine）&lt;/p&gt;
&lt;p&gt;虚拟机的实现有很多种，常见的分为 Stack Machine 和 Register Machine 前者基于栈，后者基于寄存器。
目前来说，基于栈的虚拟机比较多，像（CPython, JVM, .NET…）都是基于栈的虚拟机，二基于寄存器的虚拟机中比较出名的就是 Lua 的官方实现了，官方称这是最早的基于寄存器的虚拟机，具体如何我们就无法考究了。但是本文主要介绍的是基于栈的虚拟机，因为比较好实现。&lt;/p&gt;
&lt;p&gt;我们来看看下面这一行代码&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果用 StackMachine 来表示是这样的&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;LOAD 1
LOAD 2
LOAD 3
MULTIPLE // result: 6
ADD  // result: 7&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样应该很好理解，执行这段代码后，栈顶的值是 7，也就是我们表达式的值。那么接下来做两件事情就好好了，一是把语法树生成一连串的指令，二是执行这些指令&lt;/p&gt;
&lt;p&gt;语法树如何生成指令呢？&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;+
| \
1  *
   	 |\
   	 2 3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;粗略地画，语法树是这个样子地，那么很明显，&lt;strong&gt;生成指令地过程就是后序遍历语法树地过程&lt;/strong&gt;。理解这句话之后，就好做了。但是也有几点要注意地地方，我们通常用一个常量表，把用到地常量给储存起来，如整形，字符串等等。所以我们设计指令的时候，比如说 LOAD_C  指令就可以从常量表里面加载一个值到栈顶。&lt;/p&gt;
&lt;p&gt;下面看一下我正在实现的哈语言里面的指令集&lt;/p&gt;
&lt;pre style=&apos;color:#000020;background:#f6f8ff;&apos;&gt;&lt;span style=&apos;color:#004a43; &apos;&gt;#&lt;/span&gt;&lt;span style=&apos;color:#004a43; font-weight:bold; &apos;&gt;pragma&lt;/span&gt;&lt;span style=&apos;color:#7779bb; font-weight:bold; &apos;&gt; once&lt;/span&gt;

&lt;span style=&apos;color:#200080; font-weight:bold; &apos;&gt;namespace&lt;/span&gt; halang
&lt;span style=&apos;color:#406080; &apos;&gt;{&lt;/span&gt;
	&lt;span style=&apos;color:#200080; font-weight:bold; &apos;&gt;enum&lt;/span&gt; &lt;span style=&apos;color:#200080; font-weight:bold; &apos;&gt;class&lt;/span&gt; VM_CODE
	&lt;span style=&apos;color:#406080; &apos;&gt;{&lt;/span&gt;
		LOAD_C&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x00 A - load constant&lt;/span&gt;
		LOAD_V&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x01 A - load variable&lt;/span&gt;
		LOAD_G&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x02 A - load global variable&lt;/span&gt;
		LOAD_UPVAL&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;				&lt;span style=&apos;color:#595979; &apos;&gt;// 0x03 A - load upvalue&lt;/span&gt;
		STORE_V&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;				&lt;span style=&apos;color:#595979; &apos;&gt;// 0x04 A - store the top of stack to A&lt;/span&gt;
		STORE_G&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;				&lt;span style=&apos;color:#595979; &apos;&gt;// 0x05 A - store to the global value&lt;/span&gt;
		STORE_UPVAL&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;			&lt;span style=&apos;color:#595979; &apos;&gt;// 0x06 A - store to the upvalue table&lt;/span&gt;
		PUSH_INT&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;				&lt;span style=&apos;color:#595979; &apos;&gt;// 0x07 A - load A&lt;/span&gt;
		PUSH_BOOL&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;				&lt;span style=&apos;color:#595979; &apos;&gt;// 0x08 A - load A&lt;/span&gt;
		POP&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x09 A - Pop A&lt;/span&gt;
		CLOSURE&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;				&lt;span style=&apos;color:#595979; &apos;&gt;// 0x0a A - linked Function&apos;s upvalue to current env&lt;/span&gt;
		CALL&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x0b (A, B, C...) call function(A, B, C...)&lt;/span&gt;
		RETURN&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x0c A - if A != 0 return exp else return nothing&lt;/span&gt;
		IFNO&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x0d if not true, jump to the location.&lt;/span&gt;
		JMP&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x0e&lt;/span&gt;
		NOT&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x0f&lt;/span&gt;
		ADD&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x10 add the top two val&lt;/span&gt;
		SUB&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x11&lt;/span&gt;
		MUL&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x12&lt;/span&gt;
		DIV&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x13&lt;/span&gt;
		MOD&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x14&lt;/span&gt;
		POW&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x15&lt;/span&gt;
		GT&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt; LT&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x16 0x17&lt;/span&gt;
		GTEQ&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt; LTEQ&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;				&lt;span style=&apos;color:#595979; &apos;&gt;// 0x18 0x19&lt;/span&gt;
		EQ&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;						&lt;span style=&apos;color:#595979; &apos;&gt;// 0x1a&lt;/span&gt;
		OUT&lt;span style=&apos;color:#308080; &apos;&gt;,&lt;/span&gt;					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x1b&lt;/span&gt;
		STOP					&lt;span style=&apos;color:#595979; &apos;&gt;// 0x1c&lt;/span&gt;
	&lt;span style=&apos;color:#406080; &apos;&gt;}&lt;/span&gt;&lt;span style=&apos;color:#406080; &apos;&gt;;&lt;/span&gt;

&lt;span style=&apos;color:#406080; &apos;&gt;}&lt;/span&gt;&lt;span style=&apos;color:#406080; &apos;&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;至于运行，如何储存变量呢，最简单的做法就是用 Environment 方法。用一个表来储存变量，这个表当然可以用哈希表（变量名-变量值）来做，但是这样还不够快。其实我们知道，在一个环境里面，我们可以为每一个变量名编一个号，这样子这个变量表就可以用数组（变量编号-变量值）来做，这样子，访问变量和储存变量的时候就快很多了。&lt;/p&gt;
&lt;p&gt;项目 Github 地址：&lt;a href=&quot;https://github.com/vincentdchan/halang&quot;&gt;https://github.com/vincentdchan/halang&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[把正方系统的课表导出成 ics 文件]]></title><link>https://diverse.space/2016/08/export-zhengfang-course-to-ics</link><guid isPermaLink="false">https://diverse.space/2016/08/export-zhengfang-course-to-ics</guid><pubDate>Sat, 13 Aug 2016 18:27:00 GMT</pubDate><content:encoded>&lt;p&gt;文章的开始，先上一张图，打开 Siri，问 Siri：明天有什么安排？&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 719px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/34fe8bc343db15fe0a4b39070b14a564/4a359/iphone-siri.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 178.13953488372093%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAkABQDASIAAhEBAxEB/8QAGQABAQEBAQEAAAAAAAAAAAAAAAIBBAMF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQIA/9oADAMBAAIQAxAAAAH4+1dTK0s3y7W93ODJMgP/xAAZEAEBAQADAAAAAAAAAAAAAAABABEQEiD/2gAIAQEAAQUCxwJsgmOG1uza+P/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BX//EABURAQEAAAAAAAAAAAAAAAAAAAEg/9oACAECAQE/AWv/xAAYEAACAwAAAAAAAAAAAAAAAAAQMQAgQf/aAAgBAQAGPwKYUWXX/8QAHxAAAgIBBAMAAAAAAAAAAAAAAAERIUEQMVFxYYHx/9oACAEBAAE/IYbOuzmbexnPAprAiVERFOxheBT9MazyiEye43K6rT//2gAMAwEAAgADAAAAEIzAD3Qv/8QAGREBAQADAQAAAAAAAAAAAAAAAQAQESEx/9oACAEDAQE/EHhHkmyBjH//xAAYEQACAwAAAAAAAAAAAAAAAAABEBEhQf/aAAgBAgEBPxAKUrF//8QAHhABAQEAAgMAAwAAAAAAAAAAAREAITFBUWFxkeH/2gAIAQEAAT8QRURRFk51Y+iHmaJIJ0LgLg8ec+BH24kx1n8J7JP1lHExt/hvdv3BHE7o0n5wHl3Rl5AIBCbwb//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;iphone siri&quot;
        title=&quot;&quot;
        src=&quot;/static/34fe8bc343db15fe0a4b39070b14a564/4a359/iphone-siri.jpg&quot;
        srcset=&quot;/static/34fe8bc343db15fe0a4b39070b14a564/34fa0/iphone-siri.jpg 215w,
/static/34fe8bc343db15fe0a4b39070b14a564/40c27/iphone-siri.jpg 430w,
/static/34fe8bc343db15fe0a4b39070b14a564/4a359/iphone-siri.jpg 719w&quot;
        sizes=&quot;(max-width: 719px) 100vw, 719px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;为什么 siri 知道我的课表？因为我把课表导入了 iPhone 的日历中去了。&lt;/p&gt;
&lt;p&gt;因为不想再使用超级课程表等软件，加上华农宝不怎么好看，而且很麻烦的缘故，自己写了一个工具，可以把课程表导出到&lt;em&gt;ics&lt;/em&gt; 格式，可以把这个文件导入到系统的软件当中，比如iPhone自带的日历, Outlook, Google Calendar 等等你常用的日历工具……导入到 iPhone 或者 Android 手机自带的日历之后，无论是查看，通知甚至类似 Siri，小娜的各种语音助手进行查询，都会华农宝方便很多。&lt;/p&gt;
&lt;p&gt;另外，这个插件是安装在你的电脑上进行导出，它的代码完全开放，托管在 Github，你无需担心密码泄漏等安全问题，相反华农宝使用你的密码登录教务系统，会使人多了一层疑虑。&lt;/p&gt;
&lt;p&gt;目前来说，你可以通过使用 Chrome 或者 Tampermonkey 插件来使用我做的插件进行导出。&lt;/p&gt;
&lt;h2&gt;方法一：Tampermonkey（油猴）插件&lt;/h2&gt;
&lt;p&gt;Tempermonkey 插件支持的浏览器有:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Chrome&lt;/li&gt;
&lt;li&gt;Firefox&lt;/li&gt;
&lt;li&gt;Edge&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上浏览器安装 Tampermonky 插件，安装方法自行搜索。&lt;/p&gt;
&lt;p&gt;安装完成之后，去到&lt;a href=&quot;https://greasyfork.org/zh-CN/scripts/27557-%E5%8D%8E%E5%86%9C%E6%AD%A3%E6%96%B9%E7%B3%BB%E7%BB%9F%E8%AF%BE%E7%A8%8B%E5%AF%BC%E5%87%BA%E5%B7%A5%E5%85%B7&quot;&gt;这个链接&lt;/a&gt;点击安装，安装成功之后登入你的正方系统，去个人课表导出页面设置好日期之后导出就好了，然后大功告成。&lt;/p&gt;
&lt;h2&gt;方法二：Chrome插件&lt;/h2&gt;
&lt;p&gt;下载下面的 Chrome 插件**（仅华农的正方系统适用）**，安装方法请自行搜索&lt;/p&gt;
&lt;p&gt;Github 地址: &lt;a href=&quot;https://github.com/vincentdchan/ZhengFangToolkit&quot;&gt;https://github.com/vincentdchan/ZhengFangToolkit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;下载 &lt;a href=&quot;https://github.com/vincentdchan/ZhengFangToolkit/releases&quot;&gt;Chrome 插件&lt;/a&gt;，并安装，安装方法请自行搜索，安装完成后，登入正方系统，进入个人课表，选择好正确的学年和正确的学期，&lt;strong&gt;检查一下开学的第一个星期一是否正确&lt;/strong&gt;，不然的的话生成的日历全部都会错误的。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d98ad8ec48ad285cd5a2f7bf98ac1546/75a80/zhengfang-tutorial1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12.093023255813952%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAAdklEQVR42gXBSw6DIBAAUO9/q26adGVC2qSVCIbPjAJjUUAC3fS9Yc+dUnOx+rMpcFwoi06ajXEtNCoMbDLP2UogNpnxrYxPlLo/GqU+pPoLsSwQpPV22z/CmDVYQLEowLVclWK+j/z2eDEOMxwST6BL+/LN7Q/bo2xLEO6IaAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zhengfang tutorial1&quot;
        title=&quot;&quot;
        src=&quot;/static/d98ad8ec48ad285cd5a2f7bf98ac1546/914ae/zhengfang-tutorial1.png&quot;
        srcset=&quot;/static/d98ad8ec48ad285cd5a2f7bf98ac1546/2eb24/zhengfang-tutorial1.png 215w,
/static/d98ad8ec48ad285cd5a2f7bf98ac1546/05ed2/zhengfang-tutorial1.png 430w,
/static/d98ad8ec48ad285cd5a2f7bf98ac1546/914ae/zhengfang-tutorial1.png 860w,
/static/d98ad8ec48ad285cd5a2f7bf98ac1546/75a80/zhengfang-tutorial1.png 1134w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;检查完之后点击&lt;strong&gt;导出课程&lt;/strong&gt;，最后会出现一条下载链接，下载那个 ics 文件就行了。
然后把 ics 文件导入到各种软件中去，可以看看效果图&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3bb2a6695261b8704875ddc3ba966dc5/b9962/zhengfang-ios.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.27906976744186%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHZdUqKE//EABkQAAMAAwAAAAAAAAAAAAAAAAABEQIhMf/aAAgBAQABBQKMWyGTiXD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAEBAQAAAAAAAAAAAAAAAAAAMSD/2gAIAQEABj8CVcf/xAAcEAEAAgEFAAAAAAAAAAAAAAABABEQITFRcaH/2gAIAQEAAT8h1aHyXSMdkYK5mxj/2gAMAwEAAgADAAAAEJPP/8QAFhEAAwAAAAAAAAAAAAAAAAAAEBFB/9oACAEDAQE/EIx//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAMAAwEAAAAAAAAAAAAAAQARIRAxQeH/2gAIAQEAAT8Q0ZzN8QJFRtogz4RidKCK0e04/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zhengfang ios&quot;
        title=&quot;&quot;
        src=&quot;/static/3bb2a6695261b8704875ddc3ba966dc5/8d3e2/zhengfang-ios.jpg&quot;
        srcset=&quot;/static/3bb2a6695261b8704875ddc3ba966dc5/34fa0/zhengfang-ios.jpg 215w,
/static/3bb2a6695261b8704875ddc3ba966dc5/40c27/zhengfang-ios.jpg 430w,
/static/3bb2a6695261b8704875ddc3ba966dc5/8d3e2/zhengfang-ios.jpg 860w,
/static/3bb2a6695261b8704875ddc3ba966dc5/1af2f/zhengfang-ios.jpg 1290w,
/static/3bb2a6695261b8704875ddc3ba966dc5/b9962/zhengfang-ios.jpg 1334w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;导入到 iOS 之后的效果图&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a14f27774672c79aaa49d4d4952ce537/c2d13/zhengfang-win10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.697674418604656%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAB2HAAAdhwGP5fFlAAACXUlEQVR42i2RW0hTYQDHF0I1L+Qdb6WGZoH2ELImVhr4YHTTHoOohwqi1+qhB0HCEhxh9BCSEWqalqbgMjFW09zE1rYzx2akBl3mds7Ot/Od+3fOdy6x6v/05w8/+MPPstuabc3Jt2RYd2XmRiJR0zRpDvGSYpqmpmHDNDHGmm6wApJVDQpIQBoUFUHGumFaiopLK/ZW78iwZmXnEaEwwhpSVVEQsKYDyEsIQwhVVeU4FmOV5zkNY45jEUKmaVp2ZuZlFVTtqT1eUmOb9669XOOfeOnbzljXAjnip8eCzB1n7O67xK3Z9LJJ8jpWeFFECBmGYcmpac1vvFh47HqZ/dJKILKRlCMx3rsF/L+E9RjcSkr/umcTBH8LFMMrCpJkSVGUNFxQf76s9UZx05XKIx0zS5HeJaZ7IfHgA3nPRXa/T9x3kT0usvcj1eMi+9zUTyDqWJFkGSGk67qlsNJeXt9esL+l6tDJZWJj7is3G4HTa7QzCqcI6m2UfRNKOqNwOkTPrbMkFLCqiKL0//a+uhMNR8/lVjS1nLocjcHBFerZKv3UGx9cBQOexJAPDHjizz/TA574sI/+QbGKLPG8IEpSGq6sth+2XyiqbWs7e/VbnB0NwhE/MxpIjQSYF/7UOAGH/amxIDPkAxME3IaSoWEZIVVV03BJ6YEGW0d5/Znm9mvEVmIyBCZD4FWAfE2AcT85HU6NfyGnQmAiQM2EmW3AYUWRZYQxTqsqs3Ue7Oyqa+xsPn1z3vf90QrjWEw6FsmHn+g+N9W/TDvcZP8y6HNTj71gG6b1qppm/s0fP2e0cBxZnwsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zhengfang win10&quot;
        title=&quot;&quot;
        src=&quot;/static/a14f27774672c79aaa49d4d4952ce537/914ae/zhengfang-win10.png&quot;
        srcset=&quot;/static/a14f27774672c79aaa49d4d4952ce537/2eb24/zhengfang-win10.png 215w,
/static/a14f27774672c79aaa49d4d4952ce537/05ed2/zhengfang-win10.png 430w,
/static/a14f27774672c79aaa49d4d4952ce537/914ae/zhengfang-win10.png 860w,
/static/a14f27774672c79aaa49d4d4952ce537/46115/zhengfang-win10.png 1290w,
/static/a14f27774672c79aaa49d4d4952ce537/6c86f/zhengfang-win10.png 1720w,
/static/a14f27774672c79aaa49d4d4952ce537/c2d13/zhengfang-win10.png 2560w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;这是我的课表导入到 Windows 10 自带日历后的效果图，看起来效果还不错&lt;/p&gt;
&lt;p&gt;油猴插件安装地址：&lt;a href=&quot;https://greasyfork.org/zh-CN/scripts/27557-%E5%8D%8E%E5%86%9C%E6%AD%A3%E6%96%B9%E7%B3%BB%E7%BB%9F%E8%AF%BE%E7%A8%8B%E5%AF%BC%E5%87%BA%E5%B7%A5%E5%85%B7&quot;&gt;https://greasyfork.org/zh-CN/scripts/27557-%E5%8D%8E%E5%86%9C%E6%AD%A3%E6%96%B9%E7%B3%BB%E7%BB%9F%E8%AF%BE%E7%A8%8B%E5%AF%BC%E5%87%BA%E5%B7%A5%E5%85%B7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Chrome 插件下载地址：&lt;a href=&quot;https://github.com/vincentdchan/ZhengFangToolkit/releases&quot;&gt;https://github.com/vincentdchan/ZhengFangToolkit/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Github: &lt;a href=&quot;https://github.com/vincentdchan/ZhengFangToolkit&quot;&gt;https://github.com/vincentdchan/ZhengFangToolkit&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[编译原理学习笔记 2：赋值操作]]></title><link>https://diverse.space/2016/07/assign-operation</link><guid isPermaLink="false">https://diverse.space/2016/07/assign-operation</guid><pubDate>Thu, 14 Jul 2016 11:04:00 GMT</pubDate><content:encoded>&lt;p&gt;上文中，提到如果解析一个数学表达式，并把它变成一棵树，我们把这个程序称为解释器，这次我要为这个解释器加一个功能：赋值操作&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里我们要区分左值(left-value)和右值（right-value) 右值是临时结果，而左值是一个变量。至于右面是一个表达式，表达式如何解析在上一篇文章已经详细说了，所以这里就不多说。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 483px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ebf402bbe50f54bc85cfda5122c0d625/77a9e/parseLet.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.44186046511628%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB90lEQVR42mNgwAP+///PiEyTDWAG7N+/X+bChQvu69evFyDbYJimwsJCzpcvX575/////4cPHy6j2MBFixaJffz48QPIwNevX5+lxMeMMEMPHTqUevXq1X1bt271BPHr6+uZkGliDGIBMYyNjVkNDAxE7e3tOUB8CwsLTh0dHXF1dXXeVatWMYMs/P//PxNeg0EKYJptbW0ltbS02KDizFAlTC4uLvxKSkr8cnJygiBLCTrx7NmzhZs2bQpGiwBGqOvBFh4+fNjmxYsXj1+9erUbFM4YkQXj3LhxYy4oAr59+/b38OHD8ciuhrLBwfH48eOp/6Hg6tWr4dDkxYJh4NmzZ4Pev3//8uXLl8d37dqliW4zKOxA9OXLl91ev34NUnhu9+7dclgjCaZx06ZNcrBEDPUqSqRBaZbExESt8PBwBRzq4IbCYw/mGvQI8/b2FrS3t5cAsX18fLicnJykQ0NDQfqYsCZ6kEG4koKwsDAvAwMDO0wd1ABGfn5+kI+YSMo1169fT3z06NHFM2fO2CK7eP/+/QoPHjw4eOfOnTZQUBAyDKxp3bp1Nh8/fvwCitUXL17cKigokIQqYb579+42kPifP3/+HzlyJIVoF164cCHo8ePHu/fv32+AbNns2bPFHz16tObmzZslJOdvkItwyDEjxzQAbCs7cob4RawAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;parseLet&quot;
        title=&quot;&quot;
        src=&quot;/static/ebf402bbe50f54bc85cfda5122c0d625/77a9e/parseLet.png&quot;
        srcset=&quot;/static/ebf402bbe50f54bc85cfda5122c0d625/2eb24/parseLet.png 215w,
/static/ebf402bbe50f54bc85cfda5122c0d625/05ed2/parseLet.png 430w,
/static/ebf402bbe50f54bc85cfda5122c0d625/77a9e/parseLet.png 483w&quot;
        sizes=&quot;(max-width: 483px) 100vw, 483px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;解析（parsing)后，得到的是这样一棵树，那么我们要如何实现赋值(Assign)操作呢？&lt;/p&gt;
&lt;p&gt;在这里我们使用Environment实现，我们只需要一个map哈希表就可以了&lt;/p&gt;
&lt;p&gt;把3 + 4计算的结果放到a里面就行了，eval函数里面可以这样写：&lt;/p&gt;
&lt;!-- HTML generated using hilite.me --&gt;
&lt;div style=&quot;background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;&quot;&gt;
&lt;pre style=&quot;margin: 0; line-height: 125%;&quot;&gt;		&lt;span style=&quot;color: #008800; font-weight: bold;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color: #0066bb; font-weight: bold;&quot;&gt;if&lt;/span&gt; (root&lt;span style=&quot;color: #333333;&quot;&gt;-&amp;gt;&lt;/span&gt;asAssignment())
		{
			&lt;span style=&quot;color: #008800; font-weight: bold;&quot;&gt;auto&lt;/span&gt; _node &lt;span style=&quot;color: #333333;&quot;&gt;=&lt;/span&gt; root&lt;span style=&quot;color: #333333;&quot;&gt;-&amp;gt;&lt;/span&gt;asAssignment();
			env.setValue(_node&lt;span style=&quot;color: #333333;&quot;&gt;-&amp;gt;&lt;/span&gt;Identifier(), _node&lt;span style=&quot;color: #333333;&quot;&gt;-&amp;gt;&lt;/span&gt;Expression());
			&lt;span style=&quot;color: #008800; font-weight: bold;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #0000dd; font-weight: bold;&quot;&gt;0&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;
&amp;nbsp;
&lt;p&gt;这里的env里面其实是一个Map而已。&lt;/p&gt;
&lt;p&gt;在这里附上这个程序的源码：&lt;a href=&quot;https://github.com/vincentdchan/CalculatorParser&quot;&gt;https://github.com/vincentdchan/CalculatorParser&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[编译原理学习笔记 1：解析数学表达式]]></title><link>https://diverse.space/2016/07/parse-math-expression</link><guid isPermaLink="false">https://diverse.space/2016/07/parse-math-expression</guid><pubDate>Tue, 12 Jul 2016 22:50:00 GMT</pubDate><content:encoded>&lt;p&gt;说到编译原理，是计算机科学里面很重要的一个学科，你可以说编译原理无处不在。但是一般人很少可以接触到这样的理论。编译原理分前端和后端，前端包括文本解析和类型检查，后端包括优化和代码生成等等。这篇日志里我主要讨论文本解析中一个最为简单的部分：数学表达式。&lt;/p&gt;
&lt;p&gt;数学表达式： &lt;code class=&quot;language-text&quot;&gt;1 + 1 * 3 * 3 + 4 / 4 * 5&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;要求出这个表达式的值，不禁让我想起了当时学习数据结构时候的习题，这样的表达式用栈来解决。&lt;/p&gt;
&lt;p&gt;我们先要列出一个表，包括各种算术符的优先级&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;符号&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;优先级&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;+&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;-&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;*&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;/&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;然后我们需要两个栈，一个操作符的栈名为&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt; 一个数字的栈，名为&lt;code class=&quot;language-text&quot;&gt;NUM_STACK&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;因为我们的操作符都是耳目操作符，就是操作符左右都为值，比如说 &lt;code class=&quot;language-text&quot;&gt;1  * 1&lt;/code&gt;里面 &lt;code class=&quot;language-text&quot;&gt;1*&lt;/code&gt; 或者 &lt;code class=&quot;language-text&quot;&gt;*1&lt;/code&gt; 都是没有意义的，所以我们是一个数字，一个符号这样间隔读入的，这样就好办了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把第一个数字入&lt;code class=&quot;language-text&quot;&gt;NUM_STACK&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;这时，读入(SHIFT)的是一个操作符，如果&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt;顶为空，则入栈&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt;，如果不为空，则和&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt;栈顶比较，如果比&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt;栈顶优先级高，则入栈&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt;，否则的执行REDUCE操作，&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt;栈顶操作符出栈，&lt;code class=&quot;language-text&quot;&gt;NUM_STACK&lt;/code&gt;出栈两个数字，然后运算结果，再将结果入栈&lt;code class=&quot;language-text&quot;&gt;NUM_STACK&lt;/code&gt;，循环操作&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;操作流程如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;right&quot;&gt;表达式&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;NUM_STACK&lt;/code&gt;&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;OP_STACK&lt;/code&gt;&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;对比结果&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;对应的操作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1+1*3*3+4&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;NUL&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;NUL&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;SHIFT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;*3*3+4/4*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1 1&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;SHIFT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;*3+4/4*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1 1 3&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+ *&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;=&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;REDUCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;*3+4/4*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1 3&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;SHIFT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;+4/4*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1 3 3&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+ *&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;=&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;REDUCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;+4/4*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;1 9&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;=&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;REDUCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;+4/4*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;NUL&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;SHIFT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;/4*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10 4&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;SHIFT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10 4 4&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+ /&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&gt;=&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;REDUCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;*5&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10 1&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;SHIFT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;NUL&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10 1 5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+ *&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;REDUCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;NUL&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;10 5&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;+&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;REDUCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;NUL&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;15&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;NUL&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这样马上我们就算出结果了，然而编译有时并不要求我们马上算出结果，有时只需要我们保存解析的结果，以便以后运行，或者生成代码等等操作，请看下面的表达式：&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;a + b * c&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;显然我们没办法马上算出结果，但是我们可以把它变成一棵树&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 346px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/96cc5fce883cef1b318cfd3540f12ffd/8f77f/parse-tree.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.88372093023256%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB2UlEQVR42mNgwAP+///P9P//f5b6+nomBkrB////GUEYxmZgYGCkyDAQvWrVKsXjx4+7Tpw4URQqRbqhMO9NnTpV8/Xr1x9+/fr1/9GjRxe0tLR4kC0jGuzfv58FRE+ZMkX+0aNHD54/f/7/1q1be1VUVNiR5UkKs7S0NFY1NTURHx8fje7ubjtzc3MFBgYGNpK9q6WlxbZ///7MiRMnhjEwMLCAYhgWduvWrcu5fPnyrnPnzuWB5AhGAD8/v8ClS5cOgsLs7du3/zZv3pwPU+Pr6yv+7t275///////5cuX3/PmzTOFJSu8Lty1a5fJkSNHVh89erRx8uTJwtCYZgbRhw4dyjx58uSlPXv2dIN8gh5MRIUpzLDo6Gg+UVFRCQYGBlDEcGlpacnZ29uLwFwIVceIK2fAMFhBZWWlnqKiojhy8Njb24PCkM3CwkKlvr5eCW8ahWkqLi6Wv3HjxsbHjx//v3v37r6+vj5t5GSzbt06z8ePH995+fLlr1OnTvWCXI7XwLS0NMnr168vu3Pnzs/r169va2trU4PKg4Nh2bJlrvfu3bv05MmTT6dOnWqCBgdBwDh58mSp0NBQZhy5iqu7u1uM2GTJCAt0bIUDKIsiFR7w5AMAqAcJ/jCSshoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;parse tree&quot;
        title=&quot;&quot;
        src=&quot;/static/96cc5fce883cef1b318cfd3540f12ffd/8f77f/parse-tree.png&quot;
        srcset=&quot;/static/96cc5fce883cef1b318cfd3540f12ffd/2eb24/parse-tree.png 215w,
/static/96cc5fce883cef1b318cfd3540f12ffd/8f77f/parse-tree.png 346w&quot;
        sizes=&quot;(max-width: 346px) 100vw, 346px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;显然这样就容易多了，这就是我们要说的解析(parsing)，如何解析呢？方法其实和上面是一样的，不过要做一点点改动，我们知道乘法优先级比较高，当我们计算b * c的时候，我们并不是真的相乘，我们只是创建一个新的结点，把b和c作为子节点，然后把这个新结点入栈，就可以了。&lt;/p&gt;
&lt;p&gt;有了这科语法树之后，当我们知道a b c的值之后，就可以递归算出结果了，我们要计算根节点的结果，就要首先计算子节点的结果，最后回溯可得答案。当然，从另一方面，你可以通过这棵树生成中间代码，或者机器码，这样就编译出真正的程序了。&lt;/p&gt;</content:encoded></item></channel></rss>