<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>lanyon-hakyll: Lanyon Theme on Hakyll</title>
    <link href="https://github.com/hahey/lanyon-hakyll/atom.xml" rel="self" />
    <link href="https://github.com/hahey/lanyon-hakyll" />
    <id>https://github.com/hahey/lanyon-hakyll/atom.xml</id>
    <author>
        <name>Heuna Kim</name>
        
        <email>ai@heuna-kim.net</email>
        
    </author>
    <updated>2024-03-31T00:00:00Z</updated>
    <entry>
    <title>从零开始的CPS编译之旅</title>
    <link href="https://github.com/hahey/lanyon-hakyll/posts/2024-03-31-compiling-with-cps.html" />
    <id>https://github.com/hahey/lanyon-hakyll/posts/2024-03-31-compiling-with-cps.html</id>
    <published>2024-03-31T00:00:00Z</published>
    <updated>2024-03-31T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Continuation很好的捕捉了程序控制状态最基本的概念：下一步该做什么。抽象的概念应该与具体的实现无关，而喜欢用GOTO、指针、PC寄存器作为解释，和万物基于C/C++的人都没有领略到这一点。Continuation在社区中貌似一直和LISP的传说同时出现。作为一门不主流的”失败“的编程语言，经常和它一起出现的东西也被大众们贴上了不好的标签。然而当搜寻和最近PL的热门话题Algebraic effect相关的资料时，就会发现delimited continuation的字眼，甚至还有一篇2018年的论文叫做《Capturing the Future by Replaying the Past》。现实一直很荒诞。</p>
