<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>khronosyn</title>
    <link>https://syaning.github.io</link>
    <description>Alex Sun's homepage, blog and notes.</description>
    <language>en</language>
    <copyright>Copyright © 2013–2026 Alex Sun</copyright>
    <atom:link href="https://syaning.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>猪鼻环</title>
      <link>https://syaning.github.io/writing/2026/2026-03-15-pig-nose-ring.html</link>
      <guid isPermaLink="true">https://syaning.github.io/writing/2026/2026-03-15-pig-nose-ring.html</guid>
      <pubDate>Sun, 15 Mar 2026 09:00:00 GMT</pubDate>
      <description>TIP 本文部分内容由 LLM 协作完成。 本文内容为单纯的陈述，无任何影射，请勿对号入座。 一、猪鼻环 ​ 小时候在村子里，养猪的往往要给猪扎上鼻环。猪这种动物，有着长长的吻，喜欢拱来拱去。又因为是圈养，往往是一层砖砌的猪圈，地面还是土地。有的猪吃饱喝足了也睡够了没事干，就喜欢拱地，拱着拱着，就把猪圈拱塌了，然后它就脱离了束缚放飞自我。这往往让养猪的人很难受，不仅损失了猪圈，浪费了精力，甚至有可能失去一头猪。得益于老祖宗们数千年来的养猪经验，给猪扎上个鼻环，那么它再次拱来拱去的时候就会疼痛不已，逐渐也就变得驯服。我小时候就亲眼见到过一群大汉把一头猪给按</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _writing_2026_2026-03-15-pig-nose-ring" data-v-92824e67><div><div class="tip custom-block"><p class="custom-block-title">TIP</p><ul><li>本文部分内容由 LLM 协作完成。</li><li>本文内容为单纯的陈述，无任何影射，请勿对号入座。</li></ul></div><h2 id="一、猪鼻环" tabindex="-1">一、猪鼻环 <a class="header-anchor" href="#一、猪鼻环" aria-label="Permalink to &quot;一、猪鼻环&quot;">​</a></h2><p>小时候在村子里，养猪的往往要给猪扎上鼻环。猪这种动物，有着长长的吻，喜欢拱来拱去。又因为是圈养，往往是一层砖砌的猪圈，地面还是土地。有的猪吃饱喝足了也睡够了没事干，就喜欢拱地，拱着拱着，就把猪圈拱塌了，然后它就脱离了束缚放飞自我。这往往让养猪的人很难受，不仅损失了猪圈，浪费了精力，甚至有可能失去一头猪。得益于老祖宗们数千年来的养猪经验，给猪扎上个鼻环，那么它再次拱来拱去的时候就会疼痛不已，逐渐也就变得驯服。我小时候就亲眼见到过一群大汉把一头猪给按在地上，然后拿着铁丝扎在猪鼻子上再打个结，那猪的哀嚎不绝于耳，给我单纯幼小的心灵带来了极大的震撼。</p><p>当然，用现在比较流行的话来说，就是：</p><p>人们通过深度洞察猪的核心行为路径，识别出其在“拱土场景”中的高频痛点（如破坏圈舍、饲料浪费）。基于此，打造了一套以“鼻环”为载体的轻量化干预方案，精准打在神经末梢的敏感触点上，实现了对非理性行为的即时阻断。</p><p>该方案具备高 ROI、低实施成本、强可复制性等优势，形成了一套“感知-反馈-矫正”的闭环机制，有效提升了圈舍运营的颗粒度与确定性。</p><p>同时，我们坚持“以猪为本”的底层逻辑，在最小化应激的前提下，完成了对其行为模式的柔性治理，真正做到了“拿捏痛点、打出组合拳”，为规模化养殖提供了可沉淀、可复用的方法论支撑。</p><p>事实上，不只是养猪需要些手段。比如养牛的也会给牛扎鼻环让它乖乖听话，放羊的需要带上牧羊犬来管理羊群，拉磨的驴要蒙上双眼，养狗的也要恩威并施让它明白谁才是主人，骑马的要给马套上缰绳抽打鞭子。就连美猴王都得戴上个金箍。总的来说，一切的手段都是为了让它们驯服，只要老实听话，就不会有那么多痛苦。所以不得不感慨人类的智慧，针对这些不同的特性设计了若干种行之有效的方案。当然道理都是相通的，不限于圈养禽畜和放牧。比如古时候就有一个官职名称叫做“州牧”，汉昭烈帝刘备就曾担任豫州牧。这里的“牧”和“牧马”的“牧”并无太大不同，只不过管理的对象是人口和土地罢了。</p><p>话说回来，就我所知，并非所有的猪都需要被扎上鼻环。我们通过简单的逻辑分析就可以得到如下结论：</p><ol><li>既然有的猪是人工圈养的，那么就必然有些猪是野生的，那么这些野生的猪就不用遭受扎鼻环的痛苦。</li><li>猪因为不听话才会被扎鼻环。如果有的猪吃饱喝足了就是睡大觉，服服帖帖的，那么也不必遭受扎鼻环的痛苦。</li></ol><p>关于猪鼻环这个事情，我们还有一点需要明确，即猪本身是不需要鼻环的。无论一头猪是好吃懒做还是喜欢拱来拱去，这本来就是它的天性，因为它生来没有鼻环，所以它是不需要鼻环的。给猪扎鼻环是为了让猪温驯，简而言之就是消除它的不安分因素，以符合人的利益。</p><p>我们先从人的角度来分析。针对不安分的猪，简单直接地给扎上鼻环，虽然简单有效，但一定程度上也是人的一种懒惰。本质上，我们需要解决的是环境与需求错配的问题。比如：</p><ol><li>可以因材施教：对于那些安分的猪，就放任自流，让它们负责长肉；对于不安分的猪，生态放养，让它们发挥拱地天赋，帮助松土除虫。把合适的猪放到合适的位置上。</li><li>可以循循善诱：提供可拱可咬可探索的猪玩具，或者把饲料藏在稻草堆里进行觅食挑战。用正向引导替代负向惩罚。</li><li>对于不同品种不同品性的猪，设计不同的养殖模式，而非统一无差别的圈养。即尊重生物多样性，避免一刀切管理。</li></ol><p>从一头猪的角度来分析：</p><ol><li>要么选择不被人圈养，即放弃优渥的饲料和生活，到山间餐风露宿，但同时也不需要出卖自己的生命和自由。</li><li>对一头不安分的猪来说，要么继续我行我素，但是会被扎上鼻环，承受极大的肉体痛苦；要么压抑自己的天性，哪怕再想拱来拱去，也要憋着，从而承受极大的精神痛苦。也就是说，对于一头不安分的猪来说，无论如何都要承担痛苦才行。</li><li>但是对于那些本身就安分守己的猪来说，就无此痛苦之虞，但这样的猪也就真的只是一头待人宰杀的猪罢了。</li></ol><h2 id="二、猫与狗" tabindex="-1">二、猫与狗 <a class="header-anchor" href="#二、猫与狗" aria-label="Permalink to &quot;二、猫与狗&quot;">​</a></h2><p>对于有些人来说，可能吃了这么多年猪肉，却并未亲眼见过活生生的猪。但是对于猫猫狗狗，相对而言应该会更为熟悉。</p><p>不管是一头圈养的猪，还是一只宠物猫狗，从本质上来说，都是出卖自己来换取相对安稳的生活，与人类的关系都是互予互取。只不过猪出卖的生命以满足人的口腹之欲，而猫狗更多的是出卖自由来给人提供精神上的安慰。</p><p>相对而言，作为宠物的猫狗的地位要比作为食物的猪（当然这里要排除掉宠物小香猪）要高一些的。毕竟从马斯洛需求层次理论来看，猫狗满足的是人类更高层次的需求。但它们同样面临着一定的风险，比如有的人会给猫狗绝育——从生物进化的角度来说，无论如何这都是绝对反天性的。</p><p>但并非所有养猫养狗的人都会给猫狗绝育，比如村子里就几乎不会——一是因为不会额外花这个钱，二是因为相对放养也不太在意。还有一些人会考虑到猫狗本身的需求不会这样子做。但总而言之，还是有挺大一部分人会给猫狗绝育，无论他们是否认识到这事实上是给宠物施加的一种痛苦，只不过因为这些宠物不会说话无力反抗罢了。我们不能用非蠢即坏来对此行为进行评判，毕竟会涉及到多种因素，以及为了避免争议，这里并不打算做过多分析，自行判断即可。</p><h2 id="三、皇帝、将军、士兵" tabindex="-1">三、皇帝、将军、士兵 <a class="header-anchor" href="#三、皇帝、将军、士兵" aria-label="Permalink to &quot;三、皇帝、将军、士兵&quot;">​</a></h2><p>我以前有个小领导，特地开过一个会和我们探讨一个问题，即：皇帝、将军和士兵，哪个角色的压力更大。正常来说，人都倾向于用屁股说话，所以我们多数人会说士兵的压力最大，毕竟要真刀真枪上战场，面临直接的存活压力。当然那位领导并不苟同我们的观点，他的结论是皇帝的压力最大，并尝试说服我们相信。</p><p>那个时候我就觉得，绝大部分的人作为人这种动物，本质上也不过是沐猴而冠罢了。之所以用绝大部分，是因为假如你见到过异类，那么便不能以偏概全；如果未见到过，那么更不能否认，否则就是一叶障目了。</p><p>后来再想到这个问题，还是觉得很有趣。我把这个问题交给大模型，它告诉我众生皆苦。但有人是理想主义者，有人是现实主义者，有人是战略共情者，有人是愤世嫉俗者。我觉得，无论是哪一种人，只要是知行合一的，那便不能过多苛责。但假如挂羊头卖狗肉，那就有异化的嫌疑了，为人所不取。</p><p>关于“沐猴而冠”这个成语，来源是《史记·项羽本纪》：</p><p>居数日，项羽引兵西屠咸阳，杀秦降王子婴，烧秦宫室，火三月不灭；收其货宝妇女而东。人或说项王曰：“关中阻山河四塞，地肥饶，可都以霸。”项王见秦宫皆以烧残破，又心怀思欲东归，曰：“富贵不归故乡，如衣绣夜行，谁知之者！”说者曰：“人言楚人沐猴而冠耳，果然。”项王闻之，烹说者。</p><p>关于项羽，韩信对他还有一个颇为有趣的评价：</p><p>项王见人恭敬慈爱，言语呕呕，人有疾病，涕泣分食饮，至使人有功当封爵者，印刓敝，忍不能予，此所谓妇人之仁也。</p><p>从博弈论的角度来稍作分析，我们就知道项羽完全可以选择一种双赢的结果。可惜的是，项羽这个人不爱学习，这个在史记中亦有记载：</p><p>项籍少时，学书不成，去学剑，又不成。项梁怒之。籍曰：“书足以记名姓而已。剑一人敌，不足学，学万人敌。”於是项梁乃教籍兵法，籍大喜，略知其意，又不肯竟学。</p><p>总而言之，项羽的格局和之后的失败，还是吃了没文化的亏。</p><h2 id="四、爱身" tabindex="-1">四、爱身 <a class="header-anchor" href="#四、爱身" aria-label="Permalink to &quot;四、爱身&quot;">​</a></h2><p>扯着扯着，就扯远了。其实，无论是猪、狗还是猫，无论是皇帝、将军还是士兵，本质上无非是你想过怎样的生活，成为怎样的人。倘若总是在别人所谓的指引和教导下，去如何如何做，成为怎样怎样的人，那就是自动带入了圈养的猪的角色。总有人会告诉你，要听话要服从安排，那么你就可以成为“我们”的一份子，但仔细想这个“我们”里真的有“我”吗？更可悲的是如此教导你的人，也不过是另一只被圈养的猪罢了。</p><p>所以人可以出卖自己，但不能无底线地出卖自己。劳动可以出卖，精神不能出卖。就像古代被迫入青楼的女子，物理上承受奇奇怪怪的客人凌辱xp也就算了，精神上还要被嘲讽和作践，那就真的无可接受了。</p><p>老子说：贵以身为天下，若可寄天下；爱以身为天下，若可托天下。也就是说，人首先得自爱，这是一切的基础。当然也要有自知之明，否则就是烂泥糊了眼。</p></div></div>]]></content:encoded>
    </item>
    <item>
      <title>Homelab折腾总结</title>
      <link>https://syaning.github.io/tech/posts/2026/2026-01-12-homelab.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2026/2026-01-12-homelab.html</guid>
      <pubDate>Mon, 12 Jan 2026 14:00:00 GMT</pubDate>
      <description>设备 &amp;ZeroWidthSpace; 网络 路由器: 小米路由器 BE6500 Pro 交换机: 小米万兆交换机 存储: NAS: Synology DS220+ (2*16T, RAID1) 计算: MiniServer: M4 MacMini (24G+256G) DevServer: 零刻 SER8 8745HS 迷你主机 (32G+1T) 基本原则 &amp;ZeroWidthSpace; 核心理念: 配置简单 运行稳定 对网络暂无特别高的要求 设备角色： NAS 尽量只做存储使用，不部署其它不必要的服务 MiniServer 作为生产环境，部署需要长</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2026_2026-01-12-homelab" data-v-92824e67><div><h2 id="设备" tabindex="-1">设备 <a class="header-anchor" href="#设备" aria-label="Permalink to &quot;设备&quot;">​</a></h2><ul><li>网络 <ul><li>路由器: 小米路由器 BE6500 Pro</li><li>交换机: 小米万兆交换机</li></ul></li><li>存储: <ul><li>NAS: Synology DS220+ (2*16T, RAID1)</li></ul></li><li>计算: <ul><li>MiniServer: M4 MacMini (24G+256G)</li><li>DevServer: 零刻 SER8 8745HS 迷你主机 (32G+1T)</li></ul></li></ul><h2 id="基本原则" tabindex="-1">基本原则 <a class="header-anchor" href="#基本原则" aria-label="Permalink to &quot;基本原则&quot;">​</a></h2><p>核心理念:</p><ul><li>配置简单</li><li>运行稳定</li><li>对网络暂无特别高的要求</li></ul><p>设备角色：</p><ul><li>NAS 尽量只做存储使用，不部署其它不必要的服务</li><li>MiniServer 作为生产环境，部署需要长期稳定运行的服务</li><li>DevServer 作为开发环境，用于平时开发测试，以及部署一些临时服务</li><li>MiniServer 同时承担网关角色，统一流量入口</li></ul><h2 id="网络" tabindex="-1">网络 <a class="header-anchor" href="#网络" aria-label="Permalink to &quot;网络&quot;">​</a></h2><h3 id="网络拓扑" tabindex="-1">网络拓扑 <a class="header-anchor" href="#网络拓扑" aria-label="Permalink to &quot;网络拓扑&quot;">​</a></h3><p><img src="https://syaning.github.io/assets/homelab-network.BrbQ1aWv.svg" alt=""></p><h3 id="内网-dns" tabindex="-1">内网 DNS <a class="header-anchor" href="#内网-dns" aria-label="Permalink to &quot;内网 DNS&quot;">​</a></h3><p>出于部署方便，直接在 MiniServer 上 docker 部署了 <a href="https://adguard.com/en/adguard-home/overview.html" target="_blank" rel="noreferrer">AdGuard Home</a></p><div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> adguardhome</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> unless-stopped</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> ./work:/opt/adguardhome/work</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> ./conf:/opt/adguardhome/conf</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 53:53/tcp</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 53:53/udp</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 3000:3000/tcp</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">  adguard/adguardhome:v0.107.71</span></span></code></pre></div><p>然后：</p><ol><li>将路由器的 DHCP 配置中，DNS 配置修改为 MiniServer 的 IP（修改第一个 DNS 配置，第二个保留为运营商的配置作为兜底）</li><li>AdGuard Home 中设置 DNS Rewrite，将自己的内网域名 A 记录（<code>@</code> 和 <code>*</code>）配置为 MiniServer 的 IP</li></ol><p>例如：</p><ul><li>MiniServer IP 为 <code>192.168.31.2</code>，自定义内网域名为 <code>myhomelab.com</code>（仅作为示例）</li><li>路由器 DHCP 的 DNS 配置为 <code>192.168.31.2</code></li><li>AdGuard Home 中设置 <code>myhomelab.com</code> 和 <code>*.myhomelab.com</code> 为 <code>192.168.31.2</code></li></ul><p>这样后续就可以通过自定义的域名来访问内网服务了。</p><h3 id="异地组网" tabindex="-1">异地组网 <a class="header-anchor" href="#异地组网" aria-label="Permalink to &quot;异地组网&quot;">​</a></h3><p>使用 <a href="https://tailscale.com/" target="_blank" rel="noreferrer">Tailscale</a>。</p><ol><li>在 MiniServer 以及需要远程访问的设备上安装 Tailscale</li><li>之前自定义内网域名为 <code>myhomelab.com</code>，同时也购买了公网域名</li><li>在域名提供商解析域名 <code>myhomelab.com</code> 的 A 记录（<code>@</code> 和 <code>*</code>），IP 使用 tailscale 提供的 <code>100.x.y.z</code> 地址</li></ol><p>这样内外网都可以统一通过域名来进行访问。</p><h2 id="服务" tabindex="-1">服务 <a class="header-anchor" href="#服务" aria-label="Permalink to &quot;服务&quot;">​</a></h2><table tabindex="0"><thead><tr><th>服务</th><th>部署形态</th><th>部署位置</th></tr></thead><tbody><tr><td>Jellyfin</td><td>直接部署</td><td>MiniServer</td></tr><tr><td>AdGuard Home</td><td>Docker</td><td>MiniServer</td></tr><tr><td>Traefik Proxy</td><td>k8s</td><td>MiniServer</td></tr><tr><td>Gitea</td><td>k8s</td><td>MiniServer</td></tr><tr><td>Grafana</td><td>k8s</td><td>MiniServer</td></tr><tr><td>Prometheus</td><td>k8s</td><td>MiniServer</td></tr><tr><td>其它开发相关</td><td>直接部署/docker</td><td>DevServer</td></tr></tbody></table><h2 id="效果" tabindex="-1">效果 <a class="header-anchor" href="#效果" aria-label="Permalink to &quot;效果&quot;">​</a></h2><p>使用 <a href="https://gethomepage.dev/" target="_blank" rel="noreferrer">homepage</a> 部署了个首页导航，效果如下：</p><p><img src="https://syaning.github.io/assets/homelab-homepage.Vs4ZvDDU.webp" alt=""></p></div></div>]]></content:encoded>
    </item>
    <item>
      <title>图解大模型读书笔记——记忆、Agent、RAG、多模态</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-12-13-memory-rag-multimodal.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-12-13-memory-rag-multimodal.html</guid>
      <pubDate>Sat, 13 Dec 2025 07:00:00 GMT</pubDate>
      <description>记忆 ​ 直接使用未经定制的LLM时，系统默认不具备对话记忆能力。即使在前序提示词中告知模型用户的姓名等信息，模型也无法在后续对话中记住这些信息。 两种广泛应用于对话记忆保持的方法： 对话缓冲区 对话摘要 对话缓冲区 ​ 随着对话轮次增加，输入提示词的文本长度会持续增长，最终可能超出模型的词元限制。 控制上下文窗口的有效策略是仅保留最近k轮对话记录。 对话摘要 ​ 对完整对话记录进行摘要提炼，保留核心信息。 提炼摘要的过程由另一个LLM完成：该模型接收完整对话历史作为输入，并负责生成简明的摘要。使用外部LLM的显著优势在于，对话系统无须局限于单一模型。 </description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-12-13-memory-rag-multimodal" data-v-92824e67><div><h2 id="记忆" tabindex="-1">记忆 <a class="header-anchor" href="#记忆" aria-label="Permalink to &quot;记忆&quot;">​</a></h2><p>直接使用未经定制的LLM时，系统默认不具备对话记忆能力。即使在前序提示词中告知模型用户的姓名等信息，模型也无法在后续对话中记住这些信息。</p><p><img src="https://syaning.github.io/assets/memory.C5l9k51k.webp" alt=""></p><p>两种广泛应用于对话记忆保持的方法：</p><ul><li>对话缓冲区</li><li>对话摘要</li></ul><h3 id="对话缓冲区" tabindex="-1">对话缓冲区 <a class="header-anchor" href="#对话缓冲区" aria-label="Permalink to &quot;对话缓冲区&quot;">​</a></h3><p><img src="https://syaning.github.io/assets/memory-buffer.B-AYnIVU.webp" alt=""></p><ul><li>随着对话轮次增加，输入提示词的文本长度会持续增长，最终可能超出模型的词元限制。</li><li>控制上下文窗口的有效策略是仅保留最近k轮对话记录。</li></ul><h3 id="对话摘要" tabindex="-1">对话摘要 <a class="header-anchor" href="#对话摘要" aria-label="Permalink to &quot;对话摘要&quot;">​</a></h3><ul><li>对完整对话记录进行摘要提炼，保留核心信息。</li><li>提炼摘要的过程由另一个LLM完成：该模型接收完整对话历史作为输入，并负责生成简明的摘要。使用外部LLM的显著优势在于，对话系统无须局限于单一模型。</li></ul><p><img src="https://syaning.github.io/assets/memory-abstract.DRYgDVov.webp" alt=""></p><h3 id="对比" tabindex="-1">对比 <a class="header-anchor" href="#对比" aria-label="Permalink to &quot;对比&quot;">​</a></h3><p><img src="https://syaning.github.io/assets/memory-compare.5W0mVPXa.webp" alt=""></p><h2 id="agent" tabindex="-1">Agent <a class="header-anchor" href="#agent" aria-label="Permalink to &quot;Agent&quot;">​</a></h2><p>能够自主规划行动及其序列的系统被称为智能体(agent)，其核心在于利用语言模型自主制定行动决策。Agent 通过两大核心组件实现功能扩展：</p><ul><li>工具(tool)：赋予智能体完成自身无法独立处理的任务的能力。</li><li>智能体类型(agent type)：规划行动及工具使用策略的决策框架。</li></ul><p>ReAct的精妙之处在于建立了推理与行动的动态反馈机制：推理指导行动决策，行动结果“反哺”推理进程。在具体实现中，系统通过以下三阶段的循环迭代完成认知闭环：</p><ul><li>思考(thought)</li><li>行动(action)</li><li>观察(observation)</li></ul><p><img src="https://syaning.github.io/assets/react.DqoUsuKj.webp" alt=""></p><h2 id="语义搜索与rag" tabindex="-1">语义搜索与RAG <a class="header-anchor" href="#语义搜索与rag" aria-label="Permalink to &quot;语义搜索与RAG&quot;">​</a></h2><p>语义搜索(semantic search)核心在于通过语义理解而非简单的关键词匹配来实现精准检索。</p><p>尽管模型能够流畅自信地输出答案，但其内容在准确性和时效性方面仍存在不足（幻觉）。解决该问题的主要方法之一，便是构建能够实时检索相关信息并输入LLM的系统，从而生成有事实依据的答案。这种被称为RAG（检索增强生成）的技术，现已成为LLM最受瞩目的应用场景。</p><p>优化语言模型在搜索领域的应用，当前主流技术可分为三大类：</p><ul><li>稠密检索(dense retrieval)</li><li>重排序(reranking)</li><li>RAG</li></ul><h3 id="稠密检索" tabindex="-1">稠密检索 <a class="header-anchor" href="#稠密检索" aria-label="Permalink to &quot;稠密检索&quot;">​</a></h3><p>基于文本嵌入，将搜索问题转化为查询向量与文档向量的最近邻匹配过程。如图所示：接收搜索请求后，系统在文档库中进行向量比对，最终输出相关性最高的结果集合。</p><p><img src="https://syaning.github.io/assets/semantic-search-dense-retrieval.C6-dnyh8.webp" alt=""></p><h3 id="重排序" tabindex="-1">重排序 <a class="header-anchor" href="#重排序" aria-label="Permalink to &quot;重排序&quot;">​</a></h3><p>搜索系统多采用多阶段处理流程。重排序模型作为其中关键环节，负责对初步检索结果进行相关性评分，并据此优化排序。</p><p><img src="https://syaning.github.io/assets/semantic-search-reranking.DDiKbivE.webp" alt=""></p><h3 id="rag" tabindex="-1">RAG <a class="header-anchor" href="#rag" aria-label="Permalink to &quot;RAG&quot;">​</a></h3><p>生成式搜索属于广义的RAG系统范畴。这类系统通过整合检索机制来增强文本生成功能，可有效抑制幻觉现象、提升事实准确性，并使模型输出与特定数据集保持逻辑一致性。</p><p><img src="https://syaning.github.io/assets/semantic-search-rag.CDM2psti.webp" alt=""></p><h2 id="多模态llm" tabindex="-1">多模态LLM <a class="header-anchor" href="#多模态llm" aria-label="Permalink to &quot;多模态LLM&quot;">​</a></h2><h3 id="视觉transformer" tabindex="-1">视觉Transformer <a class="header-anchor" href="#视觉transformer" aria-label="Permalink to &quot;视觉Transformer&quot;">​</a></h3><p>视觉Transformer(Vision Transformer，ViT)的实现依赖Transformer架构的一个重要组件——编码器。不同于将文本分割为词元，它将原始图像切割为规则排列的图像块，进而实现特征提取。</p><p>但需注意，无法像文本词元那样直接赋予图像块固定的ID——由于图像的多样性，图像块在不同场景中的重复概率远低于文本词元。 为解决这一问题，系统会对图像块实施线性嵌入操作，将其转换为数值化的嵌入向量。当嵌入向量进入编码器后，视觉与文本模态的处理路径完全相同。</p><p><img src="https://syaning.github.io/assets/vit.BKAqqHO_.webp" alt=""></p><h3 id="多模态嵌入模型" tabindex="-1">多模态嵌入模型 <a class="header-anchor" href="#多模态嵌入模型" aria-label="Permalink to &quot;多模态嵌入模型&quot;">​</a></h3><p>多模态嵌入模型可在同一向量空间中为不同模态生成嵌入向量。</p><p><img src="https://syaning.github.io/assets/multimodal-1.BYxU0XAf.webp" alt=""></p><p>得益于共享向量空间的特性，我们可以直接比较不同模态的语义表示。</p><p><img src="https://syaning.github.io/assets/multimodal-2.D7qH8EBG.webp" alt=""></p><p>在众多多模态嵌入模型中，对比语言-图像预训练(Contrastive Language-Image Pre-training，CLIP)模型以其卓越的性能和广泛的适用性成为当前最主流的解决方案。</p><p>CLIP的实现逻辑相当简洁。设想存在一个包含数百万张图像及其对应描述文本的训练数据集，该数据集为构建跨模态对齐提供了基础。基于此数据集，可为每一组图像和描述文本创建两个向量表示。CLIP采用双编码器架构实现这一点：文本编码器处理描述文本，生成语义嵌入；图像编码器提取视觉特征，生成图像嵌入。如图所示，经过联合训练后，配对的图文数据将在向量空间中获得高度对齐的嵌入向量表示。</p><p><img src="https://syaning.github.io/assets/multimodal-3.C9s-PaOT.webp" alt=""></p></div></div>]]></content:encoded>
    </item>
    <item>
      <title>图解大模型读书笔记——提示工程</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-12-07-llm-prompt.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-12-07-llm-prompt.html</guid>
      <pubDate>Sun, 07 Dec 2025 07:00:00 GMT</pubDate>
      <description>提示词 ​ LLM是一个预测引擎。基于某个输入（即提示词），它试图预测可能跟随其后的词。从本质上讲，只需少数几个词，就能激发LLM做出响应。 提示词通常用于让LLM回答特定问题或解决特定任务。这称为基于指令的提示词。 输出参数 ​ 除了提示词，我们还可以通过调整模型参数来控制输出类型。 temperature ​ temperature（温度）决定生成文本的随机性或创造性。它定义了选择本来不太可能出现的词元的概率。其基本原理是，temperature为0时每次都会生成相同的响应，因为它总是选择可能性最大的词。较高的temperature值允许生成可能性更</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-12-07-llm-prompt" data-v-92824e67><div><h2 id="提示词" tabindex="-1">提示词 <a class="header-anchor" href="#提示词" aria-label="Permalink to &quot;提示词&quot;">​</a></h2><p>LLM是一个预测引擎。基于某个输入（即提示词），它试图预测可能跟随其后的词。从本质上讲，只需少数几个词，就能激发LLM做出响应。</p><p>提示词通常用于让LLM回答特定问题或解决特定任务。这称为基于指令的提示词。</p><p><img src="https://syaning.github.io/assets/prompt-basic.DtJPaO8E.webp" alt=""></p><h2 id="输出参数" tabindex="-1">输出参数 <a class="header-anchor" href="#输出参数" aria-label="Permalink to &quot;输出参数&quot;">​</a></h2><p>除了提示词，我们还可以通过调整模型参数来控制输出类型。</p><h3 id="temperature" tabindex="-1">temperature <a class="header-anchor" href="#temperature" aria-label="Permalink to &quot;temperature&quot;">​</a></h3><p>temperature（温度）决定生成文本的随机性或创造性。它定义了选择本来不太可能出现的词元的概率。其基本原理是，temperature为0时每次都会生成相同的响应，因为它总是选择可能性最大的词。较高的temperature值允许生成可能性更小的词。</p><ul><li>较高的temperature（如0.8）通常会产生更多样化的输出。</li><li>较低的temperature（如0.2）会产生更具确定性的输出。</li></ul><p><img src="https://syaning.github.io/assets/prompt-temperature.C2qKrAmS.webp" alt=""></p><h3 id="top-p" tabindex="-1">top_p <a class="header-anchor" href="#top-p" aria-label="Permalink to &quot;top_p&quot;">​</a></h3><p>top-p采样，也称为核采样(nucleus sampling)，是一种控制LLM可以考虑哪些词元子集（核）的采样技术。它会考虑概率最高的若干词元，直到达到其累积概率限制。</p><ul><li>如果我们将top_p设置为0.1，模型会从概率最高的词元开始考虑，直到这些词元的累积概率达到0.1。</li><li>如果我们将top_p设置为1，模型会考虑所有词元。</li></ul><p><img src="https://syaning.github.io/assets/prompt-top-p.CL37za3e.webp" alt=""></p><h2 id="高级提示工程" tabindex="-1">高级提示工程 <a class="header-anchor" href="#高级提示工程" aria-label="Permalink to &quot;高级提示工程&quot;">​</a></h2><h3 id="提示词组件" tabindex="-1">提示词组件 <a class="header-anchor" href="#提示词组件" aria-label="Permalink to &quot;提示词组件&quot;">​</a></h3><p>常见组件：</p><ul><li>角色定位：描述LLM应该扮演什么角色。例如，如果你想问一个关于天体物理学的问题，可以使用“你是一位天体物理学专家”。</li><li>指令：任务本身。指令应该尽可能具体，避免留下太大的解释空间。</li><li>上下文：描述问题或任务背景的附加信息。它回答了“为什么提出这个指令”这样的问题。</li><li>格式：LLM输出生成文本的格式。如果不指定格式，LLM会自行决定格式，这在自动化系统中会造成麻烦。</li><li>受众：生成文本的目标对象。这也描述了输出的水平。在教育目的下，使用ELI5（Explain like I&#39;m 5，“向5岁的孩子解释”）通常很有帮助。</li><li>语气：LLM在生成文本中应该使用的语气。如果你要给老板写一封正式的邮件，你肯定不想使用非正式的语气。</li><li>数据：与任务本身相关的主要数据。</li></ul><p><img src="https://syaning.github.io/assets/prompt-components.Dlr-ppdW.webp" alt=""></p><p>对模块化组件进行迭代是实体工程的重要步骤：</p><p><img src="https://syaning.github.io/assets/prompt-iteration.DjesMObR.webp" alt=""></p><h3 id="上下文学习-提供示例" tabindex="-1">上下文学习：提供示例 <a class="header-anchor" href="#上下文学习-提供示例" aria-label="Permalink to &quot;上下文学习：提供示例&quot;">​</a></h3><p>我们可以为LLM提供我们想要完成的目标任务的具体示例。这通常被称为上下文学习(in-context learning)，其中，我们为模型提供正确的示例。</p><ul><li>零样本提示不使用示例</li><li>单样本提示使用一个示例</li><li>少样本提示使用两个或更多示例</li></ul><p><img src="https://syaning.github.io/assets/prompt-context-learning.BvvOsBrK.webp" alt=""></p><h3 id="链式提示-分解问题" tabindex="-1">链式提示：分解问题 <a class="header-anchor" href="#链式提示-分解问题" aria-label="Permalink to &quot;链式提示：分解问题&quot;">​</a></h3><p>将一个提示词的输出作为下一个提示词的输入，从而创建一个连续的交互链来解决我们的问题。</p><p>链式提示技术让LLM能够在每个独立问题上投入更多时间，而不是一次性解决整个问题。</p><p><img src="https://syaning.github.io/assets/prompt-chain.DdotolHc.webp" alt=""></p><h2 id="推理" tabindex="-1">推理 <a class="header-anchor" href="#推理" aria-label="Permalink to &quot;推理&quot;">​</a></h2><h3 id="思维链-先思考再回答" tabindex="-1">思维链：先思考再回答 <a class="header-anchor" href="#思维链-先思考再回答" aria-label="Permalink to &quot;思维链：先思考再回答&quot;">​</a></h3><p>在提示词中提供了一些示例，展示了模型在生成响应之前应该进行的推理。这些推理过程被称为“思考”。这对于涉及较高复杂度的任务（如数学问题）帮助很大。添加这个推理步骤使模型能够在推理过程中充分利用更多的计算资源。相比于基于几个词元直接计算出完整的答案，在推理过程中每增加一个词元都能让LLM的输出更稳定。</p><p><img src="https://syaning.github.io/assets/prompt-thought-chain.DZAdJ5ym.webp" alt=""></p><p>虽然思维链是一种增强生成模型输出的好方法，但它需要在提示词中包含一个或多个推理示例，而用户可能无法获取这些示例。我们也可以不提供示例，而是直接要求生成模型进行推理（零样本思维链）。这种方法有多种形式，一种常见且有效的方法是使用“让我们逐步思考”(Let&#39;s think step-by-step)。</p><h3 id="自洽性-采样输出" tabindex="-1">自洽性：采样输出 <a class="header-anchor" href="#自洽性-采样输出" aria-label="Permalink to &quot;自洽性：采样输出&quot;">​</a></h3><p>用相同的提示词向生成模型多次提问，并将占多数的结果作为最终答案。在此过程中，可以通过调整不同的temperature和top_p值来影响每个答案，以提高采样的多样性。</p><p><img src="https://syaning.github.io/assets/prompt-sampling.CpPIj6oo.webp" alt=""></p><h3 id="思维树-探索中间步骤" tabindex="-1">思维树：探索中间步骤 <a class="header-anchor" href="#思维树-探索中间步骤" aria-label="Permalink to &quot;思维树：探索中间步骤&quot;">​</a></h3><p>思维树(tree-of-thought，ToT)可以对多个想法进行深入探索。工作原理如下：当面对需要多个推理步骤的问题时，将其分解成多个部分通常会有所帮助。在每个步骤中，生成模型会被提示探索当前问题的不同解决方案。然后，它对最佳解决方案进行投票，并继续进行下一步。</p><p><img src="https://syaning.github.io/assets/prompt-tree-of-thought.Y6aiTrWk.webp" alt=""></p><h2 id="输出验证" tabindex="-1">输出验证 <a class="header-anchor" href="#输出验证" aria-label="Permalink to &quot;输出验证&quot;">​</a></h2><p>三种控制生成模型输出的方法。</p><ul><li>示例：提供多个预期输出的示例。</li><li>语法：控制词元选择过程。</li><li>微调：在包含预期输出的数据上对模型进行微调。</li></ul><h3 id="语法-约束采样" tabindex="-1">语法：约束采样 <a class="header-anchor" href="#语法-约束采样" aria-label="Permalink to &quot;语法：约束采样&quot;">​</a></h3><p>用于约束和验证生成模型输出的软件包：Guidance、Guardrails、LMQL等。在某种程度上，它们利用生成模型来验证自己的输出。生成模型将输出作为新的提示词，并尝试基于一些预定义的规则进行验证。</p><p><img src="https://syaning.github.io/assets/prompt-validate.DutBf58f.webp" alt=""></p></div></div>]]></content:encoded>
    </item>
    <item>
      <title>图解大模型读书笔记——文本分类与文本聚类</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-11-29-llm-classfication-cluster.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-11-29-llm-classfication-cluster.html</guid>
      <pubDate>Sat, 29 Nov 2025 07:00:00 GMT</pubDate>
      <description>文本分类 ​ 表示模型和生成模型都可用于分类，但它们的方法不同。 使用表示模型进行文本分类 ​ 使用特定任务模型直接进行分类，或使用通用嵌入向量间接进行分类。 使用特定任务模型分类：例如 Twitter-roBerta-base for Sentiment Analysis 模型做情感分析。 使用嵌入向量分类： 监督分类： 第一步：使用嵌入模型将文本输入转换为嵌入向量 第二步：将这些嵌入向量将作为分类器的输入特征 零样本分类（zero-shot classfication） 计算文档-标签对的余弦相似度 使用余弦相似度来检查给定文档与候选标签的相似程度，</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-11-29-llm-classfication-cluster" data-v-92824e67><div><h2 id="文本分类" tabindex="-1">文本分类 <a class="header-anchor" href="#文本分类" aria-label="Permalink to &quot;文本分类&quot;">​</a></h2><p>表示模型和生成模型都可用于分类，但它们的方法不同。</p><p><img src="https://syaning.github.io/assets/classfication-1.KxEpKXkg.webp" alt=""></p><h3 id="使用表示模型进行文本分类" tabindex="-1">使用表示模型进行文本分类 <a class="header-anchor" href="#使用表示模型进行文本分类" aria-label="Permalink to &quot;使用表示模型进行文本分类&quot;">​</a></h3><p>使用特定任务模型直接进行分类，或使用通用嵌入向量间接进行分类。</p><p><img src="https://syaning.github.io/assets/classfication-2.BA6yP4ta.webp" alt=""></p><ul><li>使用特定任务模型分类：例如 Twitter-roBerta-base for Sentiment Analysis 模型做情感分析。</li><li>使用嵌入向量分类： <ul><li>监督分类： <ul><li>第一步：使用嵌入模型将文本输入转换为嵌入向量</li><li>第二步：将这些嵌入向量将作为分类器的输入特征</li></ul></li><li>零样本分类（zero-shot classfication） <ul><li>计算文档-标签对的余弦相似度</li><li>使用余弦相似度来检查给定文档与候选标签的相似程度，并选择与文档相似度最高的标签</li></ul></li></ul></li></ul><h3 id="混淆矩阵" tabindex="-1">混淆矩阵 <a class="header-anchor" href="#混淆矩阵" aria-label="Permalink to &quot;混淆矩阵&quot;">​</a></h3><p><img src="https://syaning.github.io/assets/classfication-evaluate.DLK6-BKC.webp" alt=""></p><ul><li>精确率(precision)衡量模型识别出的结果中有多少是相关的，用来评估相关结果的准确性。</li><li>召回率(recall)指的是所有相关类别中有多少被成功识别出来，用来评估模型找到所有相关结果的能力。</li><li>准确率(accuracy)指的是模型在所有预测中做出正确预测的比例，用来评估模型的整体准确性。</li><li>F1分数(F1 score)平衡了精确率和召回率，用于衡量模型的整体性能。</li></ul><p><img src="https://syaning.github.io/assets/classfication-confusion-matrix.B05_DVso.webp" alt=""></p><h3 id="使用生成模型进行文本分类" tabindex="-1">使用生成模型进行文本分类 <a class="header-anchor" href="#使用生成模型进行文本分类" aria-label="Permalink to &quot;使用生成模型进行文本分类&quot;">​</a></h3><p><img src="https://syaning.github.io/assets/classfication-3.N8PL2u_R.webp" alt=""></p><p>我们需要帮助模型理解上下文，并引导它得出我们想要的答案。这种引导过程主要通过给模型输入指令或提示词来完成。迭代改进提示词以获得期望的输出的过程被称为提示工程(prompt engineering)。</p><p><img src="https://syaning.github.io/assets/classfication-4.CtHCR3ue.webp" alt=""></p><h2 id="文本聚类" tabindex="-1">文本聚类 <a class="header-anchor" href="#文本聚类" aria-label="Permalink to &quot;文本聚类&quot;">​</a></h2><h3 id="文本聚类的一般步骤" tabindex="-1">文本聚类的一般步骤 <a class="header-anchor" href="#文本聚类的一般步骤" aria-label="Permalink to &quot;文本聚类的一般步骤&quot;">​</a></h3><ol><li>使用嵌入模型(embedding model)将输入文档转换为嵌入向量。</li></ol><p><img src="https://syaning.github.io/assets/cluster-step1.BMnkUK9e.webp" alt=""></p><ol start="2"><li>使用降维模型(dimensionality reduction model)将嵌入向量降至更低维度空间。</li></ol><p><img src="https://syaning.github.io/assets/cluster-step2.DpQIHQeO.webp" alt=""></p><ol start="3"><li>使用聚类模型(cluster model)对降维后的嵌入向量进行聚类。</li></ol><p><img src="https://syaning.github.io/assets/cluster-step3.DZOLwZiE.webp" alt=""></p><h3 id="降维" tabindex="-1">降维 <a class="header-anchor" href="#降维" aria-label="Permalink to &quot;降维&quot;">​</a></h3><ul><li>随着维度的增加，每个维度中可能的取值数量呈指数级增长，在每个维度中寻找所有子空间变得越来越复杂。</li><li>降维是一种压缩技术，其底层算法并不是随意删除维度这么简单。</li><li>降维算法： <ul><li>主成分分析(Principal Component Analysis，PCA)</li><li>统一流形逼近和投影(Uniform Manifold Approximation and Projection，UMAP)</li></ul></li></ul><h3 id="聚类算法" tabindex="-1">聚类算法 <a class="header-anchor" href="#聚类算法" aria-label="Permalink to &quot;聚类算法&quot;">​</a></h3><ul><li>基于质心，例如 k-means，需要生成一组预设的簇</li><li>基于密度，HDBSCAN（Hierarchical Density-Based Spatial Clustering of Applications with Noise，具有噪声的分层密度空间聚类）。HDBSCAN是聚类算法DBSCAN的层次化变体，它无须显式指定簇的数量就能发现密集的（微型）簇。作为一种基于密度的方法，HDBSCAN 还可以检测数据中的离群点，即不属于任何簇的数据点。</li></ul><p><img src="https://syaning.github.io/assets/cluster-model.BQ8VIRtF.webp" alt=""></p><h3 id="主题建模" tabindex="-1">主题建模 <a class="header-anchor" href="#主题建模" aria-label="Permalink to &quot;主题建模&quot;">​</a></h3><p>在文本数据集合中寻找主题或潜在语义的思路，通常被称为主题建模。传统上，主题建模的目标是找到一组最能代表、捕捉主题含义的关键词或短语。</p><p>主题建模技术并非将主题标记为“手语”，而是使用诸如“手势”“语言”“翻译”等关键词来描述主题。因此，结果并不是单一的标签，用户需要通过这些关键词来理解主题的含义。</p><p>经典方法，如潜在狄利克雷分配(latent Dirichlet allocation，LDA)，假设每个主题都由语料库词表中词的概率分布来表示。这些经典方法通常使用词袋技术提取文本数据的主要特征，而没有考虑词和短语的上下文及含义。相比之下，文本聚类示例则考虑了这两方面，因为它依赖基于Transformer的嵌入向量，这种嵌入向量通过注意力机制针对语义相似性和上下文含义进行了优化。</p><p>BERTopic：一个模块化主题建模框架</p><p>我们可以对词的初始分布进行重新排序，以改进最终的表示。(即 Rerank)</p><p><img src="https://syaning.github.io/assets/cluster-rerank.Bg-jQK1J.webp" alt=""></p></div></div>]]></content:encoded>
    </item>
    <item>
      <title>图解大模型读书笔记——简介</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-11-27-llm-intro.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-11-27-llm-intro.html</guid>
      <pubDate>Thu, 27 Nov 2025 07:00:00 GMT</pubDate>
      <description>语言人工智能发展历史简介 ​ 词袋 ​ 语言人工智能历史始于一种名为词袋(bag-of-words)的技术，其工作原理如下： 分词(tokenization)，即将句子拆分成单个词或子词（词元，token） 将每个句子中所有不同的词组合起来，创建一个可用于表示句子的词表(vocabulary) 使用词表，我们只需计算每个句子中词出现的次数，就创建了一个词袋 word2vec ​ word2vec（词向量）于2013年发布，是首批成功利用嵌入 (embedding)这个概念来捕捉文本含义的技术之一。嵌入是数据的向量表示，试图捕捉数据的含义。为此，word2</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-11-27-llm-intro" data-v-92824e67><div><h2 id="语言人工智能发展历史简介" tabindex="-1">语言人工智能发展历史简介 <a class="header-anchor" href="#语言人工智能发展历史简介" aria-label="Permalink to &quot;语言人工智能发展历史简介&quot;">​</a></h2><p><img src="https://syaning.github.io/assets/history.M3HQ0_M0.webp" alt=""></p><h3 id="词袋" tabindex="-1">词袋 <a class="header-anchor" href="#词袋" aria-label="Permalink to &quot;词袋&quot;">​</a></h3><p>语言人工智能历史始于一种名为词袋(bag-of-words)的技术，其工作原理如下：</p><ol><li>分词(tokenization)，即将句子拆分成单个词或子词（词元，token）</li></ol><p><img src="https://syaning.github.io/assets/bag-of-words-1.DS7SixuP.webp" alt=""></p><ol start="2"><li>将每个句子中所有不同的词组合起来，创建一个可用于表示句子的词表(vocabulary)</li></ol><p><img src="https://syaning.github.io/assets/bag-of-words-2.BlYo_GoC.webp" alt=""></p><ol start="3"><li>使用词表，我们只需计算每个句子中词出现的次数，就创建了一个词袋</li></ol><p><img src="https://syaning.github.io/assets/bag-of-words-3.RSSajVxL.webp" alt=""></p><h3 id="word2vec" tabindex="-1">word2vec <a class="header-anchor" href="#word2vec" aria-label="Permalink to &quot;word2vec&quot;">​</a></h3><p>word2vec（词向量）于2013年发布，是首批成功利用嵌入 (embedding)这个概念来捕捉文本含义的技术之一。嵌入是数据的向量表示，试图捕捉数据的含义。为此，word2vec通过在大量文本数据（如整个维基百科）上训练来学习词的语义表示。</p><p>我们首先为词表中的每个词分配一个向量嵌入，比如说每个词有50个随机初始化的值。然后在每个训练步骤中，我们从训练数据中取出词对(pairs of words)，用模型尝试预测它们是否可能在句子中相邻。在训练过程中，word2vec会学习词与词之间的关系，并将这些信息提炼到词嵌入中。如果两个词各自的相邻词集合有更大的交集，它们的词嵌入向量就会更接近，反之亦然。</p><p><img src="https://syaning.github.io/assets/word2vec.CrRZa0vj.webp" alt=""></p><p>词嵌入可以用多种属性来表示一个词的含义。由于嵌入向量的大小是固定的，这些属性需要经过精心选择，以构建用来代表词的“心智表征”的抽象表示。在实践中，这些属性通常相当抽象，很少与单一实体或人类可识别的概念相关。然而，这些属性组合在一起对计算机来说是有意义的，是将人类语言转换为计算机语言行之有效的方式。</p><h3 id="嵌入的类型" tabindex="-1">嵌入的类型 <a class="header-anchor" href="#嵌入的类型" aria-label="Permalink to &quot;嵌入的类型&quot;">​</a></h3><ul><li>文档嵌入（词袋）</li><li>句子嵌入</li><li>词嵌入（word2vec）</li><li>词元嵌入</li></ul><p><img src="https://syaning.github.io/assets/embedding-types.iJf-mo1S.webp" alt=""></p><h3 id="注意力机制-attention" tabindex="-1">注意力机制（Attention） <a class="header-anchor" href="#注意力机制-attention" aria-label="Permalink to &quot;注意力机制（Attention）&quot;">​</a></h3><p>例如通过 RNN 来进行翻译。这种上下文嵌入方式存在局限性，因为它仅用一个嵌入向量来表示整个输入，使得处理较长的句子变得困难。</p><p><img src="https://syaning.github.io/assets/attention-1.vjmWRSg-.webp" alt=""></p><p>注意力机制通过选择性地聚焦于句子中最关键的词，来突出其重要性。</p><p><img src="https://syaning.github.io/assets/attention-2.Abs_6FXF.webp" alt=""></p><p>通过在解码步骤中添加这些注意力机制，RNN可以为输入序列中的每个词生成与潜在输出相关的信号。这并不仅仅是将上下文嵌入传递给解码器，而是传递所有输入词的隐藏状态。</p><p><img src="https://syaning.github.io/assets/attention-3.C58icBQQ.webp" alt=""></p><h3 id="transformer" tabindex="-1">Transformer <a class="header-anchor" href="#transformer" aria-label="Permalink to &quot;Transformer&quot;">​</a></h3><p>2017年发表的著名论文“Attention is All You Need”首次探讨了注意力机制的真正威力，以及驱动LLM展现出惊人能力的核心所在。作者提出了一种被称为Transformer 的网络架构，它完全基于注意力机制，摒弃了此前提到的RNN。与RNN相比，Transformer支持并行训练，这大大加快了训练速度。</p><p>在Transformer中，编码和解码组件相互堆叠。这种架构仍然是自回归的，每个新生成的词都被模型用于生成下一个词。</p><p><img src="https://syaning.github.io/assets/transformer.CAM3a13F.webp" alt=""></p><h3 id="表示模型-仅编码器模型" tabindex="-1">表示模型：仅编码器模型 <a class="header-anchor" href="#表示模型-仅编码器模型" aria-label="Permalink to &quot;表示模型：仅编码器模型&quot;">​</a></h3><p>原始的Transformer模型是一个编码器-解码器架构，虽然非常适合翻译任务，但难以用于其他任务，比如文本分类。 2018年，研究人员提出了一种名为BERT（bidirectional encoder representations from Transformers，基于Transformer的双向编码器表示）的新架构，它可以应用于各种任务，并在未来几年成为语言人工智能的基石。BERT是一个仅编码器架构，专注于语言表示。</p><p><img src="https://syaning.github.io/assets/bert.Yhcg_jmS.webp" alt=""></p><h3 id="生成模型-仅解码器模型" tabindex="-1">生成模型：仅解码器模型 <a class="header-anchor" href="#生成模型-仅解码器模型" aria-label="Permalink to &quot;生成模型：仅解码器模型&quot;">​</a></h3><p>与BERT的仅编码器架构类似，2018年出现了一种用于处理生成任务的仅解码器架构—— GPT（生成式预训练Transformer，现在被称为GPT-1，以区别于后续版本）。GPT因其生成能力而得名。它与BERT编码器堆叠架构类似，堆叠了多个解码器块。</p><p><img src="https://syaning.github.io/assets/gpt.yPBA3KML.webp" alt=""></p><h2 id="词元和嵌入" tabindex="-1">词元和嵌入 <a class="header-anchor" href="#词元和嵌入" aria-label="Permalink to &quot;词元和嵌入&quot;">​</a></h2><p><img src="https://syaning.github.io/assets/token-and-embedding.PmMDSgx9.webp" alt=""></p><p>分词器如何分解文本：</p><ul><li>流行的方法包括字节对编码（BPE，byte pair encoding，广泛用于GPT模型）和WordPiece（用于BERT模型）。</li><li>分词器需要在特定数据集上进行训练，以建立能最好地表示该数据集的词表。</li><li>分词器处理输入（输入文本转为token id）和输出（token id转为输出）。</li></ul><p>分词方案：</p><p><img src="https://syaning.github.io/assets/tokenize.DGJ-GEYS.webp" alt=""></p><h2 id="llm-的内部机制" tabindex="-1">LLM 的内部机制 <a class="header-anchor" href="#llm-的内部机制" aria-label="Permalink to &quot;LLM 的内部机制&quot;">​</a></h2><ul><li>模型并不是一次性生成所有文本，而是一次生成一个词元。每个词元生成步骤都是模型的一次前向传播（在机器学习中，前向传播指的是输入进入神经网络并流经计算图，最终在另一端产生输出所需的计算过程）。</li><li>在生成当前词元后，我们将输出词元追加到输入提示词的末尾，从而调整下一次生成的输入提示词。</li><li>当前的Transformer模型对一次可以处理的词元数量有限制，这个限制被称为模型的上下文长度。一个具有4K上下文长度的模型只能处理4000个词元。</li><li>在生成第二个词元时，我们只是简单地将输出词元追加到输入的末尾，然后再次通过模型进行前向传播。如果模型能够缓存之前的计算结果（特别是注意力机制中的一些特定向量），就不需要重复计算之前的流，而只需要计算最后一条流了。这种优化技术被称为键-值(key-value，KV)缓存，它能显著加快生成过程。</li></ul></div></div>]]></content:encoded>
    </item>
    <item>
      <title>Velox 开发环境搭建</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-10-18-velox-dev.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-10-18-velox-dev.html</guid>
      <pubDate>Sat, 18 Oct 2025 14:00:00 GMT</pubDate>
      <description>1. 简介 ​ Velox 是由 Meta 开源的一款基于 C++ 实现的通用向量执行引擎，旨在为各种数据系统（如查询引擎、分析数据库、ETL 工具等）提供高性能的底层执行能力。 Velox 的核心设计目标是提升大规模数据分析的性能和效率，同时通过模块化设计促进代码复用，避免各大系统重复“造轮子”。 文档参考 。 2. 开发环境搭建 ​ 根据项目 README 的介绍，在机器上直接构建，很容易遇到各种问题。目前尝试下来，最方便的方式是在容器环境中构建使用。官方提供了 各种环境的镜像 。 我所使用的机器环境： 零刻 SER8 CPU: AMD 锐龙 R7-</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-10-18-velox-dev" data-v-92824e67><div><h2 id="_1-简介" tabindex="-1">1. 简介 <a class="header-anchor" href="#_1-简介" aria-label="Permalink to &quot;1. 简介&quot;">​</a></h2><p><a href="https://github.com/facebookincubator/velox" target="_blank" rel="noreferrer">Velox</a> 是由 Meta 开源的一款基于 C++ 实现的通用向量执行引擎，旨在为各种数据系统（如查询引擎、分析数据库、ETL 工具等）提供高性能的底层执行能力。</p><p>Velox 的核心设计目标是提升大规模数据分析的性能和效率，同时通过模块化设计促进代码复用，避免各大系统重复“造轮子”。</p><p><a href="https://facebookincubator.github.io/velox/index.html" target="_blank" rel="noreferrer">文档参考</a>。</p><h2 id="_2-开发环境搭建" tabindex="-1">2. 开发环境搭建 <a class="header-anchor" href="#_2-开发环境搭建" aria-label="Permalink to &quot;2. 开发环境搭建&quot;">​</a></h2><p>根据项目 README 的介绍，在机器上直接构建，很容易遇到各种问题。目前尝试下来，最方便的方式是在容器环境中构建使用。官方提供了<a href="https://github.com/facebookincubator/velox/pkgs/container/velox-dev" target="_blank" rel="noreferrer">各种环境的镜像</a>。</p><p>我所使用的机器环境：</p><ul><li>零刻 SER8</li><li>CPU: AMD 锐龙 R7-8745HS (8核16线程)</li><li>RAM: 32G</li></ul><p>Dockerfile 和构建命令参考如下：</p><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-oFjzV" id="tab-tqBUKpg" checked><label data-title="Dockerfile" for="tab-tqBUKpg">Dockerfile</label><input type="radio" name="group-oFjzV" id="tab-R4CeWsP"><label data-title="build.sh" for="tab-R4CeWsP">build.sh</label></div><div class="blocks"><div class="language-Dockerfile vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">Dockerfile</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ghcr.io/facebookincubator/velox-dev:ubuntu-22.04</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">ARG</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> HTTP_PROXY</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">ARG</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> HTTPS_PROXY</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> HTTP_PROXY=${HTTP_PROXY}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> HTTPS_PROXY=${HTTPS_PROXY}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">RUN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> apt install -y vim &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    cd / &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    rm -rf velox &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    git clone https://github.com/facebookincubator/velox.git &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    cd velox &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    ./scripts/setup-ubuntu.sh &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    make &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    cmake --install _build/release &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    cp -r /velox/velox/external/utf8proc /usr/local/include/velox/external/ &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    rm -rf /velox/_build &amp;&amp; \</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    rm -rf /velox/deps-download</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> HTTP_PROXY=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;&quot;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> HTTPS_PROXY=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;&quot;</span></span></code></pre></div><div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">proxy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;http://192.168.31.2:1087&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">date</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> +&quot;%Y%m%d&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> build</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  --build-arg</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> HTTP_PROXY=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">${proxy} </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">\</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  --build-arg</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> HTTPS_PROXY=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">${proxy} </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">\</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">  -t</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> velox-dev:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">${tag} </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">.</span></span></code></pre></div></div></div><div class="tip custom-block"><p class="custom-block-title">TIP</p><ul><li>因为众所周知的网络原因，拉代码或者安装依赖的时候会比较慢，此时可以设置 http 代理。如果不需要可不设置。</li><li>编译 Velox 比较耗资源，默认 <code>make -j</code> 参数是线程数 (<code>getconf _NPROCESSORS_CONF</code>)，内存可能不够，可暂时调大 swap (在自己的环境中调到 32G 可正常编译不卡)</li></ul><div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># 关闭现有 swap</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> swapoff</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -a</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># 原有 swap 文件为 /swap.img，新建 /swapfile</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> fallocate</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -l</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 32G</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> /swapfile</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># 设置权限</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> chmod</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 600</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> /swapfile</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># 格式化为 swap</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> mkswap</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> /swapfile</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># 启用新的 swap</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> swapon</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> /swapfile</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># 验证</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> swapon</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --show</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># 或</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">free</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -h</span></span></code></pre></div></div><h2 id="_3-测试" tabindex="-1">3. 测试 <a class="header-anchor" href="#_3-测试" aria-label="Permalink to &quot;3. 测试&quot;">​</a></h2><ul><li>相关代码放在 <a href="https://github.com/syaning/examples/tree/master/velox" target="_blank" rel="noreferrer">github</a> 上</li><li>也可以直接使用构建好的镜像 <code>docker pull ghcr.io/syaning/velox-dev:20251018</code></li></ul><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-kgcTf" id="tab-rBOOBP9" checked><label data-title="demo.cpp" for="tab-rBOOBP9">demo.cpp</label><input type="radio" name="group-kgcTf" id="tab-x5eSZ4A"><label data-title="CMakeLists.txt" for="tab-x5eSZ4A">CMakeLists.txt</label><input type="radio" name="group-kgcTf" id="tab-xWTdZwn"><label data-title="build_and_run.sh" for="tab-xWTdZwn">build_and_run.sh</label></div><div class="blocks"><div class="language-cpp vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">cpp</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">#include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;iostream&gt;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">#include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;velox/common/memory/Memory.h&quot;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">#include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;velox/vector/BaseVector.h&quot;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">#include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;velox/vector/ComplexVector.h&quot;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">#include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;velox/vector/ConstantVector.h&quot;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">#include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;velox/vector/FlatVector.h&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">using</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> namespace</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> facebook</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">velox</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">    // init memory pool</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">    memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">MemoryManager</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">initialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">({});</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> memoryManager </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">MemoryManager</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">getInstance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rootPool </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> memoryManager-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">addRootPool</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;root&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> leafPool </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rootPool-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">addLeafChild</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;leaf&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">    // create id vector with values 1 to 7</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> BaseVector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">INTEGER</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, leafPool.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">idValues </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> id-&gt;asFlatVector</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;int32_t&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">mutableRawValues</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">vector_size_t</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> i </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">; i </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> id-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(); </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">++</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">i)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        idValues[i] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> static_cast&lt;int32_t&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(i </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">+</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">    // create constant vector with value 42</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> con </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">        BaseVector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">createConstant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">INTEGER</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Variant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">42</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, leafPool.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">());</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">    // create day_of_week vector with string values</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> dow </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> BaseVector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VARCHAR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, leafPool.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">dowValues </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> dow-&gt;asFlatVector</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">StringView</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">mutableRawValues</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">();</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">    std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::vector</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> days </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">        &quot;monday&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">        &quot;tuesday&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">        &quot;wednesday&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">        &quot;thursday&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">        &quot;friday&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">        &quot;saturday&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">        &quot;sunday&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">};</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">vector_size_t</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> i </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">; i </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> dow-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(); </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">++</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">i)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        dowValues[i] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> StringView</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(days[i]);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">    // create row vector with the above three vectors as children</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">    std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::vector</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> names </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;id&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;constant_42&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;day_of_week&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">};</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">    std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::vector</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">TypePtr</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> types </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">INTEGER</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">INTEGER</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VARCHAR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()};</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rowType </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> ROW</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">move</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(names), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">move</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(types));</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">    std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::vector</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">VectorPtr</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> children </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {id, con, dow};</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    auto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rowVector </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">make_shared</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">RowVector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt;(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        leafPool.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), rowType, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">nullptr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, id-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">move</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(children));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">    std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::cout </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;&lt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rowVector-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::endl;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">    std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::cout </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;&lt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> rowVector-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, rowVector-&gt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&lt;&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::endl;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><div class="language-cmake vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">cmake</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">cmake_minimum_required</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VERSION</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> 3.18)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">project</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(velox_demo LANGUAGES CXX)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(CMAKE_CXX_STANDARD 20)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(CMAKE_CXX_STANDARD_REQUIRED </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(CMAKE_FIND_LIBRARY_PREFERENCE </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;STATIC&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">CMAKE_FIND_LIBRARY_SUFFIXES</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;.a&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;.so&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">find_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(VELOX_INCLUDE_DIR velox</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">          HINTS /usr/local/include)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">find_library</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(VELOX_LIBRARY velox</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">             HINTS /usr/local/lib/velox</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">                   /usr/local/lib)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">include</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(FindPackageHandleStandardArgs)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">find_package_handle_standard_args(Velox REQUIRED</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    VELOX_INCLUDE_DIR VELOX_LIBRARY)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(VELOX_FOUND)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    add_library</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(velox UNKNOWN </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">IMPORTED</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    set_target_properties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(velox PROPERTIES</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">        IMPORTED_LOCATION</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> ${VELOX_LIBRARY}</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">        INTERFACE_INCLUDE_DIRECTORIES</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> ${VELOX_INCLUDE_DIR}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    )</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">endif</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">add_executable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(demo demo.cpp)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">target_link_libraries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(demo </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">PRIVATE</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    velox</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    folly</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    boost_context</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    double-conversion</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    event</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    gflags</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    glog</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    re2</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    unwind</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> build</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">mkdir</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> build</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> build</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">cmake</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> ..</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">cmake</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --build</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> .</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">./demo</span></span></code></pre></div></div></div></div></div>]]></content:encoded>
    </item>
    <item>
      <title>ANTLR 快速入门</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-02-11-antlr.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-02-11-antlr.html</guid>
      <pubDate>Tue, 11 Feb 2025 14:00:00 GMT</pubDate>
      <description>1. ANTLR 介绍 ​ ANTLR (ANother Tool for Language Recognition) 是一款强大的语法分析器生成工具。它广泛应用于构建语言识别工具，如编译器、解释器、解析器等。ANTLR 通过定义语法规则自动生成词法分析器和语法分析器，极大地简化了开发过程。 通常来说，如果我们需要实现一个编程语言或者 DSL（Domain Specific Language）的编译器或解释器，需要： 定义语法：使用 ANTLR 的话，我们可以通过 ANTLR 元语言（ANTLR&amp;#39;s meta-language）来定义语法。 词法</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-02-11-antlr" data-v-92824e67><div><h2 id="_1-antlr-介绍" tabindex="-1">1. ANTLR 介绍 <a class="header-anchor" href="#_1-antlr-介绍" aria-label="Permalink to &quot;1. ANTLR 介绍&quot;">​</a></h2><p><a href="https://www.antlr.org/" target="_blank" rel="noreferrer">ANTLR</a> (ANother Tool for Language Recognition) 是一款强大的语法分析器生成工具。它广泛应用于构建语言识别工具，如编译器、解释器、解析器等。ANTLR 通过定义语法规则自动生成词法分析器和语法分析器，极大地简化了开发过程。</p><p>通常来说，如果我们需要实现一个编程语言或者 DSL（Domain Specific Language）的编译器或解释器，需要：</p><ol><li>定义语法：使用 ANTLR 的话，我们可以通过 ANTLR 元语言（ANTLR&#39;s meta-language）来定义语法。</li><li>词法分析（Lexical Analysis）或词法符号化（Tokenizing）：将源代码的字符序列分割为有意义的符号单元（token），例如关键字、标识符、运算符、分隔符等。这一步的程序我们称之为 Lexer 或 Tokenizer。</li><li>语法分析（Syntax Analysis）：将 tokens 生成表示程序结构的抽象语法树（Abstract Syntax Tree, AST），或者叫做语法分析树（Parse Tree）或者句法树（Syntax Tree）。这一步的程序我们称之为 Parser。</li><li>对语法树遍历执行。</li></ol><p><img src="https://syaning.github.io/assets/antlr-intro.C7sG5sLA.svg" alt=""></p><h2 id="_2-实现一个计算器" tabindex="-1">2. 实现一个计算器 <a class="header-anchor" href="#_2-实现一个计算器" aria-label="Permalink to &quot;2. 实现一个计算器&quot;">​</a></h2><div class="tip custom-block"><p class="custom-block-title">TIP</p><p>示例代码参考 <a href="https://github.com/syaning/examples/tree/master/antlr/calculator" target="_blank" rel="noreferrer">https://github.com/syaning/examples/tree/master/antlr/calculator</a></p></div><h3 id="_2-1-需求" tabindex="-1">2.1 需求 <a class="header-anchor" href="#_2-1-需求" aria-label="Permalink to &quot;2.1 需求&quot;">​</a></h3><p>实现一个计算器程序：</p><ul><li>支持加减乘除、取模、幂运算</li><li>暂时只考虑非负整数操作，不考虑浮点数和负数</li></ul><h3 id="_2-2-安装" tabindex="-1">2.2 安装 <a class="header-anchor" href="#_2-2-安装" aria-label="Permalink to &quot;2.2 安装&quot;">​</a></h3><p>参考 <a href="https://github.com/antlr/antlr4/blob/master/doc/getting-started.md" target="_blank" rel="noreferrer">Getting Started with ANTLR v4</a> 下载：</p><div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -O</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> https://www.antlr.org/download/antlr-4.13.2-complete.jar</span></span></code></pre></div><h3 id="_2-3-语法定义" tabindex="-1">2.3 语法定义 <a class="header-anchor" href="#_2-3-语法定义" aria-label="Permalink to &quot;2.3 语法定义&quot;">​</a></h3><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-ZDRtt" id="tab-qkF9cTK" checked><label data-title="CalculatorLexer.g4" for="tab-qkF9cTK">CalculatorLexer.g4</label><input type="radio" name="group-ZDRtt" id="tab-KpGfjGZ"><label data-title="CalculatorParser.g4" for="tab-KpGfjGZ">CalculatorParser.g4</label></div><div class="blocks"><div class="language-txt vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>lexer grammar CalculatorLexer;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>ADD : &#39;+&#39;;</span></span>
