<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Terts Diepraam</title>
    <subtitle>Personal website of Terts Diepraam</subtitle>
    <link rel="self" type="application/atom+xml" href="https://terts.dev/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://terts.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-03-18T00:00:00+00:00</updated>
    <id>https://terts.dev/atom.xml</id>
    <entry xml:lang="en">
        <title>No Semicolons Needed</title>
        <published>2026-03-18T00:00:00+00:00</published>
        <updated>2026-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://terts.dev/blog/no-semicolons-needed/"/>
        <id>https://terts.dev/blog/no-semicolons-needed/</id>
        
        <content type="html" xml:base="https://terts.dev/blog/no-semicolons-needed/">&lt;p&gt;I&#x27;m making a scripting language called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;NLnetLabs&#x2F;roto&quot;&gt;Roto&lt;&#x2F;a&gt;.
Like so many programming languages before it, it has the goal of being easy to
use and read. Many languages end up making semicolons to delimit or terminate
statements optional to that end. I want that too!&lt;&#x2F;p&gt;
&lt;p&gt;This sounds simple, but how do they implement that? How do they decide where
a statement ends without an explicit terminator? To illustrate the problem, we
can take an expression and format it a bit weirdly. We can start with an example
in Rust:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; u32&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; u32&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;          -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In Rust, that is perfectly unambiguous. Now let&#x27;s do the same in Python:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable z-parameter z-function z-language z-python&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We get an &quot;unexpected indent&quot; error! Since Python doesn&#x27;t require semicolons, it
gets confused. As it turns out, many languages have different solutions to this
problem. Here&#x27;s Gleam, for instance:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;          -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s allowed! And if we &lt;code&gt;echo foo(4)&lt;&#x2F;code&gt; we get &lt;code&gt;5&lt;&#x2F;code&gt; just like in Rust. So, how
does Gleam determine that the expression continues on the second line?&lt;&#x2F;p&gt;
&lt;p&gt;I think these differences are important, especially when you&#x27;re interested
in programming language design. The syntax of the language need to be intuitive
and clear for the programmer, so that they can confidently explain how an
expression gets parsed.&lt;&#x2F;p&gt;
&lt;p&gt;Usually, those syntactic rules are obvious; in many languages, function
arguments are delimited by &lt;code&gt;()&lt;&#x2F;code&gt;. The rules for newline-separated statements are
often vaguer and differ from language to language. Users are often told either
to defensively put semicolons in their code or not to worry about it. Both seem
like (minor) failures of language design to me.&lt;&#x2F;p&gt;
&lt;p&gt;How then do I find an approach for Roto that doesn&#x27;t have these problems? I
decided that my best course of action was to look at what 11 (!) languages are
doing and how their approaches stack up. This post is that exploration. I don&#x27;t
really have an answer on what&#x27;s best, but I hope this is still be an informative
overview.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;note&quot;&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;&#x2F;strong&gt;: I&#x27;m not fluent in all the languages below, in fact, some of them I&#x27;ve
barely used. I&#x27;ve tried to cite sources where possible but I might still have
gotten some details wrong. Let me know if you find any mistakes!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;contents&quot;&gt;Contents&lt;&#x2F;h1&gt;
&lt;p&gt;This is a long post, so I&#x27;d understand if you just want to jump to your favorite
language, so here are links to all the sections:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#python&quot;&gt;Python&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#go&quot;&gt;Go&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#kotlin&quot;&gt;Kotlin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#swift&quot;&gt;Swift&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#javascript&quot;&gt;JavaScript&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#gleam&quot;&gt;Gleam&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#lua&quot;&gt;Lua&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#ruby&quot;&gt;Ruby&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#r&quot;&gt;R&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#julia&quot;&gt;Julia&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;#odin&quot;&gt;Odin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h1 id=&quot;languages&quot;&gt;Languages&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;python&quot;&gt;Python&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start with the language most famous for whitespace sensitivity ([citation
needed]). Python assumes that one line is one statement. In its
&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;python-ref&quot;&gt;grammar&lt;&#x2F;a&gt;, it describes what it calls &lt;em&gt;logical lines&lt;&#x2F;em&gt;. These are
constructed from one or more &lt;em&gt;physical lines&lt;&#x2F;em&gt;, i.e. the lines you see in your
editor.&lt;&#x2F;p&gt;
&lt;p&gt;There are 2 ways that physical lines can be joined:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;either &lt;strong&gt;explicitly&lt;&#x2F;strong&gt; with the &lt;code&gt;\&lt;&#x2F;code&gt; token,&lt;&#x2F;li&gt;
&lt;li&gt;or &lt;strong&gt;implicitly&lt;&#x2F;strong&gt; while the end of the line is enclosed between delimiters
such as &lt;code&gt;()&lt;&#x2F;code&gt;, &lt;code&gt;[]&lt;&#x2F;code&gt;, &lt;code&gt;{}&lt;&#x2F;code&gt; or triple quotes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The reference gives these examples:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Explicit joining&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1900&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; year&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-python&quot;&gt; and&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; month&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 12&lt;&#x2F;span&gt;&lt;span&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-python&quot;&gt;   and&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; day&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 31&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-python&quot;&gt; and&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; hour&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 24&lt;&#x2F;span&gt;&lt;span&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-python&quot;&gt;   and&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; minute&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 60&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-python&quot;&gt; and&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; second&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 60&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Looks like a valid date&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Implicit joining&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;month_names&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Januari&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Februari&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Maart&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;      #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; These are the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;               &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;April&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;   &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Mei&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;      &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Juni&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;       #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Dutch names&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;               &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Juli&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Augustus&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;September&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; for the months&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;               &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Oktober&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;November&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;December&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; of the year&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Having only these rules would be fairly error prone. You can see this by
considering the example from the introduction again:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you forget to put a backslash at the end of the first line, Python would
simply treat that as two statements. Luckily, Python has a solution: it strictly
enforces correct indentation. Since the &lt;code&gt;- 3&lt;&#x2F;code&gt; is on a new line, it must have the
same indentation as the line before.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s consider the consequences of Python&#x27;s approach. It is quite principled
and strict about its statement separation. It is also very unambiguous.
It&#x27;s easy to keep the rule of &quot;one line, one statement&quot; in your head while
programming with the two exceptions being quite explicit.&lt;&#x2F;p&gt;
&lt;p&gt;A somewhat ironic consequence for an indentation-based language, however,
is that Python&#x27;s rules have encouraged the community to embrace explicit
delimiters. For example, the ubiquitous code formatters &lt;code&gt;black&lt;&#x2F;code&gt; and &lt;code&gt;ruff&lt;&#x2F;code&gt; both
prefer parentheses over backslashes.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &amp;quot;Unidiomatic&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  +&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  +&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  +&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &amp;quot;Idiomatic&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;    long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;    +&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;    +&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;    +&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; long_function_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I think Python&#x27;s system is pretty good! It&#x27;s simple, it&#x27;s clear and the
indentation rules are likely to catch any mistakes. From my time writing Python,
I don&#x27;t remember this getting in my way much, except for sometimes having to
wrap expressions in &lt;code&gt;()&lt;&#x2F;code&gt;. I was never really surprised by this behavior.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;python-ref&quot;&gt;Python Reference&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;black.readthedocs.io&#x2F;en&#x2F;stable&#x2F;the_black_code_style&#x2F;current_style.html#how-black-wraps-lines&quot;&gt;Black documentation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;go&quot;&gt;Go&lt;&#x2F;h2&gt;
&lt;p&gt;Go&#x27;s approach is very different from Python&#x27;s. Go&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;doc&#x2F;effective_go#semicolons&quot;&gt;official book&lt;&#x2F;a&gt;
states:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Like C, Go&#x27;s formal grammar uses semicolons to terminate statements, but
unlike in C, those semicolons do not appear in the source. Instead, the lexer
uses a simple rule to insert semicolons automatically as it scans, so the
input text is mostly free of them.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The first thing that I dislike about this is that it encourages thinking of
semicolons being inserted instead of statements being terminated. I find that
to be a roundabout way of thinking about the problem. But alas, this is what
we&#x27;re dealing with. I want to highlight something in that text: the semicolons
are inserted by the &lt;em&gt;lexer&lt;&#x2F;em&gt;. The reasoning behind this is this that it keeps the
rule for automatic semicolon insertion are very simple.&lt;&#x2F;p&gt;
&lt;p&gt;Go&#x27;s lexer inserts a semicolon after the following tokens if they appear just
before a newline or a &lt;code&gt;}&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;an identifier,&lt;&#x2F;li&gt;
&lt;li&gt;a basic literal,&lt;&#x2F;li&gt;
&lt;li&gt;or one of &lt;code&gt;break&lt;&#x2F;code&gt;, &lt;code&gt;continue&lt;&#x2F;code&gt;, &lt;code&gt;fallthrough&lt;&#x2F;code&gt;, &lt;code&gt;return&lt;&#x2F;code&gt;, &lt;code&gt;++&lt;&#x2F;code&gt;, &lt;code&gt;--&lt;&#x2F;code&gt;, &lt;code&gt;)&lt;&#x2F;code&gt;
or &lt;code&gt;}&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Simple enough! Let&#x27;s go to our introductory example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment z-go&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment z-go&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic z-go&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic z-go&quot;&gt;   -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Those lines end with numbers so the lexer inserts semicolons:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment z-go&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment z-go&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic z-go&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic z-go&quot;&gt;   -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Just like Python, that seems error prone! But as we run this, Go has a nice
surprise in the form of an error (not just a warning!):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-3 (untyped int constant) is not used&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, it has some guardrails in place to prevent mistakes. Even when I replace &lt;code&gt;-3&lt;&#x2F;code&gt;
with more complex expressions it usually errors on unused values that might
occur by accident. That&#x27;s good!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;blog&#x2F;no-semicolons-needed&#x2F;go-medium&quot;&gt;This post&lt;&#x2F;a&gt; gives us an example that doesn&#x27;t error and where the
newline changes behavior. It first requires a bit of setup:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; g&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; f&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Inner func called&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then these snippets have a different meaning:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;m not too worried about this to be honest; it looks like this requires pretty
convoluted code to be considered ambiguous.&lt;&#x2F;p&gt;
&lt;p&gt;Now remember that this semicolon insertion is done entirely by the lexer. That
means that semicolons sometimes get inserted at unexpected places:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &amp;lt;- semicolon inserted here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;  ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;     &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &amp;lt;- semicolon inserted here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Both of these result in parse errors. The fix is to adhere to Go&#x27;s mandatory
formatting style:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;  ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; or&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  x&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s fair, even if it seems a little pedantic. I like these formatting
choices, but I&#x27;d prefer if the &quot;wrong style&quot; was still syntactically valid and
a formatter would be able to fix it. As it stands with Go, its formatter also
errors on these invalid snippets. This strictness also seems to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;7062276&#x2F;why-do-i-get-an-error-when-putting-opening-braces-on-the-next-line-in-go&quot;&gt;lead to
confusion for newcomers&lt;&#x2F;a&gt; every once in a while, particularly if they come
from languages like Java, where braces are often put on a separate line.&lt;&#x2F;p&gt;
&lt;p&gt;So, Go&#x27;s approach is simple, but in my opinion not very friendly. It is
saved by disallowing some unused values, but I&#x27;m not competent enough with
writing Go to evaluate whether that covers all ambiguous cases.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;doc&#x2F;effective_go#semicolons&quot;&gt;Effective Go&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.codegenes.net&#x2F;blog&#x2F;syntax-error-unexpected-semicolon-or-newline-expecting&#x2F;&quot;&gt;Blog post on codegenes.net&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;golangspec&#x2F;automatic-semicolon-insertion-in-go-1990338f2649&quot;&gt;Automatic semicolon insertion in Go&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;7062276&#x2F;why-do-i-get-an-error-when-putting-opening-braces-on-the-next-line-in-go&quot;&gt;Stack Overflow: Why do I get an error when putting opening braces on the next line in Go?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;kotlin&quot;&gt;Kotlin&lt;&#x2F;h2&gt;
&lt;p&gt;As far as I can tell, Kotlin does not have simple &quot;rules&quot; for when a newline
separates two statements, like Python and Go have. Instead, it makes newlines an
explicit part of the grammar. So, for each construct where a newline is allowed,
it opts into it explicitly. I&#x27;ll spare you the BNF-like notation, but it seems
to boil down to this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Statements are separated by one or more newlines or &lt;code&gt;;&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If a construct is unambiguously incomplete at the end of a line, it is
allowed to continue on the next line.&lt;&#x2F;li&gt;
&lt;li&gt;Delimited constructs (like function calls) allow newlines within them.&lt;&#x2F;li&gt;
&lt;li&gt;Newlines are not allowed before &lt;code&gt;(&lt;&#x2F;code&gt;, &lt;code&gt;[&lt;&#x2F;code&gt; or &lt;code&gt;{&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Binary operators seem to fall into two camps:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;&#x2F;code&gt;, &lt;code&gt;||&lt;&#x2F;code&gt;, &lt;code&gt;?:&lt;&#x2F;code&gt;, &lt;code&gt;as&lt;&#x2F;code&gt;, &lt;code&gt;as?&lt;&#x2F;code&gt;, &lt;code&gt;.&lt;&#x2F;code&gt; and &lt;code&gt;.?&lt;&#x2F;code&gt; allow newlines on both side of
the operator,&lt;&#x2F;li&gt;
&lt;li&gt;the rest of the operators only allow a newline &lt;em&gt;after&lt;&#x2F;em&gt; the operator.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Prefix unary operators allow a newline after themselves.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This approach of baking newline handling into the grammar gives the
language designers a lot of control, but this comes at the cost of simplicity
and transparency. This approach is like the opposite of Go&#x27;s. It can get pretty
nuanced and I can&#x27;t find a clear explanation of it.&lt;&#x2F;p&gt;
&lt;p&gt;My best attempt at summarizing this approach: an expression is allowed to
continue on the next line if that is unambiguous in the grammar.&lt;&#x2F;p&gt;
&lt;p&gt;After that theory, we try our example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(y)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives us &lt;code&gt;8&lt;&#x2F;code&gt; with an unused value warning. That makes sense because &lt;code&gt;-&lt;&#x2F;code&gt;,
&lt;code&gt;+&lt;&#x2F;code&gt; and many other infix operators only allow newlines &lt;em&gt;after&lt;&#x2F;em&gt; the operator.
However, the logical operators &lt;code&gt;&amp;amp;&amp;amp;&lt;&#x2F;code&gt; and &lt;code&gt;||&lt;&#x2F;code&gt; allow newlines on both sides.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This is one expression:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;      ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This is two expressions:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;      +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Another case where the &quot;continue if unambiguous&quot; approach gets into trouble is
when very similar operators have different rules. Kotlin has the &lt;code&gt;::&lt;&#x2F;code&gt; and &lt;code&gt;.&lt;&#x2F;code&gt;
operators to respectively access a method and a field of a class. Of these two,
&lt;code&gt;.&lt;&#x2F;code&gt; allows newlines on both sides, but &lt;code&gt;::&lt;&#x2F;code&gt; doesn&#x27;t. That is because &lt;code&gt;::&lt;&#x2F;code&gt; is
also a valid start of a callable reference expression.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .bar      &lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; one expression!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; baz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;quux&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; two expressions!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since newlines are part of the grammar explicitly and therefore plainly
disallowed in some places, I expected that this would give me an error
because &lt;code&gt;+&lt;&#x2F;code&gt; only allows newlines after the operator in the grammar:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;    1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;    +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But it works! I think it makes sense that they added this behavior, but I cannot
find traces of this behavior in the specification. If somebody could show me
where this is documented, I&#x27;d love to see it!&lt;&#x2F;p&gt;
&lt;p&gt;The vibe that I get from this implementation is that Kotlin&#x27;s designers try
really hard to make the behavior intuitive, regardless of how many rules and
exceptions they need. I guess that if people never run into problems with it,
then they don&#x27;t need to understand it fully either. I&#x27;m not sure I agree with
this fully, but it&#x27;s a somewhat reasonable position.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;39318457&#x2F;what-are-the-rules-of-semicolon-inference&quot;&gt;This Stack Overflow answer&lt;&#x2F;a&gt; echoes that sentiment:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The rule is: Don&#x27;t worry about this and don&#x27;t use semicolons at all [...].
The compiler will tell you when you get it wrong, guaranteed. Even if you
accidentally add an extra semicolon the syntax highlighting will show you it
is unnecessary with a warning of &quot;redundant semicolon&quot;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;We could characterize this approach &quot;don&#x27;t worry, your IDE will fix it&quot; and I
guess that&#x27;s fair when the company behind the language creates IDEs. Although if
that is truly the consensus in the community, they&#x27;ve done a pretty good job!&lt;&#x2F;p&gt;
&lt;p&gt;Another potential problem might be that all these complex rules might make it
difficult to write custom parsers for Kotlin. I wouldn&#x27;t want to be the person
responsible for maintaining its tree-sitter grammar for instance.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kotlinlang.org&#x2F;spec&#x2F;syntax-and-grammar.html&quot;&gt;Kotlin language specification: Syntax and Grammar&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;39318457&#x2F;what-are-the-rules-of-semicolon-inference&quot;&gt;Stack Overflow: What are the rules of semicolon inference?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;swift&quot;&gt;Swift&lt;&#x2F;h2&gt;
&lt;p&gt;There is a somewhat obvious approach that hasn&#x27;t come up yet: just parse as far
as you can ignoring newlines. Swift takes that approach and it&#x27;s not hard to see why:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That prints &lt;code&gt;5&lt;&#x2F;code&gt; as we would expect. The downside is that this prints &lt;code&gt;5&lt;&#x2F;code&gt; too:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But that&#x27;s not too bad if you just have the rule that the language does not
have significant whitespace. That&#x27;s a rule people should be able to remember.
Interestingly, Swift does have &lt;em&gt;some&lt;&#x2F;em&gt; significant whitespace to prevent
mistakes. For example, it is not allowed to put multiple statements on a single
line:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; error!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;They seem to have decided to ignore that in their grammar specification, but it
is part of the compiler.&lt;&#x2F;p&gt;
&lt;p&gt;With this approach, the most confusing examples I can find are around symbols
that can be both unary and binary operators (our eternal nemesis). This snippet
prints &lt;code&gt;8&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;      -3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Why? Because Swift has some special rules for parsing operators. If an operator
has whitespace on both or neither side, it&#x27;s parsed as an infix operator. If it
has only whitespace on the left, it&#x27;s a prefix operator. And finally, if only
has whitespace on the right, it&#x27;s a postfix operator. This means that this also
parses as two statements:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-any-method&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Swift&#x27;s designers seem to be aware of this problem (obviously) and therefore
emit a warning on unused values, which would trigger on the example above. That
should catch most erroneous cases.&lt;&#x2F;p&gt;
&lt;p&gt;Another tweak they made is that the parentheses of a function call must be on
the same line as the name of the function. If it isn&#x27;t, then expression will
end after the first line. For example, The snippet below is parsed as two lines.
They check whether the &lt;code&gt;(&lt;&#x2F;code&gt; is at the start of the line and do not continue
parsing if that&#x27;s the case. The same is also done for &lt;code&gt;[&lt;&#x2F;code&gt;. This is a pretty
good rule! You can check the JavaScript section to see how a language can get
this wrong.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s also worth discussing error reporting for syntax errors. Swift cannot
easily guess where a statement is supposed to end if the syntax isn&#x27;t correct.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This snippet is obviously wrong, because there&#x27;s a missing &lt;code&gt;)&lt;&#x2F;code&gt;, but Swift
instead complains about a missing &lt;code&gt;,&lt;&#x2F;code&gt; and a &lt;em&gt;circular reference&lt;&#x2F;em&gt; because we&#x27;re
using &lt;code&gt;y&lt;&#x2F;code&gt; before we declare it. It does that because it doesn&#x27;t know that
the statement was supposed to end. Now, to be fair, I found only &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;softwareengineering.stackexchange.com&#x2F;questions&#x2F;291987&#x2F;why-does-swift-not-require-semicolons#comment811665_291989&quot;&gt;1 comment&lt;&#x2F;a&gt;
complaining about that, so it might not be a big deal. I haven&#x27;t written enough
Swift code to judge.&lt;&#x2F;p&gt;
&lt;p&gt;I like this approach a lot. It seems intuitive yet simple to understand and
debug. The error messages might take a bit of a hit compared to languages with
explicit semicolons, but you also get no &quot;missing semicolon&quot; errors so that&#x27;s
a bit of a trade-off.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.swift.org&#x2F;swift-book&#x2F;documentation&#x2F;the-swift-programming-language&#x2F;statements&quot;&gt;Swift Language Reference: Statements&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.swift.org&#x2F;swift-book&#x2F;documentation&#x2F;the-swift-programming-language&#x2F;lexicalstructure#Operators&quot;&gt;Swift Language Reference: Lexical Structure: Operators&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forums.swift.org&#x2F;t&#x2F;what-are-the-rules-of-automatic-semicolon-insertion-in-swift&#x2F;43532&quot;&gt;Swift Forum: What are the rules of semicolon insertion in Swift?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;swiftlang&#x2F;swift&#x2F;blob&#x2F;46e93980d4489345e7125eeec98d90c02a794b63&#x2F;include&#x2F;swift&#x2F;Parse&#x2F;Token.h#L231&quot;&gt;Swift&#x27;s parser&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;javascript&quot;&gt;JavaScript&lt;&#x2F;h2&gt;
&lt;p&gt;JavaScript seems to be the language that has given Automatic Semicolon Insertion
a bad reputation. Its rules are pretty complex, but luckily there&#x27;s an excellent
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Lexical_grammar#automatic_semicolon_insertion&quot;&gt;MDN article&lt;&#x2F;a&gt; about it.&lt;&#x2F;p&gt;
&lt;p&gt;There are three important cases where a semicolon is inserted:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If a token is encountered that is not allowed by the grammar that either&lt;&#x2F;p&gt;
&lt;p&gt;a. is separated by at least one newline with the previous token, or&lt;&#x2F;p&gt;
&lt;p&gt;b. if the token is &lt;code&gt;}&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;If the end of the input is reached and that is not allowed by the grammar.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;If a newline is encountered in certain expressions such as after &lt;code&gt;return&lt;&#x2F;code&gt;,
&lt;code&gt;break&lt;&#x2F;code&gt; or &lt;code&gt;continue&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Note that this is not everything! There are many exceptions to these rules, such
as that no semicolons are inserted in the &lt;code&gt;for&lt;&#x2F;code&gt; statement&#x27;s head and that no
semicolon is inserted in places where it creates an empty expression.&lt;&#x2F;p&gt;
&lt;p&gt;All in all, this means that our example is parsed as one line:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js z-variable z-other z-constant&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-js z-variable z-other z-readwrite&quot;&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;        -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; is parsed as&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js z-variable z-other z-constant&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-js z-variable z-other z-readwrite&quot;&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;        -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-js&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The complexity of these rules is kind of a problem in itself as these rules
are hard to remember. The worst part of this feature that the first rule only
triggers on invalid syntax. The MDN article is full of examples where this goes
wrong, such as these snippets, which are both parsed as a single line:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js z-variable z-other z-constant&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;     &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &amp;lt;- no semicolon inserted!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-meta z-brace z-round z-js&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-js&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-js&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-js&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js z-variable z-other z-constant&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;     &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &amp;lt;- and also not here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-meta z-brace z-square z-js z-meta z-brace z-square&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-square z-js z-meta z-brace z-square&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;forEach&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-js&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-js z-variable z-other z-object&quot;&gt;console&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;log&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-js&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you want to code without semicolons in JS, you have to think about whether
consecutive lines would be valid syntax if they were joined. Or you have to
learn a whole lot of rules such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Never put the operand of &lt;code&gt;return&lt;&#x2F;code&gt;, &lt;code&gt;break&lt;&#x2F;code&gt;, etc. on a separate line.&lt;&#x2F;li&gt;
&lt;li&gt;If a line starts with one of &lt;code&gt;(&lt;&#x2F;code&gt;, &lt;code&gt;[&lt;&#x2F;code&gt;, &lt;code&gt;`&lt;&#x2F;code&gt;, &lt;code&gt;+&lt;&#x2F;code&gt;, &lt;code&gt;-&lt;&#x2F;code&gt;, &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;, prefix it with
a semicolon, or end the previous line with a semicolon.&lt;&#x2F;li&gt;
&lt;li&gt;And more!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;No wonder that many people just opt to write the semicolons in JS. Take for
instance this quote from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;javascript-the-good&#x2F;9780596517748&#x2F;apas03.html&quot;&gt;JavaScript: The Good Parts&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;JavaScript has a mechanism that tries to correct faulty programs by
automatically inserting semicolons. Do not depend on this. It can mask more
serious errors.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In conclusion, you could write JS without semicolons, but the fact that many
people recommend you always add semicolons is quite damning. I haven&#x27;t seen that
sentiment with the other languages in this post and it means that the feature
does more harm than good. This feature is too complex doesn&#x27;t even manage to be
robust. Quite honestly, this feature is a disaster.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Lexical_grammar#automatic_semicolon_insertion&quot;&gt;MDN: Lexical Grammar&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;javascript-the-good&#x2F;9780596517748&#x2F;apas03.html&quot;&gt;JavaScript: The Good Parts&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;gleam&quot;&gt;Gleam&lt;&#x2F;h2&gt;
&lt;p&gt;Gleam&#x27;s approach is very similar to Swift&#x27;s: it also just parses the expressions
until they naturally end. Swift had a few exceptions to this though, so let&#x27;s
investigate what Gleam does.&lt;&#x2F;p&gt;
&lt;p&gt;First, we can look at our recurring example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As we might expect, that&#x27;s parsed as one expression. However, we can remove one
space to change that:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Kind of like Swift, Gleam seems to parse the &lt;code&gt;-3&lt;&#x2F;code&gt; as a single token if it is
preceded by whitespace and as a binary operator otherwise. I couldn&#x27;t find a
source for this so the details might be off here.&lt;&#x2F;p&gt;
&lt;p&gt;Gleam&#x27;s approach of parsing everything regardless of whitespace does have some
strange consequences. For example, this is accepted and parses as 2 expressions:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;  1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I would personally require a newline there if I was designing Gleam, but this
is technically unambiguous. Gleam&#x27;s formatter will also put the expressions on
separate lines and Gleam will warn you about an unused value, so you&#x27;ll notice
that something&#x27;s off soon enough.&lt;&#x2F;p&gt;
&lt;p&gt;This is parsed as one expression, i.e. a function call:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now if you&#x27;ve written any Gleam, you might be yelling at your screen: &quot;That
isn&#x27;t ambiguous!&quot; And you&#x27;d be right; it can only be a function call, because
Gleam uses &lt;code&gt;{}&lt;&#x2F;code&gt; for grouping expressions. So, if we use &lt;code&gt;{}&lt;&#x2F;code&gt; it&#x27;s not a function
call anymore:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  { &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In another stroke of genius ambiguity prevention, Gleam doesn&#x27;t have list
indexing with &lt;code&gt;[]&lt;&#x2F;code&gt;. So this is also parsed as two expressions:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;gleam&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  foo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [ &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt; ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s interesting that Gleam doesn&#x27;t have the same guardrails that Swift has.
It gets away with that by having a very unambiguous grammar. This is very
impressive language design. Its rules are also pretty easy to grasp, so it looks
like a pretty good implementation to me.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tour.gleam.run&#x2F;everything&#x2F;&quot;&gt;Gleam Language Tour&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;lua&quot;&gt;Lua&lt;&#x2F;h2&gt;
&lt;p&gt;Speaking of languages that just parse the thing as far as they can, Lua does
that too! The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lua.org&#x2F;pil&#x2F;1.1.html&quot;&gt;book&lt;&#x2F;a&gt; says:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A semicolon may optionally follow any statement. Usually, I use semicolons
only to separate two or more statements written in the same line, but this is
just a convention. Line breaks play no role in Lua&#x27;s syntax[.]&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This means that it basically works like Gleam! What sets it apart is that it
does have indexing with &lt;code&gt;[]&lt;&#x2F;code&gt; and groups expressions with &lt;code&gt;()&lt;&#x2F;code&gt;. Here&#x27;s an
example that requires a semicolon to prevent it being parsed as a single
statement:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; end&lt;&#x2F;span&gt;&lt;span&gt;)(); &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; semicolon is required here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; end&lt;&#x2F;span&gt;&lt;span&gt;)()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There might be even more problematic cases, but I&#x27;m not experienced enough with
Lua to find them.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lua.org&#x2F;pil&#x2F;1.1.html&quot;&gt;Programming in Lua&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;r&quot;&gt;R&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve seen before that some languages insert semicolons when reading further
would be invalid. R sort of takes the opposite approach: it inserts a semicolon
when the grammar allows it. Here&#x27;s the official explanation from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cran.r-project.org&#x2F;doc&#x2F;manuals&#x2F;r-release&#x2F;R-lang.html#Separators-1&quot;&gt;R Language
Definition&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Newlines have a function which is a combination of token separator and
expression terminator. If an expression can terminate at the end of the line the
parser will assume it does so, otherwise the newline is treated as whitespace.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;There&#x27;s one exception to this rule, which is that the &lt;code&gt;else&lt;&#x2F;code&gt; keyword can appear
on a separate line.&lt;&#x2F;p&gt;
&lt;p&gt;That approach is somewhat reminiscent of Python&#x27;s. However, R allows expressions
to continue to the next line if they are incomplete. Our recurring example
would parse as two expressions because the grammar allows the expression to end
after the &lt;code&gt;x&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;r&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But with a slight modification it parses as one expression:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;r&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;    3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The result is that you&#x27;d almost never have to worry about the next expression
being parsed as part of the former. They are only joined explicitly, for example
with parentheses or trailing operators. On the downside, I would generally
prefer to write the operator at the start of the next line, which we can only do
if we wrap the expression in parentheses (just like with Python).&lt;&#x2F;p&gt;
&lt;p&gt;It looks like a pretty good approach. I like that the newline has some semantic
meaning and it doesn&#x27;t feel confusing.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cran.r-project.org&#x2F;doc&#x2F;manuals&#x2F;r-release&#x2F;R-lang.html#Separators-1&quot;&gt;R Language Definition&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;ruby&quot;&gt;Ruby&lt;&#x2F;h2&gt;
&lt;p&gt;Another famously semicolonless language is of course Ruby. It has a very similar
approach to R, but — as is becoming a bit of a theme — not quite the same. Like
R, it splits statements by lines, but allows the expression to continue if it&#x27;s
incomplete. So we can basically copy our examples for R verbatim:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 2 expressions&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 1 expression&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;-&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;    3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But Ruby has a few more tricks up its sleeve. First, you can end a line with
&lt;code&gt;\&lt;&#x2F;code&gt; to explicitly continue the expression on the next line, kind of like Python.
Second, it has a special rule that lines starting with a &lt;code&gt;.&lt;&#x2F;code&gt;, &lt;code&gt;&amp;amp;&amp;amp;&lt;&#x2F;code&gt; or &lt;code&gt;||&lt;&#x2F;code&gt; are
a continuation of the line before. It does that to allow method chaining and
logical chains.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-class&quot;&gt;File&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;read&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;strip&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sort&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-class&quot;&gt;File&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;empty?&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;  ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-class&quot;&gt; File&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;size&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;  ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-class&quot;&gt; File&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;read&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;strip&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;empty?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I find this slightly confusing, because it&#x27;s strange that some operators can
start the next statement but not all of them. I guess it&#x27;s not too bad to
remember 3 exceptions. So, it looks pretty good!&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.ruby-lang.org&#x2F;en&#x2F;4.0&#x2F;syntax&#x2F;layout_rdoc.html&quot;&gt;Ruby Documentation: Layout&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;julia&quot;&gt;Julia&lt;&#x2F;h2&gt;
&lt;p&gt;Documentation on how Julia&#x27;s syntax works was a bit hard to find, so I looked
at their parsing code. This means I have to guess a little bit at what the
intention is.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some things I tried:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;b &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; -&amp;gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;c &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;  4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; -&amp;gt; -1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;d &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; -&amp;gt; -1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It seems to be dependent on the kind of expression whether a newline
continues a statement. But in general, they seem to prefer splitting into
multiple lines if that is legal. The newline is really treated as a separator in
the parser. In that sense, it matches other languages with a lot of use in the
scientific community, such as Python and R.&lt;&#x2F;p&gt;
&lt;p&gt;If anybody knows where to find documentation on Julia&#x27;s syntax, let me know!&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JuliaLang&#x2F;JuliaSyntax.jl&#x2F;blob&#x2F;main&#x2F;src&#x2F;julia&#x2F;parser.jl&quot;&gt;JuliaSyntax.jl&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;odin&quot;&gt;Odin&lt;&#x2F;h2&gt;
&lt;p&gt;While I was working on this post, Odin&#x27;s creator GingerBill released a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gingerbill.org&#x2F;article&#x2F;2026&#x2F;02&#x2F;19&#x2F;choosing-a-language-based-on-syntax&#x2F;&quot;&gt;blog
post&lt;&#x2F;a&gt; that contained an explanation of Odin&#x27;s approach. What I
found particularly interesting are the reasons he cites for making semicolons
optional:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;There were two reasons I made them optional:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;To make the grammar consistent, coherent, and simpler&lt;&#x2F;li&gt;
&lt;li&gt;To honestly shut up these kinds of bizarre people&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It looks like he didn&#x27;t care much for that feature himself. What&#x27;s nice about
this post is that he lays out some reasoning for Odin&#x27;s approach. He describes
it as a mix of Python and Go, where semicolon insertion is done by the
lexer, but not within &lt;code&gt;()&lt;&#x2F;code&gt;, &lt;code&gt;{}&lt;&#x2F;code&gt; and &lt;code&gt;[]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Another exception he lays out is that Odin has a few exceptions to allow braces
to start on the next line:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;odin&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;a_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; ::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; proc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;a_procedure_declaration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; ::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; proc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-definition z-variable&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;another_procedure_declaration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; ::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; proc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;another_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; ::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; proc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; note the extra newline separating the signature from a `{`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; this is just a block&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In a way, this looks like the opposite of Go, where instead of enforcing a
certain coding style, they go out of their way to allow other coding styles
than their own. This rule seems a sign that their grammar might be a bit too
&quot;overloaded&quot;, using very similar syntax for different concepts. But hey, they
probably had good reasons to do so.&lt;&#x2F;p&gt;
&lt;p&gt;Sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gingerbill.org&#x2F;article&#x2F;2026&#x2F;02&#x2F;19&#x2F;choosing-a-language-based-on-syntax&#x2F;&quot;&gt;Choosing a Language Based on its Syntax&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;a-different-idea&quot;&gt;A Different Idea&lt;&#x2F;h2&gt;
&lt;p&gt;Here is an idea I haven&#x27;t seen being used and I wonder whether it makes sense.&lt;&#x2F;p&gt;
&lt;p&gt;The only language that seems to consider indentation at all is Python, but only
to restrict mistakes. I would love to see a language try to implement a rule
where only an indented line is considered part of the previous expression.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; two expressions!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; one expression!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This feels quite intuitive to me. I could see this being a replacement for
Python&#x27;s line joining with &lt;code&gt;\&lt;&#x2F;code&gt;. A problem, of course, is that now the
indentation always needs to be correct and many developers (myself included)
like to just have their formatter deal with the indentation. In any case,
it might be an interesting lint to consider for a language with optional
semicolons.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h1&gt;
&lt;p&gt;We made it to the end! I think the best way to summarize this document is by
grouping the languages:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Split statements on newlines, with exceptions
&lt;ul&gt;
&lt;li&gt;Python&lt;&#x2F;li&gt;
&lt;li&gt;Ruby&lt;&#x2F;li&gt;
&lt;li&gt;R&lt;&#x2F;li&gt;
&lt;li&gt;Julia&lt;&#x2F;li&gt;
&lt;li&gt;Odin&lt;&#x2F;li&gt;
&lt;li&gt;Kotlin&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Continue statements on the next line, unless that&#x27;s invalid
&lt;ul&gt;
&lt;li&gt;JavaScript&lt;&#x2F;li&gt;
&lt;li&gt;Swift&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Let the lexer insert semicolons
&lt;ul&gt;
&lt;li&gt;Go&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Do not consider whitespace while parsing
&lt;ul&gt;
&lt;li&gt;Lua&lt;&#x2F;li&gt;
&lt;li&gt;Gleam&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote class=&quot;note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;&#x2F;strong&gt;: These categories are not perfect, for some languages, you could make
the argument that they fit in multiple categories.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;You could make some other categories as well. For example, you could call
Python, Ruby, R, Julia, and Odin &lt;em&gt;conservative&lt;&#x2F;em&gt; in their parsing, they usually
stop parsing at a newline. Lua, Gleam and Swift, on the other hand, are more
&lt;em&gt;greedy&lt;&#x2F;em&gt;: they usually keep parsing across newlines as far as they can.&lt;&#x2F;p&gt;
&lt;p&gt;Another distinction to make is how it is implemented. JavaScript, Go and Odin
have at least some part of the semicolon insertion implemented in the lexer,
while many other languages make it part of the parser.&lt;&#x2F;p&gt;
&lt;p&gt;A final interesting category are the languages that are entirely insensitive
to whitespace, such as Lua and Gleam. Even though Swift gets close to this
category, it turned out to have some whitespace-sensitive rules.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;This turned out to be a much more complicated topic than I expected! While
there are approaches I like better than others, not all languages should use the
same solution, because there might be other ways that the syntax differs that
should be taken into account.&lt;&#x2F;p&gt;
&lt;p&gt;Nevertheless, I guess this is the part where I have to give my opinion about
all of this, so here are some guidelines I would use (which you may very well
disagree with):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Prefer defining clear rules over baking it into your parser.&lt;&#x2F;li&gt;
&lt;li&gt;Keep those rules as simple as possible (looking at you, JS).&lt;&#x2F;li&gt;
&lt;li&gt;Use a parser that splits on newlines in most cases, instead of continuing
expressions greedily onto the next line.&lt;&#x2F;li&gt;
&lt;li&gt;Think about the rest of your language&#x27;s syntax and what problems might arise
with your chosen approach.&lt;&#x2F;li&gt;
&lt;li&gt;Add tooling to help catch mistakes (such as warnings on unused values) to
prevent the most ambiguous cases.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Do you agree? What do you think is best? Have I missed any important languages?
Do you have cool ideas for better implementations? Let me know be responding to
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.online&#x2F;@terts&#x2F;116251236166999491&quot;&gt;this post on Mastodon&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;acknowledgments&quot;&gt;Acknowledgments&lt;&#x2F;h1&gt;
&lt;p&gt;Thanks to Thijs Vromen, waffle and Anne Stijns for proofreading drafts
of this post. Any mistakes are my own. You can send corrections to
&lt;a href=&quot;mailto:terts.diepraam@gmail.com&quot;&gt;terts.diepraam@gmail.com&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.online&#x2F;@terts&#x2F;116251236166999491&quot;&gt;on
Mastodon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;No LLMs were used while writing this piece, neither for gathering information or
for writing.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Calling JIT-compiled Roto scripts from Rust</title>
        <published>2026-02-01T00:00:00+00:00</published>
        <updated>2026-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://terts.dev/talks/roto-fosdem26/"/>
        <id>https://terts.dev/talks/roto-fosdem26/</id>
        
        <content type="html" xml:base="https://terts.dev/talks/roto-fosdem26/">&lt;h1 id=&quot;recording&quot;&gt;Recording&lt;&#x2F;h1&gt;
&lt;p&gt;Coming up!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;slides&quot;&gt;Slides&lt;&#x2F;h1&gt;
&lt;div class=&quot;wrapper&quot;&gt;
  &lt;iframe src=&quot;&#x2F;Roto @ FOSDEM.pdf&quot; width=&quot;100%&quot; height=&quot;100%&quot;&gt;
  &lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;style&gt;
  .wrapper { width: 100%; aspect-ratio: 16&#x2F;9 }
&lt;&#x2F;style&gt;
&lt;h1 id=&quot;links&quot;&gt;Links&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;Roto: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NLnetLabs&#x2F;roto&quot;&gt;repo&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;roto.docs.nlnetlabs.nl&quot;&gt;docs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Rotonda: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NLnetLabs&#x2F;rotonda&quot;&gt;repo&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rotonda.docs.nlnetlabs.nl&quot;&gt;docs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;RustWeek: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;rustweek.org&quot;&gt;website&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;NLnet Labs: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nlnetlabs.nl&quot;&gt;website&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NLnetLabs&quot;&gt;GitHub&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;social.nlnetlabs.nl&#x2F;@nlnetlabs&quot;&gt;Mastodon&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Me: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;terts.dev&quot;&gt;website&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tertsdiepraam&quot;&gt;GitHub&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.online&#x2F;@terts&quot;&gt;Mastodon&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;links&quot;&gt;more&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Roto: A fast and safe scripting language for Rust</title>
        <published>2025-10-09T00:00:00+00:00</published>
        <updated>2025-10-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://terts.dev/talks/roto-eurorust25/"/>
        <id>https://terts.dev/talks/roto-eurorust25/</id>
        
        <content type="html" xml:base="https://terts.dev/talks/roto-eurorust25/">&lt;h1 id=&quot;recording&quot;&gt;Recording&lt;&#x2F;h1&gt;
&lt;div class=&quot;wrapper&quot;&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;100%&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;7xJEFPlfy7Y?si=yw1078A0SPjklU-s&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;h1 id=&quot;slides&quot;&gt;Slides&lt;&#x2F;h1&gt;
&lt;div class=&quot;wrapper&quot;&gt;
  &lt;iframe src=&quot;&#x2F;Roto @ EuroRust.pdf&quot; width=&quot;100%&quot; height=&quot;100%&quot;&gt;
  &lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;style&gt;
  .wrapper { width: 100%; aspect-ratio: 16&#x2F;9 }
&lt;&#x2F;style&gt;
&lt;h1 id=&quot;links&quot;&gt;Links&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;Demo: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tertsdiepraam&#x2F;roto-demo&quot;&gt;repo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Roto: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NLnetLabs&#x2F;roto&quot;&gt;repo&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;roto.docs.nlnetlabs.nl&quot;&gt;docs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Rotonda: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NLnetLabs&#x2F;rotonda&quot;&gt;repo&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rotonda.docs.nlnetlabs.nl&quot;&gt;docs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;RustWeek: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;rustweek.org&quot;&gt;website&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;NLnet Labs: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nlnetlabs.nl&quot;&gt;website&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NLnetLabs&quot;&gt;GitHub&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;social.nlnetlabs.nl&#x2F;@nlnetlabs&quot;&gt;Mastodon&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Me: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;terts.dev&quot;&gt;website&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tertsdiepraam&quot;&gt;GitHub&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.online&#x2F;@terts&quot;&gt;Mastodon&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;terts.dev&#x2F;links&quot;&gt;more&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Analyzing uutils coreutils dependencies</title>
        <published>2024-09-01T00:00:00+00:00</published>
        <updated>2024-09-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://terts.dev/blog/uutils-dependencies/"/>
        <id>https://terts.dev/blog/uutils-dependencies/</id>
        
        <content type="html" xml:base="https://terts.dev/blog/uutils-dependencies/">&lt;p&gt;uutils is a full rewrite of the GNU coreutils in Rust. That&#x27;s a big effort and the project is quite large. As a result, the dependency graph is huge too. A large dependency graph is hard to reason about and to keep up to date, so it makes development harder.&lt;&#x2F;p&gt;
&lt;p&gt;In this post I want to investigate how we can analyze large dependency trees and how we can keep them in check.This is an exploration and I don&#x27;t pretend to have all the answers, but hopefully this is useful.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-sparked-this-investigation&quot;&gt;What sparked this investigation&lt;&#x2F;h1&gt;
&lt;p&gt;When uutils is posted to HackerNews or some other forum, we always get criticism about our number of dependencies. The lazy version of that criticism is that somebody just looks at the &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt;, counts the entries in it and cries something along the lines of &quot;382 dependencies?! For coreutils?! These devs are useless!&quot;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to verify this number, an easy command is using &lt;code&gt;ripgrep&lt;&#x2F;code&gt; to count the &lt;code&gt;[[package]]&lt;&#x2F;code&gt; entries:
rg -F &#x27;[[package]]&#x27; Cargo.lock --count&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Of course, if you know a bit about &lt;code&gt;cargo&lt;&#x2F;code&gt;, you know that this is not quite fair. There&#x27;s many kinds of dependencies, and only some are important to most people. For example, nobody cares about the number of dependencies that are only used in testing and aren&#x27;t part of the actual shipped binaries. Also, more than 100 of those entries are not dependencies; they are our own crates.&lt;&#x2F;p&gt;
&lt;p&gt;Nevertheless, there are good reasons to care about dependencies:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Dependencies need to be kept up to date in the project&lt;&#x2F;li&gt;
&lt;li&gt;Dependencies need to be maintained&lt;&#x2F;li&gt;
&lt;li&gt;Dependencies need to be reviewed&lt;&#x2F;li&gt;
&lt;li&gt;Dependencies need to be deduplicated (more about that later)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So, I set out to do a more in depth analysis, so that I can decide for myself how bad our dependencies actually are.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-better-dependency-count&quot;&gt;A better dependency count&lt;&#x2F;h1&gt;
&lt;p&gt;Let&#x27;s start with some simple analysis of &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt;. While &lt;code&gt;ripgrep&lt;&#x2F;code&gt; is amazing, &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt; is structured data, so it&#x27;s not the best tool for the job. Instead, I&#x27;ll be using &lt;code&gt;nu&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We can open and load &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt; like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; open Cargo.lock | from toml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;╭─────────┬──────────────────╮&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ package │ [table 382 rows] │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ version │ 3                │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;╰─────────┴──────────────────╯&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: nushell usually parses TOML automatically, but it only does that for files with the &lt;code&gt;.toml&lt;&#x2F;code&gt; extension.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That&#x27;s handy! We get the count for free! Since we don&#x27;t care about the version, let&#x27;s limit ourselves to the &lt;code&gt;package&lt;&#x2F;code&gt; field:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let package = (open Cargo.lock | from toml | get package)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives us a giant table with the following columns:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;checksum&lt;&#x2F;li&gt;
&lt;li&gt;name&lt;&#x2F;li&gt;
&lt;li&gt;source&lt;&#x2F;li&gt;
&lt;li&gt;version&lt;&#x2F;li&gt;
&lt;li&gt;dependencies&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We can now easily filter our own crates out because those do not have a &lt;code&gt;source&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; $package | compact source&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We are now left with a table of 273 external crates. However, some of those are duplicated. I&#x27;ll talk more about that later. For now, let&#x27;s deduplicate.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let $external = ($package | compact source | uniq-by name)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That leaves us with 259 external crates.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;transitive-dependencies&quot;&gt;Transitive dependencies&lt;&#x2F;h1&gt;
&lt;p&gt;So now, let&#x27;s see which ones are direct dependencies and which are transitive. There&#x27;s probably tools for doing this, but I&#x27;m having with &lt;code&gt;nu&lt;&#x2F;code&gt;, so I&#x27;ll stick with it.&lt;&#x2F;p&gt;
&lt;p&gt;First, let&#x27;s get a list of the names of direct dependencies:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let direct = ($package | where source? == null | get dependencies? | flatten | compact | sort | uniq)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This includes our own crates so we have to filter them out. I&#x27;m not a nushell expert so there&#x27;s probably a better way to do this, but this is the command I came up with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let direct_external = ($direct | where {|name| $external | any {|ex| $ex.name == $name}})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives us a list of 94 dependencies, which means we have 165 transitive dependencies.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;counting-projects-instead-of-crates&quot;&gt;Counting projects instead of crates&lt;&#x2F;h1&gt;
&lt;p&gt;So, that&#x27;s about as far as I can go with only &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt;. Luckily, &lt;code&gt;crates.io&lt;&#x2F;code&gt; has an API! Let&#x27;s see if we can use it from &lt;code&gt;nu&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; http get crates.io&#x2F;api&#x2F;v1&#x2F;crates&#x2F;clap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;╭────────────┬────────────────────╮&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ categories │ [table 1 row]      │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ crate      │ {record 19 fields} │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ keywords   │ [table 5 rows]     │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│ versions   │ [table 360 rows]   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;╰────────────┴────────────────────╯&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That seems to work! Let&#x27;s do that for all our external dependencies.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let external2 = (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    $external &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | get name &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | each {|name| &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        print $name; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        http get $&amp;quot;https:&#x2F;&#x2F;crates.io&#x2F;api&#x2F;v1&#x2F;crates&#x2F;($name)&amp;quot; | get crate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Time to apply an assumption: two crates belong to the same &quot;project&quot; if they have the same repository. It&#x27;s not 100% true, but it&#x27;s close enough for me.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; $external2 | uniq-by repository | length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;202&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ok, now let&#x27;s do the same for direct external dependencies:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let direct_external2 = ($direct_external | each {|name| print $name; http get $&amp;quot;https:&#x2F;&#x2F;crates.io&#x2F;api&#x2F;v1&#x2F;crates&#x2F;($name)&amp;quot; | get crate})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; $direct_external2 | uniq-by repository | length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;86&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;who-are-we-depending-on&quot;&gt;Who are we depending on?&lt;&#x2F;h1&gt;
&lt;p&gt;Who&#x27;s in charge of these dependencies? Crates have owners, but I think the github organisation&#x2F;owner is a better indicator of owner in this case. Let&#x27;s see where that leads us.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let owners = ($external2 | compact repository | group-by { get repository | url parse | get path | path split | get 1 })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; $owners | columns | length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;126&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Many owners are well-known and trusted individuals and organisations in the Rust community. Names like &quot;dtolnay&quot;, &quot;BurntSushi&quot;, &quot;matklad&quot;, &quot;seanmonstar&quot;, &quot;rust-cli&quot;, &quot;clap-rs&quot;, &quot;rust-lang&quot;, &quot;chronotope&quot;, &quot;crossbeam-rs&quot; and, of course, &quot;nushell&quot;. However, there are plenty of names that are unfamiliar to me. That does not mean that the project is bad, but it does mean that we need to be more careful with those dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re interested, here are the organisations with the most crates used by uutils:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rust-lang&lt;&#x2F;code&gt;: 21&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;RustCrypto&lt;&#x2F;code&gt;: 11&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;dtolnay&lt;&#x2F;code&gt;: 10&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;microsoft&lt;&#x2F;code&gt;: 9&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;rust-cli&lt;&#x2F;code&gt;: 8&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;BurntSushi&lt;&#x2F;code&gt;: 8&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I got those counts with this command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; $owners | transpose | insert size { get column1 | length } | sort-by size&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;what-s-actually-used-on-unix&quot;&gt;What&#x27;s actually used (on Unix)?&lt;&#x2F;h1&gt;
&lt;p&gt;Coming back to something I talked about before: which dependencies are actually relevant. &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt; includes much more than what we&#x27;re actually using. &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt; contains the entire dependency graph, including dependencies that we are not using, either because they are optional or because we don&#x27;t use the feature of a depedency that enables that dependency. We need a better way to get that info.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily, we have that info in the form of &lt;code&gt;cargo tree&lt;&#x2F;code&gt;. I&#x27;ll let &lt;code&gt;cargo tree&lt;&#x2F;code&gt; print out all the non-dev dependencies, which are the ones that end up in the final binary. I&#x27;ll also limit to the features that can be enabled on Unix and leave out Windows and other platforms. Cargo tree gives us a string output, so parsing it is a bit ugly, but this seems to work:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let used_crates = (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cargo tree -e no-dev --features unix &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | rg &amp;quot;[a-zA-Z_-]+ v[0-9.]+&amp;quot; -o&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | lines&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | each { |line| str substring 0..($line | str index-of &amp;quot; &amp;quot;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | uniq&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Again, let&#x27;s intersect with the external deps:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let used_external = ($external.name | where {|name| $used_crates | any {|used| $used == $name }})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; $used_external | length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;158&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And external &amp;amp; direct:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; let used_direct_external = ($direct_external | where {|name| $used_crates | any {|used| $used == $name }})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; $used_direct_external | length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;70&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;concluding-the-counts&quot;&gt;Concluding the counts&lt;&#x2F;h1&gt;
&lt;p&gt;So we have several &quot;dependency counts&quot; now. Here&#x27;s an overview:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;External crates: 259&lt;&#x2F;li&gt;
&lt;li&gt;External &quot;projects&quot;: 202&lt;&#x2F;li&gt;
&lt;li&gt;External used crates: 158&lt;&#x2F;li&gt;
&lt;li&gt;Direct external crates: 94&lt;&#x2F;li&gt;
&lt;li&gt;Direct external &quot;projects&quot;: 84&lt;&#x2F;li&gt;
&lt;li&gt;Direct external used crates: 70&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Which one you care about is up to you.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;making-a-good-nu-script-for-this&quot;&gt;Making a good nu script for this&lt;&#x2F;h1&gt;
&lt;p&gt;All the commands above we&#x27;re just improvised. We can probably do a bit better.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>uutils&#x2F;coreutils &amp; the quest for compatibility</title>
        <published>2023-10-26T00:00:00+00:00</published>
        <updated>2023-10-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://terts.dev/talks/uutils-delft-meetup/"/>
        <id>https://terts.dev/talks/uutils-delft-meetup/</id>
        
        <content type="html" xml:base="https://terts.dev/talks/uutils-delft-meetup/">&lt;h1 id=&quot;recording&quot;&gt;Recording&lt;&#x2F;h1&gt;
&lt;div class=&quot;wrapper&quot;&gt;
    &lt;iframe width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;aspect-ratio: 16&#x2F;9&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;fu2sxy6OR54&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;h1 id=&quot;links&quot;&gt;Links&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tertsdiepraam&#x2F;&quot;&gt;@tertsdiepraam on GitHub&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.online&#x2F;@terts&quot;&gt;@terts@mastodon.online&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;uutils.github.io&#x2F;&quot;&gt;uutils.github.io&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;uutils&#x2F;coreutils&quot;&gt;uutils&#x2F;coreutils on GitHub&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discord.gg&#x2F;wQVJbvJ&quot;&gt;uutils Discord&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Subtext and Metatext of Code</title>
        <published>2022-11-01T00:00:00+00:00</published>
        <updated>2022-11-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://terts.dev/blog/subtext-and-metatext/"/>
        <id>https://terts.dev/blog/subtext-and-metatext/</id>
        
        <content type="html" xml:base="https://terts.dev/blog/subtext-and-metatext/">&lt;p&gt;Programming relies heavily on implicit information, probably even more than we
would like to admit. When we come across foreign code, we make bold assumptions
and rely on conventions to try to build a mental model of the code. If that&#x27;s
easy to do, we call the code &quot;clear&quot;, but that term is too vague. Instead, I
would like to propose that we borrow some concepts from linguistics: subtext and
metatext.&lt;&#x2F;p&gt;
&lt;p&gt;If we want our code to be understood by others, we need to explain the intent
and context behind the code to the reader. We can do this explicitly via
comments or implicitly, by including signals that hint toward the function of
the code (e.g. choosing clear variable names). I call these hints the &lt;em&gt;subtext&lt;&#x2F;em&gt;
of the code. Similarly, I call the direct explanation via comments &lt;em&gt;metatext&lt;&#x2F;em&gt;,
because they are a part of the code that explains the code and are therefore
self-referential.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;text-subtext-metatext&quot;&gt;Text, subtext &amp;amp; metatext&lt;&#x2F;h1&gt;
&lt;p&gt;Let&#x27;s make these definitions a bit more formal.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;&lt;em&gt;text&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt; is all the source code for a library or application.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;em&gt;subtext&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt; is the context, intent and reasoning behind the code implied
by the text.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;em&gt;metatext&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt; is anything that directly explains the code. For example,
comments and documentation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The code below is basically devoid of subtext and metatext. It is a function
calculating the factorial of a natural number, but nothing in the code hints to
that functionality.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable z-parameter z-function z-language z-python&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    else&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our goal is to change the text such that it includes hints to the intent, that
is, to add the subtext. We can start doing this by choosing better names. The
&lt;code&gt;factorial&lt;&#x2F;code&gt; name explains that this defines a function that computes a
factorial. &lt;code&gt;n&lt;&#x2F;code&gt; implies by convention that the input should be a natural number.
The result is much clearer!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; factorial&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable z-parameter z-function z-language z-python&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    else&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; factorial&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, we can add metatext with comments and type hints. Note that in
statically typed languages, types are a part of the text, not the metatext, but
in this post, I&#x27;m using untypechecked Python where type hints are nothing more
than comments in disguise.&lt;&#x2F;p&gt;
&lt;p&gt;In the metatext, we can explain anything not immediately obvious from the code.
We explicitly state that &lt;code&gt;n&lt;&#x2F;code&gt; should be an &lt;code&gt;int&lt;&#x2F;code&gt; and that &lt;code&gt;n&lt;&#x2F;code&gt; should be a natural
number (i.e. greater than zero). We also state the limitations of the code in
the metatext.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; factorial&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable z-parameter z-function z-language z-python&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    Computes the factorial of a natural number `n`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    Warning: this function will not terminate if `n` is negative.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TODO&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;: Rewrite without recursion for better performance.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; We calculate the factorial by recursion. The base case of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; the recursion comes from the fact that 0! = 1.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    else&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; factorial&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It should be obvious that good code has both good subtext and good metatext. In
most cases, neither is sufficient to convey the intent efficiently. However,
your opinion on what constitutes good sub- or metatext might differ from my
opinion and that&#x27;s okay. It also depends on the context and purpose of the code.
For example, the requirements change depending on whether it is production code
or code written for educational purposes.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;basic-subtext&quot;&gt;Basic subtext&lt;&#x2F;h1&gt;
&lt;p&gt;Now, let&#x27;s explore how we can use subtext to our advantage. We start with this
example of some intentionally bad Python code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;z&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    z&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The code above computes the sum of 3 numbers, but you have to read the full code
to understand it, because the subtext is missing. We can add subtext with better
variable names:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;numbers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;total_sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; number&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; numbers&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    total_sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span&gt; number&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, when you see the variables &lt;code&gt;numbers&lt;&#x2F;code&gt; and &lt;code&gt;total_sum&lt;&#x2F;code&gt;, you can already guess
what the rest of the code is going to do and you only have to check that
assumption. Of course, subtext can also imply incorrect information, for
example, if the variable names do not match the actual computation&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;numbers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;total_product&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; number&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; numbers&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    total_product&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span&gt; number&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Changing variable names is not all we can do to improve subtext. To show our
intent even more clearly, we can call the &lt;code&gt;sum&lt;&#x2F;code&gt; function instead of using a
&lt;code&gt;for&lt;&#x2F;code&gt; loop. The difference is that a &lt;code&gt;for&lt;&#x2F;code&gt; loop is a general construct for all
kinds of loops, but that &lt;code&gt;sum&lt;&#x2F;code&gt; can only be used for, well, summing things. So
&lt;code&gt;sum&lt;&#x2F;code&gt; carries the subtext that our intent is to sum things.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;numbers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;total_sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;numbers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this final version, the code is immediately obvious, because the subtext
supports the text. When people say that code should be &quot;self-documenting&quot;, this
is what they mean: that the subtext resolves most questions about the code.&lt;&#x2F;p&gt;
&lt;p&gt;Note that there are also multiple pieces of the code signalling that we are
summing the numbers (the variable and function name). If there is consistency
between multiple signals, that will help the reader to confidently build a
mental model of the code.&lt;&#x2F;p&gt;
&lt;p&gt;Code organization is also part of the subtext. Putting functions next to each
other in a file can implies that they are linked in some way. Similarly, if a
function is defined far away from where it is called it is taken out of its own
context and therefore harder to understand. This is often the case when there is
some catch-all &lt;code&gt;utils&lt;&#x2F;code&gt; module, which tend to contain a bunch of unrelated
functions.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Basic subtext guidelines&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Give variables, functions and types descriptive names.&lt;&#x2F;li&gt;
&lt;li&gt;Give source files descriptive names.&lt;&#x2F;li&gt;
&lt;li&gt;Break complicated expressions up into several steps (with descriptive names).&lt;&#x2F;li&gt;
&lt;li&gt;Use specialized functions and language constructs instead general functions
and language constructs.&lt;&#x2F;li&gt;
&lt;li&gt;Organize the source files in a logical way.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;conventional-subtext&quot;&gt;Conventional subtext&lt;&#x2F;h1&gt;
&lt;p&gt;Subtext is also about convention, both within a codebase and within the larger
programming community. For example, &lt;code&gt;n&lt;&#x2F;code&gt; implies that a variable holds an integer
and &lt;code&gt;f&lt;&#x2F;code&gt; is often used for functions. Using other names, like &lt;code&gt;b&lt;&#x2F;code&gt; and &lt;code&gt;k&lt;&#x2F;code&gt;, could
be confusing to other programmers. Similarly, using &lt;code&gt;f&lt;&#x2F;code&gt; for an integer would
also be confusing.&lt;&#x2F;p&gt;
&lt;p&gt;Python has two main constructs for applying some function to the elements of a
list: &lt;code&gt;map&lt;&#x2F;code&gt; and list comprehensions.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; double&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable z-parameter z-function z-language z-python&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-annotation z-python&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;numbers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; map&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doubled&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt; list&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;double&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; numbers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; list comprehension&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doubled&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;double&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; numbers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In my experience, most python programmers prefer the list comprehension and will
use that instinctively. So, the list comprehension should be used, even if you
do not agree with that preference (e.g. if you have a background in functional
programming), because it carries subtext by convention established in the Python
community.&lt;&#x2F;p&gt;
&lt;p&gt;However, if &lt;code&gt;map&lt;&#x2F;code&gt;, &lt;code&gt;filter&lt;&#x2F;code&gt; and &lt;code&gt;fold&lt;&#x2F;code&gt; are already used a lot in the codebase,
then the &lt;code&gt;map&lt;&#x2F;code&gt; might be more appropriate, because it carries subtext by
convention established in the codebase itself, overriding the conventions from
the larger Python community.&lt;&#x2F;p&gt;
&lt;p&gt;Conventions are usually difficult to pick up for programmers that are new to a
language or framework, because you usually pick them up over time. Relying too
much on conventional subtext is therefore a pitfall. If you&#x27;re learning a new
technology or codebase it&#x27;s therefore a good idea to study existing code to
figure out the conventions, so you can apply them (in moderation) to your own
code.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Conventional subtext guidelines&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Study the conventions of languages, frameworks and codebases.&lt;&#x2F;li&gt;
&lt;li&gt;Establish and maintain clear conventions (e.g. by using a consistent naming
scheme).&lt;&#x2F;li&gt;
&lt;li&gt;Do not break with convention without a good reason.&lt;&#x2F;li&gt;
&lt;li&gt;Be aware that newcomers might have a hard time understanding code that relies
solely on conventional subtext.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;metatext&quot;&gt;Metatext&lt;&#x2F;h1&gt;
&lt;p&gt;Subtext is often not enough to explain the full context and intent of the code.
For instance, it is really difficult to express assumptions and edge cases in
text and subtext alone. In these cases, we need to resort to metatext. Metatext
is the text in the code that directly explains the code. Some examples are
comments, docstrings and type hints. None of these have any influence on what
the code does, but they exist to explain what the code does and why.&lt;&#x2F;p&gt;
&lt;p&gt;Metatext is constantly in danger of going out of sync with the text, even more
so than subtext. So it&#x27;s important to update documentation along with changes to
the code.&lt;&#x2F;p&gt;
&lt;p&gt;Because comments appear alongside the code, it is only useful when it doesn&#x27;t
repeat information that is already easily gathered from the text or subtext. If
a comment is necessary to explain the basic operation, you should first consider
improving the subtext. But that doesn&#x27;t mean that all code can (or should) be
entirely self-documenting, because context is hard to convey via subtext. Below
is an example of a bad and good comment.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Bad: states the obvious&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Divide x by 100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; &#x2F;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Good: gives reason for division&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Convert percentage into fraction&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; &#x2F;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On the other hand, docstrings should state some things that are obvious from the
code, because we cannot assume that a user of the function will look at the
function body. An ideal docstring contains everything that a caller of a
function might want to know and nothing more.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Metatext guidelines&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use comments mostly for information that cannot be put into subtext (context,
reasoning, etc.).&lt;&#x2F;li&gt;
&lt;li&gt;Docstrings should contain all that a caller needs to know.&lt;&#x2F;li&gt;
&lt;li&gt;Keep metatext up to date with the code.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;Clear code is code where the text, subtext and metatext all support each other
to convey the intent, reasoning and context behind a computation. For every
change you make to code, you should consider how you can update and improve the
subtext and metatext such that your change becomes clear to others.&lt;&#x2F;p&gt;
&lt;hr style=&quot;margin: 2em 0;&quot;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;One might call this code &quot;ironic&quot;, because the text and subtext do not
match, but that concept does not seem particularly useful in the context of
programming. If you know an application for ironic code, please let me know!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Trying to rewrite assert! to assert_eq! with a declarative macro</title>
        <published>2020-06-16T00:00:00+00:00</published>
        <updated>2020-06-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://terts.dev/blog/assert-macro/"/>
        <id>https://terts.dev/blog/assert-macro/</id>
        
        <content type="html" xml:base="https://terts.dev/blog/assert-macro/">&lt;p&gt;Rust has a couple of assert macros in its standard library:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;assert!&lt;&#x2F;code&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;assert_eq!&lt;&#x2F;code&gt;, and&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;assert_ne!&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The first just takes a boolean expression and panics if it evaluates to &lt;code&gt;false&lt;&#x2F;code&gt;.
The other two are just special cases where you pass to arguments and the
assertion checks that whether they are equal or not, respectively, but their
output is more helpful, because they print both operands, instead of just saying
that the assertion failed.&lt;&#x2F;p&gt;
&lt;p&gt;This brings up an interesting question: why can the macro not just detect that
the expression in &lt;code&gt;assert!&lt;&#x2F;code&gt; is &lt;code&gt;A == B&lt;&#x2F;code&gt; or &lt;code&gt;A != B&lt;&#x2F;code&gt; and use &lt;code&gt;assert_eq!&lt;&#x2F;code&gt; or
&lt;code&gt;assert_ne!&lt;&#x2F;code&gt; automatically? In many cases that would be a bit easier to read. Compare these calls:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s not much of a difference, but I like the first one just a bit better.&lt;&#x2F;p&gt;
&lt;p&gt;A proc macro could definitely do this (as for example the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;assert2&quot;&gt;&lt;code&gt;assert2&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; crate proves), but is it also
possible with a declarative macro?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Before you continue, I want to spoil the end: I failed.&lt;&#x2F;strong&gt; I was able to cover the most common cases, but there are expressions which I couldn&#x27;t find a solution for. Still, I think it&#x27;s interesting to investigate why this problem is hard and what techniques I used along the way.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;why-the-simple-solution-does-not-work&quot;&gt;Why the simple solution does not work&lt;&#x2F;h1&gt;
&lt;p&gt;Declarative macros in Rust are basically pattern matches on the token streams of
their input. In theory we could therefore first define a case for &lt;code&gt;==&lt;&#x2F;code&gt;, then one
for &lt;code&gt;!=&lt;&#x2F;code&gt; and finally fall back to the general case. It would look like this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: To make this all work properly in a library, all nested macros need to
be qualified with an absolute path, so that they don&#x27;t need to be imported by
the calling code.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fancy_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; assert_ne!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sadly, the compiler complains with the following error:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;error: `$a:expr` is followed by `==`, which is not allowed for `expr` fragments&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; --&amp;gt; src&#x2F;main.rs:2:14&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2 |     ($a:expr == $b:expr) =&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  |              ^^ not allowed after `expr` fragments&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  = note: allowed there are: `=&amp;gt;`, `,` or `;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We cannot use a &lt;code&gt;==&lt;&#x2F;code&gt; token after an expression, which makes sense because the
&lt;code&gt;==&lt;&#x2F;code&gt; could itself be part of the expression. Technically, it might be possible
to parse with backtracking but the pattern matching for declarative macros is
not that advanced. So it seems like we can&#x27;t use expressions for this purpose.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;matching-on-token-trees&quot;&gt;Matching on token trees&lt;&#x2F;h1&gt;
&lt;p&gt;Luckily, Rust also allows us to match on token trees, which are single tokens or
tokens in matching delimiters like &lt;code&gt;()&lt;&#x2F;code&gt;, &lt;code&gt;[]&lt;&#x2F;code&gt; and &lt;code&gt;{}&lt;&#x2F;code&gt;. So instead of matching
on the expression, we&#x27;ll match on a list of token trees and find the &lt;code&gt;==&lt;&#x2F;code&gt;
ourselves.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the general idea: we have some marker token in the token tree that we
move recursively through the list of token trees. When we encounter a &lt;code&gt;==&lt;&#x2F;code&gt; or
&lt;code&gt;!=&lt;&#x2F;code&gt;, we expand to a &lt;code&gt;assert_eq!&lt;&#x2F;code&gt; or &lt;code&gt;assert_ne!&lt;&#x2F;code&gt; respectively.&lt;&#x2F;p&gt;
&lt;p&gt;Here is our first attempt at this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If the token after the `;` is ==, we use assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If the token after the `;` is !=, we use assert_ne!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert_ne!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If we reached the last token, we use assert!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;last&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;last&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Else we recurse by putting the `;` to the right&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fancy_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tail&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Again, this doesn&#x27;t compile with the following error:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;error: local ambiguity when calling macro `internal_assert`: multiple parsing options: built-in NTs tt (&amp;#39;prev&amp;#39;) or 1 other option.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --&amp;gt; src&#x2F;main.rs:17:56&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;17 |     ($head:tt $($tail:tt)*) =&amp;gt; { internal_assert!($head; $($tail)*) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |                                                        ^&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;46 |     fancy_assert!(5 == 6);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |     --------------------- in this macro invocation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   = note: this error originates in the macro `fancy_assert` (in Nightly builds, run with -Z macro-backtrace for more info)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;error: could not compile `assert_macro` due to previous error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This error is very similar to the last one, because it&#x27;s ambiguous whether the
&lt;code&gt;;&lt;&#x2F;code&gt; should be parsed as a token tree or as the &lt;code&gt;;&lt;&#x2F;code&gt; in the pattern.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-cursed-solution&quot;&gt;A cursed solution&lt;&#x2F;h1&gt;
&lt;p&gt;How can we put that &lt;code&gt;;&lt;&#x2F;code&gt; somewhere unambiguous? Well, we could use every other
position as a marker that could be either &lt;code&gt;,&lt;&#x2F;code&gt; or &lt;code&gt;;&lt;&#x2F;code&gt; an expression like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2 + 4 == 3 + 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;would first be expanded into&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2; +, 4, ==, 3, +, 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then we can match those tokens without the &lt;code&gt;tt&lt;&#x2F;code&gt; pattern!&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what that looks like in code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert_ne!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;last&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;last&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fancy_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tail&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And it works! I was happy with this for a little while, but I still found this
solution to be lacking a bit. The insertion of &lt;code&gt;,&lt;&#x2F;code&gt; just didn&#x27;t sit right with
me. So let&#x27;s try another solution.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;brackets-to-the-rescue&quot;&gt;Brackets to the rescue&lt;&#x2F;h1&gt;
&lt;p&gt;I had kept thinking about this &quot;marker&quot; as the obvious solution, but the fact
that the marker needs to be a token itself makes things difficult. So what
symbol could we use that&#x27;s not a token tree? Let&#x27;s review the definition of a
token tree:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;a single token or tokens in matching delimiters (), [], or {}&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That means the delimiters are special! We can use those! Because we already have
so many &lt;code&gt;()&lt;&#x2F;code&gt; in our rules, I think it&#x27;s best if we make &lt;code&gt;{}&lt;&#x2F;code&gt; our special
symbols.This looks much better than our previous attempt:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If the first token on the right is ==, we use assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If the first token on the right is !=, we use assert_ne!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert_ne!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If we did not encounter `==` or `!=`, we use assert!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If we have tokens left, but it&amp;#39;s not `==` or `!=` move&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; the token to the left braces.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fancy_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tokens&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tokens&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s it! I have tested this with the following expressions:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;5&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; =&amp;gt; assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;5&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; =&amp;gt; assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; =&amp;gt; assert!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type z-rust z-entity z-name z-type&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; =&amp;gt; assert!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Note that we can even use `==` inside the expression, because&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; everything between parentheses is a single token tree.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; =&amp;gt; assert!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;chaining-comparison-operators&quot;&gt;Chaining comparison operators&lt;&#x2F;h1&gt;
&lt;p&gt;However, there is one case we did not think about yet: what if there are
multiple comparison operators in the expression? As it turns out, normal Rust
does not allow that. For example, this expression does not compile:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But in our last version, this does compile:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fancy_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;because it gets rewritten to&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And so the final expression that Rust gets only has one comparison operator.
This seems like it might trip people up, so we should restrict that. This
essentially boils down to asserting that the tokens form a valid expression.&lt;&#x2F;p&gt;
&lt;p&gt;A naive solution would be to only accept expressions as input an then pass that
as tokens to the internal macro (&lt;code&gt;internal_assert!&lt;&#x2F;code&gt; is unchanged from above):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fancy_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It compiles, so I tried to run this with &lt;code&gt;fancy_assert!(5 == 6)&lt;&#x2F;code&gt; and...&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;thread &amp;#39;main&amp;#39; panicked at &amp;#39;assertion failed: 5 == 6&amp;#39;, src&#x2F;main.rs:46:5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Oh, it broke the macro? We have a valid assertion, but we just get the normal
&lt;code&gt;assert!&lt;&#x2F;code&gt;, not &lt;code&gt;assert_eq!&lt;&#x2F;code&gt;. It seems like Rust is somehow changing the tokens
of the expression and it is parsed as a single token tree.&lt;&#x2F;p&gt;
&lt;p&gt;This means that our public macro needs to accept a list of token trees which
need to be passed to the internal macro directly. But, we can still fix it! All
we have to do is make a third macro that only accepts valid expressions!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; is_expr&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fancy_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        is_expr!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;is_expr!&lt;&#x2F;code&gt; macro does not actually generate any code, it just acts as a
guard, which generates this error when we try to chain comparison operators:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;error: comparison operators cannot be chained&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --&amp;gt; src&#x2F;main.rs:50:24&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;50 |     fancy_assert!(true == true == true); &#x2F;&#x2F; =&amp;gt; assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |                        ^^      ^^&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;help: split the comparison into two&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;50 |     fancy_assert!(true == true &amp;amp;&amp;amp; true == true); &#x2F;&#x2F; =&amp;gt; assert_eq!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |                                +++++++&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Problem solved! Right?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;lower-precedence-operators&quot;&gt;Lower precedence operators&lt;&#x2F;h1&gt;
&lt;p&gt;What if we follow the suggestion from the compiler in that last error message? That would not work because it would expand to&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;assert_eq!(true, true &amp;amp;&amp;amp; true == true);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s using the wrong precedence.&lt;&#x2F;p&gt;
&lt;p&gt;So if any of these tokens appear, we need to fall back to &lt;code&gt;assert!&lt;&#x2F;code&gt;. So before we use our &lt;code&gt;interal_assert&lt;&#x2F;code&gt; macro, we can first scan for those tokens. Luckily, we can do that with a similar technique to above.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; scan_lower_precedence&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        internal_assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        scan_lower_precedence!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fancy_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; is_expr!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; scan_lower_precedence!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;if-expressions&quot;&gt;If expressions&lt;&#x2F;h1&gt;
&lt;p&gt;At this point, I really thought I was done and I was ready to put this all in a crate and publish it with great fanfare. But it turned out there were bigger problems than &lt;code&gt;&amp;amp;&amp;amp;&lt;&#x2F;code&gt; and &lt;code&gt;||&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The problem lies with &lt;code&gt;if&lt;&#x2F;code&gt;, &lt;code&gt;match&lt;&#x2F;code&gt;, &lt;code&gt;for&lt;&#x2F;code&gt; &amp;amp; &lt;code&gt;while&lt;&#x2F;code&gt; which can all contain &lt;code&gt;==&lt;&#x2F;code&gt; in their expressions without them being enclosed within delimiters.&lt;&#x2F;p&gt;
&lt;p&gt;I kept trying an I got &lt;code&gt;if&lt;&#x2F;code&gt; expressions mostly working, by using a stack to keep track of nested &lt;code&gt;if&lt;&#x2F;code&gt; statements. Here it is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The first argument is the callback for the macro with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; which we should continue execution when the stack is&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; empty.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The second argument is a stack of if&amp;#39;s and matches that&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; we are currently in. The base case is [] and each item&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; wraps the last [if [if []]].&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If we find an `if`, we push it to the stack, if we find&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; the end we pop it.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; parse_cond&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Found the end of an if expression and if is at the top of the stack&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ident&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;then&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;block&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; else&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;else&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;block&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        parse_cond_or_callback!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;then&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; else&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Found the start of an if expression, put it on the stack and continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ident&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        parse_cond!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; if&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Some other token, we just recurse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ident&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        parse_cond!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;curr&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Expand to the callback if the stack is empty or recurse otherwise&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; parse_cond_or_callback&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ident&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-logical&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ident&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; parse_cond!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;cb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;stack&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; For the macros below other cases are the same as before.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; internal_assert&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        parse_cond!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;internal_assert&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; if&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    &#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; snip &lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;macro_rules!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; scan_lower_precedence&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;tt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        parse_cond!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;scan_lower_precedence&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;prev&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; if&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    &#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; snip &lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This works, but I couldn&#x27;t find a solution for the other cases like &lt;code&gt;match&lt;&#x2F;code&gt; and the code would frankly become too complicated. Instead the &quot;solution&quot; would just be to give a compile-time error if we encounter any &lt;code&gt;match&lt;&#x2F;code&gt;, &lt;code&gt;while&lt;&#x2F;code&gt; or &lt;code&gt;for&lt;&#x2F;code&gt;. It&#x27;s unsatisfying, but maybe someone with better macro-fu skills can do better.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;ending-this-madness&quot;&gt;Ending this madness&lt;&#x2F;h1&gt;
&lt;p&gt;The truth is that were only working with crude approximations of Rust syntax and that it is extremely hard to prove that it covers all cases. Instead of using the code from this post, I recommend &lt;code&gt;assert2&lt;&#x2F;code&gt;, which has this feature and much more and is actually able to parse the code.&lt;&#x2F;p&gt;
&lt;p&gt;I would like to see some of the functionality of &lt;code&gt;assert2&lt;&#x2F;code&gt; in the standard &lt;code&gt;assert&lt;&#x2F;code&gt;, since it would greatly improve the default testing facilities in Rust. I&#x27;d love to see some discussion in that space.&lt;&#x2F;p&gt;
&lt;p&gt;This whole ordeal was a fun experiment though! The technique for parsing is also interesting on its own. The Little Book of Rust Macros has a section on this technique, which they call &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;veykril.github.io&#x2F;tlborm&#x2F;decl-macros&#x2F;patterns&#x2F;tt-muncher.html&quot;&gt;TT munching&lt;&#x2F;a&gt;. It&#x27;s a powerful technique, but also has quadratic time complexity, so use with caution. I can highly recommend looking at the Little Book if you need some advanced macro techniques. They also explain other techniques I used, like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;veykril.github.io&#x2F;tlborm&#x2F;decl-macros&#x2F;patterns&#x2F;callbacks.html&quot;&gt;callbacks&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;veykril.github.io&#x2F;tlborm&#x2F;decl-macros&#x2F;patterns&#x2F;tt-bundling.html&quot;&gt;TT bundling&lt;&#x2F;a&gt;. I foolishly figured these out myself, because I forgot about the book, but I recommend checking it out!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks to Lucas, Jonathan &amp;amp; Arav for solving this problem with me, providing interesting test cases and proofreading drafts of this post.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
