<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Andrey Listopadov</title>
    <link>https://andreyor.st/categories/clojurefnl/</link>
    <image>
      <title>Andrey Listopadov</title>
      <link>https://andreyor.st/categories/clojurefnl/</link>
      <url>https://andreyor.st/categories/clojurefnl/favicon-64.png</url>
    </image>
    <description>Posts from the 'clojurefnl' category by Andrey Listopadov</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <copyright>Andrey Listopadov 2020-2026 - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</copyright>
    <lastBuildDate>Mon, 29 Jun 2026 00:29:00 +0300</lastBuildDate>
    <atom:link href="https://andreyor.st/categories/clojurefnl/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Clojure on Fennel part four: Parsing (again)</title>
      <link>https://andreyor.st/posts/2026-06-29-clojure-on-fennel-part-four-parsing-again/</link>
      <guid>https://andreyor.st/posts/2026-06-29-clojure-on-fennel-part-four-parsing-again/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-07-clojure-on-fennel-part-one-persistent-data-structures/&#34;&gt;part one: persistent data structures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/&#34;&gt;part two: immutable.fnl optimizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-27-clojure-on-fennel-part-three-parsing/&#34;&gt;part three: parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;part four: parsing (again)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Other than that, the parsing is complete, and we can look at the compiler part of the ClojureFnl project.