<p>出于对CPS的好奇，2023年我完成毕业设计之后，就计划使用CPS作为IR，写一个函数式的编译器。最早参考的是《Compiling with continuation》这本书，在经历了几个月”使用Haskell还是OCaml来实现“的反复横跳之后，同事告诉我，你看的材料有些过时了，应该使用second class而非first class的continuation在IR中抽象程序的控制流，否则很难做到编译的结果是高效的。</p>
<h1 id="compiling-with-continuation-continued">Compiling with Continuation, continued</h1>
<p>重新参考标题这篇2007年的论文，将toy ML编译到untyped CPS的规则看起来很简单（同时用k和花体k的部分除外，对近视人士非常坏）：</p>
<figure>
<img src="../images/cps-1.png" alt="Naive CPS transformation" />
<figcaption aria-hidden="true">Naive CPS transformation</figcaption>
</figure>
<p>在General rewrites表格里还包含了function/continuation inlining，constant folding，dead code elimination通用优化的规则。</p>
<figure>
<img src="../images/cps-rewrite-rule.png" alt="rewrite-rule" />
<figcaption aria-hidden="true">rewrite-rule</figcaption>
</figure>
<p>所有的规则都可以编写在一个pass中，论文宣称可以在线性时间内做完这些。不过算法需要几轮迭代才能达到不动点，因此最坏的情况是O(N^2)的。我们可以维护一个census上下文：尽可能接近地表示变量在程序中的出现次数。精确的census信息能够增加一轮迭代里shrinking reductions操作，以此减少迭代次数。我们可以函数式地实现这个算法，大概形状是：</p>
<figure>
<img src="../images/image.png" alt="alt text" />
<figcaption aria-hidden="true">alt text</figcaption>
</figure>
<p>此时一名眼疾手快的Haskell批惊呼被论文骗了！因为这个算法的实现大部分是纯的，唯独需要一点点的副作用去维护重写时使用的fresh ident数量。写haskell的时候多爽，写到一半要加上State monad和do notation的时候就有多狼狈。那能怎么办，我又不想用unsafe操作开洞，修改后的代码大概是这样的：</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Value</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Var</span> <span class="dt">Name</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">I32</span> <span class="dt">Int</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Unit</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Tuple</span> [<span class="dt">Name</span>]</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Cont</span> <span class="dt">Name</span> <span class="dt">Term</span> <span class="co">-- env x e</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Fn</span> <span class="dt">Name</span> (<span class="dt">Maybe</span> <span class="dt">Name</span>) [<span class="dt">Name</span>] <span class="dt">Term</span> <span class="co">-- k env x e</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>, <span class="dt">Ord</span>, <span class="dt">Read</span>, <span class="dt">Data</span>)</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Cont</span> <span class="ot">=</span> <span class="dt">Name</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Function</span> <span class="ot">=</span> <span class="dt">Name</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Argument</span> <span class="ot">=</span> <span class="dt">Name</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Closure</span> <span class="ot">=</span> <span class="dt">Name</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Term</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">LetVal</span> <span class="dt">Name</span> <span class="dt">Value</span> <span class="dt">Term</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">LetSel</span> <span class="dt">Name</span> <span class="dt">Int</span> <span class="dt">Name</span> <span class="dt">Term</span> </span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">LetCont</span> <span class="dt">Name</span> <span class="dt">Name</span> <span class="dt">Term</span> <span class="dt">Term</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">LetConts</span> [(<span class="dt">Name</span>, <span class="dt">Value</span>)] <span class="dt">Term</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">LetFns</span> [(<span class="dt">Name</span>, <span class="dt">Value</span>)] <span class="dt">Term</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Continue</span> <span class="dt">Name</span> <span class="dt">Name</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Apply</span> <span class="dt">Function</span> <span class="dt">Cont</span> (<span class="dt">Maybe</span> <span class="dt">Closure</span>) [<span class="dt">Argument</span>]</span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">LetPrim</span> <span class="dt">Name</span> <span class="dt">Primitive</span> [<span class="dt">Name</span>] <span class="dt">Term</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Switch</span> <span class="dt">Name</span> [<span class="dt">C.Constant</span>] [<span class="dt">Term</span>] (<span class="dt">Maybe</span> <span class="dt">Term</span>)</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Halt</span> <span class="dt">Name</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>, <span class="dt">Ord</span>, <span class="dt">Read</span>, <span class="dt">Data</span>)</span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a><span class="ot">simp ::</span> <span class="dt">Census</span> <span class="ot">-&gt;</span> <span class="dt">Env</span> <span class="ot">-&gt;</span> <span class="dt">Subst</span> <span class="ot">-&gt;</span> <span class="dt">Term</span> <span class="ot">-&gt;</span> <span class="dt">CompEnv</span> <span class="dt">Term</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>simp census env s p <span class="ot">=</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> p <span class="kw">of</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a>    <span class="dt">LetVal</span> x v l <span class="ot">-&gt;</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a>      <span class="kw">if</span> count census x <span class="op">==</span> <span class="dv">0</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a>        <span class="kw">then</span> simp census env s l</span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a>        <span class="kw">else</span> <span class="kw">do</span></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a>          v&#39; <span class="ot">&lt;-</span> simpVal census env s v</span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a>          <span class="dt">LetVal</span> x v&#39; <span class="op">&lt;$&gt;</span> simp census (addEnv env x v&#39;) s l</span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a>    <span class="dt">LetSel</span> x i y l <span class="ot">-&gt;</span></span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a>      <span class="kw">let</span> y&#39; <span class="ot">=</span> applySubst s y</span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a>       <span class="kw">in</span> <span class="kw">case</span> <span class="fu">lookup</span> env y&#39; <span class="kw">of</span></span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a>            <span class="dt">Just</span> (<span class="dt">Tuple</span> elems) <span class="ot">-&gt;</span> simp census env (extendSubst s x (elems <span class="op">!!</span> i)) l</span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a>            _ <span class="ot">-&gt;</span> <span class="dt">LetSel</span> x i y&#39; <span class="op">&lt;$&gt;</span> simp census env s l</span>
<span id="cb1-43"><a href="#cb1-43" aria-hidden="true" tabindex="-1"></a>    <span class="dt">LetCont</span> k x l m <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-44"><a href="#cb1-44" aria-hidden="true" tabindex="-1"></a>      l&#39; <span class="ot">&lt;-</span> simp census env s l</span>
<span id="cb1-45"><a href="#cb1-45" aria-hidden="true" tabindex="-1"></a>      <span class="kw">case</span> count census k <span class="kw">of</span></span>
<span id="cb1-46"><a href="#cb1-46" aria-hidden="true" tabindex="-1"></a>        <span class="dv">0</span> <span class="ot">-&gt;</span> simp census env s m</span>
<span id="cb1-47"><a href="#cb1-47" aria-hidden="true" tabindex="-1"></a>        _ <span class="ot">-&gt;</span> <span class="dt">LetCont</span> k x l&#39; <span class="op">&lt;$&gt;</span> simp census (addEnv env k (<span class="dt">Cont</span> x l&#39;)) s m</span>
<span id="cb1-48"><a href="#cb1-48" aria-hidden="true" tabindex="-1"></a>    <span class="dt">LetFns</span> fns m <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-49"><a href="#cb1-49" aria-hidden="true" tabindex="-1"></a>      fns&#39; <span class="ot">&lt;-</span> <span class="fu">mapM</span> (\(x, b) <span class="ot">-&gt;</span> (x,) <span class="op">&lt;$&gt;</span> simpVal census env s b) fns</span>
<span id="cb1-50"><a href="#cb1-50" aria-hidden="true" tabindex="-1"></a>      m&#39; <span class="ot">&lt;-</span> simp census env s m</span>
<span id="cb1-51"><a href="#cb1-51" aria-hidden="true" tabindex="-1"></a>      <span class="fu">pure</span> <span class="op">$</span> <span class="dt">LetFns</span> fns&#39; m&#39;</span>
<span id="cb1-52"><a href="#cb1-52" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Continue</span> k x <span class="ot">-&gt;</span></span>
<span id="cb1-53"><a href="#cb1-53" aria-hidden="true" tabindex="-1"></a>      <span class="kw">let</span> x&#39; <span class="ot">=</span> applySubst s x</span>
<span id="cb1-54"><a href="#cb1-54" aria-hidden="true" tabindex="-1"></a>       <span class="kw">in</span> <span class="kw">let</span> k&#39; <span class="ot">=</span> applySubst s k</span>
<span id="cb1-55"><a href="#cb1-55" aria-hidden="true" tabindex="-1"></a>           <span class="kw">in</span> <span class="kw">case</span> <span class="fu">lookup</span> env k&#39; <span class="kw">of</span></span>
<span id="cb1-56"><a href="#cb1-56" aria-hidden="true" tabindex="-1"></a>                <span class="dt">Just</span> (<span class="dt">Cont</span> y l) <span class="ot">-&gt;</span> inst l <span class="op">&gt;&gt;=</span> simp census env (extendSubst s y x&#39;)</span>
<span id="cb1-57"><a href="#cb1-57" aria-hidden="true" tabindex="-1"></a>                _ <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="op">$</span> <span class="dt">Continue</span> k&#39; x&#39;</span>
<span id="cb1-58"><a href="#cb1-58" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Apply</span> f k _ xs <span class="ot">-&gt;</span></span>
<span id="cb1-59"><a href="#cb1-59" aria-hidden="true" tabindex="-1"></a>      <span class="kw">let</span> f&#39; <span class="ot">=</span> applySubst s f</span>
<span id="cb1-60"><a href="#cb1-60" aria-hidden="true" tabindex="-1"></a>          k&#39; <span class="ot">=</span> applySubst s k</span>
<span id="cb1-61"><a href="#cb1-61" aria-hidden="true" tabindex="-1"></a>          xs&#39; <span class="ot">=</span> <span class="fu">map</span> (applySubst s) xs</span>
<span id="cb1-62"><a href="#cb1-62" aria-hidden="true" tabindex="-1"></a>       <span class="kw">in</span> <span class="kw">case</span> <span class="fu">lookup</span> env f&#39; <span class="kw">of</span></span>
<span id="cb1-63"><a href="#cb1-63" aria-hidden="true" tabindex="-1"></a>            <span class="dt">Just</span> (<span class="dt">Fn</span> k1 _ xs1 l) <span class="ot">-&gt;</span></span>
<span id="cb1-64"><a href="#cb1-64" aria-hidden="true" tabindex="-1"></a>              inst l <span class="op">&gt;&gt;=</span> simp census env (extendSubst (extendSubsts s (<span class="fu">zip</span> xs1 xs&#39;)) k1 k&#39;)</span>
<span id="cb1-65"><a href="#cb1-65" aria-hidden="true" tabindex="-1"></a>            _ <span class="ot">-&gt;</span> <span class="fu">pure</span> <span class="op">$</span> <span class="dt">Apply</span> f&#39; k&#39; <span class="dt">Nothing</span> xs&#39;</span>
<span id="cb1-66"><a href="#cb1-66" aria-hidden="true" tabindex="-1"></a>    <span class="dt">LetPrim</span> n op ns t <span class="ot">-&gt;</span></span>
<span id="cb1-67"><a href="#cb1-67" aria-hidden="true" tabindex="-1"></a>      <span class="kw">let</span> fallback <span class="ot">=</span> simp census env s t</span>
<span id="cb1-68"><a href="#cb1-68" aria-hidden="true" tabindex="-1"></a>       <span class="kw">in</span> <span class="kw">if</span> count census n <span class="op">==</span> <span class="dv">0</span></span>
<span id="cb1-69"><a href="#cb1-69" aria-hidden="true" tabindex="-1"></a>            <span class="kw">then</span> fallback</span>
<span id="cb1-70"><a href="#cb1-70" aria-hidden="true" tabindex="-1"></a>            <span class="kw">else</span></span>
<span id="cb1-71"><a href="#cb1-71" aria-hidden="true" tabindex="-1"></a>              <span class="kw">let</span> ns&#39; <span class="ot">=</span> <span class="fu">map</span> (applySubst s) ns</span>
<span id="cb1-72"><a href="#cb1-72" aria-hidden="true" tabindex="-1"></a>               <span class="kw">in</span> <span class="kw">case</span> (op, <span class="fu">mapM</span> (<span class="fu">lookup</span> env) ns&#39;) <span class="kw">of</span></span>
<span id="cb1-73"><a href="#cb1-73" aria-hidden="true" tabindex="-1"></a>                    (<span class="dt">Primitive.EQ</span>, <span class="dt">Just</span> [<span class="dt">I32</span> a, <span class="dt">I32</span> b]) <span class="ot">-&gt;</span></span>
<span id="cb1-74"><a href="#cb1-74" aria-hidden="true" tabindex="-1"></a>                      <span class="kw">let</span> v <span class="ot">=</span> <span class="kw">if</span> a <span class="op">==</span> b <span class="kw">then</span> <span class="dt">I32</span> <span class="dv">1</span> <span class="kw">else</span> <span class="dt">I32</span> <span class="dv">0</span> <span class="kw">in</span> <span class="dt">LetVal</span> n v <span class="op">&lt;$&gt;</span> simp census (addEnv env n v) s t</span>
<span id="cb1-75"><a href="#cb1-75" aria-hidden="true" tabindex="-1"></a>                    (<span class="dt">Add2</span>, <span class="dt">Just</span> [<span class="dt">I32</span> a, <span class="dt">I32</span> b]) <span class="ot">-&gt;</span></span>
<span id="cb1-76"><a href="#cb1-76" aria-hidden="true" tabindex="-1"></a>                      <span class="kw">let</span> v <span class="ot">=</span> <span class="dt">I32</span> <span class="op">$</span> a <span class="op">+</span> b <span class="kw">in</span> <span class="dt">LetVal</span> n v <span class="op">&lt;$&gt;</span> simp census (addEnv env n v) s t</span>
<span id="cb1-77"><a href="#cb1-77" aria-hidden="true" tabindex="-1"></a>                    _ <span class="ot">-&gt;</span> <span class="dt">LetPrim</span> n op ns&#39; <span class="op">&lt;$&gt;</span> fallback</span></code></pre></div>
<p>完整代码在effektos仓库。</p>
<p>这篇论文还给出了另一种基于图的O(N)的实现，从基准测试的结果单独来看也很优秀。不过由于数据结构的不同，将CPS IR转换成图也需要开销，放整个编译管线中来看起来没有明显的优势。所以这部分的细节我就跳过了。（再一次说明优化时参考基准测试有多重要！）</p>
<h1 id="cps但是closure-passing-style">CPS，但是Closure Passing Style</h1>
<p>下一步当然是闭包转换。这部分参考了USTC的Advanced Topics in Software Technologies<a href="http://staff.ustc.edu.cn/~bjhua/courses/ats/2015/labs/lab2.pdf">讲义</a>。</p>
<figure>
<img src="../images/closure-conversion.png" alt="closure conversion" />
<figcaption aria-hidden="true">closure conversion</figcaption>
</figure>
<p>上图的实现是：</p>
<ul>
<li>提取function/cont的自由变量成一个事先声明的tuple, 里面塞着自由变量和”函数指针“。这个tuple命名成function/cont的名字去代替它们。</li>
<li>所有的function/cont apply都重写成取出”函数指针“，然后apply这个函数。</li>
<li>在function/cont内部插入取出闭包中的自由变量的代码。</li>
</ul>
<p>在实践中我把function hoisting在这一步之后做了。经过了closure conversion，function没有自由变量捕获之后也就不必嵌套在scope里。</p>
<p>经过进一步的known function分析可以把一些闭包优化掉（参考《Optimizing Closures in O(0) time》）。但是现在更紧急的是，所有的continuation都带上了闭包，每个控制流的转移都需要传递闭包，这显然是不能接受的。</p>
<h1 id="cps-are-ssa-done-right">CPS are SSA done right</h1>
<p>如何把continuation的闭包全部优化掉，这部分问题困扰了我一周。目前来看，second class continuation无法被当作函数一样被传递，能不能把cont编译成GOTO label，cont apply编译成Jump？后来群友指路<a href="https://cs420.epfl.ch/archive/20/notes.html">EPFL的课程</a>，Intermediate representations这节课的讲义揭示了modern CPS和SSA的关系：</p>
<figure>
<img src="../images/comparison-of-irs.png" alt="comparison of IRs" />
<figcaption aria-hidden="true">comparison of IRs</figcaption>
</figure>
<blockquote>
<p>In other words, functional IRs based on continuations, like CPS/L3, are SSA done right, in the sense that they avoid introducing the weird (and ultimetely useless) ϕ-functions.</p>
</blockquote>
<p><del>好好好，现在感觉SSA也非常的函数式，联想到传统编译器爱用SSA，再联想到GCC RTL那一堆括号的中间格式，让我们做个”X也是函数式编程语言“和”X也是lisp“的九宫格，看看C/C++们的骄傲处在”不主流的失败者阵营“九宫格的哪个位置。</del></p>
<p>那么，当最终的编译目标是x86这样的平台时，只需要像编译SSA那样把CPS编译到RTL（register transfer language），把continuation编译成GOTO label，continuation application编译成goto，把continuation参数的传递翻译成<code>MOV opernad, reg</code>。RTL的抽象已经非常接近常见的汇编，下一步，只需要写个寄存器分配，翻亿下指令集手册把工业垃圾的各种屎山规则纳入考虑，就可以产出高效的编译结果了。</p>
<h1 id="人类友好的cps-ir">人类友好的CPS IR</h1>
<p>虽然CPS是做对了的SSA，但是打印出来的效果不太易读。</p>
<figure>
<img src="../images/cps-bad-indent.png" alt="不能透露姓名的鸽" />
<figcaption aria-hidden="true">不能透露姓名的鸽</figcaption>
</figure>
<p>针对这个某群群友讨论了几点改善办法:</p>
<ol type="1">
<li><p>从CPS IR的pretty printer改进</p>
<ol type="a">
<li><p>对于仅apply一次的cont，可以打印的时候inline进来，就像OCaml的<code>let*</code>重载和haskell的do notation那样</p></li>
<li><p>把cont放到where后，例如</p></li>
</ol>
<pre><code>letcont k1 x = body in 
  letcont k2 x = body in 
    letcont k3 x = body in expr</code></pre>
<p>打印成</p>
<pre><code>body
where k1 x = body
      k2 x = body
      k3 x = body</code></pre></li>
<li><p>从CPS生成改进</p>
<ol type="a">
<li>只在控制流处生成cont。<code>let x = value</code>，<code>let x = y primOp z</code>是不需要的，避免缩进层数过深</li>
</ol></li>
</ol>
<p>在我的实践项目effektos的做法是1b和2a的结合，并且将所有的嵌套的cont挪出来拍平。结合function hoisting，这样打印结果最多只有两层缩进：</p>
<pre><code>LetFix 
  f env k x =
     ...
     where
     cont1 x = body
     cont2 x = body
  g env k x =  
     ...
     where
     cont1 x = body
     cont2 x = body
in 
expr</code></pre>
<h1 id="compiling-with-continuation-to-be-continued">Compiling with Continuation, to be continued</h1>
<p>这篇博客只是简单记录一下探索CPS IR遇到的问题和有用的参考。对于我正在的进行的项目effktos还需要阅读更多论文，解决更多的问题才能产出真正可用的函数式编译器（又是一大堆体力活！）。除去已经提及的，这里留下一些正在进行的主题以及参考, 有些和CPS不相关。</p>
<ul>
<li>pattern matching
<ul>
<li><a href="http://moscova.inria.fr/~maranget/papers/ml05e-maranget.pdf">Compiling Pattern Matching to Good Decision Tree</a></li>
<li><a href="https://julesjacobs.com/notes/patternmatching/patternmatching.pdf">How to compile pattern matching</a></li>
</ul></li>
<li>algebraic effect
<ul>
<li><a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/08/algeff-tr-2016-v2.pdf">Algebraic Effects for Functional Programming (Type Directed Compilation of Row-typed Algebraic Effects)</a></li>
</ul></li>
<li>fold/build fusing
<ul>
<li><a href="https://github.com/ocharles/papers/blob/master/Stream%20Fusion:%20From%20Lists%20to%20Streams%20to%20Nothing%20At%20All.pdf">Stream Fusion from lists to streams to nothing at all</a></li>
<li><a href="https://www.microsoft.com/en-us/research/publication/playing-by-the-rules-rewriting-as-a-practical-optimisation-technique-in-ghc/">Playing by the rules: rewriting as a practical optimisation technique in GHC</a></li>
</ul></li>
</ul>]]></summary>
</entry>
<entry>
    <title>evoBasic VM ISA设计</title>
    <link href="https://github.com/hahey/lanyon-hakyll/posts/2023-01-06-evm.html" />
    <id>https://github.com/hahey/lanyon-hakyll/posts/2023-01-06-evm.html</id>
    <published>2023-01-05T18:01:30Z</published>
    <updated>2023-01-05T18:01:30Z</updated>
    <summary type="html"><![CDATA[<h1 id="operand-types">operand types</h1>
<table>
<thead>
<tr class="header">
<th>Format</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>boolean</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>i8</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>i16</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>i32</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>i64</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>u8</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>u16</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>u32</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>u64</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>f32</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>f64</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>ref</code></td>
<td>reference to instance</td>
</tr>
<tr class="odd">
<td><code>hld</code></td>
<td>handle of /Fields/Argument/local variable/static field</td>
</tr>
<tr class="even">
<td><code>ftn</code></td>
<td>reference to method slot</td>
</tr>
<tr class="odd">
<td><code>vftn</code></td>
<td>reference to virtual function slot</td>
</tr>
<tr class="even">
<td><code>sftn</code></td>
<td>reference to static function</td>
</tr>
<tr class="odd">
<td><code>ctor</code></td>
<td>reference to ctor</td>
</tr>
<tr class="even">
<td><code>emconst</code></td>
<td>enum constant</td>
</tr>
<tr class="odd">
<td><code>record&lt;Token&gt;</code></td>
<td>memory of whole record</td>
</tr>
</tbody>
</table>
<h1 id="instructions">instructions</h1>
<h2 id="function-loading-and-invocation">Function Loading and Invocation</h2>
<p><code>option count</code> is omitted in invocation when function declaration did not have any optional parameter.</p>
<ul>
<li><p>virtual function</p>
<p>…, ref, <code>arg2 value</code>, <code>arg1 value</code>, <code>option value2</code>, <code>option info2</code>, <code>option value1</code> , <code>option info1</code>, <code>u8 option count</code>, <code>paramArray</code>, vftn | …, <code>result1 value</code>, <code>result2 value</code></p></li>
<li><p>method</p>
<p>…, ref, <code>arg2 value</code>, <code>arg1 value</code>, <code>option value2</code>, <code>option info2</code>, <code>option value1</code> , <code>option info1</code>, <code>u8 option count</code>, <code>paramArray</code>, ftn | …, <code>result1 value</code>, <code>result2 value</code></p></li>
<li><p>static function</p>
<p>…, <code>arg2 value</code>, <code>arg1 value</code>, <code>option value2</code>, <code>option info2</code>, <code>option value1</code> , <code>option info1</code>, <code>u8 option count</code>, <code>paramArray</code>, sftn | …, <code>result1 value</code>, <code>result2 value</code></p></li>
<li><p>constructor</p>
<p>…, ref, <code>arg2 value</code>, <code>arg1 value</code>, <code>option value2</code>, <code>option info2</code>, <code>option value1</code> , <code>option info1</code>, <code>u8 option count</code>, <code>paramArray</code>, ctor | …</p></li>
</ul>
<table>
<thead>
<tr class="header">
<th>Format</th>
<th>Operand</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>ldsftn &lt;Token&gt;</code></td>
<td>…</td>
<td>…, sftn</td>
</tr>
<tr class="even">
<td><code>ldvftn &lt;Token&gt;</code></td>
<td>…</td>
<td>…, vftn</td>
</tr>
<tr class="odd">
<td><code>ldftn &lt;Token&gt;</code></td>
<td>…</td>
<td>…, ftn</td>
</tr>
<tr class="even">
<td><code>ldctor &lt;Token&gt;</code></td>
<td>…</td>
<td>…, ctor</td>
</tr>
<tr class="odd">
<td><code>ldforeign &lt;Token&gt;</code></td>
<td>…</td>
<td>…, foreign</td>
</tr>
<tr class="even">
<td><code>wrapsftn</code></td>
<td>…, sftn</td>
<td>…, dlg</td>
</tr>
<tr class="odd">
<td><code>wrapvftn</code></td>
<td>…, vftn, ref</td>
<td>…, dlg</td>
</tr>
<tr class="even">
<td><code>wrapftn</code></td>
<td>…, ftn, ref</td>
<td>…, dlg</td>
</tr>
<tr class="odd">
<td><code>wrapctor</code></td>
<td>…, ctor</td>
<td>…, dlg</td>
</tr>
<tr class="even">
<td><code>wrapforeign</code></td>
<td>…, foreign</td>
<td>…, dlg</td>
</tr>
<tr class="odd">
<td><code>callmethod</code></td>
<td>…, ftn, ref, …</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>callvirtual</code></td>
<td>…, vftn, ref, …</td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>callstatic</code></td>
<td>…, sftn, …</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>callctor</code></td>
<td>…, ctor</td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>callforeign</code></td>
<td>…, foreign</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>callintrinsic &lt;Token&gt;</code></td>
<td>…</td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>calldlg</code></td>
<td>…, dlg</td>
<td>…</td>
</tr>
</tbody>
</table>
<h2 id="fieldsparameterslocal-variablesenum-constant-access">Fields/Parameters/Local Variables/Enum Constant Access</h2>
<table>
<thead>
<tr class="header">
<th>Format</th>
<th>Operand</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>starg.&lt;Type&gt;</code></td>
<td>…, <code>value</code>, u16</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>ldarg.&lt;Type&gt;</code></td>
<td>…, u16</td>
<td>…, <code>value</code></td>
</tr>
<tr class="odd">
<td><code>ldarga</code></td>
<td>…, u16</td>
<td>…, hld</td>
</tr>
<tr class="even">
<td><code>stloc.&lt;Type&gt;</code></td>
<td>…, <code>value</code>, u16</td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>ldloc.&lt;Type&gt;</code></td>
<td>…, u16</td>
<td>…, <code>value</code></td>
</tr>
<tr class="even">
<td><code>ldloca</code></td>
<td>…, u16</td>
<td>…, hld</td>
</tr>
<tr class="odd">
<td><code>stfld.&lt;Type&gt; &lt;Token&gt;</code></td>
<td>…, <code>value</code>, <code>ref or hld</code></td>
<td>…</td>
</tr>
<tr class="even">
<td><code>ldfld.&lt;Type&gt; &lt;Token&gt;</code></td>
<td>…, <code>ref, hld or record</code></td>
<td>…, <code>value</code></td>
</tr>
<tr class="odd">
<td><code>ldflda &lt;Token&gt;</code></td>
<td>…, <code>ref or hld</code></td>
<td>…, hld</td>
</tr>
<tr class="even">
<td><code>stsfld.&lt;Type&gt; &lt;Token&gt;</code></td>
<td>…, <code>value</code></td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>ldsfld.&lt;Type&gt; &lt;Token&gt;</code></td>
<td>…</td>
<td>…, <code>value</code></td>
</tr>
<tr class="even">
<td><code>ldsflda &lt;Token&gt;</code></td>
<td>…</td>
<td>…, hld</td>
</tr>
<tr class="odd">
<td><code>testopt</code></td>
<td>…, u16 * n, u8,</td>
<td>…, <code>boolean</code></td>
</tr>
<tr class="even">
<td><code>ldoptinfo &lt;Token&gt;</code></td>
<td>…</td>
<td>…, <code>option info</code></td>
</tr>
<tr class="odd">
<td><code>ldenumc &lt;Token&gt;</code></td>
<td>…</td>
<td>…, <code>enum constant</code></td>
</tr>
</tbody>
</table>
<h2 id="array">Array</h2>
<p>数组操作指令中的Token为元素类型的Token。</p>
<table>
<thead>
<tr class="header">
<th>Format</th>
<th>Operand</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>stelemr.&lt;Type&gt; &lt;Token&gt;</code></td>
<td>…, ref, i32, <code>value</code></td>
<td>…</td>
</tr>
<tr class="even">
<td><code>stelem.&lt;Type&gt; &lt;Token&gt;</code></td>
<td>…, <code>value</code>, ref, i32</td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>ldelem.&lt;Type&gt; &lt;Token&gt;</code></td>
<td>…, ref, i32</td>
<td>…, <code>value</code></td>
</tr>
<tr class="even">
<td><code>ldelema &lt;Token&gt;</code></td>
<td>…, ref, i32</td>
<td>…, hld</td>
</tr>
<tr class="odd">
<td><code>newarray &lt;Token&gt;</code></td>
<td>…, i32</td>
<td>…, ref</td>
</tr>
</tbody>
</table>
<h2 id="control-flow">Control Flow</h2>
<table>
<thead>
<tr class="header">
<th>Format</th>
<th>Operand</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>jif &lt;u32 address&gt;</code></td>
<td>…, boolean</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>br &lt;u32 address&gt;</code></td>
<td>…</td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>ret</code></td>
<td>…</td>
<td>…</td>
</tr>
</tbody>
</table>
<h2 id="data-operation">Data Operation</h2>
<table>
<thead>
<tr class="header">
<th>Format</th>
<th>Operand</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>nop</code></td>
<td>…</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>dup</code></td>
<td>…, value</td>
<td>…, value, value</td>
</tr>
<tr class="odd">
<td><code>push.&lt;Type&gt; &lt;Value&gt;</code></td>
<td>…</td>
<td>…, <code>value</code></td>
</tr>
<tr class="even">
<td><code>pop.&lt;Type&gt;</code></td>
<td>… <code>value</code></td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>store.&lt;Type&gt;</code></td>
<td>…, <code>value</code>, hld</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>load.&lt;Type&gt;</code></td>
<td>…, hld</td>
<td>…, <code>value</code></td>
</tr>
<tr class="odd">
<td><code>ldnothing</code></td>
<td>…</td>
<td>…, <code>nothing</code></td>
</tr>
<tr class="even">
<td><code>convert &lt;Type&gt; &lt;Type&gt;</code></td>
<td>…, <code>value</code></td>
<td>…, <code>value</code></td>
</tr>
<tr class="odd">
<td><code>castClass &lt;Token&gt; &lt;Token&gt;</code></td>
<td>…, ref</td>
<td>ref</td>
</tr>
<tr class="even">
<td><code>instanceof &lt;classToken&gt;</code></td>
<td>…,ref</td>
<td>…, boolean</td>
</tr>
<tr class="odd">
<td><code>ldstr &lt;Token&gt;</code></td>
<td>…</td>
<td>…, ref</td>
</tr>
<tr class="even">
<td><code>newobj &lt;Token&gt;</code></td>
<td>…</td>
<td>…, ref</td>
</tr>
<tr class="odd">
<td><code>pin</code></td>
<td>…, ref</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>unpin</code></td>
<td>…, ref</td>
<td>…</td>
</tr>
</tbody>
</table>
<h2 id="exception-handling">Exception Handling</h2>
<table>
<thead>
<tr class="header">
<th>Format</th>
<th>Operand</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>throw</code></td>
<td>…,ref</td>
<td>…</td>
</tr>
<tr class="even">
<td><code>enter &lt;constructed Token of exception class&gt; &lt;u32 address&gt;</code></td>
<td>…</td>
<td>…</td>
</tr>
<tr class="odd">
<td><code>leave &lt;constructed Token of exeception class&gt;</code></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="binary-operation">Binary operation</h2>
<p>transform: …, <code>lhs</code>, <code>rhs</code> -&gt; …, <code>result</code></p>
<ul>
<li>Add</li>
<li>Sub</li>
<li>Mul</li>
<li>Div</li>
<li>And</li>
<li>Or</li>
<li>Xor</li>
<li>EQ</li>
<li>NE</li>
<li>LT</li>
<li>GT</li>
<li>LE</li>
<li>GE</li>
<li>Mod</li>
</ul>
<h2 id="unary-operation">Unary operation</h2>
<p>transform: …, <code>value</code> -&gt; …, <code>value</code></p>
<ul>
<li>Neg</li>
<li>Not</li>
</ul>]]></summary>
</entry>
<entry>
    <title>evoBasic: 半完成的现代BASIC语言编译器</title>
    <link href="https://github.com/hahey/lanyon-hakyll/posts/2023-01-04-evobasic.html" />
    <id>https://github.com/hahey/lanyon-hakyll/posts/2023-01-04-evobasic.html</id>
    <published>2023-01-03T18:01:30Z</published>
    <updated>2023-01-03T18:01:30Z</updated>
    <summary type="html"><![CDATA[<p>这是我的一个从2021年初开始，到2022年的计划。最初的目标是构建一个延续了classic visual basic风格的编程语言，能够编译我初中时的vb6程序（修改过的）到linux平台。</p>
<p>VB6的中文社区一直想要一个这样的编译器，他们拒绝迁移到1998年后的下一个版本vb.net,认为vb.net完全是另一门全新的编程语言，而微软抛弃了他们。这也是我第一次从编译器开发者的角度和社区的程序员打交道，结果当然是不愉快的：这样的背景决定了他们对编程语言的态度已经保守到了让人无法忍受的地步。</p>
<p>后来我把目标转向学习和实践编译原理和虚拟机设计与实现，再后来感觉到继续做这个项目学习不到什么新的知识，而只是一堆又一堆的体力活在等着我，这个项目就不了了之了。</p>
<p>最后，这个项目实现了一个BASIC编译器和类jvm虚拟机。</p>
<hr />
<p>该项目目前实现和未实现的功能如下：</p>
<ol type="1">
<li>编译器</li>
</ol>
<ul class="task-list">
<li><input type="checkbox" checked="" />原始类型如：无符号整形(Byte)、有符号整型(Short,Integer,Long)、浮点(Single,Double)、布尔型(Boolean)、以及存储Unicode codepoint的Rune。</li>
<li><input type="checkbox" checked="" />原始类型的隐式转换与装箱</li>
<li><input type="checkbox" checked="" />用户定义Type类型(类似Struct)</li>
<li><input type="checkbox" />枚举类型</li>
<li><input type="checkbox" checked="" />数组</li>
<li><input type="checkbox" checked="" />UTF8字符串</li>
<li><input type="checkbox" checked="" />前向引用</li>
<li><input type="checkbox" />控制流程语句： if..then..else, while..wend, for..next, return 等</li>
<li><input type="checkbox" checked="" />函数：Function与Sub、参数按值(Byval)或地址(Byref)传递语义、可选参数(Optional)、变长参数(ParamArray)</li>
<li><input type="checkbox" />运算符与表达式：+, -, *, /, mod, and, or, xor, not</li>
<li><input type="checkbox" checked="" />运算符重载</li>
<li><input type="checkbox" />模块化：Module、二进制包生成、二进制包导入(import)、可见性控制(Public和Private)</li>
<li><input type="checkbox" checked="" />面向对象特性：类、继承、虚函数、多态</li>
<li><input type="checkbox" checked="" />接口</li>
<li><input type="checkbox" checked="" />错误处理：throw、try..catch语句</li>
<li><input type="checkbox" checked="" />函数作为参数传递</li>
<li><input type="checkbox" checked="" />可选参数默认值</li>
<li><input type="checkbox" checked="" />局部变量类型推导</li>
</ul>
<p>编译器目前无任何代码优化。</p>
<ol start="2" type="1">
<li>虚拟机</li>
</ol>
<ul class="task-list">
<li><input type="checkbox" checked="" />二进制包加载和执行</li>
<li><input type="checkbox" checked="" />二进制包兼容性检查</li>
<li><input type="checkbox" checked="" />copying GC</li>
<li><input type="checkbox" />分代GC</li>
<li><input type="checkbox" />错误处理与部分安全(空指针、下标越界等)检查</li>
<li><input type="checkbox" checked="" />外部函数调用</li>
</ul>
<h2 id="evobasic原理概览">evoBasic原理概览</h2>
<pre><code>    +---------------------+
    | 源码和二进制包依赖     |
    +---------------------+
            |
            | 输入
            V
    +----------------+   编译生成字节码     +-------------+   
    |  编译器ebc      |  ---------------&gt;  |   .bkg文件  | 
    +----------------+                    +-------------+
                                               | 输入执行
                                               V
                                         +-------------+
                                         |  虚拟机 evm  |
                                         +-------------+
</code></pre>
<h2 id="示例选择排序">示例：选择排序</h2>
<ol type="1">
<li>创建文本文件test.eb，输入如下内容：</li>
</ol>
<div class="sourceCode" id="cb2"><pre class="sourceCode vb"><code class="sourceCode monobasic"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">Sub </span>Swap(a <span class="kw">As</span> <span class="dt">Integer</span>, b <span class="kw">As</span> <span class="dt">Integer</span>)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>	<span class="kw">Dim</span> t <span class="kw">As</span> <span class="dt">Integer</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>	t = a</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>	a = b</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>	b = t</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="kw">End Sub</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="kw">Sub </span>SelectionSort(<span class="kw">Byval</span> arr <span class="kw">As</span> <span class="dt">Integer</span>[])</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>	<span class="kw">Dim</span> A <span class="kw">As</span> <span class="dt">Integer</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>	<span class="kw">For Dim</span> i = 0 <span class="kw">To</span> arr.Length()-1</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>		<span class="kw">Dim</span> m <span class="kw">As</span> <span class="dt">Integer</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>		m = i</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>		<span class="kw">For Dim</span> j = i+1 <span class="kw">To</span> arr.Length()-1</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>			<span class="kw">If </span>arr[m] &gt; arr[j] <span class="kw">then</span> m = j</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>		<span class="kw">Next</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>		Swap(arr[i], arr[m])</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>	<span class="kw">Next</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="kw">End Sub</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="kw">Sub </span>PrintArray(<span class="kw">Byval</span> ls <span class="kw">As</span> <span class="dt">Integer</span>[])</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>	<span class="kw">For Dim</span> i = 0 <span class="kw">To</span> ls.length()-1</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>		Print(ls[i])</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>	<span class="kw">Next</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a>	Println(<span class="st">&quot;;&quot;</span>)</span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a><span class="kw">End Sub</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a><span class="kw">Sub </span>Main()</span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a>	<span class="kw">Dim</span> a <span class="kw">As</span> <span class="dt">Integer</span>[]</span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a>	a = [9,8,7,6,5,4,3,2,1]</span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a>	SelectionSort(a)</span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a>	PrintArray(a)</span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a><span class="kw">End Sub</span></span></code></pre></div>
<ol start="2" type="1">
<li>使用ebc编译test.eb。</li>
</ol>
<p>注意目前需要使用-p选项手动添加包依赖文件夹。请确保这个文件夹下包含builtin.bkg</p>
<pre><code>    ebc test.eb -p C:/Path/To/Bkg/Folder</code></pre>
<ol start="3" type="1">
<li>使用evm运行output.bkg</li>
</ol>
<pre><code></code></pre>
<p>下面是evoBasic的详细说明。</p>
<hr />
<h1 id="一般规则">一般规则</h1>
<ul>
<li><strong>evoBasic是大小写不敏感的</strong></li>
<li><strong>evoBasic语法严格定义换行</strong></li>
<li>注释的语法是 <code>/* 注释 */</code> 和 <code>//注释</code></li>
</ul>
<h1 id="语句">1 语句</h1>
<h2 id="变量定义语句">1.1 变量定义语句</h2>
<pre><code>    Dim a As Integer, b as Long</code></pre>
<h2 id="流程控制语句">1.2 流程控制语句</h2>
<h3 id="选择结构">1.2.1 选择结构</h3>
<pre><code>    if exp1 then 
       ...
    else if exp2 then
       ...
    else
       ...
    end if</code></pre>
<pre><code>    if exp then statement1() else statement2() </code></pre>
<pre><code>    if exp then statement1() </code></pre>
<p>目前不支持Select语句。</p>
<h3 id="循环结构">1.2.2 循环结构</h3>
<pre><code>    while exp
        ...
    wend</code></pre>
<pre><code>    Dim i As Integer
    For i = 0 To 100 Step 2
        ...
    Next</code></pre>
<pre><code>    //for循环的迭代变量只能是Integer类型
    For Dim i = 100 To 0 
        ...
    Next</code></pre>
<h3 id="异常抛出与处理语句">1.2.3 异常抛出与处理语句</h3>
<pre><code>    Sub Main()
        Try
            Test()
        Catch a As OutOfRangeException //下标越界异常
            ...
        Catch a As ConversionException //转换异常
            ...
        End Try
    End Sub

    Sub Test()
        Throw New OutOfRangeException(10,5)
    End Sub</code></pre>
<h3 id="其他流程控制语句">1.2.4 其他流程控制语句</h3>
<table>
<thead>
<tr class="header">
<th>语句</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>Continue</code></td>
<td>跳过一次当前循环</td>
</tr>
<tr class="even">
<td><code>break</code></td>
<td>退出当前循环</td>
</tr>
<tr class="odd">
<td><code>Exit Sub</code></td>
<td>从Sub中返回</td>
</tr>
<tr class="even">
<td><code>Exit For</code></td>
<td>退出For…Next循环</td>
</tr>
<tr class="odd">
<td><code>Exit Loop</code></td>
<td>退出While…Wend循环</td>
</tr>
<tr class="even">
<td><code>Return 表达式</code></td>
<td>返回值exp</td>
</tr>
</tbody>
</table>
<h1 id="表达式">2 表达式</h1>
<h1 id="运算符优先级">2.1 运算符优先级</h1>
<table>
<thead>
<tr class="header">
<th>运算符</th>
<th>描述</th>
<th>优先级（越小的优先级越高）</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>a(b,c,d...)</code></td>
<td>函数调用</td>
<td>1</td>
</tr>
<tr class="even">
<td><code>a[b]</code></td>
<td>下标索引</td>
<td>1</td>
</tr>
<tr class="odd">
<td><code>a Is TypeName</code></td>
<td>类型判断</td>
<td>1</td>
</tr>
<tr class="even">
<td><code>a As TypeName</code></td>
<td>类型转换</td>
<td>1</td>
</tr>
<tr class="odd">
<td><code>a.b</code></td>
<td>成员寻找</td>
<td>1</td>
</tr>
<tr class="even">
<td><code>New TypeName(a,b,c...)</code></td>
<td>类实例化</td>
<td>1</td>
</tr>
<tr class="odd">
<td><code>Optional a,b,c...</code></td>
<td>可选参数测试</td>
<td>1</td>
</tr>
<tr class="even">
<td><code>-a</code></td>
<td>负数</td>
<td>2</td>
</tr>
<tr class="odd">
<td><code>+a</code></td>
<td>正数</td>
<td>2</td>
</tr>
<tr class="even">
<td><code>*</code>, <code>/</code>, <code>Mod</code></td>
<td>乘除、取模</td>
<td>3</td>
</tr>
<tr class="odd">
<td><code>+</code>, <code>-</code></td>
<td>加减</td>
<td>4</td>
</tr>
<tr class="even">
<td><code>&gt;</code>,<code>&gt;=</code>,<code>==</code>,<code>&lt;&gt;</code>,<code>=&lt;</code>,<code>&lt;</code></td>
<td>大于、大于等于、等于、不等于、小于等于、小于</td>
<td>5</td>
</tr>
<tr class="odd">
<td><code>Not</code></td>
<td>非运算</td>
<td>6</td>
</tr>
<tr class="even">
<td><code>And</code>, <code>Or</code>, <code>Xor</code></td>
<td>和、或、异或</td>
<td>7</td>
</tr>
<tr class="odd">
<td><code>a = b</code></td>
<td>赋值</td>
<td>8</td>
</tr>
</tbody>
</table>
<p>evobasic并没有定义运算符和优先级的功能，且运算符功能与其他命令式语言相似，因此只对部分运算符的功能作详细介绍。</p>
<ul>
<li>类型判断运算符</li>
</ul>
<pre><code>    a Is TypeA</code></pre>
<p>判断实例a类型是否为TypeA，返回值为Boolean。</p>
<ul>
<li>类型转换运算符</li>
</ul>
<pre><code>    a As TypeB</code></pre>
<p>将实例a转换为TypeB并返回。如果无法进行转换，则产生<code>ConversionException</code>异常。</p>
<ul>
<li>可选参数测试运算符</li>
</ul>
<pre><code>    Optional a</code></pre>
<p>判断函数调用者是否提供了可选参数a的实参。返回值为Boolean类型。</p>
<pre><code>    Optional a,b,c</code></pre>
<p>判断函数调用者是否提供了可选参数a,b,c的实参。返回值为Boolean类型。</p>
<ul>
<li>赋值运算符</li>
</ul>
<pre><code>    a = b</code></pre>
<p>将b的值赋给a，然后返回a的值。</p>
<h2 id="字面量">2.2 字面量</h2>
<h3 id="字符串字面量和字符字面量">2.2.1 字符串字面量和字符字面量</h3>
<p>evoBasic使用<code>""</code>定义字符串字面量,包含任意数量的字符，<code>''</code>定义字符字面量，包含一个unicode codepoint。</p>
<p>字符转义使用与c语言类似的方式，来在字符串字面量和字符字面量中表示特殊字符。</p>
<table>
<thead>
<tr class="header">
<th>转义字符</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>\n</code></td>
<td>换行</td>
</tr>
<tr class="even">
<td><code>\r</code></td>
<td>回车</td>
</tr>
<tr class="odd">
<td><code>\t</code></td>
<td>制表符</td>
</tr>
</tbody>
</table>
<p>有关字符处理的更多信息，见类型、内置库字符串章节。</p>
<h3 id="数组字面量">2.2.2 数组字面量</h3>
<p>使用<code>[exp1,exp2,exp3]</code>表示的任意个元素。返回数组类型为<code>Object[]</code>。</p>
<h3 id="其他字面量">2.2.3 其他字面量</h3>
<table>
<thead>
<tr class="header">
<th>字面量</th>
<th>可能的值</th>
<th>类型</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>布尔</td>
<td>True,false</td>
<td>Boolean</td>
</tr>
<tr class="even">
<td>整型字面量</td>
<td>1, 2, 3 ….</td>
<td>Integer</td>
</tr>
<tr class="odd">
<td>浮点字面量</td>
<td>1.1, 3.14, …</td>
<td>Double</td>
</tr>
<tr class="even">
<td>空对象</td>
<td>Nothing</td>
<td></td>
</tr>
</tbody>
</table>
<h1 id="类型">3 类型</h1>
<h2 id="原始类型">3.1 原始类型</h2>
<table>
<thead>
<tr class="header">
<th>原始类型</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Byte</td>
<td>8位无符号整型</td>
</tr>
<tr class="even">
<td>Short</td>
<td>16位有符号整型</td>
</tr>
<tr class="odd">
<td>Integer</td>
<td>32位有符号整型</td>
</tr>
<tr class="even">
<td>Long</td>
<td>64位有符号整型</td>
</tr>
<tr class="odd">
<td>Single</td>
<td>32位单精度浮点</td>
</tr>
<tr class="even">
<td>Double</td>
<td>64位双精度浮点</td>
</tr>
<tr class="odd">
<td>Boolean</td>
<td>布尔类型</td>
</tr>
<tr class="even">
<td>Rune</td>
<td>32位无符号整型，表示Unicode的一个码点（codepoint）</td>
</tr>
</tbody>
</table>
<h2 id="枚举类型">3.2 枚举类型</h2>
<p>定义一系列的枚举，它可以有一个32位无符号整型的值。</p>
<pre><code>    enum MyEnum
        MyValO   // 0
        MyVal1 = 100
        MyVal2 = 50
        MyVal3   // 51
    end enum</code></pre>
<h2 id="记录类型未实现">3.3 记录类型（未实现）</h2>
<p>带名字的积类型。功能等同c语言的struct。</p>
<pre><code>    Type MyType
        FieldA As Integer
        FieldB As Boolean
    End Type</code></pre>
<p>不支持合类型以及匿名积类型(Tuple)。</p>
<h2 id="类类型">3.4 类类型</h2>
<pre><code>    Class A Extend B
        ...
    End Class</code></pre>
<p>evobasic支持继承。当省略Extend B时，默认继承Object。
### 3.4.2 静态成员和静态字段</p>
<p>该特性与c#相似。</p>
<pre><code>    Class MyClass
        Public Static Sub MyStaticSub()

        End Sub

        Private Static A As Integer
    End Class</code></pre>
<h3 id="构造函数">3.4.3 构造函数</h3>
<pre><code>    Class Layout
        Public New(Byval Width As Integer, Byval Height As Integer)
            ...
        End New
    End Class

    Class Derived Extend Base
        Public New(Byval Label As String, Byval Width As Integer, Byval Height As Ineger) Extend(Width,Height)
            ...
        End New
    End Class</code></pre>
<p>构造函数是实例化用户定义类的窗口。编译器不会提供类的默认构造函数，当一个类没有声明构造函数时，它就无法实例化。
构造函数的参数列表后的Extend(a,b,c…)是构造函数委托，语法与函数调用相似，它将参数转发给基类的构造函数；基类的构造函数在子类的构造函数之前执行。当基类直接为Object时，构造函数委托可以省略。</p>
<p>evobasic不支持重载，一个类只能拥有一个构造函数。如果要表达“从不同的输入构造实例”的抽象，请使用可选参数。如果在不同的输入构造时，几个可选参数之间的选择存在组合和互斥关系，请将构造函数设为私有，额外包装几个静态函数去调用它。</p>
<p>例如，刻画一个文件对象，该文件对象能通过以下途径构造：
* 从输入Byte[]读取文本并储存
* 从路径Path指向的本地文件读取
* 从网络路径NetPath指向的空间下载
* 新建一个空文件</p>
<p>下面使用可选参数刻画文件对象：</p>
<pre><code>    Class MyFile
        Public New(Optional Byval data As Byte[], Optional Byval Path As String, Optional NetPath As String, Optional Byval Empty As Boolen)
            If Optional data Then

            Else If Optional Path Then

            Else if Optional NetPath Then

            Else if Optional Empty Then

            Else
                // throw Error
            End if
        End New
    End Class</code></pre>
<p>缺点是显而易见的，几个可选参数的输入具有互斥关系，即，一个文件既不可能又是从本地加载，又是新建未保存的空文件。而这个构造函数却能够同时接受所有可选的输入。</p>
<p>下面通过将构造函数隐藏，使用静态函数包装的方式实现这种互斥的输入关系。</p>
<pre><code>    Class MyFile
        Private New(Optional Byval data As Byte[], Optional Byval Path As String, Optional NetPath As String, Optional Byval Empty As Boolen)
            ...
        End New

        Public Static Function FromPath(Byval Path As String) As MyFile
            Return New MyFile(Path: Path)
        End Function

        Public Static Function FromWeb(Byval NetPath As String) As MyFile
            Return New MyFile(NetPath: NetPath)
        End Function

        Public Static Function FromBytes(Byval data As Bytes) As MyFile
            Return New MyFile(data: data)
        End Function

        Public Static Function Create() As MyFile
            Return New MyFile(Empty: True)
        End Function
    End Class</code></pre>
<h3 id="虚函数与多态">3.4.4 虚函数与多态</h3>
<pre><code>    Class Animal
        Public Virtual Function Bark() As String
            Throw New NotImplementedException(&quot;Animal.Bark not implemented&quot;)
        End Function
    End Class

    Class Dog Extend Animal
        Public Override Function Bark() As String
            Return &quot;汪&quot;
        End Funtion
    End Class

    Class Cat Extend Animal
        Public Override Function Bark() As String
            Return &quot;喵&quot;
        End Function
    End Class</code></pre>
<h3 id="特殊处理的类">3.4.5 特殊处理的类</h3>
<p>一些类虽然使用用户代码实现在builtin.bkg当中，但是会被编译器特殊对待。</p>
<ul>
<li>Object</li>
</ul>
<p>Object类是evobasic的顶层类型（top type），也即，所有的类型都可以转换成Object。Object不继承任何类。</p>
<table>
<thead>
<tr class="header">
<th>公开成员</th>
<th></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>Virtual Function ToString() As String</code></td>
<td>序列化成字符串并返回</td>
</tr>
</tbody>
</table>
<ul>
<li>Array</li>
</ul>
<p>Array继承了Object。所有数组都从Array派生。</p>
<table>
<thead>
<tr class="header">
<th>公开成员</th>
<th></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>Function Length() As Integer</code></td>
<td>返回数组长度，即元素数量</td>
</tr>
</tbody>
</table>
<h2 id="数组类型">3.5 数组类型</h2>
<p>数组是一种特殊的类，它的大小一经创建就是不可更改的。</p>
<p>声明接受一个Integer数组</p>
<pre><code>Dim a As Integer[]

Sub Foo(Byval b As Integer[])

End Sub</code></pre>
<p>创建一个数组，指定大小</p>
<pre><code>    Dim a As Integer[]
    a = New Integer[6]
    Print(a.Length()) //输出6</code></pre>
<p>使用Array类型变量存储任意数组，并通过Is表达式判断类型。</p>
<pre><code>	Dim a As Array
	a = New Integer[5]
	Print(a is Integer[]) //输出True</code></pre>
<h2 id="函数类型">3.6 函数类型</h2>
<ul>
<li>无返回值函数</li>
</ul>
<pre><code>    Sub MyFunc(Byval Input As Integer, ParamArray Byval xs As Object[])
        ...
    End Sub</code></pre>
<ul>
<li>有返回值函数</li>
</ul>
<pre><code>    Function MyFunc(Byval Input As Integer, Optional Byval a As Long) As Integer
        ...
    End Function</code></pre>
<p>不支持函数重载。</p>
<h3 id="函数参数的求值策略">3.6.1 函数参数的求值策略</h3>
<p>与vb相同, 函数参数有两种求值策略Byval和Byref，分别为按值传递和按址传递。
evobasic没有指针，通过Byref可以代替部分指针的功能，配合Class可以模拟二级指针的部分功能。</p>
<h3 id="可选参数与变长参数列表">3.6.2 可选参数与变长参数列表</h3>
<p>可选参数表示该参数是可缺省的。通过Optional表达式可以测试参数是否被调用方提供。与vb不同，在传递可选时必须指定参数名，如下</p>
<pre><code>    Sub Foo(Byval n1 As Integer, Byval n2 as Integer, Optional Byval a As Integer, Optional b As Integer)
        if optional a,b then
            ...
        end if
    end sub

    Sub Main()
        // Foo(1,2,3,4)         错误
        Foo(1,2, a: 3, b: 4)  //正确
    End Sub</code></pre>
<p>变长参数列表是类型为Object[]的数组, 求值策略必须为Byval，且只能声明在参数列表的最后。它允许函数调用时传递任意数量的参数；当然，直接传递一个Object[]数组作为整个变长参数列表的值也是允许的。</p>
<pre><code>    Sub Foo(Byval a As String, ParamArray Byval arr As Object[])
        ...
    End Sub

    Sub Main()
        Foo(&quot;第一次输入:&quot;,1,2,3,4,5,6)

        Dim arr As Object[]
        arr = [1,2,3,4,5,6]
        Foo(&quot;等效的输入:&quot;, arr)
    End Sub</code></pre>
<h1 id="隐式转换与装箱">4 隐式转换与装箱</h1>
<h2 id="类型提升">4.1 类型提升</h2>
<p>类似于c语言的整型提升规则，evoBasic将此种规则从整型推广到了浮点类型。
两种类型A,B的值a,b参与二元运算时，将a,b隐式转换到类型C后再进行运算。
表格中行是类型A，列为类型B。表格中的内容为转换后的类型C。如果内容为空，则表示两种类型无法进行二元运算。</p>
<table>
<thead>
<tr class="header">
<th>B二元运算</th>
<th>Boolean</th>
<th>Byte</th>
<th>Short</th>
<th>Rune</th>
<th>Integer</th>
<th>Long</th>
<th>Single</th>
<th>Double</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Boolean</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="even">
<td>Byte</td>
<td></td>
<td></td>
<td>Integer</td>
<td></td>
<td>Integer</td>
<td>Long</td>
<td>Double</td>
<td>Double</td>
</tr>
<tr class="odd">
<td>Short</td>
<td></td>
<td>Integer</td>
<td></td>
<td></td>
<td>Integer</td>
<td>Long</td>
<td>Double</td>
<td>Double</td>
</tr>
<tr class="even">
<td>Rune</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="odd">
<td>Integer</td>
<td></td>
<td>Integer</td>
<td>Integer</td>
<td></td>
<td></td>
<td>Long</td>
<td>Double</td>
<td>Double</td>
</tr>
<tr class="even">
<td>Long</td>
<td></td>
<td>Long</td>
<td>Long</td>
<td></td>
<td>Long</td>
<td></td>
<td>Double</td>
<td>Double</td>
</tr>
<tr class="odd">
<td>Single</td>
<td></td>
<td>Double</td>
<td>Double</td>
<td></td>
<td>Double</td>
<td>Double</td>
<td>Double</td>
<td></td>
</tr>
<tr class="even">
<td>Double</td>
<td></td>
<td>Double</td>
<td>Double</td>
<td></td>
<td>Double</td>
<td>Double</td>
<td>Double</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="截断">4.2 截断</h2>
<p>当表示范围更宽的类型A的值a，传递给范围更窄的类型B中存储时，将发生隐式转换。
行是类型B，列为类型A。</p>
<table>
<thead>
<tr class="header">
<th>A</th>
<th>Byte</th>
<th>Short</th>
<th>Integer</th>
<th>Long</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Byte</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="even">
<td>Short</td>
<td>是</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="odd">
<td>Integer</td>
<td>是</td>
<td>是</td>
<td></td>
<td></td>
</tr>
<tr class="even">
<td>Long</td>
<td>是</td>
<td>是</td>
<td>是</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="变宽-widening">4.3 变宽 (widening)</h2>
<table>
<thead>
<tr class="header">
<th>B</th>
<th>Byte</th>
<th>Short</th>
<th>Rune</th>
<th>Integer</th>
<th>Long</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Byte</td>
<td></td>
<td>是</td>
<td></td>
<td>是</td>
<td>是</td>
</tr>
<tr class="even">
<td>Short</td>
<td></td>
<td></td>
<td></td>
<td>是</td>
<td>是</td>
</tr>
<tr class="odd">
<td>Rune</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="even">
<td>Integer</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>是</td>
</tr>
<tr class="odd">
<td>Long</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="浮点类型转换">4.4 浮点类型转换</h2>
<pre><code>        int[][] floatingPoint = {/*Boolean Byte Short Rune Integer Long Single Double Object*/
            /*Boolean*/new int[]{   NA,     NA,   NA,  NA,  NA,    NA,  N,     N,     NA },
            /*Byte*/   new int[]{   NA,     NA,   NA,  NA,  NA,    NA,  Y,     Y,     NA },
            /*Short*/  new int[]{   NA,     NA,   NA,  NA,  NA,    NA,  Y,     Y,     NA },
            /*Rune*/   new int[]{   NA,     NA,   NA,  NA,  NA,    NA,  N,     N,     NA },
            /*Integer*/new int[]{   NA,     NA,   NA,  NA,  NA,    NA,  Y,     Y,     NA },
            /*Long*/   new int[]{   NA,     NA,   NA,  NA,  NA,    NA,  Y,     Y,     NA },
            /*Single*/ new int[]{   N,      Y,    Y,   N,   Y,     Y,   NA,    Y,     NA },
            /*Double*/ new int[]{   N,      Y,    Y,   N,   Y,     Y,   Y,     NA,    NA },
            /*Object*/ new int[]{   NA,     NA,   NA,  NA,  NA,    NA,  NA,    NA,    NA }
        };</code></pre>
<h2 id="原始类型装箱">4.5 原始类型装箱</h2>
<p>关于装箱是什么，参考 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/types/boxing-and-unboxing</p>
<p>以下情况将发生原始类型装箱：
1. 将原始类型传递给Object类型参数
2. 将原始类型赋值给Object类型变量
3. 将原始类型从返回值为Object的函数返回
4. 将原始类型显式地转换成Object</p>
<p>通过显式的As表达式转型，可以进行拆箱。</p>
<pre><code>Function Foo(Byval A As Object) As Object
    Return 5 //装箱
End Function

Sub Main()
    Dim a As Object
    a = Foo(3.14) //装箱
    If a Is Integer Then
        Dim num As Integer
        num = a As Integer //拆箱
        Println(a) //装箱，因为Println函数的参数是Object类型
    End If
End Sub
</code></pre>
<h1 id="内置库">5 内置库</h1>
<h2 id="字符串">5.1 字符串</h2>
<p>字符串类型String是类的一种，定义在builtin包中。内部通过UTF32编码，使用Rune存储，一个Rune对应一个unicode codepoint。</p>
<pre><code>    Dim str As String
    Dim a As Rune, b As Rune, c As Rune
    str = “你好世界”
    a = &#39;y&#39;
    b = &#39;1&#39;
    c = &#39;蛤&#39;</code></pre>
<p>在代码中，字符串字面量会在编译期间转换成String。因此这样也是合法的：</p>
<pre><code>    Dim length As Integer
    length = &quot;你好世界&quot;.length()  //length为4</code></pre>
<p>String被设计为不可变的。所有修改操作都会返回一个新的String。</p>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>方法</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>New String(Byval Sequence As Rune[])</code></td>
<td>从Unicode码点数组构造String</td>
</tr>
<tr class="even">
<td><code>s.Length()</code></td>
<td>返回String中Rune的数量</td>
</tr>
<tr class="odd">
<td><code>s.IndexGet(Byval i As Integer)</code></td>
<td>获取第i个Rune</td>
</tr>
<tr class="even">
<td><code>s.ToString()</code></td>
<td>返回它自身</td>
</tr>
<tr class="odd">
<td><code>s.Concat(Byval str As String)</code></td>
<td>返回它与str拼接后的字符串</td>
</tr>
<tr class="even">
<td><code>s.Append(Byval r As Rune)</code></td>
<td>返回它与r拼接后的字符串</td>
</tr>
<tr class="odd">
<td><code>String.Fold(Byval ls As String[], Optional Byval Separator As Rune)</code></td>
<td>将字符串数组拼接，以可选的Rune插入到每个元素之间，返回拼接后的字符串。</td>
</tr>
</tbody>
</table>
<h2 id="输出函数">5.2 输出函数</h2>
<p>evoBasic目前没有输入函数。由于暂不支持外部函数调用，因此目前输出函数是通过虚拟机内置函数实现的。</p>
<pre><code>    Println(&quot;你好&quot;,&quot;世界！&quot;)
    PrintFmt(&quot;第{}次输出:{}\n&quot;, 2, &quot;不是字符串也可以直接打印&quot;)
    Print(1.1, 2.2, 3.3, 4.4, 5.5, 6.6)</code></pre>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>函数</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>Sub Print(ParamArray Byval xs As Object[])</code></td>
<td>打印任意数量的对象，在每个元素之间插入空格</td>
</tr>
<tr class="even">
<td><code>Sub Println(ParamArray Byval xs As Object[])</code></td>
<td>如上，并在尾部追加换行</td>
</tr>
<tr class="odd">
<td><code>Sub PrintFmt(Byval fmt As String, ParamArray Byval args As Object[])</code></td>
<td>格式化输出</td>
</tr>
</tbody>
</table>
<p>以上三个打印函数也是在builtin.bkg包中通过用户代码实现的，<strong>而非在编译器中硬编码的特殊规则！尽可能少地硬编码是一种良好的设计取向，使得用户能够更多地调整软件的行为。不要说语言的设计方式与最终用户无关，只要最终用户依赖的的包中有任何一个因此获得更好的抽象和性能，那么最终用户也能间接因此受益。</strong> 当然，目前evobasic中存在很多违反这种原则的设计。这并非我在对待这件事上双重标准，而是在实践这个原则的道路上，我的能力和时间不足导致的。关于实现细节，请看Object.ToString()、虚函数覆写、原始类型装箱和虚拟机内置函数。</p>
<h2 id="text模块">5.3 Text模块</h2>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>函数</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>Function Format(Byval fmt As String,ParamArray Byval args As Object[]) As String</code></td>
<td>类似PrintFmt，不同的是将结果字符串返回而非直接打印</td>
</tr>
</tbody>
</table>
<h2 id="list类">5.4 List类</h2>
<p>与数组不同的是，List的空间是可变的。当List空间不够时，自动扩大List的容量。</p>
<table>
<thead>
<tr class="header">
<th>成员</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>New(ParamArray Byval xs As Object[])</code></td>
<td>从Object[]数组构造列表</td>
</tr>
<tr class="even">
<td><code>Function IndexGet(Byval i As Integer) As Object</code></td>
<td>获得第i个元素</td>
</tr>
<tr class="odd">
<td><code>Sub IndexSet(Byval i As Integer,Byval value As Object)</code></td>
<td>将对象放置在第i个空间</td>
</tr>
<tr class="even">
<td><code>Function Length() As Integer</code></td>
<td>获得元素数量</td>
</tr>
<tr class="odd">
<td><code>Function Capacity() As Integer</code></td>
<td>获得列表当前的容量</td>
</tr>
<tr class="even">
<td><code>Sub Add(Byval Value As Object)</code></td>
<td>加入一个元素到末端</td>
</tr>
<tr class="odd">
<td><code>Sub Expand()</code></td>
<td>扩大列表容量，每次扩大两倍</td>
</tr>
<tr class="even">
<td><code>Override Function ToString() As String</code></td>
<td>序列化列表成字符串</td>
</tr>
</tbody>
</table>
<h2 id="stack类">5.5 Stack类</h2>
<p>栈，后进先出的数据结构。</p>
<table>
<thead>
<tr class="header">
<th>成员</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>New()</code></td>
<td>实例化</td>
</tr>
<tr class="even">
<td><code>Function Pop() As Object</code></td>
<td>弹出一个元素</td>
</tr>
<tr class="odd">
<td><code>Sub Push(Byval Value As Object)</code></td>
<td>推入一个元素</td>
</tr>
<tr class="even">
<td><code>Function Empty() As Boolean</code></td>
<td>判断是否为空</td>
</tr>
</tbody>
</table>
<h2 id="linkedlist-linkednode">5.6 LinkedList &amp; LinkedNode</h2>
<p>链表和链表节点。</p>
<table>
<thead>
<tr class="header">
<th>LinkedNode成员</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>New()</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>Dim Previous As LinkedNode</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>Dim Succeed As LinkedNode</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>Dim Value As Object</code></td>
<td></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th>LinkedList成员</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>New()</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>Function Length() As Integer</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>Sub Add(Byval Value As Object)</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>Sub PushBack(Byval Value As Object)</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>Sub PushFront(Byval Value As Object)</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>Sub RemoveBack()</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>Sub RemoveFront()</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>Function Back() As Object</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>Function Front() As Object</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>Function FrontNode() As LinkedNode</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>Function BackNode() As LinkedNode</code></td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="异常类">5.7 异常类</h2>
<p>Exception类是所有异常类的基类，成员如下</p>
<table>
<thead>
<tr class="header">
<th>成员</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>New(Byval Name As String, Byval Msg As String)</code></td>
<td>构造函数</td>
</tr>
<tr class="even">
<td><code>Function GetMessage() As String</code></td>
<td>获得构造时输入的Msg</td>
</tr>
<tr class="odd">
<td><code>Override Function ToString() As String</code></td>
<td>序列化成字符串</td>
</tr>
<tr class="even">
<td><code>Function PrintTrace() As String</code></td>
<td>打印堆栈信息</td>
</tr>
</tbody>
</table>
<p>其他异常类</p>
<table>
<thead>
<tr class="header">
<th>异常</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>NotImplementedException</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>OutOfRangeException</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>ConversionException</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>NullPointerException</code></td>
<td></td>
</tr>
<tr class="odd">
<td><code>OptionMissingException</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>EvmInternalException</code></td>
<td></td>
</tr>
</tbody>
</table>]]></summary>
</entry>

</feed>