<span class="line"><span>SUB : &#39;-&#39;;</span></span>
<span class="line"><span>MUL : &#39;*&#39;;</span></span>
<span class="line"><span>DIV : &#39;/&#39;;</span></span>
<span class="line"><span>MOD : &#39;%&#39;;</span></span>
<span class="line"><span>EXP : &#39;**&#39;;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>LEFT_PAREN  : &#39;(&#39;;</span></span>
<span class="line"><span>RIGHT_PAREN : &#39;)&#39;;</span></span>
<span class="line"><span>NUMBER      : [1-9] [0-9]*;</span></span>
<span class="line"><span>WHITESPACE  : [ \t\r\n\u000C]+ -&gt; skip;</span></span></code></pre></div><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>parser grammar CalculatorParser;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>options {</span></span>
<span class="line"><span>    tokenVocab = CalculatorLexer;</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>prog: expr EOF;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>expr: expr EXP expr</span></span>
<span class="line"><span>    | expr (MUL|DIV|MOD) expr</span></span>
<span class="line"><span>    | expr (ADD|SUB) expr</span></span>
<span class="line"><span>    | NUMBER</span></span>
<span class="line"><span>    | LEFT_PAREN expr RIGHT_PAREN</span></span>
<span class="line"><span>    ;</span></span></code></pre></div></div></div><h3 id="_2-4-调试" tabindex="-1">2.4 调试 <a class="header-anchor" href="#_2-4-调试" aria-label="Permalink to &quot;2.4 调试&quot;">​</a></h3><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-MAvC3" id="tab-rTcq3Ru" checked><label data-title="grun.sh" for="tab-rTcq3Ru">grun.sh</label></div><div class="blocks"><div class="language-shell vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">alias</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> antlr4-parse</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;java -Xmx500M -cp &quot;./antlr-4.13.2-complete.jar:$CLASSPATH&quot; org.antlr.v4.gui.TestRig&#39;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">antlr4-parse</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> *</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">.g4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> prog</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -gui</span></span></code></pre></div></div></div><p>执行 <code>./grun.sh</code>，输入表达式，例如 <code>(1 + 2) * 3 - (5 % 3) ** 2</code>，然后按 <code>Ctrl + D</code>：</p><p><img src="https://syaning.github.io/assets/antlr-grun.82nuRUzu.png" alt=""></p><p>可以看到解析的语法树如下：</p><p><img src="https://syaning.github.io/assets/antlr-grun-gui.BUMKrD_V.png" alt=""></p><p>除了 <code>-gui</code> 选项外，还可以使用 <code>-tree</code>（以 LISP 格式输出语法分析树）或者 <code>-tokens</code>（输出词法符号流）选项来进行调试。</p><h3 id="_2-5-代码生成" tabindex="-1">2.5 代码生成 <a class="header-anchor" href="#_2-5-代码生成" aria-label="Permalink to &quot;2.5 代码生成&quot;">​</a></h3><p>该例子中，我们生成 Go 的代码，如果需要生成其它语言的代码，可以参考官方文档。</p><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-IjW8s" id="tab-hGt2QpH" checked><label data-title="generate.sh" for="tab-hGt2QpH">generate.sh</label><input type="radio" name="group-IjW8s" id="tab-GRswXtI"><label data-title="generate.go" for="tab-GRswXtI">generate.go</label></div><div class="blocks"><div class="language-shell vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">alias</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> antlr4</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;java -Xmx500M -cp &quot;./antlr-4.13.2-complete.jar:$CLASSPATH&quot; org.antlr.v4.Tool&#39;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">antlr4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -Dlanguage=Go</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -visitor</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -package</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> parsing</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> *</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">.g4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -o</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> ../parsing</span></span></code></pre></div><div class="language-go vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">go</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">package</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> grammar</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">//go:generate ./generate.sh</span></span></code></pre></div></div></div><p>执行 <code>go generate ./...</code>，可以生成解析器代码，生成的代码在 <code>../parser</code> 目录下。</p><ul><li><code>calculator_lexer.go</code>: 词法分析器</li><li><code>calculator_parser.go</code>: 语法分析器</li><li><code>*.tokens</code>: 词法符号表，定义了标记及其对应的整数标识符，帮助理解和调试标记识别过程</li><li><code>*.interp</code>: 包含了解析器和词法分析器的内部状态信息，用于优化解析性能和支持高级功能</li><li><code>calculatorparser_listener.go</code>: 定义了监听器接口，允许你在解析过程中监听特定事件</li><li><code>calculatorparser_base_listener.go</code>: 监听器的默认实现</li><li><code>calculatorparser_visitor.go</code>: 定义了访问器接口，允许你遍历和处理抽象语法树</li><li><code>calculatorparser_base_visitor.go</code>: 访问器的默认实现</li></ul><h3 id="_2-6-实现-listener" tabindex="-1">2.6 实现 Listener <a class="header-anchor" href="#_2-6-实现-listener" aria-label="Permalink to &quot;2.6 实现 Listener&quot;">​</a></h3><p>语法解析的过程如下图所示：</p><ol><li>原始文本解析成 ChartStream</li><li>经过 lexer 将 ChartStream 解析为 TokenStream</li><li>经过 parser 将 TokenStream 解析为语法树，语法树包含两类节点：RuleNode（子树的根节点）和 TerminalNode（叶子结点）</li></ol><p><img src="https://syaning.github.io/assets/antlr-parser.B2AcwAEa.svg" alt=""></p><p>对应的语法树如下所示，其中 <code>ProgContext</code> 和 <code>ExprContext</code> 都是 <code>calculator_parser.go</code> 中生成的代码。</p><p><img src="https://syaning.github.io/assets/antlr-parser-tree.CTU3-sL8.svg" alt=""></p><p>语法树会按照深度优先的方式进行遍历，过程如下图所示：</p><ul><li>每个 RuleNode 的访问都有 Enter 和 Exit 两个监听</li><li>TerminalNode 的访问会有 VisitTerminal 监听</li></ul><p><img src="https://syaning.github.io/assets/antlr-parser-listener.DshSip8W.svg" alt=""></p><p>可以先实现一个简单的 Listener:</p><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-LU8ZR" id="tab-OJfqBpM" checked><label data-title="calculator_listener.go" for="tab-OJfqBpM">calculator_listener.go</label><input type="radio" name="group-LU8ZR" id="tab-aIIvGIm"><label data-title="main.go" for="tab-aIIvGIm">main.go</label></div><div class="blocks"><div class="language-go vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">go</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">package</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> calc</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fmt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">github.com/antlr4-go/antlr/v4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/parsing</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> CalculatorListener</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> struct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserListener</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> NewCalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        BaseCalculatorParserListener: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitTerminal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">node</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> antlr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">TerminalNode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;VisitTerminal&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, node.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">EnterProg</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ProgContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;EnterProg&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitProg</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ProgContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;ExitProg&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">EnterExpr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExprContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;EnterExpr&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitExpr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExprContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;ExitExpr&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><div class="language-go vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">go</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">package</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> main</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/calc</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/parsing</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">github.com/antlr4-go/antlr/v4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> parseTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">prog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">antlr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ParseTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    input </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> antlr.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewInputStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(prog)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    lexer </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCalculatorLexer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(input)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    stream </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> antlr.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCommonTokenStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(lexer, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    parser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCalculatorParser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(stream)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parser.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Prog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    prog </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> `12 + 34`</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    tree </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> parseTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(prog)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    listener </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> calc.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    antlr.ParseTreeWalkerDefault.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Walk</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(listener, tree)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div></div></div><p>可以看到输出的结果为：</p><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>EnterProg 12+34&lt;EOF&gt;</span></span>
<span class="line"><span>EnterExpr 12+34</span></span>
<span class="line"><span>EnterExpr 12</span></span>
<span class="line"><span>VisitTerminal 12</span></span>
<span class="line"><span>ExitExpr 12</span></span>
<span class="line"><span>VisitTerminal +</span></span>
<span class="line"><span>EnterExpr 34</span></span>
<span class="line"><span>VisitTerminal 34</span></span>
<span class="line"><span>ExitExpr 34</span></span>
<span class="line"><span>ExitExpr 12+34</span></span>
<span class="line"><span>VisitTerminal &lt;EOF&gt;</span></span>
<span class="line"><span>ExitProg 12+34&lt;EOF&gt;</span></span></code></pre></div><p>然后我们可以按照如下的思路，实现一个能实际进行计算的 Listener：</p><ul><li><code>VisitTerminal()</code> 的时候，如果是数值类型，则入栈（也可以在 <code>ExitExpr()</code> 的时候，判断如果子节点只有一个，则进行入栈）</li><li><code>ExitExpr()</code> 的时候，如果有三个子节点，且中间的节点为 TerminalNode，则从栈中取出两个元素，进行计算后将结果入栈</li><li><code>ExitProg()</code> 的时候，正常栈中只有最终的结果，直接返回即可</li></ul><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-l0FvM" id="tab-QgYIyHN" checked><label data-title="calculator_listener.go" for="tab-QgYIyHN">calculator_listener.go</label></div><div class="blocks"><div class="language-go vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">go</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">package</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> calc</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fmt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">math</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">strconv</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">github.com/antlr4-go/antlr/v4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/parsing</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> CalculatorListener</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> struct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserListener</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    nums []</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> NewCalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        BaseCalculatorParserListener: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> len</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">        panic</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;Invalid expression&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    size </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> len</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    val </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.nums[size</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.nums[:size</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> val</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitTerminal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">node</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> antlr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">TerminalNode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> node.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetSymbol</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetTokenType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserNUMBER {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        num, err </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> strconv.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ParseInt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(node.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> err </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> nil</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">            l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, num)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitProg</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ProgContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> len</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">        panic</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;Invalid expression&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">])</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitExpr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExprContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetChildCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> op, ok </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetChild</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">antlr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">TerminalNode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">); ok {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">            right, left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">            switch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> op.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetSymbol</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetTokenType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">            case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserADD:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">                l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">            case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserSUB:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">                l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">            case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserMUL:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">                l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">            case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserDIV:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">                l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">            case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserMOD:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">                l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">%</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">            case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserEXP:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">                l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(math.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Pow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">float64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(left), </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">float64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(right))))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div></div></div><h3 id="_2-7-分支标签" tabindex="-1">2.7 分支标签 <a class="header-anchor" href="#_2-7-分支标签" aria-label="Permalink to &quot;2.7 分支标签&quot;">​</a></h3><p>我们可以进一步优化，为 <code>expr</code> 的不同分支打上标签，来优化 Listener 的实现。</p><ul><li>为 <code>expr</code> 的每个分支打上标签（即 <code># xxx</code>)，并且为操作符号指定名称为 <code>op</code></li><li>然后重新生成代码，会发现每个分支都有单独的 <code>Enter / Exit</code> 方法</li><li><code>ExitNumber()</code> 的时候数值入栈，<code>ExitExp() / ExitMulDivMod() / ExitAddSub()</code> 的时候进行相应的运算即可</li></ul><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-ZxS1W" id="tab-OrUMkM9" checked><label data-title="CalculatorParser.g4" for="tab-OrUMkM9">CalculatorParser.g4</label><input type="radio" name="group-ZxS1W" id="tab-kXaV3E7"><label data-title="calculator_listener.go" for="tab-kXaV3E7">calculator_listener.go</label></div><div class="blocks"><div class="language-txt vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>parser grammar CalculatorParser;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>options {</span></span>
<span class="line"><span>    tokenVocab = CalculatorLexer;</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>prog: expr EOF;</span></span>
<span class="line"><span></span></span>
<span class="line highlighted"><span>expr: expr op=EXP expr            # Exp</span></span>
<span class="line highlighted"><span>    | expr op=(MUL|DIV|MOD) expr  # MulDivMod</span></span>
<span class="line highlighted"><span>    | expr op=(ADD|SUB) expr      # AddSub</span></span>
<span class="line highlighted"><span>    | NUMBER                      # Number</span></span>
<span class="line highlighted"><span>    | LEFT_PAREN expr RIGHT_PAREN # Parens</span></span>
<span class="line"><span>    ;</span></span></code></pre></div><div class="language-go vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">go</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">package</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> calc</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fmt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">math</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">strconv</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/parsing</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> CalculatorListener</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> struct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserListener</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    nums []</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> NewCalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        BaseCalculatorParserListener: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> len</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">        panic</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;Invalid expression&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    size </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> len</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    val </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.nums[size</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.nums[:size</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> val</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitProg</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ProgContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> len</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">        panic</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;Invalid expression&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">])</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitNumber</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NumberContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    num, err </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> strconv.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ParseInt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> err </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> nil</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, num)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitExp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExpContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    right, left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(math.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Pow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">float64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(left), </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">float64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(right))))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitMulDivMod</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">MulDivModContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    right, left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    switch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetOp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetTokenType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserMUL:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserDIV:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserMOD:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">%</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">l </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExitAddSub</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">AddSubContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    right, left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), l.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    switch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetOp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetTokenType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserADD:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    case</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserSUB:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        l.nums </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> append</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(l.nums, left</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">right)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div></div></div><h3 id="_2-8-实现-visitor" tabindex="-1">2.8 实现 visitor <a class="header-anchor" href="#_2-8-实现-visitor" aria-label="Permalink to &quot;2.8 实现 visitor&quot;">​</a></h3><p>使用 visitor 的方式区别于事件监听的方式，而是可以手动控制如何遍历子节点，以及访问特定类型节点时如何做。下面是一个完整的实现。</p><div class="tip custom-block"><p class="custom-block-title">TIP</p><p>这里需要注意 <code>Visit</code> 函数一定要重写，否则无法正常执行。</p></div><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-wTuaG" id="tab-bJtCVOv" checked><label data-title="calculator_visitor.go" for="tab-bJtCVOv">calculator_visitor.go</label><input type="radio" name="group-wTuaG" id="tab-bBPRg8g"><label data-title="main.go" for="tab-bBPRg8g">main.go</label></div><div class="blocks"><div class="language-go vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">go</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">package</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> calc</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">math</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">strconv</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/parsing</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">github.com/antlr4-go/antlr/v4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> CalculatorVisitor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> struct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserVisitor</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> NewCalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        BaseCalculatorParserVisitor: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&amp;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">BaseCalculatorParserVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">v </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">tree</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> antlr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ParseTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{} {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> tree.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Accept</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(v)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">v </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitProg</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ProgContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{} {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">v </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitNumber</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NumberContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{} {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    val, _ </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> strconv.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ParseInt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> val</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">v </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitExp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ExpContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{} {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)).(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    right </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)).(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(math.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Pow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">float64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(left), </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">float64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(right)))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">v </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitMulDivMod</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">MulDivModContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{} {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)).(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    right </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)).(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetOp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetTokenType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserMUL {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> right</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetOp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetTokenType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserDIV {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> right</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">%</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> right</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">v </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitAddSub</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">AddSubContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{} {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)).(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    right </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)).(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">int64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetOp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">GetTokenType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.CalculatorParserADD {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> right</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> left </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> right</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">v </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">*</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">CalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">VisitParens</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">parsing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ParensContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">interface</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{} {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> v.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(ctx.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Expr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><div class="language-go vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">go</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">package</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> main</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fmt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/calc</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">calculator/parsing</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">    &quot;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">github.com/antlr4-go/antlr/v4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> parseTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">prog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">antlr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ParseTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    input </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> antlr.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewInputStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(prog)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    lexer </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCalculatorLexer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(input)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    stream </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> antlr.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCommonTokenStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(lexer, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    parser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parsing.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCalculatorParser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(stream)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> parser.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Prog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">func</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    prog </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> `(1 + 2) * 3 - (5 % 3) * 2`</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    tree </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> parseTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(prog)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    visitor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> calc.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">NewCalculatorVisitor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> visitor.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Visit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(tree)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    fmt.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">Println</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(result)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div></div></div></div></div>]]></content:encoded>
    </item>
    <item>
      <title>杂谈：一些工作思考</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-02-08-ramble.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-02-08-ramble.html</guid>
      <pubDate>Sat, 08 Feb 2025 02:30:00 GMT</pubDate>
      <description>工作思考 ​ 有种说法是“一流公司做标准，二流公司做品牌，三流公司做服务，四流公司做价格”。本质上也就是“顶层架构→实现→集成”。做产品也是类似，尽可能地去做标准，做平台，让别人集成你而不是自己苦哈哈地去做各种集成。大道至简，小而美胜过大而无当。 不去做所有的事，不去赚所有的钱。考虑到边际效应，合作共赢，各取所长，才是最高效的商业逻辑。避免把技术密集型的事情做成劳动密集型。 不要被客户第一这句口号所绑架。客户第一只是一层皮，最终一定是商业利益。为了特定需求做定制，服务于少数几个客户，这只是狭隘的客户第一，与商业利益也是相悖的。真正的客户第一，面向的客户一</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-02-08-ramble" data-v-92824e67><div><h2 id="工作思考" tabindex="-1">工作思考 <a class="header-anchor" href="#工作思考" aria-label="Permalink to &quot;工作思考&quot;">​</a></h2><ul><li>有种说法是“一流公司做标准，二流公司做品牌，三流公司做服务，四流公司做价格”。本质上也就是“顶层架构→实现→集成”。做产品也是类似，尽可能地去做标准，做平台，让别人集成你而不是自己苦哈哈地去做各种集成。大道至简，小而美胜过大而无当。</li><li>不去做所有的事，不去赚所有的钱。考虑到边际效应，合作共赢，各取所长，才是最高效的商业逻辑。避免把技术密集型的事情做成劳动密集型。</li><li>不要被客户第一这句口号所绑架。客户第一只是一层皮，最终一定是商业利益。为了特定需求做定制，服务于少数几个客户，这只是狭隘的客户第一，与商业利益也是相悖的。真正的客户第一，面向的客户一定是广大的群体，需求一定是通用的，这与最终的商业利益也是一致的。</li><li>公司层面讲究 ROI，对于自己也是如此。尽量做有价值的事情，做对自己成长或收获有帮助的事情。靠谱的事情认真做。不靠谱的事情大多是层层拍脑袋的无稽之谈，或者无价值无意义的琐事，交差即可。</li></ul><h2 id="优雅编码" tabindex="-1">优雅编码 <a class="header-anchor" href="#优雅编码" aria-label="Permalink to &quot;优雅编码&quot;">​</a></h2><ul><li>尽量用 Pure Function，避免副作用。</li><li>避免很多个平铺参数，可以使用一个结构参数来替代，也方便后续扩展。</li><li>除非复用的公共变量，否则尽量就近定义。</li><li>合理注释，关键信息、关键逻辑一定要注释，如有必要添加相关的说明以及对应的设计文档链接。</li><li>可复用组件。DRY 原则。</li><li>避免滥封装工具 Utils。避免无效封装，例如对函数做一层不必要的包装，空字符串额外定义为 nilString 等。</li><li>合理组织代码结构，避免大文件、大函数。</li><li>在开发过程中重构，防微杜渐。不断重构本质上就是常常打扫卫生，而不是等到房间乱到不能再乱的时候统一丢弃。</li><li>模块化设计，高内聚低耦合。本质上就是收纳，相关物品打包放盒子里，大盒子套小盒子。</li><li>业务逻辑和算法逻辑分离。业务逻辑可以调用算法，算法逻辑中不要耦合业务。</li></ul><h2 id="关注的项目" tabindex="-1">关注的项目 <a class="header-anchor" href="#关注的项目" aria-label="Permalink to &quot;关注的项目&quot;">​</a></h2><ul><li><a href="https://github.com/maybe-finance/maybe" target="_blank" rel="noreferrer">maybe</a> - The OS for your personal finances.</li><li><a href="https://github.com/glanceapp/glance" target="_blank" rel="noreferrer">glance</a> - A self-hosted dashboard that puts all your feeds in one place.</li><li><a href="https://github.com/asdf-vm/asdf" target="_blank" rel="noreferrer">asdf</a> - Manage multiple runtime versions with a single CLI tool, extendable via plugins.</li><li><a href="https://github.com/lucide-icons/lucide" target="_blank" rel="noreferrer">lucide</a> - Beautiful &amp; consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons.</li><li><a href="https://github.com/apernet/OpenGFW" target="_blank" rel="noreferrer">OpenGFW</a></li></ul></div></div>]]></content:encoded>
    </item>
    <item>
      <title>杂谈：Less is More</title>
      <link>https://syaning.github.io/tech/posts/2025/2025-01-27-ramble.html</link>
      <guid isPermaLink="true">https://syaning.github.io/tech/posts/2025/2025-01-27-ramble.html</guid>
      <pubDate>Mon, 27 Jan 2025 12:30:00 GMT</pubDate>
      <description>Less is More ​ 过年回到老家，突然之间多了许多琐碎的事情。仅仅是过年前的准备都有好多。贴春联、挂灯笼，还有各种各样的仪式和讲究。并非是对传统习俗有什么偏见，只是对我来说，最讨厌繁文缛节，尤其是各种各样没有意义的流程，很多甚至都不能称之为习俗，可能只是家庭范围内流传下来的习惯罢了。生活简单一些，自己身心也轻松一些，不是更好么。 由此想到简洁与繁琐的对立。比如许多音乐 APP 都喜欢做得臃肿无比，加入了各种广告、社交、直播等功能，实在是让人喜欢不起来，所以最终我还是用回了 Apple Music。还有国内的许多的流媒体应用，也是各种花式的操作，</description>
      <content:encoded><![CDATA[<div style="position:relative;" class="vp-doc _tech_posts_2025_2025-01-27-ramble" data-v-92824e67><div><h2 id="less-is-more" tabindex="-1">Less is More <a class="header-anchor" href="#less-is-more" aria-label="Permalink to &quot;Less is More&quot;">​</a></h2><p>过年回到老家，突然之间多了许多琐碎的事情。仅仅是过年前的准备都有好多。贴春联、挂灯笼，还有各种各样的仪式和讲究。并非是对传统习俗有什么偏见，只是对我来说，最讨厌繁文缛节，尤其是各种各样没有意义的流程，很多甚至都不能称之为习俗，可能只是家庭范围内流传下来的习惯罢了。生活简单一些，自己身心也轻松一些，不是更好么。</p><p>由此想到简洁与繁琐的对立。比如许多音乐 APP 都喜欢做得臃肿无比，加入了各种广告、社交、直播等功能，实在是让人喜欢不起来，所以最终我还是用回了 Apple Music。还有国内的许多的流媒体应用，也是各种花式的操作，暂停了也得看个广告，我其实就想看下画面细节，但是做不到。以至于我宁愿找资源存在 NAS 上然后用 Jellyfin 来看。虽然其中有很多商业的因素存在，但从用户的角度来说，我还是更喜欢纯粹一些的使用体验，至少作为单一的用户个体，我有选择不被恶心的权利。</p><p>关于纯粹，许多人许多事情，在浑浑噩噩的发展过程中，已经变得失其本心了。</p><p>一直很喜欢的《道德经》第二十二章：</p><blockquote><p>曲则全，枉则直，洼则盈，敝则新，少则多，多则惑。是以圣人执一为天下式。不自见，故明；不自是，故彰；不自伐，故有功；不自矜，故能长。夫唯不争，故天下莫能与之争。……</p></blockquote><h2 id="关注的项目" tabindex="-1">关注的项目 <a class="header-anchor" href="#关注的项目" aria-label="Permalink to &quot;关注的项目&quot;">​</a></h2><ul><li><a href="https://markmap.js.org/" target="_blank" rel="noreferrer">markmap</a>: 基于 markdown 的思维导图，感觉后面有需要的话可以在 vitepress 里去集成下，这样博客就直接可以生成思维导图了，不需要嵌入图片。</li><li><a href="https://utteranc.es/" target="_blank" rel="noreferrer">utterances</a>: 基于 github issue 的评论系统，不过暂时没打算给博客加评论，先收藏下。</li><li><a href="https://gitbutler.com/" target="_blank" rel="noreferrer">GitButler</a>: 使用 Tauri + Rust 实现的 git 分支管理应用，看着挺好看的。不过个人更倾向于命令行来使用，先关注下。</li><li><a href="https://typst.app/" target="_blank" rel="noreferrer">typst</a>: 新的排版系统，对标 LaTeX.</li></ul><h2 id="one-more-thing" tabindex="-1">One More Thing <a class="header-anchor" href="#one-more-thing" aria-label="Permalink to &quot;One More Thing&quot;">​</a></h2><p>新年快乐！🎆</p></div></div>]]></content:encoded>
    </item>
  </channel>
</rss>