But that&amp;rsquo;s gonna be in the next post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sike!&lt;/p&gt;
&lt;p&gt;Wasn&amp;rsquo;t my intention to fake out like this again, but while working on the compiler, I had an important realization that led me to redesign the parser completely.
And it&amp;rsquo;s a bit of a shame, because I already had a decent part of the compiler working, being able to run the REPL and do various cool things with it.
But, better sooner than later, I guess.&lt;/p&gt;
&lt;p&gt;I had a good amount of the post about the compiler already written too, but now I&amp;rsquo;ll have to discard all of that.
So instead, I decided to talk about the new parser by explaining why the old one failed me.
And to do so, I&amp;rsquo;ll give you a brief look on how the compiler currently works with parsed code.&lt;/p&gt;
&lt;h2 id=&#34;old-parser-and-compiler&#34;&gt;Old parser and compiler&lt;/h2&gt;
&lt;p&gt;So, in the previous post, we&amp;rsquo;ve reached a point where the parser can read code into a tagged tree.
I spent a fair amount of time discussing how I wanted this specifically, and now it feels like I&amp;rsquo;m backpedalling, but tagged tree is not the way forward.
Here&amp;rsquo;s the idea.&lt;/p&gt;
&lt;p&gt;Currently, we&amp;rsquo;re reading this expression &lt;code&gt;(+ 1 2)&lt;/code&gt; into:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;+&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:number&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:number&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The compiler then walks this tree, taking the first value of each node to decide what to do.
First, it sees &lt;code&gt;:code&lt;/code&gt; - that&amp;rsquo;s just an entry point, containing multiple top-level expressions.
Next, it sees &lt;code&gt;:list&lt;/code&gt; and dispatches to a function dedicated to compiling lists.&lt;/p&gt;
&lt;p&gt;This function checks the first element of the list, sees that it is a &lt;code&gt;:symbol&lt;/code&gt;, and checks whether this symbol is somehow special.
While &lt;code&gt;+&lt;/code&gt; doesn&amp;rsquo;t look that special, it is to the compiler, because Clojure&amp;rsquo;s &lt;code&gt;+&lt;/code&gt; and Fennel&amp;rsquo;s &lt;code&gt;+&lt;/code&gt; are not the same thing.
In Clojure, &lt;code&gt;+&lt;/code&gt; is a function, in Fennel, it is a special.&lt;/p&gt;
&lt;p&gt;So the compiler replaces &lt;code&gt;+&lt;/code&gt; with &lt;code&gt;clojure_core_ns.add&lt;/code&gt;, and then proceeds over the rest of the forms in the list, recursively calling itself over each.
In the end, we get this, written into a string builder:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure_core_ns.add&lt;/span&gt; 1 2)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a somewhat simplified explanation, because I&amp;rsquo;m omitting scope resolution, symbol shadowing, and other such things that the compiler currently tracks.
For actual specials, like Clojure&amp;rsquo;s &lt;code&gt;let*&lt;/code&gt;, the story is a bit different too.
The compiler has a dedicated &lt;code&gt;compile-special&lt;/code&gt; function for all Clojure specials provided by &lt;code&gt;cljlib&lt;/code&gt; as Fennel macros.
And that&amp;rsquo;s where things started to go haywire.&lt;/p&gt;
&lt;p&gt;Look at this code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;def &lt;/span&gt;^&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:private&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;foo&lt;/span&gt; 42)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s read like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;def&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:private&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:number&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;42&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s a problem - the parser reads metadata &lt;code&gt;^:private&lt;/code&gt;, and metadata in Clojure is attached to symbols, so the grammar I use reads the next value after the metadata and wraps it into a single node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:private&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The compiler, while compiling this list, sees &lt;code&gt;def&lt;/code&gt; as the first symbol and enters &lt;code&gt;compile-special.def&lt;/code&gt;.
But &lt;code&gt;def&lt;/code&gt; expects a symbol to bind the value to, while here we have a different node type, called &lt;code&gt;:metadata&lt;/code&gt;.
So my compiler had to account for all cases where metadata can appear - and it wasn&amp;rsquo;t easy to do.&lt;/p&gt;
&lt;p&gt;I tried to side-step this by always expecting metadata, because it can appear almost anywhere, and discarding it, because Fennel&amp;rsquo;s concept of metadata is a bit different.
In which I mostly succeeded.
But this wasn&amp;rsquo;t the only problematic thing to support.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s another example, now from &lt;a href=&#34;https://github.com/borkdude/edamame/blob/cd392b2246acced63099ae605f7580942a9cd462/src/edamame/impl/parser.cljc#L92&#34; target=&#34;_blank&#34;&gt;Edamame&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defn- &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;read-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Read in a single logical token from the reader&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ^&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;String&lt;/span&gt; [#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;?&lt;/span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:clj&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rdr&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljs&lt;/span&gt; ^&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;not-native&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rdr&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljr&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rdr&lt;/span&gt;) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_kind&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;initch&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;font-weight:bold&#34;&gt;loop &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;sb&lt;/span&gt; #&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;?&lt;/span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:clj&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;StringBuilder.&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljs&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;StringBuffer.&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljr&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;StringBuilder.&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;initch&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;font-weight:bold&#34;&gt;if &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;or &lt;/span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;whitespace?&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;macro-terminating?&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;nil? &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;font-weight:bold&#34;&gt;do &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;when &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;r/unread&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rdr&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;str &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;sb&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;recur&lt;/span&gt; #&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;?&lt;/span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:clj&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.append&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;sb&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;) &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljs&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.append&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;sb&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;) &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljr&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.Append&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;sb&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;str &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ch&lt;/span&gt;))) (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;r/read-char&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rdr&lt;/span&gt;)))))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It reads into this tagged tree:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;defn-&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;read-token&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n  &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\&amp;#34;Read in a single logical token from the reader\&amp;#34;&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n  &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:clj&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljs&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;not-native&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljr&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;_kind&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;initch&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n  &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;loop&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;sb&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:clj&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;StringBuilder.&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n               &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljs&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;StringBuffer.&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n               &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljr&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;StringBuilder.&amp;#34;&lt;/span&gt;]]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n         &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;initch&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n    &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;if&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;or&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;whitespace?&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n            &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;macro-terminating?&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n            &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;nil?&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n      &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;do&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;when&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n            &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;r/unread&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n          &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;str&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;sb&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n      &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;recur&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:clj&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;.append&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;sb&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljs&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;.append&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;sb&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljr&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;.Append&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;sb&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;str&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ch&amp;#34;&lt;/span&gt;]]]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;r/read-char&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;]]]]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, it&amp;rsquo;s abysmal, but bear with me.
I had to work with this, after all, thinking that this is a blessing.&lt;/p&gt;
&lt;p&gt;The compiler sees &lt;code&gt;defn-&lt;/code&gt; and enters the &lt;code&gt;compile-special.defn-&lt;/code&gt; function.
Defn expects a function name, then a vector for its arguments.
Here, instead of the vector, we have &lt;code&gt;metadata&lt;/code&gt; node again.
This is fine, since I just mentioned above that I managed to sidestep this problem.&lt;/p&gt;
&lt;p&gt;Since this is an arglist, we need to compile it in a special way, adding its symbols to the function scope, etc.
However, instead of arguments, we see the &lt;code&gt;conditional&lt;/code&gt; node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:conditional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:clj&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljs&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;not-native&amp;#34;&lt;/span&gt;]] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;]] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:cljr&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;rdr&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;_kind&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;initch&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I didn&amp;rsquo;t think it was allowed in Clojure to use conditional reading inside forms like this.
But apparently it&amp;rsquo;s OK, and my compiler failed to deal with it.&lt;/p&gt;
&lt;p&gt;So now, any node can not only be a &lt;code&gt;metadata&lt;/code&gt; node, but also a &lt;code&gt;conditional&lt;/code&gt; node.
This complicates things, but at this point I&amp;rsquo;m still thinking that I can persevere.&lt;/p&gt;
&lt;p&gt;So I did.
I added support for almost all specials provided by &lt;code&gt;cljlib&lt;/code&gt;.
The main thing that was left to do were macros.
And then it hit me:&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t do macros like that!&lt;/p&gt;
&lt;p&gt;Why?
Why, of course, because macros don&amp;rsquo;t emit tagged trees that my compiler understands.
They emit code!&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a simple macro:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defmacro &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;unless&lt;/span&gt; [&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;test &lt;/span&gt;&amp;amp; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;body&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  `(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;when &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;not &lt;/span&gt;~&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;test&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     ~@&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;body&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can parse it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;defmacro&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;unless&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;&amp;amp;&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:backtick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;when&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;not&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:unquote&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:unquote-splicing&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;]]]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can probably even compile it to something that we could then call during the compiler step.
However, what will &lt;code&gt;(unless true (println 42))&lt;/code&gt; thing return?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(when (not true) (println 42))&lt;/code&gt;, of course.
It&amp;rsquo;s a Lisp macro, what else did you expect?&lt;/p&gt;
&lt;p&gt;But what does the compiler expect?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;when&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;not&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;println&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:number&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;42&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, it wants this.&lt;/p&gt;
&lt;p&gt;I have to convert macro&amp;rsquo;s output into a string, parse it, and feed it into the compiler if I want macros to work at all.
And that&amp;rsquo;s BAD.
If only I realized this sooner!&lt;/p&gt;
&lt;p&gt;This was the final nail in the coffin of the tagged tree approach for my project.
I knew I had to rewrite the parser to emit actual data structures that I&amp;rsquo;ll be able to emit from macros as well, and compile them.&lt;/p&gt;
&lt;h2 id=&#34;new-reader&#34;&gt;New &lt;del&gt;parser&lt;/del&gt; reader&lt;/h2&gt;
&lt;p&gt;So I decided that I need a proper lisp reader that will produce data structures:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; Welcome to Fennel Proto REPL 0.6.4-dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; Fennel version: 1.7.0-dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; Lua version: PUC Lua 5.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; Work directory: ~/Projects/fennel/ClojureFnl/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;local &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;reader&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;require &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:impl.reader&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;reader.read-string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;(def ^:private foo 42)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;foo&lt;/span&gt; 42)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;local &lt;/span&gt;{&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;second&lt;/span&gt;} (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;require &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:clojure.core&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;meta&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;second&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;reader.read-string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;(def ^:private foo 42)&amp;#34;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:private&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;true&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As can be seen, the new &lt;code&gt;reader&lt;/code&gt; module provides the &lt;code&gt;read-string&lt;/code&gt; function that produces data structures.
Notably, there are no longer any metadata nodes - metadata is assigned to the symbol &lt;code&gt;foo&lt;/code&gt; in this case.&lt;/p&gt;
&lt;p&gt;Same goes for the reader conditionals:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;reader.read-string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;[0 #?(:clj 1 :cljfnl 2) #?@(:clj [2 3] :cljfnl [3 4]) 5]&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[0 2 3 4 5]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These are now correctly spliced at read time.&lt;/p&gt;
&lt;p&gt;This also solved macro problems for the most part:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;reader.read-string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;`(+ ~x ~@y ~z)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/seq&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/concat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/list&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;quote &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/+&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;z&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And it matches what Clojure itself does:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Clojure&lt;/span&gt; 1.12&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;read-string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;`(+ ~x ~@y ~z)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/seq&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/concat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/list&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;quote &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/+&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.core/list&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;z&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Due to this change, the compiler doesn&amp;rsquo;t have to know about syntax quote (&lt;code&gt;`&lt;/code&gt;) at all, and thus macros will correctly return these kinds of lists.&lt;/p&gt;
&lt;p&gt;So, now we can read Clojure code into data structures.
Previously, the parser returned &lt;code&gt;[:code A B ...]&lt;/code&gt; kind of result, where &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, and the rest are all top-level forms.
New reader also does this, but returns them as a list:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;reader.read-string-all&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;1 \&amp;#34;str\&amp;#34; :keyword ::namespaced #:map{:key :val}&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;str&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:user/namespaced&lt;/span&gt; {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:map/key&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:val&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, there&amp;rsquo;s a problem.&lt;/p&gt;
&lt;p&gt;Consider the &lt;code&gt;::namespaced&lt;/code&gt; keyword.
Both my reader and Clojure read it as &lt;code&gt;:user/namespaced&lt;/code&gt;, but this is because the default namespace in the REPL is &lt;code&gt;user&lt;/code&gt;.
For my parser, it&amp;rsquo;s mostly an arbitrary choice because it doesn&amp;rsquo;t know anything about runtime.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Clojure&lt;/span&gt; 1.12&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;read-string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;::x&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:user/x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;ns &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;foo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;foo=&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;read-string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;::x&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo/x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And when compiling a file, the file might have an &lt;code&gt;ns&lt;/code&gt; declaration, or even multiple of them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;ns &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;foo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;println &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;::x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;ns &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;bar&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;println &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;::x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we were to run this code, we would see &lt;code&gt;:foo/x&lt;/code&gt; then &lt;code&gt;:bar/x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My parser currently reads all input, meaning it transforms all top-level forms from text to data.
However, this also means that it doesn&amp;rsquo;t understand anything about namespaces.&lt;/p&gt;
&lt;p&gt;Previously, this was fine because the compiler handled this - the tagged tree didn&amp;rsquo;t resolve anything.
Currently, however, we need to construct namespaced keys, and we need to know the namespace.
But it&amp;rsquo;s not possible unless we parse the input expression by expression, instead of all at once.&lt;/p&gt;
&lt;p&gt;Hence, I had to update the grammar so it could parse expression by expression.
This way, I could read source code form by form, and compile each form separately.
And if the compiler encounters an &lt;code&gt;ns&lt;/code&gt; declaration, it could update some state, so the reader would know how to read namespaced keywords, and other things that may include namespaces.&lt;/p&gt;
&lt;p&gt;But we still need to pass this state to the reader.
And my reader is based on a PEG grammar.&lt;/p&gt;
&lt;p&gt;Luckily for me, the &lt;code&gt;lpeg&lt;/code&gt; library I use has a &lt;code&gt;Carg&lt;/code&gt; function that can pass additional arguments into the PEG parser.
This way I can write my transformation function &lt;code&gt;T&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;fn &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;T&lt;/span&gt; [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;tag&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;patt&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;font-weight:bold&#34;&gt;/ &lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;* &lt;/span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Ct&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;* &lt;/span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Cc&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;tag&lt;/span&gt;) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;patt&lt;/span&gt;)) (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Carg&lt;/span&gt; 1))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;fn &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;state&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; ----8&amp;lt;----&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:macro-keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;bo&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;read-macro-keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;bo&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:conditional&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;read-conditional&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:conditional-splicing&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;conditional-splicing&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; ----8&amp;lt;----&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_&lt;/span&gt;))))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This state can be passed anew after reading each expression, or mutated in place - either way, the reader now knows how to access state.
The reader itself is still stateless.&lt;/p&gt;
&lt;p&gt;The reader now supports all Clojure syntax and produces data structures:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1                                                   &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0.5                                                 &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 0.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;e10&lt;/span&gt;                                                &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 10000000000.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;/2&lt;/span&gt;                                                 &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 1/2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;N&lt;/span&gt;                                                  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 1N&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1.33333333333333333333333333333333&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;M&lt;/span&gt;                 &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 1.33333333333333333333333333333333M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;xFF&lt;/span&gt;                                                &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 255&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;017                                                 &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;16&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rFF&lt;/span&gt;                                               &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; 255&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;\c&lt;/span&gt;                                                  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; &amp;#34;c&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;                                            &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; &amp;#34;string&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;\u&lt;/span&gt;0041                                              &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; &amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;\o&lt;/span&gt;101                                               &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; &amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt;                                            &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; :keyword&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:namespaced/keyword&lt;/span&gt;                                 &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; :namespaced/keyword&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;::auto-keyword&lt;/span&gt;                                      &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; :user/auto-keyword&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;symbol &lt;/span&gt;                                             &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; symbol&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;namespaced/symbol&lt;/span&gt;                                   &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; namespaced/symbol&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#39;var&lt;/span&gt;                                               &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; (var var)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#39;quoted-symbol&lt;/span&gt;                                      &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; (quote quoted-symbol)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;list&lt;/span&gt;)                                              &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; (list)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;vector&lt;/span&gt;]                                            &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; [vector]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;map&lt;/span&gt;}                                          &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; {hash map}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:namespaced&lt;/span&gt;{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:map&lt;/span&gt; 42}                               &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; {:namespaced/map 42}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;&#34;&gt;::&lt;/span&gt;{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:auto&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:namespaced&lt;/span&gt;}                              &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; {:user/auto :namespaced}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#{&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;set&lt;/span&gt;}                                         &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; #{set hash}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;regexp&amp;#34;&lt;/span&gt;                                           &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; &amp;#34;regexp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;dereferencing&lt;/span&gt;                                      &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; (clojure.core/deref dereferencing)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;+ &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;%1&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;%2&lt;/span&gt;)                                          &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; (fn* [p__0__ p__1__] (+ p__0__ p__1__))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;^&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:meta&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt;                                         &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_discard&lt;/span&gt;                                           &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;?&lt;/span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljfnl&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;reader&amp;#34;&lt;/span&gt;) #&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;?&lt;/span&gt;@(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:cljfnl&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:conditionals&lt;/span&gt;])] &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; [&amp;#34;reader&amp;#34; :conditionals]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;                                                 &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;true&lt;/span&gt;                                                &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;false&lt;/span&gt;                                               &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;##&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;NaN&lt;/span&gt;                                               &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; .nan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;##&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Inf&lt;/span&gt;                                               &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; .inf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;inst&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;2022&amp;#34;&lt;/span&gt;                                        &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; #&amp;lt;inst: 2022-01-01T00:00:00.000-00:00&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;uuid&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;c6e8050a-789b-4305-b518-8f0f7c31da7a&amp;#34;&lt;/span&gt;        &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; &amp;#34;c6e8050a-789b-4305-b518-8f0f7c31da7a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note about data structures: even though we&amp;rsquo;re working in Fennel, the produced maps, vectors and lists are custom data structures that are implemented in the &lt;code&gt;cljlib&lt;/code&gt; library.
These are implementations of persistent data structures I&amp;rsquo;ve talked about in &lt;a href=&#34;https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/&#34;&gt;part 2&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;further-work&#34;&gt;Further work&lt;/h2&gt;
&lt;p&gt;With that, I can work on the compiler, now for real.
It&amp;rsquo;s a shame that I basically have to do all the work on the compiler again from scratch, because the underlying data has changed so much, but this simplifies a lot of things, so I&amp;rsquo;m OK with that.&lt;/p&gt;
&lt;p&gt;One thing that the reader still doesn&amp;rsquo;t support yet is read-time evaluation with &lt;code&gt;#=expr&lt;/code&gt;.
This requires a working compiler, and I&amp;rsquo;ll have to first implement it, then integrate it back into the reader.&lt;/p&gt;
&lt;p&gt;Another thing I was thinking about was to drop the LPEG parser altogether.
Maybe, when I have the compiler working and have support for all Clojure runtime semantics in place, I&amp;rsquo;ll make a fork of the Edamame parser and replace the current reader with it, adding support for ClojureFnl via reader conditionals.
This will remove a C library dependency, which is a good thing for distribution.
I still have another C library for arbitrary-precision numbers, but it can be worked around.&lt;/p&gt;
&lt;p&gt;But, lesson learned - when implementing a lisp, even if it is just a transpiler, don&amp;rsquo;t try to cut corners, and make a proper reader.&lt;/p&gt;
&lt;p&gt;Next post, &lt;strong&gt;for sure&lt;/strong&gt;, will be about the compiler!&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Clojure on Fennel part four: Parsing (again)&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Mon, 29 Jun 2026 00:29:00 +0300</pubDate>
    </item><item>
      <title>Clojure on Fennel part three: parsing</title>
      <link>https://andreyor.st/posts/2026-04-27-clojure-on-fennel-part-three-parsing/</link>
      <guid>https://andreyor.st/posts/2026-04-27-clojure-on-fennel-part-three-parsing/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-07-clojure-on-fennel-part-one-persistent-data-structures/&#34;&gt;part one: persistent data structures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/&#34;&gt;part two: immutable.fnl optimizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;part three: parsting&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-06-29-clojure-on-fennel-part-four-parsing-again/&#34;&gt;part four: parsing (again)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The two previous posts were not related to the compiler itself, but were kicked off by the start of the compiler development.
I&amp;rsquo;d say this project was the reason that I made proper immutable data structures for Fennel and Lua.
But now we&amp;rsquo;re finally going to talk about the compiler itself!
And we&amp;rsquo;ll start with the parsing stage - transforming Clojure code into something that can be operated on by the compiler.&lt;/p&gt;
&lt;h2 id=&#34;manual-single-pass-parser-and-compiler&#34;&gt;Manual single-pass parser and compiler&lt;/h2&gt;
&lt;p&gt;When I started this project, as I usually do, I underestimated the complexity at hand.
My first attempt was a single-pass compiler that accepted a stream of characters of Clojure code and re-compiled it on the fly.&lt;/p&gt;
&lt;p&gt;My idea was to write a simple recursive-descent parser that, when accepting a character would decide how to translate it to Fennel, assuming my &lt;a href=&#34;https://gitlab.com/andreyorst/fennel-cljlib&#34; target=&#34;_blank&#34;&gt;fennel-cljlib&lt;/a&gt; library handled most of the semantics.
So, for example, when encountering &lt;code&gt;[&lt;/code&gt; it would translate it to &lt;code&gt;(cljlib.vector&lt;/code&gt; and thus &lt;code&gt;[1 2 3]&lt;/code&gt; would descend to &lt;code&gt;compile-vector&lt;/code&gt; and expand to &lt;code&gt;(cljlib.vector 1 2 3)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The idea is simple, and at first I thought that it would suffice, but then I remembered that there are a lot of other things in Clojure that I have to consider.
For example &lt;code&gt;#_&lt;/code&gt; - the ignore form syntax, and &lt;code&gt;^foo&lt;/code&gt; - the metadata syntax.
This complicated things a lot, so I added initial support for them and moved on - because I decided that this will be a bootstrap compiler for a proper Clojure parser.&lt;/p&gt;
&lt;h2 id=&#34;clojure-parsers&#34;&gt;Clojure parsers&lt;/h2&gt;
&lt;p&gt;After some time I &lt;a href=&#34;https://gitlab.com/andreyorst/clojurefnl/-/commit/b42b79f7b77f7f1489c3a0667bf9301a0d5ccb10&#34; target=&#34;_blank&#34;&gt;had a compiler&lt;/a&gt; that could compile most of the arbitrary code I threw at it.
Of course, it was not exactly to the Clojure spec, but it worked and I was going to replace it anyway.
So I started looking at available Clojure parsers written in Clojure.&lt;/p&gt;
&lt;h3 id=&#34;edamame&#34;&gt;Edamame&lt;/h3&gt;
&lt;p&gt;I started with &lt;a href=&#34;https://github.com/borkdude/edamame&#34; target=&#34;_blank&#34;&gt;Edamame&lt;/a&gt;.
The choice was based on the fact that the &lt;a href=&#34;https://github.com/squint-cljs/squint&#34; target=&#34;_blank&#34;&gt;Squint&lt;/a&gt; project uses it and my project is similar to Squint, or so I thought.
This parser is also used in &lt;a href=&#34;https://github.com/babashka/sci&#34; target=&#34;_blank&#34;&gt;SCI&lt;/a&gt; - a Clojure interpreter, so it seemed like a good fit.&lt;/p&gt;
&lt;p&gt;The first thing I did was &lt;a href=&#34;https://gitlab.com/andreyorst/clojurefnl/-/commit/da15660171e8b2411679d5aa5a90ef7020eb5b84&#34; target=&#34;_blank&#34;&gt;make sure that Edamame at least compiles&lt;/a&gt; with my bootstrap compiler.
The fact that it compiles doesn&amp;rsquo;t mean that it works - it just translated everything into Fennel, with bits of cljlib here and there.
So the next step was to make it work.&lt;/p&gt;
&lt;p&gt;But then I had a realization that I don&amp;rsquo;t need to do that at all - I could just parse Edamame with Edamame, and write a compiler against its output!
Meaning, I could use Edamame&amp;rsquo;s parser directly until my compiler is complete enough to compile Edamame.
And since my idea was to replace bootstrap compiler in the end anyway, I decided that it doesn&amp;rsquo;t make much sense to make it work enough to fully compile Edamame into a runnable Fennel code.
So I used Edamame to parse Edamame, and got back&amp;hellip;&lt;/p&gt;
&lt;p&gt;&amp;hellip; the same Edamame source.&lt;/p&gt;
&lt;p&gt;Imagine my face when I realized that Edamame doesn&amp;rsquo;t produce an abstract syntax tree with all information, but instead it just produces Clojure data structures, such as lists, vectors, and other stuff.
And all of the information about the source code sits inside metadata of these data structures, not in the output.
Yes, it does transform some Clojure forms into lists, like &lt;code&gt;@foo&lt;/code&gt; is transformed into &lt;code&gt;(deref foo)&lt;/code&gt;, but that&amp;rsquo;s still nothing to me, because Fennel has no list data type and its support for metadata is nowhere near compatibility with Clojure.&lt;/p&gt;
&lt;p&gt;I get it, I should have read the readme with more attention, and don&amp;rsquo;t make assumptions based on wild guesses, so it&amp;rsquo;s fine.
Still, I wasn&amp;rsquo;t dropping the idea to use an existing Clojure parser, and the bootstrap compiler combo.
I just need a different parser.&lt;/p&gt;
&lt;h3 id=&#34;rewrite-clj&#34;&gt;Rewrite-clj&lt;/h3&gt;
&lt;p&gt;Next one I looked at was &lt;a href=&#34;https://github.com/clj-commons/rewrite-clj&#34; target=&#34;_blank&#34;&gt;rewrite-clj&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This library does return a tree of objects.
It&amp;rsquo;s still a list, but at least it contains nodes that can be translated into Fennel objects.
However, it still had a few problems.&lt;/p&gt;
&lt;p&gt;First, its source code is much bigger than that of Edamame.
Its API is more complicated, and it expects the use of Zippers, which I don&amp;rsquo;t yet have in cljlib.
I didn&amp;rsquo;t want such a big parser for my compiler, given that it&amp;rsquo;s job is quite simple - produce an AST that I will always walk in full.&lt;/p&gt;
&lt;p&gt;Second, it&amp;rsquo;s still producing some Clojure data structures that I can&amp;rsquo;t cleanly map to Fennel, so it would mean that the compiler itself would depend on cljlib.
And I didn&amp;rsquo;t want that.&lt;/p&gt;
&lt;p&gt;Luckily for me, their user guide mentioned another parser.&lt;/p&gt;
&lt;h3 id=&#34;parcera&#34;&gt;Parcera&lt;/h3&gt;
&lt;p&gt;Finally, &lt;a href=&#34;https://github.com/carocad/parcera&#34; target=&#34;_blank&#34;&gt;parcera&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Looking at the readme, I finally saw what I wanted to see:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;ns &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;example.core&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:require&lt;/span&gt; [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parcera.core&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:as&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parcera&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;;parse clojure code from a string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parcera/ast&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;str &lt;/span&gt;&amp;#39;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;ns &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parcera.core&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:require&lt;/span&gt; [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.data&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:as&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                               [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;clojure.string&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:as&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;str&lt;/span&gt;]))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; =&amp;gt; returns a data structure with the result from the parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ns&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;parcera.core&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:require&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt; (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;clojure.data&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:as&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt; (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;clojure.string&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:as&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;str&amp;#34;&lt;/span&gt;)))))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tagged tree.
Yes, it&amp;rsquo;s still a list, but at least this can be easily mapped to Fennel sequential tables.
So I opened the code and&amp;hellip;&lt;/p&gt;
&lt;p&gt;&amp;hellip;there&amp;rsquo;s nothing there!
Turns out, this project uses an ANTLR grammar for parsing Clojure.&lt;/p&gt;
&lt;p&gt;At this point I was quite frustrated with the fact that there&amp;rsquo;s no single Clojure parser that could provide something similar to what parcera provides, but written in pure Clojure.
Or at least in Java, which I could hand-translate as a last resort.
Yes, there&amp;rsquo;s a Clojure parser inside the Clojure project itself, written in Java, but my goal was to recompile an existing parser written in Clojure.&lt;/p&gt;
&lt;h2 id=&#34;grammar-based-parser&#34;&gt;Grammar-based parser&lt;/h2&gt;
&lt;p&gt;After a bit of consideration, I decided that a grammar-based approach is not that bad of an idea after all.
I couldn&amp;rsquo;t use the ANTLR grammar directly, but at least I had it, and it seemed complete enough.&lt;/p&gt;
&lt;p&gt;In Lua there&amp;rsquo;s a great library called &lt;a href=&#34;https://www.inf.puc-rio.br/~roberto/lpeg/&#34; target=&#34;_blank&#34;&gt;LPeg&lt;/a&gt; that can generate parsers at runtime from a PEG grammar, and I wanted to try it out for quite some time.
But to do that I needed a PEG grammar for Clojure.
ANTLR and PEG are quite different, but it&amp;rsquo;s not impossible to translate one grammar into another.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s what I did:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Clojure PEG Grammar
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Adapted from https://github.com/carocad/parcera (ANTLR4 grammar v0.11.6)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Key differences:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# - Unlike ANTLR, PEG choices are ordered (/).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#   More specific alternatives need to come first.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# - No separate lexer phase.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#   All rules operate on characters directly.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# - ANTLR fragment rules are prefixed with &amp;#39;_&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# - ANTLR&amp;#39;s SENTINEL (catch-all for invalid tokens) has no direct PEG equivalent.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#   The parser will silently fail on invalid input.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Parser rules
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;code        &amp;lt;- input* !.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;input       &amp;lt;- ignore / form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ignore      &amp;lt;- whitespace / comment / discard
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;form        &amp;lt;- literal / collection / reader_macro
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# sets and namespaced maps are under dispatch (they start with #)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;collection  &amp;lt;- list / vector / map
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list        &amp;lt;- &amp;#39;(&amp;#39; input* &amp;#39;)&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vector      &amp;lt;- &amp;#39;[&amp;#39; input* &amp;#39;]&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;map         &amp;lt;- &amp;#39;{&amp;#39; input* &amp;#39;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# macro_keyword before keyword (&amp;#39;::&amp;#39; is longer than &amp;#39;:&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;literal     &amp;lt;- macro_keyword / keyword / string / number / character / symbol
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;keyword       &amp;lt;- KEYWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;macro_keyword &amp;lt;- MACRO_KEYWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;string        &amp;lt;- STRING
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# ordering: most-specific prefix first; LONG last (least specific)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;number        &amp;lt;- HEXADECIMAL / OCTAL / RADIX / DOUBLE / RATIO / LONG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# NAMED_CHAR first (longest); UNICODE before UNICODE_CHAR (more specific)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;character     &amp;lt;- NAMED_CHAR / OCTAL_CHAR / UNICODE / UNICODE_CHAR
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;symbol        &amp;lt;- SYMBOL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# unquote_splicing before unquote (&amp;#39;~@&amp;#39; vs &amp;#39;~&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;reader_macro &amp;lt;- unquote_splicing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              / unquote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              / metadata
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              / backtick
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              / quote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              / dispatch
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              / deref
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;metadata &amp;lt;- ((metadata_entry / deprecated_metadata_entry) ignore*)+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ( symbol
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / collection
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / set
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / namespaced_map
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / tag
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / fn
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / unquote_splicing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / unquote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / conditional_splicing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / conditional
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / deref
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / quote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / backtick
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            / var_quote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;metadata_entry            &amp;lt;- &amp;#39;^&amp;#39; ignore* (map / symbol / string / keyword / macro_keyword / conditional)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# #^ is deprecated syntax for metadata
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;deprecated_metadata_entry &amp;lt;- &amp;#39;#^&amp;#39; ignore* (map / symbol / string / keyword / macro_keyword / conditional)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;backtick          &amp;lt;- &amp;#39;`&amp;#39; ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;quote             &amp;lt;- &amp;#34;&amp;#39;&amp;#34; ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# negative lookahead prevents &amp;#39;~&amp;#39; from matching when &amp;#39;~@&amp;#39; follows
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;unquote           &amp;lt;- &amp;#39;~&amp;#39; !&amp;#39;@&amp;#39; ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;unquote_splicing  &amp;lt;- &amp;#39;~@&amp;#39; ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;deref             &amp;lt;- &amp;#39;@&amp;#39; ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# conditional_splicing before conditional (&amp;#39;#?@&amp;#39; vs &amp;#39;#?&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# tag last (most general: &amp;#39;#&amp;#39; + symbol)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dispatch &amp;lt;- conditional_splicing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / conditional
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / set
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / namespaced_map
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / fn
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / regex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / var_quote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / symbolic
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / eval
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          / tag
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# no whitespace allowed between &amp;#39;#&amp;#39; and the delimiter
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fn                    &amp;lt;- &amp;#39;#&amp;#39; list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;regex                 &amp;lt;- &amp;#39;#&amp;#39; STRING
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set                   &amp;lt;- &amp;#39;#{&amp;#39; input* &amp;#39;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;namespaced_map        &amp;lt;- &amp;#39;#&amp;#39; (macro_keyword / keyword / auto_resolve) ignore* map
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;auto_resolve          &amp;lt;- &amp;#39;::&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;var_quote             &amp;lt;- &amp;#34;#&amp;#39;&amp;#34; ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;discard               &amp;lt;- &amp;#39;#_&amp;#39; ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tag                   &amp;lt;- &amp;#39;#&amp;#39; symbol ignore* form
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;conditional           &amp;lt;- &amp;#39;#?&amp;#39; whitespace* list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;conditional_splicing  &amp;lt;- &amp;#39;#?@&amp;#39; whitespace* list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# ##Inf, ##-Inf, ##NaN  (allows arbitrary symbolic values)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;symbolic              &amp;lt;- &amp;#39;##&amp;#39; ignore* SYMBOL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;eval                  &amp;lt;- &amp;#39;#=&amp;#39; ignore* (symbol / list / conditional)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;whitespace &amp;lt;- WHITESPACE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;comment    &amp;lt;- COMMENT
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Lexical rules (terminals)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HEXADECIMAL &amp;lt;- _SIGN? &amp;#39;0&amp;#39; [xX] [0-9A-Fa-f]+ _BIG_INT?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OCTAL       &amp;lt;- _SIGN? &amp;#39;0&amp;#39; [0-7]+ _BIG_INT?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# base 2-36, then r/R, then digits in that base
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RADIX       &amp;lt;- _SIGN? ([2-9] / [12] [0-9] / &amp;#39;3&amp;#39; [0-6]) [rR] [0-9a-zA-Z]+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RATIO       &amp;lt;- _SIGN? _DIGIT+ &amp;#39;/&amp;#39; _DIGIT+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LONG        &amp;lt;- _SIGN? _DECIMAL _BIG_INT?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# FRACTION EXPONENT before FRACTION alone (longer match first)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DOUBLE      &amp;lt;- _SIGN? _DECIMAL+ ((_FRACTION _EXPONENT / _FRACTION / _EXPONENT) &amp;#39;M&amp;#39;? / &amp;#39;M&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_BIG_INT  &amp;lt;- &amp;#39;N&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_FRACTION &amp;lt;- &amp;#39;.&amp;#39; _DIGIT*
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_EXPONENT &amp;lt;- [eE] _SIGN? _DIGIT+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DECIMAL  &amp;lt;- &amp;#39;0&amp;#39; / [1-9] _DIGIT*
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;STRING &amp;lt;- &amp;#39;&amp;#34;&amp;#39; ([^&amp;#34;\\] / &amp;#39;\\&amp;#39; .)* &amp;#39;&amp;#34;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# \p{White_Space} approximation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WHITESPACE &amp;lt;- [ \t\n\r\f,]+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COMMENT &amp;lt;- (&amp;#39;;&amp;#39; / &amp;#39;#!&amp;#39;) [^\r\n]*
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Named characters must not be followed by name-chars (word boundary)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAMED_CHAR   &amp;lt;- &amp;#39;\\&amp;#39; (&amp;#39;newline&amp;#39; / &amp;#39;return&amp;#39; / &amp;#39;space&amp;#39; / &amp;#39;tab&amp;#39; / &amp;#39;formfeed&amp;#39; / &amp;#39;backspace&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                !(_ALLOWED_NAME_CHARACTER / _DIGIT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UNICODE      &amp;lt;- &amp;#39;\\&amp;#39; &amp;#39;u&amp;#39; [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                !(_ALLOWED_NAME_CHARACTER / _DIGIT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# octal char: 0-377; longest alternative first
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OCTAL_CHAR   &amp;lt;- &amp;#39;\\&amp;#39; &amp;#39;o&amp;#39; ([0-3] [0-7] [0-7] / [0-7] [0-7] / [0-7])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                !(_ALLOWED_NAME_CHARACTER / _DIGIT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# any single non-combining-mark character after backslash
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UNICODE_CHAR &amp;lt;- &amp;#39;\\&amp;#39; ![\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF] .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                !(_ALLOWED_NAME_CHARACTER / _DIGIT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# ::/ is NOT a valid macro keyword
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MACRO_KEYWORD &amp;lt;- &amp;#39;::&amp;#39; (_SIMPLE_KEYWORD &amp;#39;/&amp;#39;)? _SIMPLE_KEYWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;KEYWORD       &amp;lt;- &amp;#39;:&amp;#39; (_SIMPLE_KEYWORD &amp;#39;/&amp;#39;)? (_SIMPLE_KEYWORD / &amp;#39;/&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_SIMPLE_KEYWORD &amp;lt;- _KEYWORD_HEAD _KEYWORD_BODY+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 / _KEYWORD_HEAD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_KEYWORD_BODY &amp;lt;- _KEYWORD_HEAD / &amp;#39;:&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_KEYWORD_HEAD &amp;lt;- _ALLOWED_NAME_CHARACTER / _DIGIT / [#&amp;#39;] / _SIGN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SYMBOL &amp;lt;- (_SIMPLE_SYMBOL &amp;#39;/&amp;#39;)? (_SIMPLE_SYMBOL / &amp;#39;/&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# longer alternatives first for correct PEG matching
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_SIMPLE_SYMBOL &amp;lt;- _SIGN _SYMBOL_HEAD _SYMBOL_BODY*
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                / _ALLOWED_NAME_CHARACTER (_SYMBOL_HEAD / _DIGIT / &amp;#39;:&amp;#39;) _SYMBOL_BODY*
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                / _ALLOWED_NAME_CHARACTER
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                / _SIGN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_SYMBOL_BODY &amp;lt;- _SYMBOL_HEAD / _DIGIT / &amp;#39;:&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_SYMBOL_HEAD &amp;lt;- _ALLOWED_NAME_CHARACTER / [#&amp;#39;] / _SIGN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Characters allowed in names: everything EXCEPT reserved characters.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Equivalent to ANTLR ~[\p{White_Space},()[\]{}&amp;#34;@~^;`\\:#&amp;#39;/0-9+-]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Note: \p{White_Space} approximated with common whitespace chars here.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_ALLOWED_NAME_CHARACTER &amp;lt;- ![ \t\n\r\f,()\[\]{}&amp;#34;@~^;`\\:#&amp;#39;/0-9+\-] .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_SIGN  &amp;lt;- [+\-]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DIGIT &amp;lt;- [0-9]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But of course, you can&amp;rsquo;t just load a PEG grammar into LPeg.
Instead, you need to write the grammar programmatically, using LPeg&amp;rsquo;s API.
I&amp;rsquo;ll spare you the details, but here&amp;rsquo;s a &lt;a href=&#34;https://gitlab.com/andreyorst/clojurefnl/-/blob/main/src/impl/parser.fnl&#34; target=&#34;_blank&#34;&gt;full source code of the parser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After conversion work, I finally had a parser that could produce an AST:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;local &lt;/span&gt;{&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parse&lt;/span&gt;} (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;require &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:impl.parser&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parse&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;(ns parcera.core
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;             (:require [clojure.data :as data]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;                       [clojure.string :as str]))&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;ns&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;parcera.core&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n             &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:require&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;clojure.data&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:as&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n                       &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:vector&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;clojure.string&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:as&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;str&amp;#34;&lt;/span&gt;]]]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or not:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parse&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;(ns parcera.core
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;             (:require [clojure.data :as data]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;                       [clojure.string :as str])&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; &amp;lt;- missing &amp;#39;)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Turns out, LPeg doesn&amp;rsquo;t have any way to report errors if the input string doesn&amp;rsquo;t match.
It simply returns &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Luckily for me, there&amp;rsquo;s an additional library, called &lt;a href=&#34;https://github.com/sqmedeiros/lpeglabel&#34; target=&#34;_blank&#34;&gt;lpeglabel&lt;/a&gt; that can help with that.
It implements an extension to the LPeg library that allows defining tagged failures:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parse&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;(ns parcera.core
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;             (:require [clojure.data :as data]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;                       [clojure.string :as str])&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;unknown&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:3:49&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Parse&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;error&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;EOF&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;while &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;reading&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;Expected&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;an&lt;/span&gt; &amp;#39;)&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now I have a working Clojure parser with a usable output.&lt;/p&gt;
&lt;h2 id=&#34;the-parser&#34;&gt;The parser&lt;/h2&gt;
&lt;p&gt;I mentioned before that Clojure metadata and special characters did introduce problems to my original parser.
Well, with this grammar, this is no longer an issue:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;each &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;s&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;ipairs &lt;/span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;@foo&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;^Foo bar&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;^{:baz true} qux&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;#_1 2&amp;#34;&lt;/span&gt;])]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;pp&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parse&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;s&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:code&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:deref&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:code&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata-entry&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Foo&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:code&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:metadata-entry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:map&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:keyword&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;:baz&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:symbol&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;qux&amp;#34;&lt;/span&gt;]]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:code&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:discard&lt;/span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:number&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;]] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:whitespace&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:number&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, the parser doesn&amp;rsquo;t transform &lt;code&gt;@foo&lt;/code&gt; into &lt;code&gt;(deref foo)&lt;/code&gt;, but it&amp;rsquo;s OK, as I can handle it in the compiler easily now.
Metadata is fully supported by the parser and is stripped in the compiler.
And discard (&lt;code&gt;#_&lt;/code&gt;) actually works in all of its supported ways, i.e. &lt;code&gt;#_#_ 1 2&lt;/code&gt; works fine.&lt;/p&gt;
&lt;p&gt;Each parser result starts with &lt;code&gt;:code&lt;/code&gt;, an entry point for the compiler.
And the compiler itself is just a simple recursive descent over all of the nodes in a loop.
So, that&amp;rsquo;s good, right?&lt;/p&gt;
&lt;p&gt;Well, yes and no.
There are some cons to this approach.&lt;/p&gt;
&lt;p&gt;First, and foremost - this parser doesn&amp;rsquo;t support streaming, meaning I can&amp;rsquo;t stream chunks of code and compile them.
Well, it&amp;rsquo;s not impossible, but currently it works on strings only.
It is possible to write a second parser that would split text into top-level expressions, but for now it&amp;rsquo;s an optimization waiting to happen.
Right now, this means that files will be parsed in full - having a greater footprint in memory.&lt;/p&gt;
&lt;p&gt;Second, and arguably more important - this is a C library dependency.
Both LPeg and lpeglabel are C libraries, meaning that the compiler now works only on Lua runtimes that can load object files.
And the choice of systems where this compiler can run is now limited to those that can compile these libraries.
Not that big of a deal, if you ask me, since C runs almost everywhere, but it&amp;rsquo;ll make the compiler difficult to ship as a no-dependency binary.
It&amp;rsquo;s possible to compile a Fennel script into a binary and include all of its dependencies, but it would mean that I&amp;rsquo;ll have to produce binaries for many different operating systems and architectures.
Or, alternatively, require people to install LPeg and lpeglabel through luarocks.&lt;/p&gt;
&lt;p&gt;For now, I&amp;rsquo;m willing to accept this.
There&amp;rsquo;s a &lt;a href=&#34;https://github.com/pygy/LuLPeg&#34; target=&#34;_blank&#34;&gt;port of LPeg in pure Lua&lt;/a&gt;, but it&amp;rsquo;s quite slow and doesn&amp;rsquo;t have support for lpeglabel features.
There&amp;rsquo;s an &lt;a href=&#34;https://github.com/pygy/LuLPeg/issues/27&#34; target=&#34;_blank&#34;&gt;open request&lt;/a&gt; for that, but it&amp;rsquo;s been sitting open for quite some time.&lt;/p&gt;
&lt;p&gt;Other than that, the parsing is complete and we can look at the compiler part of the ClojureFnl project.
But that&amp;rsquo;s gonna be in the next post.&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Clojure on Fennel part three: parsing&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Mon, 27 Apr 2026 19:48:00 +0300</pubDate>
    </item><item>
      <title>Clojure on Fennel part two: immutable.fnl optimizations</title>
      <link>https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/</link>
      <guid>https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-07-clojure-on-fennel-part-one-persistent-data-structures/&#34;&gt;part one: persistent data structures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;part two: immutable.fnl optimizations&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-27-clojure-on-fennel-part-three-parsing/&#34;&gt;part three: parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-06-29-clojure-on-fennel-part-four-parsing-again/&#34;&gt;part four: parsing (again)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;So the next post will hopefully be about the compiler itself.&lt;/p&gt;
&lt;p&gt;Unless I get distracted again.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sike!&lt;/p&gt;
&lt;p&gt;While I did some work on the compiler, I&amp;rsquo;m not feeling ready to talk about it yet.
I want the post about it to be, well, more in-depth, so for now, let&amp;rsquo;s go back to immutable data structures.
I got some comments on their performance, which can be summarized to &amp;ldquo;bad&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;And I agree!
The performance is not great, especially when compared to plain Lua tables.
However, my benchmarking process was a bit flawed, and I&amp;rsquo;ve improved it since.&lt;/p&gt;
&lt;p&gt;This post won&amp;rsquo;t be long, but I wanted to make it anyway, to illustrate what I&amp;rsquo;m dealing with.
Again, this is not the final version, I&amp;rsquo;m still working on possible optimizations as I write this, but some things have improved already, though not by much.&lt;/p&gt;
&lt;p&gt;There were some low-hanging fruits that I could tackle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vector had a local &lt;code&gt;fragment&lt;/code&gt; function with pure arithmetic.
I already have the same &lt;code&gt;fragment&lt;/code&gt; function in my bitops module that uses native rshift+band.
The modulo operation is expensive.&lt;/li&gt;
&lt;li&gt;A similar case of using bit operations instead of modulo was in HashMap&amp;rsquo;s &lt;code&gt;index&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Increased the popcount table to 256 entries instead of 16.
Halves the loop iterations for typical bitmaps.&lt;/li&gt;
&lt;li&gt;Probably premature, but replaced most instances of &lt;code&gt;faccumulate&lt;/code&gt;, &lt;code&gt;fcollect&lt;/code&gt;, etc. with plain &lt;code&gt;for&lt;/code&gt; loops.
These loops are quite hot, and Fennel compiles them to have additional assignment at each iteration.
Should be free, but actually had a small impact.&lt;/li&gt;
&lt;li&gt;More pre-allocations when creating Lua tables.
Using &lt;code&gt;[nil nil nil nil]&lt;/code&gt; tells the Lua compiler to pre-allocate four slots when creating the table, meaning it won&amp;rsquo;t resize once we insert elements.
Tables in Lua resize by a factor of 2 and copy everything on resize, which is not great for performance.&lt;/li&gt;
&lt;li&gt;Vector iterator is now stateful.
And also fast.&lt;/li&gt;
&lt;li&gt;Internal fields for data structures used long, elaborate keys, which meant string comparison in certain cases of table lookup would be unnecessarily long.
Again, changing them to shorter keys should not matter that much, but it did have some impact.&lt;/li&gt;
&lt;li&gt;HashMap branching factor was increased to 32.
After some tests, it proved to be slightly better, despite what I said in my previous post.
More on that later.&lt;/li&gt;
&lt;li&gt;Hashes are now cached in a weak table.
Helps avoid re-hashing the same values at the expense of memory.
Not sure how well it will scale.&lt;/li&gt;
&lt;li&gt;Optimized array copying for sizes less than 4 by doing direct table construction.
Avoids loop overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these changes in place (and some more I omitted), here are some of the benchmarks:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/lua.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;These images show per-commit changes to the performance.
Really helped me track what was worth keeping, and what wasn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the LuaJIT version of the benchmark:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/luajit.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Here&amp;rsquo;s another graph:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/hashmap.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;This is me testing how hash map branching factor affects operations.
As can be seen, switching from 16 to 32 makes inserts slower on PUC Lua 5.5 but makes indexing faster.
On LuaJIT both operations are faster.&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/insertion-lua.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Looking at the charts, the &lt;code&gt;7989c32&lt;/code&gt; commit with that change (the second orange bar) — insertions are slower than the previous commit, and indexing is faster than the previous commit.
Unfortunately, LuaJIT charts are noisy and I can&amp;rsquo;t confirm the speedup there.
Something to do with instabilities in the LuaJIT tracer, I guess.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; I know, the results for some operations are worse than before all optimizations that took place.
This is still a work in progress.
Just shows how important relative measuring is.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The results are a mixed bag.
Most operations that improved did so by only 1 or 2 milliseconds overall, meaning the per-operation cost is still quite high, compared to Lua tables.
Yes, some became faster, so I&amp;rsquo;m keeping these changes, but I can also see that for some operations, they actually got slower than they were before any optimization work.
The reason for that may be that I fixed a few bugs along the way.
Anyway, I tried to prioritize transient collection variants, as they benefit most from optimizations given how they&amp;rsquo;re used, and LuaJIT performance where possible.&lt;/p&gt;
&lt;p&gt;Some bars are not meaningful for certain operations.
E.g., I had changes that had nothing to do with Persistent Vector, but affected it anyway, so this benchmark is still a bit flawed.
At least those differences are within the deviation range.&lt;/p&gt;
&lt;p&gt;Anyhow, just wanted to show that optimization is hard, and not all changes yield the results you expect.
In the case of this library, I don&amp;rsquo;t think there are more low-hanging fruits to optimize, or at least I don&amp;rsquo;t see any.
For example, there are a lot of table allocations that are hard to get rid of in the current implementation.
A deeper analysis is needed, probably by people with more Lua knowledge.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still satisfied with some improvements I got, but we&amp;rsquo;ll see if more are on the way.&lt;/p&gt;
&lt;h2 id=&#34;update&#34;&gt;Update&lt;/h2&gt;
&lt;p&gt;After a lot more benchmarks, flamegraphs, and throwing stuff at the wall, I think I can&amp;rsquo;t do much more.
I decided to update this post instead of writing another one (and really should have waited before publishing the original) because at that point, I thought there was nothing more to do.
Now I think there really isn&amp;rsquo;t.
So the results are in:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/lua-final.png&#34;
         alt=&#34;Figure 1: PUC Lua 5.5 (green - before, orange - after)&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 1: &lt;/span&gt;PUC Lua 5.5 (green - before, orange - after)&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/luajit-final.png&#34;
         alt=&#34;Figure 2: LuaJIT (green - before, orange - after)&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 2: &lt;/span&gt;LuaJIT (green - before, orange - after)&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As can be seen, the changes are more dramatic on Lua than on LuaJIT.
Mostly because it&amp;rsquo;s hard to optimize for LuaJIT without sacrificing performance on Lua, so I re-prioritized Lua, since LuaJIT is already much faster.&lt;/p&gt;
&lt;p&gt;The final benchmark tables are below.&lt;/p&gt;
&lt;h4 id=&#34;puc-lua-5-dot-5&#34;&gt;PUC Lua 5.5&lt;/h4&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;PersistentVector
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;build 50000&lt;/td&gt;
&lt;td&gt;0.18 ms&lt;/td&gt;
&lt;td&gt;18.88 ms&lt;/td&gt;
&lt;td&gt;102x&lt;/td&gt;
&lt;td&gt;0.378 us&lt;/td&gt;
&lt;td&gt;119x&lt;/td&gt;
&lt;td&gt;0.440 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup random&lt;/td&gt;
&lt;td&gt;0.48 ms&lt;/td&gt;
&lt;td&gt;11.44 ms&lt;/td&gt;
&lt;td&gt;24x&lt;/td&gt;
&lt;td&gt;0.229 us&lt;/td&gt;
&lt;td&gt;28x&lt;/td&gt;
&lt;td&gt;0.266 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;assoc random&lt;/td&gt;
&lt;td&gt;0.31 ms&lt;/td&gt;
&lt;td&gt;42.72 ms&lt;/td&gt;
&lt;td&gt;139x&lt;/td&gt;
&lt;td&gt;0.854 us&lt;/td&gt;
&lt;td&gt;222x&lt;/td&gt;
&lt;td&gt;1.4 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all&lt;/td&gt;
&lt;td&gt;0.14 ms&lt;/td&gt;
&lt;td&gt;14.56 ms&lt;/td&gt;
&lt;td&gt;106x&lt;/td&gt;
&lt;td&gt;0.291 us&lt;/td&gt;
&lt;td&gt;150x&lt;/td&gt;
&lt;td&gt;0.415 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate&lt;/td&gt;
&lt;td&gt;0.60 ms&lt;/td&gt;
&lt;td&gt;2.74 ms&lt;/td&gt;
&lt;td&gt;5x&lt;/td&gt;
&lt;td&gt;0.055 us&lt;/td&gt;
&lt;td&gt;17x&lt;/td&gt;
&lt;td&gt;0.196 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;TransientVector
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;build 50000&lt;/td&gt;
&lt;td&gt;0.19 ms&lt;/td&gt;
&lt;td&gt;6.55 ms&lt;/td&gt;
&lt;td&gt;35x&lt;/td&gt;
&lt;td&gt;0.131 us&lt;/td&gt;
&lt;td&gt;41x&lt;/td&gt;
&lt;td&gt;0.154 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;assoc random&lt;/td&gt;
&lt;td&gt;0.31 ms&lt;/td&gt;
&lt;td&gt;17.64 ms&lt;/td&gt;
&lt;td&gt;57x&lt;/td&gt;
&lt;td&gt;0.353 us&lt;/td&gt;
&lt;td&gt;67x&lt;/td&gt;
&lt;td&gt;0.408 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all&lt;/td&gt;
&lt;td&gt;0.14 ms&lt;/td&gt;
&lt;td&gt;5.74 ms&lt;/td&gt;
&lt;td&gt;42x&lt;/td&gt;
&lt;td&gt;0.115 us&lt;/td&gt;
&lt;td&gt;51x&lt;/td&gt;
&lt;td&gt;0.142 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;PersistentHashMap
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000&lt;/td&gt;
&lt;td&gt;2.01 ms&lt;/td&gt;
&lt;td&gt;81.44 ms&lt;/td&gt;
&lt;td&gt;41x&lt;/td&gt;
&lt;td&gt;1.6 us&lt;/td&gt;
&lt;td&gt;82x&lt;/td&gt;
&lt;td&gt;3.2 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup random&lt;/td&gt;
&lt;td&gt;0.92 ms&lt;/td&gt;
&lt;td&gt;34.03 ms&lt;/td&gt;
&lt;td&gt;37x&lt;/td&gt;
&lt;td&gt;0.681 us&lt;/td&gt;
&lt;td&gt;81x&lt;/td&gt;
&lt;td&gt;1.7 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;contains random&lt;/td&gt;
&lt;td&gt;1.82 ms&lt;/td&gt;
&lt;td&gt;33.08 ms&lt;/td&gt;
&lt;td&gt;18x&lt;/td&gt;
&lt;td&gt;0.662 us&lt;/td&gt;
&lt;td&gt;49x&lt;/td&gt;
&lt;td&gt;1.7 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dissoc all&lt;/td&gt;
&lt;td&gt;0.90 ms&lt;/td&gt;
&lt;td&gt;76.97 ms&lt;/td&gt;
&lt;td&gt;86x&lt;/td&gt;
&lt;td&gt;1.5 us&lt;/td&gt;
&lt;td&gt;187x&lt;/td&gt;
&lt;td&gt;3.3 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dissoc 10%&lt;/td&gt;
&lt;td&gt;0.18 ms&lt;/td&gt;
&lt;td&gt;9.81 ms&lt;/td&gt;
&lt;td&gt;56x&lt;/td&gt;
&lt;td&gt;2.0 us&lt;/td&gt;
&lt;td&gt;112x&lt;/td&gt;
&lt;td&gt;3.7 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate&lt;/td&gt;
&lt;td&gt;1.91 ms&lt;/td&gt;
&lt;td&gt;7.22 ms&lt;/td&gt;
&lt;td&gt;4x&lt;/td&gt;
&lt;td&gt;0.144 us&lt;/td&gt;
&lt;td&gt;4x&lt;/td&gt;
&lt;td&gt;0.146 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup structural keys&lt;/td&gt;
&lt;td&gt;0.14 ms&lt;/td&gt;
&lt;td&gt;2.86 ms&lt;/td&gt;
&lt;td&gt;21x&lt;/td&gt;
&lt;td&gt;0.572 us&lt;/td&gt;
&lt;td&gt;146x&lt;/td&gt;
&lt;td&gt;4.1 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;insert collision-heavy&lt;/td&gt;
&lt;td&gt;0.13 ms&lt;/td&gt;
&lt;td&gt;6.50 ms&lt;/td&gt;
&lt;td&gt;51x&lt;/td&gt;
&lt;td&gt;1.3 us&lt;/td&gt;
&lt;td&gt;134x&lt;/td&gt;
&lt;td&gt;3.5 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mixed 80/20&lt;/td&gt;
&lt;td&gt;1.92 ms&lt;/td&gt;
&lt;td&gt;53.67 ms&lt;/td&gt;
&lt;td&gt;28x&lt;/td&gt;
&lt;td&gt;1.1 us&lt;/td&gt;
&lt;td&gt;62x&lt;/td&gt;
&lt;td&gt;2.3 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;TransientHashMap
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000&lt;/td&gt;
&lt;td&gt;2.02 ms&lt;/td&gt;
&lt;td&gt;38.91 ms&lt;/td&gt;
&lt;td&gt;19x&lt;/td&gt;
&lt;td&gt;0.778 us&lt;/td&gt;
&lt;td&gt;44x&lt;/td&gt;
&lt;td&gt;1.7 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup random&lt;/td&gt;
&lt;td&gt;1.14 ms&lt;/td&gt;
&lt;td&gt;35.56 ms&lt;/td&gt;
&lt;td&gt;31x&lt;/td&gt;
&lt;td&gt;0.711 us&lt;/td&gt;
&lt;td&gt;86x&lt;/td&gt;
&lt;td&gt;1.7 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dissoc all&lt;/td&gt;
&lt;td&gt;0.89 ms&lt;/td&gt;
&lt;td&gt;44.52 ms&lt;/td&gt;
&lt;td&gt;50x&lt;/td&gt;
&lt;td&gt;0.890 us&lt;/td&gt;
&lt;td&gt;112x&lt;/td&gt;
&lt;td&gt;2.0 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mixed 80/20&lt;/td&gt;
&lt;td&gt;4.39 ms&lt;/td&gt;
&lt;td&gt;44.87 ms&lt;/td&gt;
&lt;td&gt;10x&lt;/td&gt;
&lt;td&gt;0.897 us&lt;/td&gt;
&lt;td&gt;22x&lt;/td&gt;
&lt;td&gt;1.9 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;luajit&#34;&gt;LuaJIT&lt;/h4&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;PersistentVector
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;build 50000&lt;/td&gt;
&lt;td&gt;0.11 ms&lt;/td&gt;
&lt;td&gt;8.12 ms&lt;/td&gt;
&lt;td&gt;71x&lt;/td&gt;
&lt;td&gt;0.162 us&lt;/td&gt;
&lt;td&gt;84x&lt;/td&gt;
&lt;td&gt;0.177 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup random&lt;/td&gt;
&lt;td&gt;0.06 ms&lt;/td&gt;
&lt;td&gt;0.67 ms&lt;/td&gt;
&lt;td&gt;11x&lt;/td&gt;
&lt;td&gt;0.013 us&lt;/td&gt;
&lt;td&gt;11x&lt;/td&gt;
&lt;td&gt;0.013 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;assoc random&lt;/td&gt;
&lt;td&gt;0.04 ms&lt;/td&gt;
&lt;td&gt;22.91 ms&lt;/td&gt;
&lt;td&gt;559x&lt;/td&gt;
&lt;td&gt;0.458 us&lt;/td&gt;
&lt;td&gt;731x&lt;/td&gt;
&lt;td&gt;0.585 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;10.77 ms&lt;/td&gt;
&lt;td&gt;718x&lt;/td&gt;
&lt;td&gt;0.215 us&lt;/td&gt;
&lt;td&gt;657x&lt;/td&gt;
&lt;td&gt;0.210 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;0.23 ms&lt;/td&gt;
&lt;td&gt;12x&lt;/td&gt;
&lt;td&gt;0.005 us&lt;/td&gt;
&lt;td&gt;26x&lt;/td&gt;
&lt;td&gt;0.011 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;TransientVector
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;build 50000&lt;/td&gt;
&lt;td&gt;0.05 ms&lt;/td&gt;
&lt;td&gt;0.79 ms&lt;/td&gt;
&lt;td&gt;16x&lt;/td&gt;
&lt;td&gt;0.016 us&lt;/td&gt;
&lt;td&gt;12x&lt;/td&gt;
&lt;td&gt;0.012 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;assoc random&lt;/td&gt;
&lt;td&gt;0.04 ms&lt;/td&gt;
&lt;td&gt;2.22 ms&lt;/td&gt;
&lt;td&gt;55x&lt;/td&gt;
&lt;td&gt;0.044 us&lt;/td&gt;
&lt;td&gt;28x&lt;/td&gt;
&lt;td&gt;0.022 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;0.35 ms&lt;/td&gt;
&lt;td&gt;23x&lt;/td&gt;
&lt;td&gt;0.007 us&lt;/td&gt;
&lt;td&gt;50x&lt;/td&gt;
&lt;td&gt;0.016 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;PersistentHashMap
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000&lt;/td&gt;
&lt;td&gt;0.79 ms&lt;/td&gt;
&lt;td&gt;32.32 ms&lt;/td&gt;
&lt;td&gt;41x&lt;/td&gt;
&lt;td&gt;0.646 us&lt;/td&gt;
&lt;td&gt;50x&lt;/td&gt;
&lt;td&gt;0.989 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup random&lt;/td&gt;
&lt;td&gt;0.34 ms&lt;/td&gt;
&lt;td&gt;5.78 ms&lt;/td&gt;
&lt;td&gt;17x&lt;/td&gt;
&lt;td&gt;0.116 us&lt;/td&gt;
&lt;td&gt;43x&lt;/td&gt;
&lt;td&gt;0.248 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;contains random&lt;/td&gt;
&lt;td&gt;0.30 ms&lt;/td&gt;
&lt;td&gt;5.91 ms&lt;/td&gt;
&lt;td&gt;20x&lt;/td&gt;
&lt;td&gt;0.118 us&lt;/td&gt;
&lt;td&gt;40x&lt;/td&gt;
&lt;td&gt;0.257 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dissoc all&lt;/td&gt;
&lt;td&gt;0.23 ms&lt;/td&gt;
&lt;td&gt;27.02 ms&lt;/td&gt;
&lt;td&gt;119x&lt;/td&gt;
&lt;td&gt;0.540 us&lt;/td&gt;
&lt;td&gt;191x&lt;/td&gt;
&lt;td&gt;0.894 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dissoc 10%&lt;/td&gt;
&lt;td&gt;0.05 ms&lt;/td&gt;
&lt;td&gt;5.89 ms&lt;/td&gt;
&lt;td&gt;118x&lt;/td&gt;
&lt;td&gt;1.2 us&lt;/td&gt;
&lt;td&gt;121x&lt;/td&gt;
&lt;td&gt;1.4 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate&lt;/td&gt;
&lt;td&gt;0.07 ms&lt;/td&gt;
&lt;td&gt;1.07 ms&lt;/td&gt;
&lt;td&gt;16x&lt;/td&gt;
&lt;td&gt;0.021 us&lt;/td&gt;
&lt;td&gt;30x&lt;/td&gt;
&lt;td&gt;0.040 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup structural keys&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;2.41 ms&lt;/td&gt;
&lt;td&gt;121x&lt;/td&gt;
&lt;td&gt;0.483 us&lt;/td&gt;
&lt;td&gt;119x&lt;/td&gt;
&lt;td&gt;0.498 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;insert collision-heavy&lt;/td&gt;
&lt;td&gt;0.28 ms&lt;/td&gt;
&lt;td&gt;5.20 ms&lt;/td&gt;
&lt;td&gt;19x&lt;/td&gt;
&lt;td&gt;1.0 us&lt;/td&gt;
&lt;td&gt;36x&lt;/td&gt;
&lt;td&gt;1.2 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mixed 80/20&lt;/td&gt;
&lt;td&gt;0.50 ms&lt;/td&gt;
&lt;td&gt;46.59 ms&lt;/td&gt;
&lt;td&gt;94x&lt;/td&gt;
&lt;td&gt;0.932 us&lt;/td&gt;
&lt;td&gt;40x&lt;/td&gt;
&lt;td&gt;0.513 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--list-separator--&gt;
&lt;ul&gt;
&lt;li&gt;TransientHashMap
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient&lt;/th&gt;
&lt;th&gt;ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;th&gt;ratio (old)&lt;/th&gt;
&lt;th&gt;per op  (old)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000&lt;/td&gt;
&lt;td&gt;0.91 ms&lt;/td&gt;
&lt;td&gt;30.92 ms&lt;/td&gt;
&lt;td&gt;34x&lt;/td&gt;
&lt;td&gt;0.618 us&lt;/td&gt;
&lt;td&gt;11x&lt;/td&gt;
&lt;td&gt;0.242 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup random&lt;/td&gt;
&lt;td&gt;0.32 ms&lt;/td&gt;
&lt;td&gt;34.90 ms&lt;/td&gt;
&lt;td&gt;110x&lt;/td&gt;
&lt;td&gt;0.698 us&lt;/td&gt;
&lt;td&gt;44x&lt;/td&gt;
&lt;td&gt;0.246 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dissoc all&lt;/td&gt;
&lt;td&gt;0.23 ms&lt;/td&gt;
&lt;td&gt;36.35 ms&lt;/td&gt;
&lt;td&gt;162x&lt;/td&gt;
&lt;td&gt;0.727 us&lt;/td&gt;
&lt;td&gt;57x&lt;/td&gt;
&lt;td&gt;0.271 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mixed 80/20&lt;/td&gt;
&lt;td&gt;1.29 ms&lt;/td&gt;
&lt;td&gt;40.71 ms&lt;/td&gt;
&lt;td&gt;32x&lt;/td&gt;
&lt;td&gt;0.814 us&lt;/td&gt;
&lt;td&gt;11x&lt;/td&gt;
&lt;td&gt;0.309 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Yes, the benchmark shows that some operations slowed down, some sped up.
Ideally, I&amp;rsquo;d like to have no slowdown, but that&amp;rsquo;s that.
Still, I wouldn&amp;rsquo;t trust my LuaJIT benchmark, though - graphs show that for some weird reason unrelated changes affect performance in the modules not touched by those changes at all.
So these changes are now merged into the main branch.&lt;/p&gt;
&lt;p&gt;Main bottleneck now is table allocation and array copying - things I already spent optimizing a lot of time with mixed results.
So I think I can&amp;rsquo;t do better than that.
At least without major algorithmic restructuring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With that&lt;/strong&gt;, the next post &lt;em&gt;should&lt;/em&gt; be about the compiler.&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Clojure on Fennel part two: immutable.fnl optimizations&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Wed, 15 Apr 2026 05:11:00 +0300</pubDate>
    </item><item>
      <title>Clojure on Fennel part one: Persistent Data Structures</title>
      <link>https://andreyor.st/posts/2026-04-07-clojure-on-fennel-part-one-persistent-data-structures/</link>
      <guid>https://andreyor.st/posts/2026-04-07-clojure-on-fennel-part-one-persistent-data-structures/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;part one: persistent data structures&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-15-clojure-on-fennel-part-two-immutablefnl-optimizations/&#34;&gt;part two: immutable.fnl optimizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-04-27-clojure-on-fennel-part-three-parsing/&#34;&gt;part three: parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://andreyor.st/posts/2026-06-29-clojure-on-fennel-part-four-parsing-again/&#34;&gt;part four: parsing (again)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Somewhere in 2019 I started a project that aimed to bring some of Clojure features to Lua runtime - &lt;a href=&#34;https://gitlab.com/andreyorst/fennel-cljlib&#34; target=&#34;_blank&#34;&gt;fennel-cljlib&lt;/a&gt;.
It was a library for Fennel that implemented a basic subset of &lt;code&gt;clojure.core&lt;/code&gt; namespace functions and macros.
My goal was simple - I enjoy working with Clojure, but I don&amp;rsquo;t use it for hobby projects, so I wanted Fennel to feel more Clojure-like, besides what it already provides for that.&lt;/p&gt;
&lt;p&gt;This library grew over the years, I implemented &lt;a href=&#34;https://andreyor.st/posts/2021-10-09-lazy-sequences-and-iterators/&#34;&gt;lazy sequences&lt;/a&gt;, added &lt;a href=&#34;https://gitlab.com/andreyorst/itable&#34; target=&#34;_blank&#34;&gt;immutability&lt;/a&gt;, made a &lt;a href=&#34;https://gitlab.com/andreyorst/fennel-test&#34; target=&#34;_blank&#34;&gt;testing library&lt;/a&gt;, inspired by &lt;code&gt;clojure.test&lt;/code&gt; and kaocha, and even made a &lt;a href=&#34;https://andreyor.st/posts/2023-05-15-clojures-coreasync-port-for-the-fennel-language/&#34;&gt;port of &lt;code&gt;clojure.core.async&lt;/code&gt;&lt;/a&gt;.
It was a passion project, I almost never used it to write actual software.
One notable exception is &lt;a href=&#34;https://gitlab.com/andreyorst/fenneldoc&#34; target=&#34;_blank&#34;&gt;fenneldoc&lt;/a&gt; - a tool for documentation generation for Fennel libraries.
And I haven&amp;rsquo;t seen anyone else use it for a serious project.&lt;/p&gt;
&lt;p&gt;The reason for that is simple - it was an experiment.
Corners were cut, and Fennel, being a Clojure-inspired lisp is not associated with functional programming the same way Clojure is.
As a matter of fact, I wouldn&amp;rsquo;t recommend using this library for anything serious&amp;hellip; yet.&lt;/p&gt;
&lt;p&gt;Recently, however, I started a new project: &lt;a href=&#34;https://gitlab.com/andreyorst/clojurefnl&#34; target=&#34;_blank&#34;&gt;ClojureFnl&lt;/a&gt;.
This is a Clojure-to-Fennel compiler that uses fennel-cljlib as a foundation.
It&amp;rsquo;s still in early days of development, but I&amp;rsquo;ve been working on it for a few months in private until I found a suitable way to make things work in March.
As of this moment, it is capable of compiling most of &lt;code&gt;.cljc&lt;/code&gt; files I threw at it, but running the compiled code is a different matter.
I mean, it works to some degree, but the support for standard library is far from done.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; Welcome to ClojureFnl REPL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; ClojureFnl v0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; Fennel 1.6.1 on PUC Lua 5.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defn &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;prime?&lt;/span&gt; [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;n&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;not &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;some zero? &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;map &lt;/span&gt;#(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;rem &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;%&lt;/span&gt;) (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;range &lt;/span&gt;2 &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;n&lt;/span&gt;)))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;lt;function&lt;/span&gt;&lt;span style=&#34;&#34;&gt;:&lt;/span&gt; 0&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x89ba7c550&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user=&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;for &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;range &lt;/span&gt;3 33 2)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:when&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;prime?&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(3 5 7 11 13 17 19 23 29 31)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, there was a problem.&lt;/p&gt;
&lt;p&gt;My initial implementation of immutable data structures in the &lt;a href=&#34;https://gitlab.com/andreyorst/itable&#34; target=&#34;_blank&#34;&gt;itable&lt;/a&gt; library had a serious flaw.
The whole library was a simple hack based on the copy-on-write approach and a bunch of Lua metatables to enforce immutability.
As a result, all operations were extremely slow.
It was fine as an experiment, but if I wanted to go further with ClojureFnl, I had to replace it.
The same problem plagued &lt;a href=&#34;https://gitlab.com/andreyorst/immutableredblacktree.lua&#34; target=&#34;_blank&#34;&gt;immutableredblacktree.lua&lt;/a&gt;, an implementation of a copy-on-write red-black tree I made for sorted maps.
It did a full copy of the tree each time it was modified.&lt;/p&gt;
&lt;p&gt;For associative tables it wasn&amp;rsquo;t that big of a deal - usually maps contain a small amount of keys, and &lt;code&gt;itable&lt;/code&gt; only copied levels that needed to be changed.
So, if you had a map with, say, ten keys, and each of those keys contained another map with ten keys, adding, removing or updating a key in the outer map meant only copying these ten keys - not the whole nested map.
I could do that reliably, because inner maps were immutable too.&lt;/p&gt;
&lt;p&gt;But for arrays the story is usually quite different.
Arrays often store a lot of indices, and rarely are nested (or at least not as often as maps).
And copying arrays on every change quickly becomes expensive.
I&amp;rsquo;ve mitigated some of the performance problems by implementing my version of transients, however the beauty of Clojure&amp;rsquo;s data structures is that they&amp;rsquo;re quite fast even without this optimization.&lt;/p&gt;
&lt;h2 id=&#34;proper-persistent-data-structures&#34;&gt;Proper persistent data structures&lt;/h2&gt;
&lt;p&gt;Clojure uses Persistent HAMT as a base for its hash maps and sets, and a bit-partitioned trie for vectors.
For sorted maps and sets, Clojure uses an immutable red-black tree implementation, but as far as I know it&amp;rsquo;s not doing a full copy of the tree, and it also has structural sharing properties.&lt;/p&gt;
&lt;p&gt;I started looking into existing implementations of HAMT for Lua:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/raymond-w-ko/hamt.lua&#34; target=&#34;_blank&#34;&gt;hamt.lua&lt;/a&gt; (based on &lt;a href=&#34;https://github.com/mattbierner/hamt.git&#34; target=&#34;_blank&#34;&gt;mattbierner/hamt&lt;/a&gt;)
&lt;ul&gt;
&lt;li&gt;seemed incomplete&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://codeberg.org/alloyed/ltrie&#34; target=&#34;_blank&#34;&gt;ltrie&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;no transients&lt;/li&gt;
&lt;li&gt;no hashset&lt;/li&gt;
&lt;li&gt;no ordered map (expectable, different algorithm)&lt;/li&gt;
&lt;li&gt;no compound vector/hash&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gist.github.com/SegFaultAX/3318559&#34; target=&#34;_blank&#34;&gt;Michael-Keith Bernard&amp;rsquo;s gist&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;no custom hashing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I could use one of those, notably &lt;code&gt;ltrie&lt;/code&gt; seemed the most appropriate one, but given that I&amp;rsquo;m working on a fennel library that I want later to embed into my Clojure compiler I needed a library implemented in Fennel.&lt;/p&gt;
&lt;p&gt;So I made my own library: &lt;a href=&#34;https://gitlab.com/andreyorst/immutable.fnl&#34; target=&#34;_blank&#34;&gt;immutable.fnl&lt;/a&gt;.
This library features HAMT-hash maps, hash-sets, and vectors, as well as a better implementation of a persistent red-black tree, and lazy linked lists.&lt;/p&gt;
&lt;h3 id=&#34;persistent-hash-map&#34;&gt;Persistent Hash Map&lt;/h3&gt;
&lt;p&gt;I started the implementation with a Persistent HAMT with native Lua hashing.
The data structure itself is a Hash Array Mapped Trie (HAMT) with 16-factor branching.
Thus all operations are O(Log16 N), which is effectively O(1) for a practical amount of keys.&lt;/p&gt;
&lt;p&gt;As far as I know, Clojure uses branching factor of 32, but for a Lua runtime this would mean that the popcount would be more expensive, and despite a shallower tree, each mutation would need to copy a larger sparse array.
With branching factor of 16 a map with 50K entries is ~4 levels deep, which would be ~3 with 32 branching factor.
So my logic was that it&amp;rsquo;ll be a compromise, especially since Lua is not JVM when it comes to performance.&lt;/p&gt;
&lt;p&gt;Of course, it&amp;rsquo;s not as fast as a pure Lua table, which is to be expected.
Lua tables are implemented in C, use efficient hashing, and dynamically re-allocated based on key count.
So for my implementation most operations are a lot slower, but the total time for an operation is still usable.&lt;/p&gt;
&lt;p&gt;Here are some benchmarks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Median time over 7 rounds (1 warmup discarded), N = 50000 elements.
GC stopped during measurement. Clock: os.clock (CPU).
Runtime: Fennel 1.7.0-dev on PUC Lua 5.5&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Regular operations are notably slower, when compared to Lua:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent HashMap&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 random keys&lt;/td&gt;
&lt;td&gt;2.05 ms&lt;/td&gt;
&lt;td&gt;164.80 ms&lt;/td&gt;
&lt;td&gt;80.3x slower&lt;/td&gt;
&lt;td&gt;3.3 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup 50000 random keys&lt;/td&gt;
&lt;td&gt;0.83 ms&lt;/td&gt;
&lt;td&gt;92.51 ms&lt;/td&gt;
&lt;td&gt;110.8x slower&lt;/td&gt;
&lt;td&gt;1.9 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete all&lt;/td&gt;
&lt;td&gt;0.78 ms&lt;/td&gt;
&lt;td&gt;170.78 ms&lt;/td&gt;
&lt;td&gt;219.8x slower&lt;/td&gt;
&lt;td&gt;3.4 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete 10%&lt;/td&gt;
&lt;td&gt;0.14 ms&lt;/td&gt;
&lt;td&gt;19.50 ms&lt;/td&gt;
&lt;td&gt;136.4x slower&lt;/td&gt;
&lt;td&gt;3.9 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate 50000 entries&lt;/td&gt;
&lt;td&gt;1.74 ms&lt;/td&gt;
&lt;td&gt;6.64 ms&lt;/td&gt;
&lt;td&gt;3.8x slower&lt;/td&gt;
&lt;td&gt;0.133 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For transients the situation is a bit better, but not by much:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient HashMap&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 random keys&lt;/td&gt;
&lt;td&gt;2.05 ms&lt;/td&gt;
&lt;td&gt;89.17 ms&lt;/td&gt;
&lt;td&gt;43.5x slower&lt;/td&gt;
&lt;td&gt;1.8 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete all&lt;/td&gt;
&lt;td&gt;0.76 ms&lt;/td&gt;
&lt;td&gt;104.31 ms&lt;/td&gt;
&lt;td&gt;138.0x slower&lt;/td&gt;
&lt;td&gt;2.1 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete 10%&lt;/td&gt;
&lt;td&gt;0.16 ms&lt;/td&gt;
&lt;td&gt;12.71 ms&lt;/td&gt;
&lt;td&gt;82.0x slower&lt;/td&gt;
&lt;td&gt;2.5 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;On LuaJIT numbers may seem worse, but per-operation cost is much lower, it&amp;rsquo;s just that native table operations are so much faster:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Median time over 7 rounds (1 warmup discarded), N = 50000 elements.
GC stopped during measurement. Clock: os.clock (CPU).
Runtime: Fennel 1.7.0-dev on LuaJIT 2.1.1774896198 macOS/arm64&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent HashMap&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 random keys&lt;/td&gt;
&lt;td&gt;0.86 ms&lt;/td&gt;
&lt;td&gt;49.05 ms&lt;/td&gt;
&lt;td&gt;56.8x slower&lt;/td&gt;
&lt;td&gt;0.981 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup 50000 random keys&lt;/td&gt;
&lt;td&gt;0.27 ms&lt;/td&gt;
&lt;td&gt;14.21 ms&lt;/td&gt;
&lt;td&gt;53.4x slower&lt;/td&gt;
&lt;td&gt;0.284 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete all&lt;/td&gt;
&lt;td&gt;0.13 ms&lt;/td&gt;
&lt;td&gt;48.63 ms&lt;/td&gt;
&lt;td&gt;374.1x slower&lt;/td&gt;
&lt;td&gt;0.973 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete 10%&lt;/td&gt;
&lt;td&gt;0.05 ms&lt;/td&gt;
&lt;td&gt;6.49 ms&lt;/td&gt;
&lt;td&gt;138.1x slower&lt;/td&gt;
&lt;td&gt;1.3 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate 50000 entries&lt;/td&gt;
&lt;td&gt;0.07 ms&lt;/td&gt;
&lt;td&gt;1.80 ms&lt;/td&gt;
&lt;td&gt;27.7x slower&lt;/td&gt;
&lt;td&gt;0.036 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient HashMap&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 random keys&lt;/td&gt;
&lt;td&gt;0.76 ms&lt;/td&gt;
&lt;td&gt;22.43 ms&lt;/td&gt;
&lt;td&gt;29.6x slower&lt;/td&gt;
&lt;td&gt;0.449 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete all&lt;/td&gt;
&lt;td&gt;0.15 ms&lt;/td&gt;
&lt;td&gt;34.16 ms&lt;/td&gt;
&lt;td&gt;232.4x slower&lt;/td&gt;
&lt;td&gt;0.683 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete 10%&lt;/td&gt;
&lt;td&gt;0.04 ms&lt;/td&gt;
&lt;td&gt;5.02 ms&lt;/td&gt;
&lt;td&gt;132.1x slower&lt;/td&gt;
&lt;td&gt;1.0 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;With a branching factor of 32 the situation gets worse on PUC Lua, but is slightly better on LuaJIT.
So there&amp;rsquo;s still space for fine-tuning.&lt;/p&gt;
&lt;p&gt;For hashing strings and objects I decided to use &lt;code&gt;djb2&lt;/code&gt; algorithm.
I am almost as old as this hash function, so seemed like a good fit.
JK.
The main reason to use it was that it can be implemented even if we don&amp;rsquo;t have any bit-wise operators, and Lua doesn&amp;rsquo;t have them in all of the versions.
It only uses &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, and &lt;code&gt;%&lt;/code&gt; arithmetic operators, so can be done on any Lua version.
It&amp;rsquo;s prone to collisions, and I try to mitigate that by randomizing it when the library is loaded.&lt;/p&gt;
&lt;p&gt;Still, collisions do happen, but HAMT core ensures that they will still resolve correctly by implementing a deep equality function for most objects.&lt;/p&gt;
&lt;p&gt;However, when first working on this, I noticed this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;local &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;require &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:io.gitlab.andreyorst.immutable.PersistentHashMap&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;local &lt;/span&gt;{&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash&lt;/span&gt;} (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;require &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:io.gitlab.andreyorst.immutable.impl.hash&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;161272824
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash&lt;/span&gt; {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;161272824
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2) 1 {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2} 2)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2} 2}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is an interesting loophole. What object ended up in our hash map as a key - our persistent map or plain Lua table?
Well, that depends on insertion order:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;each &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;k&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;pairs &lt;/span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2) 1 {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2} 2))]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;print &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;getmetatable &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;k&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;IPersistentHashMap&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;0&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x824d9b570&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;each &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;k&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;pairs &lt;/span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2} 2 (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hash-map&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:foo&lt;/span&gt; 1 &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:bar&lt;/span&gt; 2) 1))]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;print &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;getmetatable &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;k&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To &lt;em&gt;reiterate&lt;/em&gt;, I&amp;rsquo;m creating a hash map, with a key set to another persistent hash map, and then insert a plain Lua table with the same content.
The Lua table hashes to exactly the same hash, and goes into the same bucket, but there&amp;rsquo;s no collision, because &lt;em&gt;objects are equal by value&lt;/em&gt;.
But equality of mutable collections is very loosely defined - it may be equal right now, but the next time you look at it, it&amp;rsquo;s different.
So a different hashing was needed for persistent collections, to avoid these kinds of collision.
I ended up salting persistent collections with their prototype address in memory.&lt;/p&gt;
&lt;p&gt;Other than that, the HAMT implementation is by the book, and the rest is the interface for interacting with maps.&lt;/p&gt;
&lt;p&gt;Main operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new&lt;/code&gt; - construct a new map of key value pairs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assoc&lt;/code&gt; - associate a key with a value&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dissoc&lt;/code&gt; - remove key from the map&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conj&lt;/code&gt; - universal method for association, much like in Clojure&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contains&lt;/code&gt; - check if key is in the map&lt;/li&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt; - map size, constant time&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get&lt;/code&gt; - get a key value from a map&lt;/li&gt;
&lt;li&gt;&lt;code&gt;keys&lt;/code&gt; - get a lazy list of keys&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vals&lt;/code&gt; - get a lazy list of values&lt;/li&gt;
&lt;li&gt;&lt;code&gt;transient&lt;/code&gt; - convert a map to a transient&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Coercion/conversion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;from&lt;/code&gt; - create a map from another object&lt;/li&gt;
&lt;li&gt;&lt;code&gt;to-table&lt;/code&gt; - convert a map to a Lua table&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iterator&lt;/code&gt; - get an iterator to use in Lua loops&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Transient operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;assoc!&lt;/code&gt; - mutable &lt;code&gt;assoc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dissoc!&lt;/code&gt; - mutable &lt;code&gt;dissoc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;persistent&lt;/code&gt; - convert back to persistent variant, and mark transient as completed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This covers most of the needs in my &lt;code&gt;fennel-cljlib&lt;/code&gt; library, as anything besides it I can implement myself, or just adapt existing implementations.&lt;/p&gt;
&lt;p&gt;A Persistent Hash Set is also available as a thin wrapper around &lt;code&gt;PersistentHashMap&lt;/code&gt; with a few method changes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A note on &lt;code&gt;PersistentArrayMap&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In Clojure there is a second kind of maps that are ordered, not sorted, called a Persistent Array Map.
They are used by default when defining a map with eight keys or less, like &lt;code&gt;{:foo 1 :bar 2}&lt;/code&gt;.
The idea is simple - for such a small map, a linear search through all keys is faster than with a HAMT-based map.&lt;/p&gt;
&lt;p&gt;However, in my testing on the Lua runtime, there&amp;rsquo;s no benefit in this kind of a data structure, apart from it being an ordered variant.
Lookup is slower, because of a custom equality function, which does deep comparison.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;persistent-vector&#34;&gt;Persistent Vector&lt;/h3&gt;
&lt;p&gt;Persistent Vectors came next, and while the trie structure is similar to hash maps, vectors use direct index-based navigation instead of hashing, with a branching factor of 32.
Unlike maps, vector arrays in the HAMT are more densely packed, and therefore a higher branching factor is better for performance.
So lookup, update, and pop are O(log32 N), append can be considered O(1) amortized.&lt;/p&gt;
&lt;p&gt;Still, compared to plain Lua sequential tables the performance is not as good:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Median time over 7 rounds (1 warmup discarded), N = 50000 elements.
GC stopped during measurement. Clock: os.clock (CPU).
Runtime: Fennel 1.7.0-dev on PUC Lua 5.5&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent Vector&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 elements&lt;/td&gt;
&lt;td&gt;0.19 ms&lt;/td&gt;
&lt;td&gt;21.07 ms&lt;/td&gt;
&lt;td&gt;109.7x slower&lt;/td&gt;
&lt;td&gt;0.421 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup 50000 random indices&lt;/td&gt;
&lt;td&gt;0.47 ms&lt;/td&gt;
&lt;td&gt;14.05 ms&lt;/td&gt;
&lt;td&gt;29.7x slower&lt;/td&gt;
&lt;td&gt;0.281 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update 50000 random indices&lt;/td&gt;
&lt;td&gt;0.32 ms&lt;/td&gt;
&lt;td&gt;70.04 ms&lt;/td&gt;
&lt;td&gt;221.6x slower&lt;/td&gt;
&lt;td&gt;1.4 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all 50000 elements&lt;/td&gt;
&lt;td&gt;0.25 ms&lt;/td&gt;
&lt;td&gt;24.34 ms&lt;/td&gt;
&lt;td&gt;96.2x slower&lt;/td&gt;
&lt;td&gt;0.487 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate 50000 elements&lt;/td&gt;
&lt;td&gt;0.63 ms&lt;/td&gt;
&lt;td&gt;10.16 ms&lt;/td&gt;
&lt;td&gt;16.2x slower&lt;/td&gt;
&lt;td&gt;0.203 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient Vector&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000  elements&lt;/td&gt;
&lt;td&gt;0.19 ms&lt;/td&gt;
&lt;td&gt;7.81 ms&lt;/td&gt;
&lt;td&gt;40.3x slower&lt;/td&gt;
&lt;td&gt;0.156 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update 50000 random indices&lt;/td&gt;
&lt;td&gt;0.33 ms&lt;/td&gt;
&lt;td&gt;20.76 ms&lt;/td&gt;
&lt;td&gt;62.4x slower&lt;/td&gt;
&lt;td&gt;0.415 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all 50000 elements&lt;/td&gt;
&lt;td&gt;0.25 ms&lt;/td&gt;
&lt;td&gt;11.14 ms&lt;/td&gt;
&lt;td&gt;44.4x slower&lt;/td&gt;
&lt;td&gt;0.223 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;On LuaJIT:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Median time over 7 rounds (1 warmup discarded), N = 50000 elements.
GC stopped during measurement. Clock: os.clock (CPU).
Runtime: Fennel 1.7.0-dev on LuaJIT 2.1.1774896198 macOS/arm64&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Persistent Vector&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 elements&lt;/td&gt;
&lt;td&gt;0.10 ms&lt;/td&gt;
&lt;td&gt;7.62 ms&lt;/td&gt;
&lt;td&gt;74.0x slower&lt;/td&gt;
&lt;td&gt;0.152 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup 50000 random indices&lt;/td&gt;
&lt;td&gt;0.06 ms&lt;/td&gt;
&lt;td&gt;0.67 ms&lt;/td&gt;
&lt;td&gt;11.8x slower&lt;/td&gt;
&lt;td&gt;0.013 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update 50000 random indices&lt;/td&gt;
&lt;td&gt;0.04 ms&lt;/td&gt;
&lt;td&gt;29.13 ms&lt;/td&gt;
&lt;td&gt;710.4x slower&lt;/td&gt;
&lt;td&gt;0.583 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all 50000 elements&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;8.62 ms&lt;/td&gt;
&lt;td&gt;410.4x slower&lt;/td&gt;
&lt;td&gt;0.172 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iterate 50000 elements&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;0.57 ms&lt;/td&gt;
&lt;td&gt;28.7x slower&lt;/td&gt;
&lt;td&gt;0.011 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;Transient Vector&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 elements&lt;/td&gt;
&lt;td&gt;0.05 ms&lt;/td&gt;
&lt;td&gt;0.59 ms&lt;/td&gt;
&lt;td&gt;11.6x slower&lt;/td&gt;
&lt;td&gt;0.012 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update 50000 random indices&lt;/td&gt;
&lt;td&gt;0.04 ms&lt;/td&gt;
&lt;td&gt;2.06 ms&lt;/td&gt;
&lt;td&gt;51.6x slower&lt;/td&gt;
&lt;td&gt;0.041 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop all 50000 elements&lt;/td&gt;
&lt;td&gt;0.02 ms&lt;/td&gt;
&lt;td&gt;0.84 ms&lt;/td&gt;
&lt;td&gt;46.7x slower&lt;/td&gt;
&lt;td&gt;0.017 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I think this is an OK performance still.
Vectors don&amp;rsquo;t use hashing, instead it is a direct index traversal via bit-shifting, so there&amp;rsquo;s no hashing, just index math.&lt;/p&gt;
&lt;p&gt;Operations on vectors include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new&lt;/code&gt; - constructor&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conj&lt;/code&gt; - append to the tail&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assoc&lt;/code&gt; - change a value at given index&lt;/li&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt; - element count (constant time)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get&lt;/code&gt; - get value at given index&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pop&lt;/code&gt; - remove last&lt;/li&gt;
&lt;li&gt;&lt;code&gt;transient&lt;/code&gt; - convert to a transient&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subvec&lt;/code&gt; - create a slice of the vector in constant time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Transient operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;assoc!&lt;/code&gt; - mutable &lt;code&gt;assoc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conj!&lt;/code&gt; - mutable &lt;code&gt;conj&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pop!&lt;/code&gt; - mutable &lt;code&gt;pop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;persistent&lt;/code&gt; - convert back to persistent and finalize&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Interop:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;from&lt;/code&gt; - creates a vector from any other collection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iterator&lt;/code&gt; - returns an iterator for use in Lua loops&lt;/li&gt;
&lt;li&gt;&lt;code&gt;to-table&lt;/code&gt; - converts to a sequential Lua table&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One notable difference in both vector and hash-map is that it allows &lt;code&gt;nil&lt;/code&gt; to be used as a value (and as a key, in case of the hash-map).
Vectors don&amp;rsquo;t have the same problem that Lua sequential tables have, where length is not well-defined if the table has holes in it.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a debate for another time, whether allowing &lt;code&gt;nil&lt;/code&gt; as a value (and especially as a key) is a good decision to make, but Clojure already made it for me.
So for this project I decided to support it.&lt;/p&gt;
&lt;h3 id=&#34;persistent-red-black-tree&#34;&gt;Persistent Red-Black Tree&lt;/h3&gt;
&lt;p&gt;For sorted maps and sorted sets I chose Okasaki&amp;rsquo;s insertion and Germane &amp;amp; Might&amp;rsquo;s deletion algorithms.
Most of the knowledge I got from this amazing &lt;a href=&#34;https://matt.might.net/articles/red-black-delete/&#34; target=&#34;_blank&#34;&gt;blog post&lt;/a&gt; by Matt Might.&lt;/p&gt;
&lt;p&gt;I believe the operations are O(Log N), as for any binary tree, but given that the deletion algorithm is tricky, I&amp;rsquo;m not exactly sure:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Median time over 7 rounds (1 warmup discarded), N = 50000 elements.
GC stopped during measurement. Clock: os.clock (CPU).
Runtime: Fennel 1.7.0-dev on PUC Lua 5.5&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;PersistentTreeMap&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 random keys&lt;/td&gt;
&lt;td&gt;2.10 ms&lt;/td&gt;
&lt;td&gt;209.23 ms&lt;/td&gt;
&lt;td&gt;99.8x slower&lt;/td&gt;
&lt;td&gt;4.2 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup 50000 random keys&lt;/td&gt;
&lt;td&gt;0.88 ms&lt;/td&gt;
&lt;td&gt;82.97 ms&lt;/td&gt;
&lt;td&gt;94.2x slower&lt;/td&gt;
&lt;td&gt;1.7 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete all&lt;/td&gt;
&lt;td&gt;0.74 ms&lt;/td&gt;
&lt;td&gt;173.76 ms&lt;/td&gt;
&lt;td&gt;234.8x slower&lt;/td&gt;
&lt;td&gt;3.5 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;On LuaJIT:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Median time over 7 rounds (1 warmup discarded), N = 50000 elements.
GC stopped during measurement. Clock: os.clock (CPU).
Runtime: Fennel 1.7.0-dev on LuaJIT 2.1.1774896198 macOS/arm64&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Lua table&lt;/th&gt;
&lt;th&gt;PersistentTreeMap&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;per op&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;insert 50000 random keys&lt;/td&gt;
&lt;td&gt;0.72 ms&lt;/td&gt;
&lt;td&gt;101.08 ms&lt;/td&gt;
&lt;td&gt;140.4x slower&lt;/td&gt;
&lt;td&gt;2.0 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lookup 50000 random keys&lt;/td&gt;
&lt;td&gt;0.25 ms&lt;/td&gt;
&lt;td&gt;12.67 ms&lt;/td&gt;
&lt;td&gt;49.9x slower&lt;/td&gt;
&lt;td&gt;0.253 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete all&lt;/td&gt;
&lt;td&gt;0.14 ms&lt;/td&gt;
&lt;td&gt;56.14 ms&lt;/td&gt;
&lt;td&gt;403.9x slower&lt;/td&gt;
&lt;td&gt;1.1 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The API for sorted maps and sets is the same as to their hash counterparts with a small difference - no transients.
Clojure doesn&amp;rsquo;t do them, and I&amp;rsquo;m not doing them too.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all for benchmarks.
I know that there are many problems with this kind of benchmarking, so take it with a grain of salt.
Still, the results are far, far better than what I had with &lt;code&gt;itable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But there are two more data structures to talk about.&lt;/p&gt;
&lt;h3 id=&#34;persistent-list&#34;&gt;Persistent List&lt;/h3&gt;
&lt;p&gt;As I mentioned, I made a lazy persistent list implementation a while ago but it had its problems and I couldn&amp;rsquo;t integrate that library with the current one well enough.&lt;/p&gt;
&lt;p&gt;The main problem was that this library uses a single shared metatable per data structure, and the old implementation of lazy lists didn&amp;rsquo;t.
This difference makes it hard to check whether the object is a table, hash-map, list, vector, set, etc.
So I reimplemented them.&lt;/p&gt;
&lt;p&gt;The reason for old implementation to use different metatables was because I decided to try the approach described in &lt;a href=&#34;https://aphyr.com/posts/340-reversing-the-technical-interview&#34; target=&#34;_blank&#34;&gt;Reversing the technical interview&lt;/a&gt; post by Kyle Kingsbury (Aphyr).
I know this post is more of a fun joke, but it actually makes sense to define linked lists like that in Lua.&lt;/p&gt;
&lt;p&gt;See, tables are mutable, and you can&amp;rsquo;t do much about it.
Closures, on the other hand are much harder to mutate - you can still do it via the &lt;code&gt;debug&lt;/code&gt; module, but it&amp;rsquo;s hard, and it&amp;rsquo;s not always present.
So storing &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; in function closures was a deliberate choice.&lt;/p&gt;
&lt;p&gt;However, it meant that I needed to somehow attach metadata to the function, to make it act like a data structure, and you can&amp;rsquo;t just use &lt;code&gt;setmetatable&lt;/code&gt; on a function.
Again, you can do &lt;code&gt;debug.setmetatable&lt;/code&gt; but all function objects share the same metadata table.
So, while you can do fancy things like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;fn &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;comp&lt;/span&gt; [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;g&lt;/span&gt;] (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;fn &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;...&lt;/span&gt;] (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;g&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;...&lt;/span&gt;))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;lt;function&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;0&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x7bdb320a0&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;debug.setmetatable &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;fn &lt;/span&gt;[]) {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:__add&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;comp&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;lt;function&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;: &lt;/span&gt;0&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x7bd17f040&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; ((&lt;span style=&#34;font-weight:bold&#34;&gt;+ &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;string.reverse &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;string.upper&lt;/span&gt;) &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;OOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also notice, that our &lt;code&gt;+&lt;/code&gt; overload applied to functions in the string module.&lt;/p&gt;
&lt;p&gt;So instead, we use a table, and wrap it with a metatable that has a &lt;code&gt;__call&lt;/code&gt; metamethod, essentially making our table act like a function.
This, in turn means, that we have to create two tables per list node - one to give to the user, the other to set our &lt;code&gt;__call&lt;/code&gt; and use it as a meta-table.&lt;/p&gt;
&lt;p&gt;Convoluted, I know.
It&amp;rsquo;s all in the past now - current implementation is a simple &lt;code&gt;{:head 42 :tail {...}}&lt;/code&gt; table.
Not sure what is worse.&lt;/p&gt;
&lt;p&gt;But that meant that I had to rework how lazy lists worked, because previously it was just a metatable swap.
Now list stores a &amp;ldquo;thunk&amp;rdquo;, that when called replaces itself in the node with the &lt;code&gt;:head&lt;/code&gt; and &lt;code&gt;:tail&lt;/code&gt; keys.
Unless it&amp;rsquo;s an empty list, of course - in that case we swap the metatable to an empty list one.&lt;/p&gt;
&lt;p&gt;So Lists have three metatables now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IPersistentList&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IPersistentList$Empty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IPersistentList$Lazy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of god knows how many in the old implementation.&lt;/p&gt;
&lt;p&gt;The list interface is also better now.
Previously it was hardcoded how to construct a list from a data structure.
Current implementation also hardcodes it, but also allows to build a list in a lazy way from an iterator.&lt;/p&gt;
&lt;p&gt;This is better, because now a custom data structure that has weird iteration schema (like maps and sets in this library), we still can convert it to a list.
A general case is just:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fennel&#34; data-lang=&#34;fennel&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;PersistentList.from-iterator&lt;/span&gt; #(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;pairs &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;data&lt;/span&gt;) (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;fn &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;v&lt;/span&gt;] &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;v&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Meaning that we pass a function that will produce the iterator, and a function to capture values from that iterator.
Reminds me of clojure transducers in some way.&lt;/p&gt;
&lt;h3 id=&#34;persistent-queue&#34;&gt;Persistent Queue&lt;/h3&gt;
&lt;p&gt;And the final data structure - a persistent queue.
Fast append at the end, and also fast remove from the front.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s done by holding two collections - a linked list at the front, and a persistent vector for the rear.
So removing from the list is O(1), and appending to the vector is also pretty much O(1).&lt;/p&gt;
&lt;p&gt;Interesting things start to happen when we exhaust the list part - we need to move vector&amp;rsquo;s contents into the list.
It is done by calling &lt;code&gt;PersistentList.from&lt;/code&gt; on the rear.
And building a list out of a persistent vector is an O(1) operation as well!
Well, because nothing happens, we simply create an iterator, and build the list in a lazy way.
But since indexing the vector is essentially ~O(1), we can say that we still retain this property.&lt;/p&gt;
&lt;p&gt;Or at least that&amp;rsquo;s how I reasoned about this - I&amp;rsquo;m not that good with time-complexity stuff.&lt;/p&gt;
&lt;h2 id=&#34;clojurefnl&#34;&gt;ClojureFnl&lt;/h2&gt;
&lt;p&gt;That concludes part one about ClojureFnl.&lt;/p&gt;
&lt;p&gt;I know that this post was not about ClojureFnl at all, but I had to fix my underlying implementation first.
Now, that I have better data structures to build from, I can get back working on the compiler itself.
So the next post will hopefully be about the compiler itself.&lt;/p&gt;
&lt;p&gt;Unless I get distracted again.&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Clojure on Fennel part one: Persistent Data Structures&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Tue, 07 Apr 2026 02:47:00 +0300</pubDate>
    </item>
  </channel>
</rss>
